]> git.mxchange.org Git - friendica.git/commitdiff
Merge pull request #2441 from rabuzarus/0704_doxygen_forum
authorTobias Diekershoff <tobias.diekershoff@gmx.net>
Thu, 7 Apr 2016 19:06:32 +0000 (21:06 +0200)
committerTobias Diekershoff <tobias.diekershoff@gmx.net>
Thu, 7 Apr 2016 19:06:32 +0000 (21:06 +0200)
Update ForumManager.php

1060 files changed:
boot.php
database.sql
doc/Accesskeys.md
doc/BBCode.md
doc/Bugs-and-Issues.md
doc/Connectors.md
doc/Developers-Intro.md
doc/Home.md
doc/Settings.md
doc/api.md
doc/autoloader.md [new file with mode: 0644]
doc/database.md
doc/database/db_dsprphotoq.md [deleted file]
doc/database/db_guid.md [deleted file]
doc/database/db_sign.md
doc/de/BBCode.md
doc/de/Settings.md
doc/htconfig.md
doc/smarty3-templates.md [new file with mode: 0644]
doc/snarty3-templates.md [deleted file]
doc/themes.md
include/Contact.php
include/ForumManager.php
include/NotificationsManager.php [new file with mode: 0644]
include/Scrape.php
include/api.php
include/auth.php
include/autoloader.php [new file with mode: 0644]
include/autoloader/ClassLoader.php [new file with mode: 0644]
include/autoloader/LICENSE.composer [new file with mode: 0644]
include/autoloader/autoload_classmap.php [new file with mode: 0644]
include/autoloader/autoload_files.php [new file with mode: 0644]
include/autoloader/autoload_namespaces.php [new file with mode: 0644]
include/autoloader/autoload_psr4.php [new file with mode: 0644]
include/bbcode.php
include/contact_selectors.php
include/conversation.php
include/cron.php
include/cronhooks.php
include/dbstructure.php
include/delivery.php
include/dfrn.php
include/diaspora.php
include/discover_poco.php
include/dsprphotoq.php [deleted file]
include/event.php
include/feed.php
include/follow.php
include/group.php
include/identity.php
include/items.php
include/like.php
include/nav.php
include/network.php
include/notifier.php
include/onepoll.php
include/ostatus.php
include/plaintext.php
include/poller.php
include/post_update.php [new file with mode: 0644]
include/profile_update.php
include/pubsubpublish.php
include/queue.php
include/session.php
include/socgraph.php
include/text.php
include/update_gcontact.php
include/xml.php [new file with mode: 0644]
index.php
library/HTMLPurifier.auto.php [deleted file]
library/HTMLPurifier.autoload.php [deleted file]
library/HTMLPurifier.func.php [deleted file]
library/HTMLPurifier.includes.php [deleted file]
library/HTMLPurifier.kses.php [deleted file]
library/HTMLPurifier.path.php [deleted file]
library/HTMLPurifier.php [deleted file]
library/HTMLPurifier.safe-includes.php [deleted file]
library/HTMLPurifier/AttrCollections.php [deleted file]
library/HTMLPurifier/AttrDef.php [deleted file]
library/HTMLPurifier/AttrDef/CSS.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/AlphaValue.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/Background.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/Border.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/Color.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/Composite.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/Filter.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/Font.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/FontFamily.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/Length.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/ListStyle.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/Multiple.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/Number.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/Percentage.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/TextDecoration.php [deleted file]
library/HTMLPurifier/AttrDef/CSS/URI.php [deleted file]
library/HTMLPurifier/AttrDef/Enum.php [deleted file]
library/HTMLPurifier/AttrDef/HTML/Bool.php [deleted file]
library/HTMLPurifier/AttrDef/HTML/Class.php [deleted file]
library/HTMLPurifier/AttrDef/HTML/Color.php [deleted file]
library/HTMLPurifier/AttrDef/HTML/FrameTarget.php [deleted file]
library/HTMLPurifier/AttrDef/HTML/ID.php [deleted file]
library/HTMLPurifier/AttrDef/HTML/Length.php [deleted file]
library/HTMLPurifier/AttrDef/HTML/LinkTypes.php [deleted file]
library/HTMLPurifier/AttrDef/HTML/MultiLength.php [deleted file]
library/HTMLPurifier/AttrDef/HTML/Nmtokens.php [deleted file]
library/HTMLPurifier/AttrDef/HTML/Pixels.php [deleted file]
library/HTMLPurifier/AttrDef/Integer.php [deleted file]
library/HTMLPurifier/AttrDef/Lang.php [deleted file]
library/HTMLPurifier/AttrDef/Switch.php [deleted file]
library/HTMLPurifier/AttrDef/Text.php [deleted file]
library/HTMLPurifier/AttrDef/URI.php [deleted file]
library/HTMLPurifier/AttrDef/URI/Email.php [deleted file]
library/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php [deleted file]
library/HTMLPurifier/AttrDef/URI/Host.php [deleted file]
library/HTMLPurifier/AttrDef/URI/IPv4.php [deleted file]
library/HTMLPurifier/AttrDef/URI/IPv6.php [deleted file]
library/HTMLPurifier/AttrTransform.php [deleted file]
library/HTMLPurifier/AttrTransform/Background.php [deleted file]
library/HTMLPurifier/AttrTransform/BdoDir.php [deleted file]
library/HTMLPurifier/AttrTransform/BgColor.php [deleted file]
library/HTMLPurifier/AttrTransform/BoolToCSS.php [deleted file]
library/HTMLPurifier/AttrTransform/Border.php [deleted file]
library/HTMLPurifier/AttrTransform/EnumToCSS.php [deleted file]
library/HTMLPurifier/AttrTransform/ImgRequired.php [deleted file]
library/HTMLPurifier/AttrTransform/ImgSpace.php [deleted file]
library/HTMLPurifier/AttrTransform/Input.php [deleted file]
library/HTMLPurifier/AttrTransform/Lang.php [deleted file]
library/HTMLPurifier/AttrTransform/Length.php [deleted file]
library/HTMLPurifier/AttrTransform/Name.php [deleted file]
library/HTMLPurifier/AttrTransform/NameSync.php [deleted file]
library/HTMLPurifier/AttrTransform/SafeEmbed.php [deleted file]
library/HTMLPurifier/AttrTransform/SafeObject.php [deleted file]
library/HTMLPurifier/AttrTransform/SafeParam.php [deleted file]
library/HTMLPurifier/AttrTransform/ScriptRequired.php [deleted file]
library/HTMLPurifier/AttrTransform/Textarea.php [deleted file]
library/HTMLPurifier/AttrTypes.php [deleted file]
library/HTMLPurifier/AttrValidator.php [deleted file]
library/HTMLPurifier/Bootstrap.php [deleted file]
library/HTMLPurifier/CSSDefinition.php [deleted file]
library/HTMLPurifier/ChildDef.php [deleted file]
library/HTMLPurifier/ChildDef/Chameleon.php [deleted file]
library/HTMLPurifier/ChildDef/Custom.php [deleted file]
library/HTMLPurifier/ChildDef/Empty.php [deleted file]
library/HTMLPurifier/ChildDef/Optional.php [deleted file]
library/HTMLPurifier/ChildDef/Required.php [deleted file]
library/HTMLPurifier/ChildDef/StrictBlockquote.php [deleted file]
library/HTMLPurifier/ChildDef/Table.php [deleted file]
library/HTMLPurifier/Config.php [deleted file]
library/HTMLPurifier/ConfigSchema.php [deleted file]
library/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php [deleted file]
library/HTMLPurifier/ConfigSchema/Builder/Xml.php [deleted file]
library/HTMLPurifier/ConfigSchema/Exception.php [deleted file]
library/HTMLPurifier/ConfigSchema/Interchange.php [deleted file]
library/HTMLPurifier/ConfigSchema/Interchange/Directive.php [deleted file]
library/HTMLPurifier/ConfigSchema/Interchange/Id.php [deleted file]
library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php [deleted file]
library/HTMLPurifier/ConfigSchema/Validator.php [deleted file]
library/HTMLPurifier/ConfigSchema/ValidatorAtom.php [deleted file]
library/HTMLPurifier/ConfigSchema/schema.ser [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Core.Language.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt [deleted file]
library/HTMLPurifier/ConfigSchema/schema/info.ini [deleted file]
library/HTMLPurifier/ContentSets.php [deleted file]
library/HTMLPurifier/Context.php [deleted file]
library/HTMLPurifier/Definition.php [deleted file]
library/HTMLPurifier/DefinitionCache.php [deleted file]
library/HTMLPurifier/DefinitionCache/Decorator.php [deleted file]
library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php [deleted file]
library/HTMLPurifier/DefinitionCache/Decorator/Memory.php [deleted file]
library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in [deleted file]
library/HTMLPurifier/DefinitionCache/Null.php [deleted file]
library/HTMLPurifier/DefinitionCache/Serializer.php [deleted file]
library/HTMLPurifier/DefinitionCache/Serializer/README [deleted file]
library/HTMLPurifier/DefinitionCacheFactory.php [deleted file]
library/HTMLPurifier/Doctype.php [deleted file]
library/HTMLPurifier/DoctypeRegistry.php [deleted file]
library/HTMLPurifier/ElementDef.php [deleted file]
library/HTMLPurifier/Encoder.php [deleted file]
library/HTMLPurifier/EntityLookup.php [deleted file]
library/HTMLPurifier/EntityLookup/entities.ser [deleted file]
library/HTMLPurifier/EntityParser.php [deleted file]
library/HTMLPurifier/ErrorCollector.php [deleted file]
library/HTMLPurifier/ErrorStruct.php [deleted file]
library/HTMLPurifier/Exception.php [deleted file]
library/HTMLPurifier/Filter.php [deleted file]
library/HTMLPurifier/Filter/ExtractStyleBlocks.php [deleted file]
library/HTMLPurifier/Filter/YouTube.php [deleted file]
library/HTMLPurifier/Generator.php [deleted file]
library/HTMLPurifier/HTMLDefinition.php [deleted file]
library/HTMLPurifier/HTMLModule.php [deleted file]
library/HTMLPurifier/HTMLModule/Bdo.php [deleted file]
library/HTMLPurifier/HTMLModule/CommonAttributes.php [deleted file]
library/HTMLPurifier/HTMLModule/Edit.php [deleted file]
library/HTMLPurifier/HTMLModule/Forms.php [deleted file]
library/HTMLPurifier/HTMLModule/Hypertext.php [deleted file]
library/HTMLPurifier/HTMLModule/Image.php [deleted file]
library/HTMLPurifier/HTMLModule/Legacy.php [deleted file]
library/HTMLPurifier/HTMLModule/List.php [deleted file]
library/HTMLPurifier/HTMLModule/Name.php [deleted file]
library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php [deleted file]
library/HTMLPurifier/HTMLModule/Object.php [deleted file]
library/HTMLPurifier/HTMLModule/Presentation.php [deleted file]
library/HTMLPurifier/HTMLModule/Proprietary.php [deleted file]
library/HTMLPurifier/HTMLModule/Ruby.php [deleted file]
library/HTMLPurifier/HTMLModule/SafeEmbed.php [deleted file]
library/HTMLPurifier/HTMLModule/SafeObject.php [deleted file]
library/HTMLPurifier/HTMLModule/Scripting.php [deleted file]
library/HTMLPurifier/HTMLModule/StyleAttribute.php [deleted file]
library/HTMLPurifier/HTMLModule/Tables.php [deleted file]
library/HTMLPurifier/HTMLModule/Target.php [deleted file]
library/HTMLPurifier/HTMLModule/Text.php [deleted file]
library/HTMLPurifier/HTMLModule/Tidy.php [deleted file]
library/HTMLPurifier/HTMLModule/Tidy/Name.php [deleted file]
library/HTMLPurifier/HTMLModule/Tidy/Proprietary.php [deleted file]
library/HTMLPurifier/HTMLModule/Tidy/Strict.php [deleted file]
library/HTMLPurifier/HTMLModule/Tidy/Transitional.php [deleted file]
library/HTMLPurifier/HTMLModule/Tidy/XHTML.php [deleted file]
library/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php [deleted file]
library/HTMLPurifier/HTMLModule/XMLCommonAttributes.php [deleted file]
library/HTMLPurifier/HTMLModuleManager.php [deleted file]
library/HTMLPurifier/IDAccumulator.php [deleted file]
library/HTMLPurifier/Injector.php [deleted file]
library/HTMLPurifier/Injector/AutoParagraph.php [deleted file]
library/HTMLPurifier/Injector/DisplayLinkURI.php [deleted file]
library/HTMLPurifier/Injector/Linkify.php [deleted file]
library/HTMLPurifier/Injector/PurifierLinkify.php [deleted file]
library/HTMLPurifier/Injector/RemoveEmpty.php [deleted file]
library/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php [deleted file]
library/HTMLPurifier/Injector/SafeObject.php [deleted file]
library/HTMLPurifier/Language.php [deleted file]
library/HTMLPurifier/Language/classes/en-x-test.php [deleted file]
library/HTMLPurifier/Language/messages/en-x-test.php [deleted file]
library/HTMLPurifier/Language/messages/en-x-testmini.php [deleted file]
library/HTMLPurifier/Language/messages/en.php [deleted file]
library/HTMLPurifier/LanguageFactory.php [deleted file]
library/HTMLPurifier/Length.php [deleted file]
library/HTMLPurifier/Lexer.php [deleted file]
library/HTMLPurifier/Lexer/DOMLex.php [deleted file]
library/HTMLPurifier/Lexer/DirectLex.php [deleted file]
library/HTMLPurifier/Lexer/PEARSax3.php [deleted file]
library/HTMLPurifier/Lexer/PH5P.php [deleted file]
library/HTMLPurifier/PercentEncoder.php [deleted file]
library/HTMLPurifier/Printer.php [deleted file]
library/HTMLPurifier/Printer/CSSDefinition.php [deleted file]
library/HTMLPurifier/Printer/ConfigForm.css [deleted file]
library/HTMLPurifier/Printer/ConfigForm.js [deleted file]
library/HTMLPurifier/Printer/ConfigForm.php [deleted file]
library/HTMLPurifier/Printer/HTMLDefinition.php [deleted file]
library/HTMLPurifier/PropertyList.php [deleted file]
library/HTMLPurifier/PropertyListIterator.php [deleted file]
library/HTMLPurifier/Strategy.php [deleted file]
library/HTMLPurifier/Strategy/Composite.php [deleted file]
library/HTMLPurifier/Strategy/Core.php [deleted file]
library/HTMLPurifier/Strategy/FixNesting.php [deleted file]
library/HTMLPurifier/Strategy/MakeWellFormed.php [deleted file]
library/HTMLPurifier/Strategy/RemoveForeignElements.php [deleted file]
library/HTMLPurifier/Strategy/ValidateAttributes.php [deleted file]
library/HTMLPurifier/StringHash.php [deleted file]
library/HTMLPurifier/StringHashParser.php [deleted file]
library/HTMLPurifier/TagTransform.php [deleted file]
library/HTMLPurifier/TagTransform/Font.php [deleted file]
library/HTMLPurifier/TagTransform/Simple.php [deleted file]
library/HTMLPurifier/Token.php [deleted file]
library/HTMLPurifier/Token/Comment.php [deleted file]
library/HTMLPurifier/Token/Empty.php [deleted file]
library/HTMLPurifier/Token/End.php [deleted file]
library/HTMLPurifier/Token/Start.php [deleted file]
library/HTMLPurifier/Token/Tag.php [deleted file]
library/HTMLPurifier/Token/Text.php [deleted file]
library/HTMLPurifier/TokenFactory.php [deleted file]
library/HTMLPurifier/URI.php [deleted file]
library/HTMLPurifier/URIDefinition.php [deleted file]
library/HTMLPurifier/URIFilter.php [deleted file]
library/HTMLPurifier/URIFilter/DisableExternal.php [deleted file]
library/HTMLPurifier/URIFilter/DisableExternalResources.php [deleted file]
library/HTMLPurifier/URIFilter/HostBlacklist.php [deleted file]
library/HTMLPurifier/URIFilter/MakeAbsolute.php [deleted file]
library/HTMLPurifier/URIFilter/Munge.php [deleted file]
library/HTMLPurifier/URIParser.php [deleted file]
library/HTMLPurifier/URIScheme.php [deleted file]
library/HTMLPurifier/URIScheme/data.php [deleted file]
library/HTMLPurifier/URIScheme/ftp.php [deleted file]
library/HTMLPurifier/URIScheme/http.php [deleted file]
library/HTMLPurifier/URIScheme/https.php [deleted file]
library/HTMLPurifier/URIScheme/mailto.php [deleted file]
library/HTMLPurifier/URIScheme/news.php [deleted file]
library/HTMLPurifier/URIScheme/nntp.php [deleted file]
library/HTMLPurifier/URISchemeRegistry.php [deleted file]
library/HTMLPurifier/UnitConverter.php [deleted file]
library/HTMLPurifier/VarParser.php [deleted file]
library/HTMLPurifier/VarParser/Flexible.php [deleted file]
library/HTMLPurifier/VarParser/Native.php [deleted file]
library/HTMLPurifier/VarParserException.php [deleted file]
library/ezyang/htmlpurifier/CREDITS [new file with mode: 0644]
library/ezyang/htmlpurifier/INSTALL [new file with mode: 0644]
library/ezyang/htmlpurifier/INSTALL.fr.utf8 [new file with mode: 0644]
library/ezyang/htmlpurifier/LICENSE [new file with mode: 0644]
library/ezyang/htmlpurifier/NEWS [new file with mode: 0644]
library/ezyang/htmlpurifier/README [new file with mode: 0644]
library/ezyang/htmlpurifier/TODO [new file with mode: 0644]
library/ezyang/htmlpurifier/VERSION [new file with mode: 0644]
library/ezyang/htmlpurifier/WHATSNEW [new file with mode: 0644]
library/ezyang/htmlpurifier/WYSIWYG [new file with mode: 0644]
library/ezyang/htmlpurifier/composer.json [new file with mode: 0644]
library/ezyang/htmlpurifier/extras/ConfigDoc/HTMLXSLTProcessor.php [new file with mode: 0644]
library/ezyang/htmlpurifier/extras/FSTools.php [new file with mode: 0644]
library/ezyang/htmlpurifier/extras/FSTools/File.php [new file with mode: 0644]
library/ezyang/htmlpurifier/extras/HTMLPurifierExtras.auto.php [new file with mode: 0644]
library/ezyang/htmlpurifier/extras/HTMLPurifierExtras.autoload.php [new file with mode: 0644]
library/ezyang/htmlpurifier/extras/HTMLPurifierExtras.php [new file with mode: 0644]
library/ezyang/htmlpurifier/extras/README [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier.auto.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier.autoload.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier.composer.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier.func.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier.includes.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier.kses.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier.path.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier.safe-includes.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Arborize.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrCollections.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Background.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Border.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Color.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Composite.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Filter.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Font.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Ident.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Length.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ListStyle.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Multiple.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Number.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Percentage.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/TextDecoration.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/URI.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Clone.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Enum.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Bool.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Class.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Color.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/ID.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Length.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/LinkTypes.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/MultiLength.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Nmtokens.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Pixels.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Integer.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Lang.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Switch.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Text.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv4.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv6.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Background.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BdoDir.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BgColor.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BoolToCSS.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Border.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/EnumToCSS.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgRequired.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgSpace.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Input.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Lang.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Length.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Name.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/NameSync.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Nofollow.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeEmbed.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeObject.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeParam.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ScriptRequired.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetBlank.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Textarea.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTypes.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/AttrValidator.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Bootstrap.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/CSSDefinition.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Chameleon.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Custom.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Empty.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/List.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Optional.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Required.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/StrictBlockquote.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Config.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/Xml.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Exception.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Directive.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Id.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Validator.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema.ser [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.Predicate.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedFonts.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Trusted.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AllowHostnameUnderscore.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DisableExcludes.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EnableIDNA.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Language.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedComments.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedCommentsRegexp.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Nofollow.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeIframe.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeScripting.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetBlank.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FixInnerHTML.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.SafeIframeRegexp.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/info.ini [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ContentSets.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Context.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Definition.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Memory.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Null.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/README [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCacheFactory.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Doctype.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/DoctypeRegistry.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ElementDef.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Encoder.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/EntityParser.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ErrorCollector.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/ErrorStruct.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Exception.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Filter.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Generator.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Bdo.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/CommonAttributes.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Edit.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Forms.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Hypertext.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Iframe.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Image.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Legacy.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/List.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Name.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Nofollow.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Object.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Presentation.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Proprietary.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Ruby.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeScripting.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Scripting.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/StyleAttribute.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tables.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Target.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetBlank.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Text.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Name.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Proprietary.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Strict.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Transitional.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/XHTML.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/XMLCommonAttributes.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/IDAccumulator.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Injector.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/AutoParagraph.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/DisplayLinkURI.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/Linkify.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/PurifierLinkify.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/RemoveEmpty.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/SafeObject.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Language.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Language/classes/en-x-test.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Language/messages/en-x-test.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Language/messages/en-x-testmini.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Language/messages/en.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/LanguageFactory.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Length.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Lexer.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DirectLex.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Node.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Node/Comment.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Node/Element.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Node/Text.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/PercentEncoder.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Printer.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/PropertyList.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Queue.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Composite.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Core.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/StringHash.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/StringHashParser.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Font.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Token.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Comment.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Empty.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Token/End.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Start.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Tag.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Text.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/TokenFactory.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URI.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIDefinition.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternal.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIParser.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/data.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/file.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/http.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/https.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/mailto.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/news.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/UnitConverter.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/VarParserException.php [new file with mode: 0644]
library/ezyang/htmlpurifier/library/HTMLPurifier/Zipper.php [new file with mode: 0644]
library/ezyang/htmlpurifier/package.php [new file with mode: 0644]
library/ezyang/htmlpurifier/phpdoc.ini [new file with mode: 0644]
library/ezyang/htmlpurifier/plugins/modx.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/plugins/phorum/.gitignore [new file with mode: 0644]
library/ezyang/htmlpurifier/plugins/phorum/Changelog [new file with mode: 0644]
library/ezyang/htmlpurifier/plugins/phorum/INSTALL [new file with mode: 0644]
library/ezyang/htmlpurifier/plugins/phorum/README [new file with mode: 0644]
library/ezyang/htmlpurifier/plugins/phorum/config.default.php [new file with mode: 0644]
library/ezyang/htmlpurifier/plugins/phorum/htmlpurifier.php [new file with mode: 0644]
library/ezyang/htmlpurifier/plugins/phorum/info.txt [new file with mode: 0644]
library/ezyang/htmlpurifier/plugins/phorum/init-config.php [new file with mode: 0644]
library/ezyang/htmlpurifier/plugins/phorum/migrate.bbcode.php [new file with mode: 0644]
library/ezyang/htmlpurifier/plugins/phorum/settings.php [new file with mode: 0644]
library/ezyang/htmlpurifier/plugins/phorum/settings/form.php [new file with mode: 0644]
library/ezyang/htmlpurifier/plugins/phorum/settings/migrate-sigs-form.php [new file with mode: 0644]
library/ezyang/htmlpurifier/plugins/phorum/settings/migrate-sigs.php [new file with mode: 0644]
library/ezyang/htmlpurifier/plugins/phorum/settings/save.php [new file with mode: 0644]
library/ezyang/htmlpurifier/release1-update.php [new file with mode: 0644]
library/ezyang/htmlpurifier/release2-tag.php [new file with mode: 0644]
library/ezyang/htmlpurifier/test-settings.sample.php [new file with mode: 0644]
library/simplepie/LICENSE.txt [deleted file]
library/simplepie/README.markdown [deleted file]
library/simplepie/compatibility_test/COMPATIBILITY README.txt [deleted file]
library/simplepie/compatibility_test/sp_compatibility_test.php [deleted file]
library/simplepie/create.php [deleted file]
library/simplepie/db.sql [deleted file]
library/simplepie/demo/cli_test.php [deleted file]
library/simplepie/demo/for_the_demo/alternate_favicon.png [deleted file]
library/simplepie/demo/for_the_demo/background_blockquote.png [deleted file]
library/simplepie/demo/for_the_demo/background_menuitem.gif [deleted file]
library/simplepie/demo/for_the_demo/background_menuitem_off.gif [deleted file]
library/simplepie/demo/for_the_demo/background_menuitem_shadow.gif [deleted file]
library/simplepie/demo/for_the_demo/favicons/alternate.png [deleted file]
library/simplepie/demo/for_the_demo/favicons/blinklist.png [deleted file]
library/simplepie/demo/for_the_demo/favicons/blogmarks.png [deleted file]
library/simplepie/demo/for_the_demo/favicons/delicious.png [deleted file]
library/simplepie/demo/for_the_demo/favicons/digg.png [deleted file]
library/simplepie/demo/for_the_demo/favicons/magnolia.png [deleted file]
library/simplepie/demo/for_the_demo/favicons/myweb2.png [deleted file]
library/simplepie/demo/for_the_demo/favicons/newsvine.png [deleted file]
library/simplepie/demo/for_the_demo/favicons/reddit.png [deleted file]
library/simplepie/demo/for_the_demo/favicons/segnalo.png [deleted file]
library/simplepie/demo/for_the_demo/favicons/simpy.png [deleted file]
library/simplepie/demo/for_the_demo/favicons/spurl.png [deleted file]
library/simplepie/demo/for_the_demo/favicons/technorati.png [deleted file]
library/simplepie/demo/for_the_demo/favicons/wists.png [deleted file]
library/simplepie/demo/for_the_demo/feed.png [deleted file]
library/simplepie/demo/for_the_demo/logo_simplepie_demo.png [deleted file]
library/simplepie/demo/for_the_demo/lucida-grande-bold.swf [deleted file]
library/simplepie/demo/for_the_demo/mediaplayer.swf [deleted file]
library/simplepie/demo/for_the_demo/mediaplayer_readme.htm [deleted file]
library/simplepie/demo/for_the_demo/mini_podcast.png [deleted file]
library/simplepie/demo/for_the_demo/place_audio.png [deleted file]
library/simplepie/demo/for_the_demo/place_video.png [deleted file]
library/simplepie/demo/for_the_demo/sIFR-print.css [deleted file]
library/simplepie/demo/for_the_demo/sIFR-screen.css [deleted file]
library/simplepie/demo/for_the_demo/sifr-config.js [deleted file]
library/simplepie/demo/for_the_demo/sifr.js [deleted file]
library/simplepie/demo/for_the_demo/simplepie.css [deleted file]
library/simplepie/demo/for_the_demo/sleight.js [deleted file]
library/simplepie/demo/for_the_demo/source_files/place_audio_fireworksfile.png [deleted file]
library/simplepie/demo/for_the_demo/source_files/place_video_fireworksfile.png [deleted file]
library/simplepie/demo/for_the_demo/source_files/sIFR-r245/SifrStyleSheet.as [deleted file]
library/simplepie/demo/for_the_demo/source_files/sIFR-r245/_README_.txt [deleted file]
library/simplepie/demo/for_the_demo/source_files/sIFR-r245/options.as [deleted file]
library/simplepie/demo/for_the_demo/source_files/sIFR-r245/sIFR.as [deleted file]
library/simplepie/demo/for_the_demo/source_files/sIFR-r245/sifr.fla [deleted file]
library/simplepie/demo/for_the_demo/top_gradient.gif [deleted file]
library/simplepie/demo/for_the_demo/verdana.swf [deleted file]
library/simplepie/demo/for_the_demo/yanone-kaffeesatz-bold.swf [deleted file]
library/simplepie/demo/handler_image.php [deleted file]
library/simplepie/demo/index.php [deleted file]
library/simplepie/demo/minimalistic.php [deleted file]
library/simplepie/demo/multifeeds.php [deleted file]
library/simplepie/demo/test.php [deleted file]
library/simplepie/idn/LICENCE [deleted file]
library/simplepie/idn/ReadMe.txt [deleted file]
library/simplepie/idn/idna_convert.class.php [deleted file]
library/simplepie/idn/npdata.ser [deleted file]
library/simplepie/simplepie.inc [deleted file]
mod/_well_known.php
mod/acctlink.php
mod/acl.php
mod/admin.php
mod/allfriends.php
mod/amcd.php
mod/api.php
mod/apps.php
mod/attach.php
mod/babel.php
mod/bookmarklet.php
mod/cb.php
mod/common.php
mod/community.php
mod/contactgroup.php
mod/contacts.php
mod/content.php
mod/credits.php
mod/crepair.php
mod/delegate.php
mod/dfrn_confirm.php
mod/dfrn_notify.php
mod/dfrn_poll.php
mod/dfrn_request.php
mod/directory.php
mod/dirfind.php
mod/display.php
mod/editpost.php
mod/events.php
mod/fbrowser.php
mod/filer.php
mod/filerm.php
mod/follow.php
mod/friendica.php
mod/fsuggest.php
mod/group.php
mod/hcard.php
mod/help.php
mod/hostxrd.php
mod/ignored.php
mod/install.php
mod/invite.php
mod/item.php
mod/like.php
mod/localtime.php
mod/lockview.php
mod/login.php
mod/lostpass.php
mod/maintenance.php
mod/manage.php
mod/match.php
mod/message.php
mod/modexp.php
mod/mood.php
mod/msearch.php
mod/navigation.php
mod/network.php
mod/newmember.php
mod/nodeinfo.php
mod/nogroup.php
mod/noscrape.php
mod/notes.php
mod/notice.php
mod/notifications.php
mod/notify.php
mod/oembed.php
mod/oexchange.php
mod/openid.php
mod/opensearch.php
mod/ostatus_subscribe.php
mod/p.php
mod/parse_url.php
mod/photo.php
mod/photos.php
mod/ping.php
mod/poco.php
mod/poke.php
mod/post.php
mod/pretheme.php
mod/probe.php
mod/profile.php
mod/profile_photo.php
mod/profiles.php
mod/profperm.php
mod/proxy.php
mod/pubsub.php
mod/pubsubhubbub.php
mod/qsearch.php
mod/randprof.php
mod/receive.php
mod/redir.php
mod/regmod.php
mod/removeme.php
mod/repair_ostatus.php
mod/rsd_xml.php
mod/salmon.php
mod/search.php
mod/session.php
mod/settings.php
mod/share.php
mod/smilies.php
mod/starred.php
mod/statistics_json.php
mod/subthread.php
mod/suggest.php
mod/tagger.php
mod/tagrm.php
mod/toggle_mobile.php
mod/uexport.php
mod/uimport.php
mod/update_community.php
mod/update_display.php
mod/update_network.php
mod/update_notes.php
mod/update_profile.php
mod/videos.php
mod/view.php
mod/viewcontacts.php
mod/viewsrc.php
mod/wall_attach.php
mod/wall_upload.php
mod/wallmessage.php
mod/webfinger.php
mod/xrd.php
object/Item.php
util/README
util/createdoxygen.php [new file with mode: 0644]
util/make_credits.py
util/messages.po
view/default.php
view/global.css
view/it/messages.po
view/it/strings.php
view/ru/messages.po
view/ru/strings.php
view/templates/admin_federation.tpl
view/templates/admin_site.tpl
view/templates/contact_edit.tpl
view/templates/dfrn_request.tpl
view/templates/diasp_dec_hdr.tpl [deleted file]
view/templates/diaspora_comment.tpl [deleted file]
view/templates/diaspora_comment_relay.tpl [deleted file]
view/templates/diaspora_conversation.tpl [deleted file]
view/templates/diaspora_like.tpl [deleted file]
view/templates/diaspora_like_relay.tpl [deleted file]
view/templates/diaspora_message.tpl [deleted file]
view/templates/diaspora_photo.tpl [deleted file]
view/templates/diaspora_post.tpl [deleted file]
view/templates/diaspora_profile.tpl [deleted file]
view/templates/diaspora_relay_retraction.tpl [deleted file]
view/templates/diaspora_relayable_retraction.tpl [deleted file]
view/templates/diaspora_reshare.tpl [deleted file]
view/templates/diaspora_retract.tpl [deleted file]
view/templates/diaspora_share.tpl [deleted file]
view/templates/diaspora_signed_retract.tpl [deleted file]
view/templates/field_checkbox.tpl
view/templates/field_combobox.tpl
view/templates/field_input.tpl
view/templates/field_intcheckbox.tpl
view/templates/field_openid.tpl
view/templates/field_password.tpl
view/templates/field_radio.tpl
view/templates/field_richtext.tpl
view/templates/field_select.tpl
view/templates/field_select_raw.tpl
view/templates/field_textarea.tpl
view/templates/field_themeselect.tpl
view/templates/field_yesno.tpl
view/templates/head.tpl
view/templates/login.tpl
view/templates/notifications.tpl
view/templates/uexport.tpl
view/theme/decaf-mobile/theme.php
view/theme/duepuntozero/style.css
view/theme/duepuntozero/theme.php
view/theme/facepark/theme.php
view/theme/frost-mobile/default.php
view/theme/frost-mobile/js/main.js
view/theme/frost-mobile/style.css
view/theme/frost-mobile/templates/contact_edit.tpl
view/theme/frost-mobile/theme.php
view/theme/frost/default.php
view/theme/frost/style.css
view/theme/frost/templates/contact_edit.tpl
view/theme/frost/theme.php
view/theme/quattro/dark/style.css
view/theme/quattro/green/style.css
view/theme/quattro/lilac/style.css
view/theme/quattro/quattro.less
view/theme/quattro/theme.php
view/theme/smoothly/default.php
view/theme/smoothly/style.css
view/theme/smoothly/theme.php
view/theme/vier/css/font2.css
view/theme/vier/dark.css
view/theme/vier/font/FontAwesome.otf
view/theme/vier/font/fontawesome-webfont.eot [changed mode: 0755->0644]
view/theme/vier/font/fontawesome-webfont.svg [changed mode: 0755->0644]
view/theme/vier/font/fontawesome-webfont.ttf [changed mode: 0755->0644]
view/theme/vier/font/fontawesome-webfont.woff [changed mode: 0755->0644]
view/theme/vier/font/fontawesome-webfont.woff2 [new file with mode: 0644]
view/theme/vier/style.css
view/theme/vier/templates/contact_edit.tpl [new file with mode: 0644]
view/theme/vier/templates/wall_thread.tpl
view/theme/vier/theme.php

index 4ef30eadac888d2752e558ac93107cde9e4bb7a1..58b4bc0983983f820e4756a5a97f14c0686785c5 100644 (file)
--- a/boot.php
+++ b/boot.php
@@ -6,17 +6,19 @@
 
 /**
  * Friendica
- * 
+ *
  * Friendica is a communications platform for integrated social communications
  * utilising decentralised communications and linkage to several indie social
  * projects - as well as popular mainstream providers.
- * 
+ *
  * Our mission is to free our friends and families from the clutches of
  * data-harvesting corporations, and pave the way to a future where social
  * communications are free and open and flow between alternate providers as
  * easily as email does today.
  */
 
+require_once('include/autoloader.php');
+
 require_once('include/config.php');
 require_once('include/network.php');
 require_once('include/plugin.php');
@@ -28,7 +30,7 @@ require_once('include/cache.php');
 require_once('library/Mobile_Detect/Mobile_Detect.php');
 require_once('include/features.php');
 require_once('include/identity.php');
-
+require_once('include/pidfile.php');
 require_once('update.php');
 require_once('include/dbstructure.php');
 
@@ -463,11 +465,12 @@ class App {
        public  $plugins;
        public  $apps = array();
        public  $identities;
-       public  $is_mobile;
-       public  $is_tablet;
+       public  $is_mobile = false;
+       public  $is_tablet = false;
        public  $is_friendica_app;
        public  $performance = array();
        public  $callstack = array();
+       public  $theme_info = array();
 
        public $nav_sel;
 
@@ -588,15 +591,6 @@ class App {
                if(x($_SERVER,'SERVER_NAME')) {
                        $this->hostname = $_SERVER['SERVER_NAME'];
 
-                       // See bug 437 - this didn't work so disabling it
-                       //if(stristr($this->hostname,'xn--')) {
-                               // PHP or webserver may have converted idn to punycode, so
-                               // convert punycode back to utf-8
-                       //      require_once('library/simplepie/idn/idna_convert.class.php');
-                       //      $x = new idna_convert();
-                       //      $this->hostname = $x->decode($_SERVER['SERVER_NAME']);
-                       //}
-
                        if(x($_SERVER,'SERVER_PORT') && $_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443)
                                $this->hostname .= ':' . $_SERVER['SERVER_PORT'];
                        /*
@@ -862,11 +856,11 @@ class App {
 
                $shortcut_icon = get_config("system", "shortcut_icon");
                if ($shortcut_icon == "")
-                       $shortcut_icon = $this->get_baseurl()."/images/friendica-32.png";
+                       $shortcut_icon = "images/friendica-32.png";
 
                $touch_icon = get_config("system", "touch_icon");
                if ($touch_icon == "")
-                       $touch_icon = $this->get_baseurl()."/images/friendica-128.png";
+                       $touch_icon = "images/friendica-128.png";
 
                $tpl = get_markup_template('head.tpl');
                $this->page['htmlhead'] = replace_macros($tpl,array(
@@ -945,6 +939,25 @@ class App {
        }
 
 
+       /**
+        * @brief Removes the baseurl from an url. This avoids some mixed content problems.
+        *
+        * @param string $url
+        *
+        * @return string The cleaned url
+        */
+       function remove_baseurl($url){
+
+               // Is the function called statically?
+               if (!is_object($this))
+                       return(self::$a->remove_baseurl($url));
+
+               $url = normalise_link($url);
+               $base = normalise_link($this->get_baseurl());
+               $url = str_replace($base."/", "", $url);
+               return $url;
+       }
+
        /**
         * @brief Register template engine class
         * 
@@ -1034,22 +1047,42 @@ class App {
        function save_timestamp($stamp, $value) {
                $duration = (float)(microtime(true)-$stamp);
 
+               if (!isset($this->performance[$value])) {
+                       // Prevent ugly E_NOTICE
+                       $this->performance[$value] = 0;
+               }
+
                $this->performance[$value] += (float)$duration;
                $this->performance["marktime"] += (float)$duration;
 
-               // Trace the different functions with their timestamps
-               $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5);
+               $callstack = $this->callstack();
 
-               array_shift($trace);
+               if (!isset($this->callstack[$value][$callstack])) {
+                       // Prevent ugly E_NOTICE
+                       $this->callstack[$value][$callstack] = 0;
+               }
 
-               $function = array();
-               foreach ($trace AS $func)
-                       $function[] = $func["function"];
+               $this->callstack[$value][$callstack] += (float)$duration;
+
+       }
+
+       /**
+        * @brief Returns a string with a callstack. Can be used for logging.
+        *
+        * @return string
+        */
+       function callstack() {
+               $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 6);
 
-               $function = implode(", ", $function);
+               // We remove the first two items from the list since they contain data that we don't need.
+               array_shift($trace);
+               array_shift($trace);
 
-               $this->callstack[$value][$function] += (float)$duration;
+               $callstack = array();
+               foreach ($trace AS $func)
+                       $callstack[] = $func["function"];
 
+               return implode(", ", $callstack);
        }
 
        function mark_timestamp($mark) {
@@ -1065,6 +1098,55 @@ class App {
                return($this->is_friendica_app);
        }
 
+       /**
+        * @brief Checks if the maximum load is reached
+        *
+        * @return bool Is the load reached?
+        */
+       function maxload_reached() {
+
+               $maxsysload = intval(get_config('system', 'maxloadavg'));
+               if ($maxsysload < 1)
+                       $maxsysload = 50;
+
+               $load = current_load();
+               if ($load) {
+                       if (intval($load) > $maxsysload) {
+                               logger('system: load '.$load.' too high.');
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * @brief Checks if the process is already running
+        *
+        * @param string $taskname The name of the task that will be used for the name of the lockfile
+        * @param string $task The path and name of the php script
+        * @param int $timeout The timeout after which a task should be killed
+        *
+        * @return bool Is the process running?
+        */
+       function is_already_running($taskname, $task = "", $timeout = 540) {
+
+               $lockpath = get_lockpath();
+               if ($lockpath != '') {
+                       $pidfile = new pidfile($lockpath, $taskname);
+                       if ($pidfile->is_already_running()) {
+                               logger("Already running");
+                               if ($pidfile->running_time() > $timeout) {
+                                       $pidfile->kill();
+                                       logger("killed stale process");
+                                       // Calling a new instance
+                                       if ($task != "")
+                                               proc_run('php', $task);
+                               }
+                               return true;
+                       }
+               }
+               return false;
+       }
 }
 
 /**
@@ -1416,7 +1498,7 @@ function login($register = false, $hiddens=false) {
 
        $noid = get_config('system','no_openid');
 
-       $dest_url = $a->get_baseurl(true) . '/' . $a->query_string;
+       $dest_url = $a->query_string;
 
        if(local_user()) {
                $tpl = get_markup_template("logout.tpl");
@@ -1476,6 +1558,9 @@ function killme() {
  * @brief Redirect to another URL and terminate this process.
  */
 function goaway($s) {
+       if (!strstr(normalise_link($s), "http://"))
+               $s = App::get_baseurl()."/".$s;
+
        header("Location: $s");
        killme();
 }
@@ -1735,9 +1820,9 @@ function current_theme_url() {
 
        $opts = (($a->profile_uid) ? '?f=&puid=' . $a->profile_uid : '');
        if (file_exists('view/theme/' . $t . '/style.php'))
-               return($a->get_baseurl() . '/view/theme/' . $t . '/style.pcss' . $opts);
+               return('view/theme/'.$t.'/style.pcss'.$opts);
 
-       return($a->get_baseurl() . '/view/theme/' . $t . '/style.css');
+       return('view/theme/'.$t.'/style.css');
 }
 
 function feed_birthday($uid,$tz) {
index 70b315ea244f0be305b8647be65e3e1f4fee2c73..89b821e23a47ce2e50a0c0dc17544c304f916cf0 100644 (file)
@@ -1,6 +1,6 @@
 -- ------------------------------------------
 -- Friendica 3.5-dev (Asparagus)
--- DB_UPDATE_VERSION 1193
+-- DB_UPDATE_VERSION 1194
 -- ------------------------------------------
 
 
@@ -119,6 +119,7 @@ CREATE TABLE IF NOT EXISTS `contact` (
        `keywords` text NOT NULL,
        `gender` varchar(32) NOT NULL DEFAULT '',
        `attag` varchar(255) NOT NULL DEFAULT '',
+       `avatar` varchar(255) NOT NULL DEFAULT '',
        `photo` text NOT NULL,
        `thumb` text NOT NULL,
        `micro` text NOT NULL,
@@ -200,17 +201,6 @@ CREATE TABLE IF NOT EXISTS `deliverq` (
         PRIMARY KEY(`id`)
 ) DEFAULT CHARSET=utf8;
 
---
--- TABLE dsprphotoq
---
-CREATE TABLE IF NOT EXISTS `dsprphotoq` (
-       `id` int(10) unsigned NOT NULL auto_increment,
-       `uid` int(11) NOT NULL DEFAULT 0,
-       `msg` mediumtext NOT NULL,
-       `attempt` tinyint(4) NOT NULL DEFAULT 0,
-        PRIMARY KEY(`id`)
-) DEFAULT CHARSET=utf8;
-
 --
 -- TABLE event
 --
@@ -411,21 +401,6 @@ CREATE TABLE IF NOT EXISTS `gserver` (
         INDEX `nurl` (`nurl`)
 ) DEFAULT CHARSET=utf8;
 
---
--- TABLE guid
---
-CREATE TABLE IF NOT EXISTS `guid` (
-       `id` int(10) unsigned NOT NULL auto_increment,
-       `guid` varchar(255) NOT NULL DEFAULT '',
-       `plink` varchar(255) NOT NULL DEFAULT '',
-       `uri` varchar(255) NOT NULL DEFAULT '',
-       `network` varchar(32) NOT NULL DEFAULT '',
-        PRIMARY KEY(`id`),
-        INDEX `guid` (`guid`),
-        INDEX `plink` (`plink`),
-        INDEX `uri` (`uri`)
-) DEFAULT CHARSET=utf8;
-
 --
 -- TABLE hook
 --
@@ -926,13 +901,11 @@ CREATE TABLE IF NOT EXISTS `session` (
 CREATE TABLE IF NOT EXISTS `sign` (
        `id` int(10) unsigned NOT NULL auto_increment,
        `iid` int(10) unsigned NOT NULL DEFAULT 0,
-       `retract_iid` int(10) unsigned NOT NULL DEFAULT 0,
        `signed_text` mediumtext NOT NULL,
        `signature` text NOT NULL,
        `signer` varchar(255) NOT NULL DEFAULT '',
         PRIMARY KEY(`id`),
-        INDEX `iid` (`iid`),
-        INDEX `retract_iid` (`retract_iid`)
+        INDEX `iid` (`iid`)
 ) DEFAULT CHARSET=utf8;
 
 --
index c49e79c0ab635b91bd4d31082591b6149943e9a1..4f16ba2536d6be5691183e042930e88d48a400e3 100644 (file)
@@ -37,10 +37,7 @@ General
 * o: Profile
 * t: Contacts
 * d: Common friends
-* b: Toggle Blocked status
-* i: Toggle Ignored status
-* v: Toggle Archive status
-* r: Repair
+* r: Advanced
 
 /message
 --------
index fe7c1481f68f93e94ce7071ecec51a01d9585d50..186b1cda932aec75a34b7d780b1399d3c83abc6b 100644 (file)
@@ -143,6 +143,56 @@ Map
 You can embed maps from coordinates or addresses. 
 This require "openstreetmap" addon version 1.3 or newer.
 
+-----------------------------------------------------------
+
+Abstract for longer posts
+-------------------------
+
+If you want to spread your post to several third party networks you can have the problem that these networks have (for example) a length limitation. 
+(Like on Twitter)
+
+Friendica is using a semi intelligent mechanism to generate a fitting abstract. 
+But it can be interesting to define an own abstract that will only be displayed on the external network. 
+This is done with the [abstract]-element. 
+Example:
+
+<pre>[abstract]Totally interesting! A must-see! Please click the link![/abstract]
+I want to tell you a really boring story that you really never wanted 
+to hear.</pre>
+
+Twitter would display the text "Totally interesting! A must-see! Please click the link!". 
+On Friendica you would only see the text after "I want to tell you a really ..."
+
+It is even possible to define abstracts for separate networks:
+
+<pre>
+[abstract]Hi friends Here are my newest pictures![abstract]
+[abstract=twit]Hi my dear Twitter followers. Do you want to see my new 
+pictures?[abstract]
+[abstract=apdn]Helly my dear followers on ADN. I made sone new pictures 
+that I wanted to share with you.[abstract]
+Today I was in the woods and took some real cool pictures ...
+</pre>
+
+For Twitter and App.net the system will use the defined abstracts.
+For other networks (e.g. when you are using the "statusnet" connector that is used to post to GNU Social) the general abstract element will be used.
+
+If you use (for example) the "buffer" connector to post to Facebook or Google+ you can use this element to define an abstract for a longer blogpost that you don't want to post completely to these networks.
+
+Networks like Facebook or Google+ aren't length limited. 
+For this reason the [abstract] element isn't used. 
+Instead you have to name the explicit network:
+
+<pre>
+[abstract]These days I had a strange encounter ...[abstract]
+[abstract=goog]Helly my dear Google+ followers. You have to read my 
+newest blog post![abstract]
+[abstract=face]Hello my Facebook friends. These days happened something 
+really cool.[abstract]
+While taking pictures in the woods I had a really strange encounter ... </pre>
+
+The [abstract] element isn't working with the native OStatus connection or with connectors where we post the HTML. 
+(Like Tumblr, Wordpress or Pump.io)
 
 Special
 -------
@@ -150,5 +200,3 @@ Special
 If you need to put literal bbcode in a message, [noparse], [nobb] or [pre] are used to escape bbcode:
 
 <pre>[noparse][b]bold[/b][/noparse]</pre> : [b]bold[/b]
-
-
index 366b2ed662ad4becffa5d0e81f7edb5c66b5ebd0..0ece265a24fc0728fda46393eaef28404ff92999 100644 (file)
@@ -6,6 +6,8 @@ Bugs and Issues
 If your server has a support page, you should report any bugs/issues you encounter there first.
 Reporting to your support page before reporting to the developers makes their job easier, as they don't have to deal with bug reports that might not have anything to do with them.
 This helps us get new features faster.
+You can also contact the [friendica support forum](https://helpers.pyxis.uberspace.de/profile/helpers) and report your problem there.
+Maybe someone from another node encountered the problem as well and can help you.
 
 If you're a technical user, or your site doesn't have a support page, you'll need to use the [Bug Tracker](http://bugs.friendica.com/).
 Please perform a search to see if there's already an open bug that matches yours before submitting anything.
index cd4b643f141f6292c0bbf3d8b726c57e4bfce4c9..148352c552b2a028c9cf42bd9b184b2fb784aafa 100644 (file)
@@ -57,13 +57,15 @@ All that the pages need to have is a discoverable feed using either the RSS or A
 Twitter
 ---
 
-To follow a Twitter member, put the URL of the Twitter member's main page into the Connect box on your [Contacts](contacts) page.
+To follow a Twitter member, the Twitter-Connector (Addon) needs to be configured on your node.
+If this is the case put the URL of the Twitter member's main page into the Connect box on your [Contacts](contacts) page.
 To reply, you must have the Twitter connector installed, and reply using your own status editor.
 Begin the message with @twitterperson replacing with the Twitter username.
 
 Email
 ---
 
+If the php module for IMAP support is available on your server, Friendica can connect to email contacts as well.
 Configure the email connector from your [Settings](settings) page.
 Once this has been done, you may enter an email address to connect with using the Connect box on your [Contacts](contacts) page.
 They must be the sender of a message which is currently in your INBOX for the connection to succeed.
index 10bbd5632afa008d2b860a7a9e325020394d4540..8e3cd03b18c7fb55b4b930e02bbdf90f369d7b53 100644 (file)
@@ -83,11 +83,11 @@ Ask us to find out whom to talk to about their experiences.
 Do not worry about cross-posting.
 
 ###Client software
-There are free software clients that do somehow work with Friendica but most of them need love and maintenance.
-Also, they were mostly made for other platforms using the GNU Social API.
-This means they lack the features that are really specific to Friendica.
-Popular clients you might want to have a look at are:
-
-* [Hotot (Linux)](http://hotot.org/) - abandoned
-* [Friendica for Android](https://github.com/max-weller/friendica-for-android) - abandoned
-* You can find more working client software in [Wikipedia](https://en.wikipedia.org/wiki/Friendica).
+As Friendica is using a [Twitter/GNU Social compatible API](help/api) any of the clients for those platforms should work with Friendica as well.
+Furthermore there are several client projects, especially for use with Friendica.
+If you are interested in improving those clients, please contact the developers of the clients directly.
+
+* Android / CynogenMod: **Friendica for Android** [src](https://github.com/max-weller/friendica-for-android), [homepage](http://friendica.android.max-weller.de/) - abandoned
+* iOS: *currently no client*
+* SailfishOS: **Friendiy** [src](https://kirgroup.com/projects/fabrixxm/harbour-friendly) - developed by [Fabio](https://kirgroup.com/profile/fabrixxm/?tab=profile)
+* Windows: **Friendica Mobile** for Windows versions [before 8.1](http://windowsphone.com/s?appid=e3257730-c9cf-4935-9620-5261e3505c67) and [Windows 10](https://www.microsoft.com/store/apps/9nblggh0fhmn) - developed by [Gerhard Seeber](http://mozartweg.dyndns.org/friendica/profile/gerhard/?tab=profile)
index 3b6442867c6ccb089cea7fabadb1ff2ee1fcc4be..1f9b0cfab7c0915f272873c3f70a82e0c361479c 100644 (file)
@@ -47,8 +47,10 @@ Friendica Documentation and Resources
 * [Theme Development](help/themes)
 * [Smarty 3 Templates](help/smarty3-templates)
 * [Database schema documantation](help/database)
+* [Class Autoloading](help/autoloader)
 * [Code - Reference(Doxygen generated - sets cookies)](doc/html/)
 
+
 **External Resources**
 
 * [Main Website](http://friendica.com)
index 86254cb29e37f65dfbe521ba32813100356dd0aa..7d909afa093a51d06037e0286b2993d3507c0313 100644 (file)
@@ -11,8 +11,6 @@ Hot Keys
 Friendica traps the following keyboard events:
 
 * [Pause] - Pauses "Ajax" update activity. This is the process that provides updates without reloading the page. You may wish to pause it to reduce network usage and/or as a debugging aid for javascript developers. A pause indicator will appear at the lower right hand corner of the page. Hit the [pause] key once again to resume.
-* [F8] - Displays a language selector
-
 
 Birthday Notifications
 ---
index ced078f55669860ccd6d77a9542e43f72d616420..7d6f440c58c0008ecbaf909048c194f1b09c52ff 100644 (file)
@@ -1,12 +1,27 @@
 Friendica API\r
 ===\r
-The Friendica API aims to be compatible to the [GNU Social API](http://skilledtests.com/wiki/Twitter-compatible_API) and the [Twitter API](https://dev.twitter.com/rest/public).\r
+The Friendica API aims to be compatible to the [GNU Social API](http://wiki.gnusocial.de/gnusocial:api) and the [Twitter API](https://dev.twitter.com/rest/public).\r
 \r
 Please refer to the linked documentation for further information.\r
 \r
 ## Implemented API calls\r
 \r
 ### General\r
+#### HTTP Method\r
+\r
+API endpoints can restrict the method used to request them.\r
+Using an invalid method results in HTTP error 405 "Method Not Allowed".\r
+\r
+In this document, the required method is listed after the endpoint name. "*" means every method can be used.\r
+\r
+#### Auth\r
+\r
+Friendica supports basic http auth and OAuth 1 to authenticate the user to the api.\r
+\r
+OAuth settings can be added by the user in web UI under /settings/oauth/\r
+\r
+In this document, endpoints which requires auth are marked with "AUTH" after endpoint name\r
+\r
 #### Unsupported parameters\r
 * cursor: Not implemented in GNU Social\r
 * trim_user: Not implemented in GNU Social\r
@@ -38,9 +53,9 @@ Error body is
 json:\r
 ```\r
        {\r
-         "error": "Specific error message",\r
-         "request": "API path requested",\r
-         "code": "HTTP error code"\r
+               "error": "Specific error message",\r
+               "request": "API path requested",\r
+               "code": "HTTP error code"\r
        }\r
 ```\r
 \r
@@ -54,19 +69,20 @@ xml:
 ```\r
 \r
 ---\r
-### account/rate_limit_status\r
+### account/rate_limit_status (*; AUTH)\r
 \r
 ---\r
-### account/verify_credentials\r
+### account/verify_credentials (*; AUTH)\r
 #### Parameters\r
+\r
 * skip_status: Don't show the "status" field. (Default: false)\r
 * include_entities: "true" shows entities for pictures and links (Default: false)\r
 \r
 ---\r
-### conversation/show\r
+### conversation/show (*; AUTH)\r
 Unofficial Twitter command. It shows all direct answers (excluding the original post) to a given id.\r
 \r
-#### Parameters\r
+#### Parameter\r
 * id: id of the post\r
 * count: Items per page (default: 20)\r
 * page: page number\r
@@ -80,7 +96,7 @@ Unofficial Twitter command. It shows all direct answers (excluding the original
 * contributor_details\r
 \r
 ---\r
-### direct_messages\r
+### direct_messages (*; AUTH)\r
 #### Parameters\r
 * count: Items per page (default: 20)\r
 * page: page number\r
@@ -93,7 +109,7 @@ Unofficial Twitter command. It shows all direct answers (excluding the original
 * skip_status\r
 \r
 ---\r
-### direct_messages/all\r
+### direct_messages/all (*; AUTH)\r
 #### Parameters\r
 * count: Items per page (default: 20)\r
 * page: page number\r
@@ -102,7 +118,7 @@ Unofficial Twitter command. It shows all direct answers (excluding the original
 * getText: Defines the format of the status field. Can be "html" or "plain"\r
 \r
 ---\r
-### direct_messages/conversation\r
+### direct_messages/conversation (*; AUTH)\r
 Shows all direct messages of a conversation\r
 #### Parameters\r
 * count: Items per page (default: 20)\r
@@ -113,7 +129,7 @@ Shows all direct messages of a conversation
 * uri: URI of the conversation\r
 \r
 ---\r
-### direct_messages/new\r
+### direct_messages/new (POST,PUT; AUTH)\r
 #### Parameters\r
 * user_id: id of the user\r
 * screen_name: screen name (for technical reasons, this value is not unique!)\r
@@ -122,7 +138,7 @@ Shows all direct messages of a conversation
 * title: Title of the direct message\r
 \r
 ---\r
-### direct_messages/sent\r
+### direct_messages/sent (*; AUTH)\r
 #### Parameters\r
 * count: Items per page (default: 20)\r
 * page: page number\r
@@ -132,7 +148,7 @@ Shows all direct messages of a conversation
 * include_entities: "true" shows entities for pictures and links (Default: false)\r
 \r
 ---\r
-### favorites\r
+### favorites (*; AUTH)\r
 #### Parameters\r
 * count: Items per page (default: 20)\r
 * page: page number\r
@@ -144,22 +160,23 @@ Shows all direct messages of a conversation
 * user_id\r
 * screen_name\r
 \r
-Favorites aren't displayed to other users, so "user_id" and "screen_name". So setting this value will result in an empty array.\r
+Favorites aren't displayed to other users, so "user_id" and "screen_name" are unsupported.\r
+Set this values will result in an empty array.\r
 \r
 ---\r
-### favorites/create\r
+### favorites/create (POST,PUT; AUTH)\r
 #### Parameters\r
 * id\r
 * include_entities: "true" shows entities for pictures and links (Default: false)\r
 \r
 ---\r
-### favorites/destroy\r
+### favorites/destroy (POST,DELETE; AUTH)\r
 #### Parameters\r
 * id\r
 * include_entities: "true" shows entities for pictures and links (Default: false)\r
 \r
 ---\r
-### followers/ids\r
+### followers/ids (*; AUTH)\r
 #### Parameters\r
 * stringify_ids: Should the id numbers be sent as text (true) or number (false)? (default: false)\r
 \r
@@ -171,139 +188,7 @@ Favorites aren't displayed to other users, so "user_id" and "screen_name". So se
 Friendica doesn't allow showing followers of other users.\r
 \r
 ---\r
-### friendica/activity/<verb>\r
-#### parameters\r
-* id: item id\r
-\r
-Add or remove an activity from an item.\r
-'verb' can be one of:\r
-- like\r
-- dislike\r
-- attendyes\r
-- attendno\r
-- attendmaybe\r
-\r
-To remove an activity, prepend the verb with "un", eg. "unlike" or "undislike"\r
-Attend verbs disable eachother: that means that if "attendyes" was added to an item, adding "attendno" remove previous "attendyes".\r
-Attend verbs should be used only with event-related items (there is no check at the moment)\r
-\r
-#### Return values\r
-\r
-On success:\r
-json\r
-```"ok"```\r
-\r
-xml\r
-```<ok>true</ok>```\r
-\r
-On error:\r
-HTTP 400 BadRequest\r
-\r
----\r
-### friendica/photo\r
-#### Parameters\r
-* photo_id: Resource id of a photo.\r
-* scale: (optional) scale value of the photo\r
-\r
-Returns data of a picture with the given resource.\r
-If 'scale' isn't provided, returned data include full url to each scale of the photo.\r
-If 'scale' is set, returned data include image data base64 encoded.\r
-\r
-possibile scale value are:\r
-0: original or max size by server settings\r
-1: image with or height at <= 640\r
-2: image with or height at <= 320\r
-3: thumbnail 160x160\r
-\r
-4: Profile image at 175x175\r
-5: Profile image at 80x80\r
-6: Profile image at 48x48\r
-\r
-An image used as profile image has only scale 4-6, other images only 0-3\r
-\r
-#### Return values\r
-\r
-json\r
-```\r
-       {\r
-         "id": "photo id"\r
-         "created": "date(YYYY-MM-GG HH:MM:SS)",\r
-         "edited": "date(YYYY-MM-GG HH:MM:SS)",\r
-         "title": "photo title",\r
-         "desc": "photo description",\r
-         "album": "album name",\r
-         "filename": "original file name",\r
-         "type": "mime type",\r
-         "height": "number",\r
-         "width": "number",\r
-         "profile": "1 if is profile photo",\r
-         "link": {\r
-               "<scale>": "url to image"\r
-               ...\r
-         },\r
-         // if 'scale' is set\r
-         "datasize": "size in byte",\r
-         "data": "base64 encoded image data"\r
-       }\r
-```\r
-\r
-xml\r
-```\r
-       <photo>\r
-               <id>photo id</id>\r
-               <created>date(YYYY-MM-GG HH:MM:SS)</created>\r
-               <edited>date(YYYY-MM-GG HH:MM:SS)</edited>\r
-               <title>photo title</title>\r
-               <desc>photo description</desc>\r
-               <album>album name</album>\r
-               <filename>original file name</filename>\r
-               <type>mime type</type>\r
-               <height>number</height>\r
-               <width>number</width>\r
-               <profile>1 if is profile photo</profile>\r
-               <links type="array">\r
-                       <link type="mime type" scale="scale number" href="image url"/>\r
-                       ...\r
-               </links>\r
-       </photo>\r
-```\r
-\r
----\r
-### friendica/photos/list\r
-\r
-Returns a list of all photo resources of the logged in user.\r
-\r
-#### Return values\r
-\r
-json\r
-```\r
-       [\r
-               {\r
-                       id: "resource_id",\r
-                       album: "album name",\r
-                       filename: "original file name",\r
-                       type: "image mime type",\r
-                       thumb: "url to thumb sized image"\r
-               },\r
-               ...\r
-       ]\r
-```\r
-\r
-xml\r
-```\r
-       <photos type="array">\r
-               <photo id="resource_id"\r
-                       album="album name"\r
-                       filename="original file name"\r
-                       type="image mime type">\r
-                               "url to thumb sized image"\r
-               </photo>\r
-               ...\r
-       </photos>\r
-```\r
-\r
----\r
-### friends/ids\r
+### friends/ids (*; AUTH)\r
 #### Parameters\r
 * stringify_ids: Should the id numbers be sent as text (true) or number (false)? (default: false)\r
 \r
@@ -315,15 +200,15 @@ xml
 Friendica doesn't allow showing friends of other users.\r
 \r
 ---\r
-### help/test\r
+### help/test (*)\r
 \r
 ---\r
-### media/upload\r
+### media/upload (POST,PUT; AUTH)\r
 #### Parameters\r
 * media: image data\r
 \r
 ---\r
-### oauth/request_token\r
+### oauth/request_token (*)\r
 #### Parameters\r
 * oauth_callback\r
 \r
@@ -331,7 +216,7 @@ Friendica doesn't allow showing friends of other users.
 * x_auth_access_type\r
 \r
 ---\r
-### oauth/access_token\r
+### oauth/access_token (*)\r
 #### Parameters\r
 * oauth_verifier\r
 \r
@@ -341,7 +226,7 @@ Friendica doesn't allow showing friends of other users.
 * x_auth_mode\r
 \r
 ---\r
-### statuses/destroy\r
+### statuses/destroy (POST,DELETE; AUTH)\r
 #### Parameters\r
 * id: message number\r
 * include_entities: "true" shows entities for pictures and links (Default: false)\r
@@ -350,15 +235,21 @@ Friendica doesn't allow showing friends of other users.
 * trim_user\r
 \r
 ---\r
-### statuses/followers\r
+### statuses/followers (*; AUTH)\r
+\r
+#### Parameters\r
+\r
 * include_entities: "true" shows entities for pictures and links (Default: false)\r
 \r
 ---\r
-### statuses/friends\r
+### statuses/friends (*; AUTH)\r
+\r
+#### Parameters\r
+\r
 * include_entities: "true" shows entities for pictures and links (Default: false)\r
 \r
 ---\r
-### statuses/friends_timeline\r
+### statuses/friends_timeline (*; AUTH)\r
 #### Parameters\r
 * count: Items per page (default: 20)\r
 * page: page number\r
@@ -374,7 +265,7 @@ Friendica doesn't allow showing friends of other users.
 * contributor_details\r
 \r
 ---\r
-### statuses/home_timeline\r
+### statuses/home_timeline (*; AUTH)\r
 #### Parameters\r
 * count: Items per page (default: 20)\r
 * page: page number\r
@@ -390,7 +281,7 @@ Friendica doesn't allow showing friends of other users.
 * contributor_details\r
 \r
 ---\r
-### statuses/mentions\r
+### statuses/mentions (*; AUTH)\r
 #### Parameters\r
 * count: Items per page (default: 20)\r
 * page: page number\r
@@ -404,7 +295,7 @@ Friendica doesn't allow showing friends of other users.
 * contributor_details\r
 \r
 ---\r
-### statuses/public_timeline\r
+### statuses/public_timeline (*; AUTH)\r
 #### Parameters\r
 * count: Items per page (default: 20)\r
 * page: page number\r
@@ -418,7 +309,7 @@ Friendica doesn't allow showing friends of other users.
 * trim_user\r
 \r
 ---\r
-### statuses/replies\r
+### statuses/replies (*; AUTH)\r
 #### Parameters\r
 * count: Items per page (default: 20)\r
 * page: page number\r
@@ -432,7 +323,7 @@ Friendica doesn't allow showing friends of other users.
 * contributor_details\r
 \r
 ---\r
-### statuses/retweet\r
+### statuses/retweet (POST,PUT; AUTH)\r
 #### Parameters\r
 * id: message number\r
 * include_entities: "true" shows entities for pictures and links (Default: false)\r
@@ -441,7 +332,7 @@ Friendica doesn't allow showing friends of other users.
 * trim_user\r
 \r
 ---\r
-### statuses/show\r
+### statuses/show (*; AUTH)\r
 #### Parameters\r
 * id: message number\r
 * conversation: if set to "1" show all messages of the conversation with the given id\r
@@ -476,7 +367,7 @@ Friendica doesn't allow showing friends of other users.
 * display_coordinates\r
 \r
 ---\r
-### statuses/user_timeline\r
+### statuses/user_timeline (*; AUTH)\r
 #### Parameters\r
 * user_id: id of the user\r
 * screen_name: screen name (for technical reasons, this value is not unique!)\r
@@ -489,15 +380,28 @@ Friendica doesn't allow showing friends of other users.
 * include_entities: "true" shows entities for pictures and links (Default: false)\r
 \r
 #### Unsupported parameters\r
+\r
 * include_rts\r
 * trim_user\r
 * contributor_details\r
 \r
 ---\r
-### statusnet/config\r
+### statusnet/config (*)\r
+\r
+---\r
+### statusnet/conversation (*; AUTH)\r
+It shows all direct answers (excluding the original post) to a given id.\r
+\r
+#### Parameter\r
+* id: id of the post\r
+* count: Items per page (default: 20)\r
+* page: page number\r
+* since_id: minimal id\r
+* max_id: maximum id\r
+* include_entities: "true" shows entities for pictures and links (Default: false)\r
 \r
 ---\r
-### statusnet/version\r
+### statusnet/version (*)\r
 \r
 #### Unsupported parameters\r
 * user_id\r
@@ -507,7 +411,7 @@ Friendica doesn't allow showing friends of other users.
 Friendica doesn't allow showing followers of other users.\r
 \r
 ---\r
-### users/search\r
+### users/search (*)\r
 #### Parameters\r
 * q: name of the user\r
 \r
@@ -517,7 +421,7 @@ Friendica doesn't allow showing followers of other users.
 * include_entities\r
 \r
 ---\r
-### users/show\r
+### users/show (*)\r
 #### Parameters\r
 * user_id: id of the user\r
 * screen_name: screen name (for technical reasons, this value is not unique!)\r
@@ -533,8 +437,39 @@ Friendica doesn't allow showing friends of other users.
 \r
 ## Implemented API calls (not compatible with other APIs)\r
 \r
+\r
 ---\r
-### friendica/group_show\r
+### friendica/activity/<verb>\r
+#### parameters\r
+* id: item id\r
+\r
+Add or remove an activity from an item.\r
+'verb' can be one of:\r
+\r
+- like\r
+- dislike\r
+- attendyes\r
+- attendno\r
+- attendmaybe\r
+\r
+To remove an activity, prepend the verb with "un", eg. "unlike" or "undislike"\r
+Attend verbs disable eachother: that means that if "attendyes" was added to an item, adding "attendno" remove previous "attendyes".\r
+Attend verbs should be used only with event-related items (there is no check at the moment)\r
+\r
+#### Return values\r
+\r
+On success:\r
+json\r
+```"ok"```\r
+\r
+xml\r
+```<ok>true</ok>```\r
+\r
+On error:\r
+HTTP 400 BadRequest\r
+\r
+---\r
+### friendica/group_show (*; AUTH)\r
 Return all or a specified group of the user with the containing contacts as array.\r
 \r
 #### Parameters\r
@@ -542,22 +477,23 @@ Return all or a specified group of the user with the containing contacts as arra
 \r
 #### Return values\r
 Array of:\r
+\r
 * name: name of the group\r
 * gid: id of the group\r
 * user: array of group members (return from api_get_user() function for each member)\r
 \r
 \r
 ---\r
-### friendica/group_delete\r
+### friendica/group_delete (POST,DELETE; AUTH)\r
 delete the specified group of contacts; API call need to include the correct gid AND name of the group to be deleted.\r
 \r
----\r
-### Parameters\r
+#### Parameters\r
 * gid: id of the group to be deleted\r
 * name: name of the group to be deleted\r
 \r
 #### Return values\r
 Array of:\r
+\r
 * success: true if successfully deleted\r
 * gid: gid of the deleted group\r
 * name: name of the deleted group\r
@@ -566,19 +502,22 @@ Array of:
 \r
 \r
 ---\r
-### friendica/group_create\r
+### friendica/group_create (POST,PUT; AUTH)\r
 Create the group with the posted array of contacts as members.\r
+\r
 #### Parameters\r
 * name: name of the group to be created\r
 \r
 #### POST data\r
-JSON data as Array like the result of „users/group_show“:\r
+JSON data as Array like the result of "users/group_show":\r
+\r
 * gid\r
 * name\r
 * array of users\r
 \r
 #### Return values\r
 Array of:\r
+\r
 * success: true if successfully created or reactivated\r
 * gid: gid of the created group\r
 * name: name of the created group\r
@@ -587,26 +526,175 @@ Array of:
 \r
 \r
 ---\r
-### friendica/group_update\r
+### friendica/group_update (POST)\r
 Update the group with the posted array of contacts as members (post all members of the group to the call; function will remove members not posted).\r
+\r
 #### Parameters\r
 * gid: id of the group to be changed\r
 * name: name of the group to be changed\r
 \r
 #### POST data\r
 JSON data as array like the result of „users/group_show“:\r
+\r
 * gid\r
 * name\r
 * array of users\r
 \r
 #### Return values\r
 Array of:\r
+\r
 * success: true if successfully updated\r
 * gid: gid of the changed group\r
 * name: name of the changed group\r
 * status: „missing user“ | „ok“\r
 * wrong users: array of users, which were not available in the contact table\r
 \r
+\r
+\r
+---\r
+### friendica/notifications (GET)\r
+Return last 50 notification for current user, ordered by date with unseen item on top\r
+\r
+#### Parameters\r
+none\r
+\r
+#### Return values\r
+Array of:\r
+\r
+* id: id of the note\r
+* type: type of notification as int (see NOTIFY_* constants in boot.php)\r
+* name: full name of the contact subject of the note\r
+* url: contact's profile url\r
+* photo: contact's profile photo\r
+* date: datetime string of the note\r
+* timestamp: timestamp of the node\r
+* date_rel: relative date of the note (eg. "1 hour ago")\r
+* msg: note message in bbcode\r
+* msg_html: note message in html\r
+* msg_plain: note message in plain text\r
+* link: link to note\r
+* seen: seen state: 0 or 1\r
+\r
+\r
+---\r
+### friendica/notifications/seen (POST)\r
+Set note as seen, returns item object if possible\r
+\r
+#### Parameters\r
+id: id of the note to set seen\r
+\r
+#### Return values\r
+If the note is linked to an item, the item is returned, just like one of the "statuses/*_timeline" api.\r
+\r
+If the note is not linked to an item, a success status is returned:\r
+\r
+* "success" (json) | "&lt;status&gt;success&lt;/status&gt;" (xml)\r
+\r
+\r
+---\r
+### friendica/photo (*; AUTH)\r
+#### Parameters\r
+* photo_id: Resource id of a photo.\r
+* scale: (optional) scale value of the photo\r
+\r
+Returns data of a picture with the given resource.\r
+If 'scale' isn't provided, returned data include full url to each scale of the photo.\r
+If 'scale' is set, returned data include image data base64 encoded.\r
+\r
+possibile scale value are:\r
+\r
+* 0: original or max size by server settings\r
+* 1: image with or height at <= 640\r
+* 2: image with or height at <= 320\r
+* 3: thumbnail 160x160\r
+* 4: Profile image at 175x175\r
+* 5: Profile image at 80x80\r
+* 6: Profile image at 48x48\r
+\r
+An image used as profile image has only scale 4-6, other images only 0-3\r
+\r
+#### Return values\r
+\r
+json\r
+```\r
+       {\r
+               "id": "photo id"\r
+               "created": "date(YYYY-MM-GG HH:MM:SS)",\r
+               "edited": "date(YYYY-MM-GG HH:MM:SS)",\r
+               "title": "photo title",\r
+               "desc": "photo description",\r
+               "album": "album name",\r
+               "filename": "original file name",\r
+               "type": "mime type",\r
+               "height": "number",\r
+               "width": "number",\r
+               "profile": "1 if is profile photo",\r
+               "link": {\r
+                       "<scale>": "url to image"\r
+                       ...\r
+               },\r
+               // if 'scale' is set\r
+               "datasize": "size in byte",\r
+               "data": "base64 encoded image data"\r
+       }\r
+```\r
+\r
+xml\r
+```\r
+       <photo>\r
+               <id>photo id</id>\r
+               <created>date(YYYY-MM-GG HH:MM:SS)</created>\r
+               <edited>date(YYYY-MM-GG HH:MM:SS)</edited>\r
+               <title>photo title</title>\r
+               <desc>photo description</desc>\r
+               <album>album name</album>\r
+               <filename>original file name</filename>\r
+               <type>mime type</type>\r
+               <height>number</height>\r
+               <width>number</width>\r
+               <profile>1 if is profile photo</profile>\r
+               <links type="array">\r
+               <link type="mime type" scale="scale number" href="image url"/>\r
+                       ...\r
+               </links>\r
+       </photo>\r
+```\r
+\r
+---\r
+### friendica/photos/list (*; AUTH)\r
+\r
+Returns a list of all photo resources of the logged in user.\r
+\r
+#### Return values\r
+\r
+json\r
+```\r
+       [\r
+               {\r
+                       id: "resource_id",\r
+                       album: "album name",\r
+                       filename: "original file name",\r
+                       type: "image mime type",\r
+                       thumb: "url to thumb sized image"\r
+               },\r
+               ...\r
+       ]\r
+```\r
+\r
+xml\r
+```\r
+       <photos type="array">\r
+               <photo id="resource_id"\r
+               album="album name"\r
+               filename="original file name"\r
+               type="image mime type">\r
+                       "url to thumb sized image"\r
+               </photo>\r
+               ...\r
+       </photos>\r
+```\r
+\r
+\r
 ---\r
 ## Not Implemented API calls\r
 The following API calls are implemented in GNU Social but not in Friendica: (incomplete)\r
@@ -702,13 +790,13 @@ The following API calls from the Twitter API aren't implemented neither in Frien
 ### BASH / cURL\r
 Betamax has documentated some example API usage from a [bash script](https://en.wikipedia.org/wiki/Bash_(Unix_shell) employing [curl](https://en.wikipedia.org/wiki/CURL) (see [his posting](https://betamax65.de/display/betamax65/43539)).\r
 \r
-    /usr/bin/curl -u USER:PASS https://YOUR.FRIENDICA.TLD/api/statuses/update.xml -d source="some source id" -d status="the status you want to post"\r
+/usr/bin/curl -u USER:PASS https://YOUR.FRIENDICA.TLD/api/statuses/update.xml -d source="some source id" -d status="the status you want to post"\r
 \r
 ### Python\r
 The [RSStoFriedika](https://github.com/pafcu/RSStoFriendika) code can be used as an example of how to use the API with python. The lines for posting are located at [line 21](https://github.com/pafcu/RSStoFriendika/blob/master/RSStoFriendika.py#L21) and following.\r
 \r
-    def tweet(server, message, group_allow=None):\r
-        url = server + '/api/statuses/update'\r
-        urllib2.urlopen(url, urllib.urlencode({'status': message,'group_allow[]':group_allow}, doseq=True))\r
+def tweet(server, message, group_allow=None):\r
+url = server + '/api/statuses/update'\r
+urllib2.urlopen(url, urllib.urlencode({'status': message,'group_allow[]':group_allow}, doseq=True))\r
 \r
 There is also a [module for python 3](https://bitbucket.org/tobiasd/python-friendica) for using the API.\r
diff --git a/doc/autoloader.md b/doc/autoloader.md
new file mode 100644 (file)
index 0000000..947eade
--- /dev/null
@@ -0,0 +1,209 @@
+Autoloader\r
+==========\r
+\r
+* [Home](help)\r
+\r
+There is some initial support to class autoloading in Friendica core.\r
+\r
+The autoloader code is in `include/autoloader.php`.\r
+It's derived from composer autoloader code.\r
+\r
+Namespaces and Classes are mapped to folders and files in `library/`,\r
+and the map must be updated by hand, because we don't use composer yet.\r
+The mapping is defined by files in `include/autoloader/` folder.\r
+\r
+Currently, only HTMLPurifier library is loaded using autoloader.\r
+\r
+\r
+## A quick introdution to class autoloading\r
+\r
+The autoloader it's a way for php to automagically include the file that define a class when the class is first used, without the need to use "require_once" every time.\r
+\r
+Once is setup you don't have to use it in any way. You need a class? you use the class.\r
+\r
+At his basic is a function passed to the "spl_autoload_register()" function, which receive as argument the class name the script want and is it job to include the correct php file where that class is defined.\r
+The best source for documentation is [php site](http://php.net/manual/en/language.oop5.autoload.php).\r
+\r
+One example, based on fictional friendica code.\r
+\r
+Let's say you have a php file in "include/" that define a very useful class:\r
+\r
+```\r
+    file: include/ItemsManager.php\r
+    <?php\r
+    namespace \Friendica;\r
+    \r
+    class ItemsManager {\r
+       public function getAll() { ... }\r
+       public function getByID($id) { ... }\r
+    }\r
+```\r
+\r
+The class "ItemsManager" has been declared in "Friendica" namespace.\r
+Namespaces are useful to keep things separated and avoid names clash (could be that a library you want to use defines a class named "ItemsManager", but as long as is in another namespace, you don't have any problem)\r
+\r
+If we were using composer, we had configured it with path where to find the classes of "Friendica" namespace, and then the composer script will generate the autoloader machinery for us.\r
+As we don't use composer, we need check that the autoloader knows the Friendica namespace.\r
+So in "include/autoloader/autoload_psr4.php" there should be something like\r
+\r
+```\r
+    $vendorDir = dirname(dirname(dirname(__FILE__)))."/library";\r
+    $baseDir = dirname($vendorDir);\r
+    return array(\r
+       "Friendica" => array($baseDir."/include");\r
+    );\r
+```\r
+\r
+\r
+That tells the autoloader code to look for files that defines classes in "Friendica" namespace under "include/" folder. (And btw, that's why the file has the same name as the class it defines.)\r
+\r
+*note*: The structure of files in "include/autoloader/" has been copied from the code generated by composer, to ease the work of enable autoloader for external libraries under "library/"\r
+\r
+Let's say now that you need to load some items in a view, maybe in a fictional "mod/network.php".\r
+Somewere at the start of the scripts, the autoloader was initialized. In Friendica is done at the top of "boot.php", with "require_once('include/autoloader.php');".\r
+\r
+The code will be something like:\r
+\r
+```\r
+    file: mod/network.php\r
+    <?php\r
+    \r
+    function network_content(&$a) {\r
+       $itemsmanager = new \Friendica\ItemsManager();\r
+       $items = $itemsmanager->getAll();\r
+    \r
+       // pass $items to template\r
+       // return result\r
+    }\r
+```\r
+\r
+That's a quite simple example, but look: no "require()"!\r
+You need to use a class, you use the class and you don't need to do anything more.\r
+\r
+Going further: now we have a bunch of "*Manager" classes that cause some code duplication, let's define a BaseManager class, where to move all code in common between all managers:\r
+\r
+```\r
+    file: include/BaseManager.php\r
+    <?php\r
+    namespace \Friendica;\r
+    \r
+    class BaseManager {\r
+      public function thatFunctionEveryManagerUses() { ... }\r
+    }\r
+```\r
+\r
+and then let's change the ItemsManager class to use this code\r
+\r
+```\r
+    file: include/ItemsManager.php\r
+    <?php\r
+    namespace \Friendica;\r
+    \r
+    class ItemsManager extends BaseManager {\r
+       public function getAll() { ... }\r
+       public function getByID($id) { ... }\r
+    }\r
+```\r
+\r
+The autoloader don't mind what you need the class for. You need a class, you get the class.\r
+It works with the "BaseManager" example here, it works when we need to call static methods on a class:\r
+\r
+```\r
+    file: include/dfrn.php\r
+    <?php    \r
+    namespace \Friendica;\r
+    \r
+    class dfrn {\r
+      public static function  mail($item, $owner) { ... }\r
+    }\r
+```\r
+\r
+```\r
+    file: mod/mail.php\r
+    <?php\r
+    \r
+    mail_post($a){\r
+     ...\r
+     \Friendica\dfrn::mail($item, $owner);\r
+     ...\r
+    }\r
+```\r
+\r
+If your code is in same namespace as the class you need, you don't need to prepend it:\r
+\r
+```\r
+    file: include/delivery.php\r
+    <?php\r
+    \r
+    namespace \Friendica;\r
+    \r
+    // this is the same content of current include/delivery.php, \r
+    // but has been declared to be in "Friendica" namespace\r
+    \r
+    [...]\r
+    switch($contact['network']) {\r
+    \r
+        case NETWORK_DFRN:\r
+            if ($mail) {\r
+                $item['body'] = ...\r
+                $atom = dfrn::mail($item, $owner);\r
+            } elseif ($fsuggest) {\r
+                $atom = dfrn::fsuggest($item, $owner);\r
+                q("DELETE FROM `fsuggest` WHERE `id` = %d LIMIT 1", intval($item['id']));\r
+            } elseif ($relocate)\r
+                $atom = dfrn::relocate($owner, $uid);\r
+    [...]\r
+```\r
+\r
+This is real "include/delivery.php" unchanged, but as the code is declared to be in "Friendica" namespace, you don't need to write it when you need to use the "dfrn" class.\r
+But if you want to use classes from another library, you need to use the full namespace, e.g.\r
+\r
+```\r
+    <?php\r
+    namespace \Frienidca;\r
+    \r
+    class Diaspora {\r
+      public function md2bbcode() {\r
+        $html = \Michelf\MarkdownExtra::defaultTransform($text); \r
+      }\r
+    }\r
+```\r
+\r
+if you use that class in many places of the code and you don't want to write the full path to the class everytime, you can use the "use" php keyword\r
+\r
+```\r
+    <?php\r
+    namespace \Frienidca;\r
+    \r
+    use \Michelf\MarkdownExtra;\r
+    \r
+    class Diaspora {\r
+      public function md2bbcode() {\r
+        $html = MarkdownExtra::defaultTransform($text); \r
+      }\r
+    }\r
+```\r
+\r
+Note that namespaces are like paths in filesystem, separated by "\", with the first "\" being the global scope.\r
+You can go more deep if you want to, like:\r
+\r
+```\r
+    <?php\r
+    namespace \Friendica\Network;\r
+    \r
+    class DFRN {\r
+    }\r
+```\r
+\r
+or\r
+\r
+```\r
+    <?php\r
+    namespace \Friendica\DBA;\r
+    \r
+    class MySQL {\r
+    }\r
+```\r
+\r
+So you can think of namespaces as folders in a unix filesystem, with global scope as the root ("\").\r
+\r
index a0b28f4e84dbf331300cadeec7498bb1a57c1fa7..f48404c17d0bc219e66c40d8d84248386b948a3a 100644 (file)
@@ -15,7 +15,6 @@ Database Tables
 | [contact](help/database/db_contact)                  | contact table                                    |
 | [conv](help/database/db_conv)                        | private messages                                 |
 | [deliverq](help/database/db_deliverq)                |                                                  |
-| [dsprphotoq](help/database/db_dsprphotoq)            |                                                  |
 | [event](help/database/db_event)                      | Events                                           |
 | [fcontact](help/database/db_fcontact)                | friend suggestion stuff                          |
 | [ffinder](help/database/db_ffinder)                  | friend suggestion stuff                          |
@@ -27,7 +26,6 @@ Database Tables
 | [group](help/database/db_group)                      | privacy groups, group info                       |
 | [group_member](help/database/db_group_member)        | privacy groups, member info                      |
 | [gserver](help/database/db_gserver)                  |                                                  |
-| [guid](help/database/db_guid)                        |                                                  |
 | [hook](help/database/db_hook)                        | plugin hook registry                             |
 | [intro](help/database/db_intro)                      |                                                  |
 | [item](help/database/db_item)                        | all posts                                        |
diff --git a/doc/database/db_dsprphotoq.md b/doc/database/db_dsprphotoq.md
deleted file mode 100644 (file)
index 6af4d03..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-Table dsprphotoq
-================
-
-| Field   | Description      | Type             | Null | Key | Default | Extra          |
-|---------|------------------|------------------|------|-----|---------|----------------|
-| id      | sequential ID    | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
-| uid     |                  | int(11)          | NO   |     | 0       |                |
-| msg     |                  | mediumtext       | NO   |     | NULL    |                |
-| attempt |                  | tinyint(4)       | NO   |     | 0       |                |
-
-Return to [database documentation](help/database)
diff --git a/doc/database/db_guid.md b/doc/database/db_guid.md
deleted file mode 100644 (file)
index f607597..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-Table guid
-==========
-
-| Field   | Description      | Type             | Null | Key | Default | Extra          |
-|---------|------------------|------------------|------|-----|---------|----------------|
-| id      | sequential ID    | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
-| guid    |                  | varchar(255)     | NO   | MUL |         |                |
-| plink   |                  | varchar(255)     | NO   | MUL |         |                |
-| uri     |                  | varchar(255)     | NO   | MUL |         |                |
-| network |                  | varchar(32)      | NO   |     |         |                |
-
-Return to [database documentation](help/database)
index 8de59ac6757f32c8cb5db1de23659f4db073eadd..6986613e5940b56d7b7372b36914a0b6112836dc 100644 (file)
@@ -5,7 +5,6 @@ Table sign
 | ------------ | ------------- | ---------------- | ---- | --- | ------- | --------------- |
 | id           | sequential ID | int(10) unsigned | NO   | PRI | NULL    | auto_increment  |
 | iid          | item.id       | int(10) unsigned | NO   | MUL | 0       |                 |
-| retract_iid  |               | int(10) unsigned | NO   | MUL | 0       |                 |
 | signed_text  |               | mediumtext       | NO   |     | NULL    |                 |
 | signature    |               | text             | NO   |     | NULL    |                 |
 | signer       |               | varchar(255)     | NO   |     |         |                 |
index d3e205f0fa38a1f33b72a78dbe988cfbcf31f060..cd9fa7673e0235e886e6375472cfd8ce5df0a254 100644 (file)
@@ -131,8 +131,7 @@ Au&szlig;erdem kann *url* die genaue url zu einer ogg Datei sein, die dann per H
 
 <pre>[url]*url*[/url]</pre>
 
-Wenn *url* entweder oembed oder opengraph unterstützt wird das eingebettete
-Objekt (z.B. ein Dokument von scribd) eingebunden.
+Wenn *url* entweder oembed oder opengraph unterstützt wird das eingebettete Objekt (z.B. ein Dokument von scribd) eingebunden.
 Der Titel der Seite mit einem Link zur *url* wird ebenfalls angezeigt.
 
 Um eine Karte in einen Beitrag einzubinden, muss das *openstreetmap* Addon aktiviert werden. Ist dies der Fall, kann mit
@@ -145,11 +144,54 @@ eine Karte von [OpenStreetmap](http://openstreetmap.org) eingebettet werden. Zur
 
 oder eine Adresse in obiger Form verwendet werden.
 
+Zusammenfassung für längere Beiträge
+------------------------------------
+
+Wenn man seine Beiträge über mehrere Netzwerke verbreiten möchte, hat man häufig das Problem, dass diese Netzwerke z.B. eine Längenbeschränkung haben. 
+(Z.B. Twitter).
+
+Friendica benutzt zum Erzeugen eines Anreißtextes eine halbwegs intelligente Logik. 
+Es kann aber dennoch von Interesse sein, eine eigene Zusammenfassung zu erstellen, die nur auf dem Fremdnetzwerk dargestellt wird. 
+Dies geschieht mit dem [abstract]-Element. 
+Beispiel:
+
+<pre>[abstract]Total spannend! Unbedingt diesen Link anklicken![/abstract]
+Hier erzähle ich euch eine total langweilige Geschichte, die ihr noch 
+nie hören wolltet.</pre>
+
+Auf Twitter würde das "Total spannend! Unbedingt diesen Link anklicken!" stehen, auf Friendica würde nur der Text nach "Hier erzähle ..." erscheinen.
+
+Es ist sogar möglich, für einzelne Netzwerke eigene Zusammenfassungen zu erstellen:
+
+<pre>
+[abstract]Hallo Leute, hier meine neuesten Bilder![abstract]
+[abstract=twit]Hallo Twitter-User, hier meine neuesten Bilder![abstract]
+[abstract=apdn]Hallo App.net-User, hier meine neuesten Bilder![abstract]
+Ich war heute wieder im Wald unterwegs und habe tolle Bilder geschossen ...
+</pre>
+
+Für Twitter und App.net nimmt das System die entsprechenden Texte. 
+Bei anderen Netzwerken, bei denen der Inhalt gekürzt wird (z.B. beim "statusnet"-Connector, der für das Posten nach GNU Social verwendet wird) wird dann die Zusammenfassung unter [abstract] verwendet.
+
+Wenn man z.B. den "buffer"-Connector verwendet, um nach Facebook oder Google+ zu posten, kann man dieses Element ebenfalls verwenden, wenn man z.B. einen längeren Blogbeitrag erstellt hat, aber ihn nicht komplett in diese Netzwerke posten möchte.
+
+Netzwerke wie Facebook oder Google+ sind nicht in der Postinglänge beschränkt. 
+Aus diesem Grund greift nicht die [abstract]-Zusammenfassung. Stattdessen muss man das Netzwerk explizit angeben:
+
+<pre>
+[abstract]Ich habe neulich wieder etwas erlebt, was ich euch mitteilen möchte.[abstract]
+[abstract=goog]Hallo meine Google+-Kreislinge. Ich habe neulich wieder 
+etwas erlebt, was ich euch mitteilen möchte.[abstract]
+[abstract=face]Hallo Facebook-Freunde! Ich habe neulich wieder etwas 
+erlebt, was ich euch mitteilen möchte.[abstract]
+Beim Bildermachen im Wald habe ich neulich eine interessante Person 
+getroffen ... </pre>
+
+Das [abstract]-Element greift nicht bei der nativen OStatus-Verbindung oder bei Connectoren, die den HTML-Text posten wie z.B. die Connectoren zu Tumblr, Wordpress oder Pump.io.
+
 Spezielle Tags
 -------
 
 Wenn Du &uuml;ber BBCode Tags in einer Nachricht schreiben m&ouml;chtest, kannst Du [noparse], [nobb] oder [pre] verwenden um den BBCode Tags vor der Evaluierung zu sch&uuml;tzen:
 
 <pre>[noparse][b]fett[/b][/noparse]</pre> : [b]fett[/b]
-
-
index 988b3657c0f944e8c16b08b6c36c0fb9d3fff772..4ad9f39ba55fdf4180da2596b02ebeed0712b84e 100644 (file)
@@ -14,9 +14,6 @@ Friendica erfasst die folgenden Tastaturbefehle:
 
 * [Pause] - Pausiert die Update-Aktivität via "Ajax". Das ist ein Prozess, der Updates durchführt, ohne die Seite neu zu laden. Du kannst diesen Prozess pausieren, um deine Netzwerkauslastung zu reduzieren und/oder um es in der Javascript-Programmierung zum Debuggen zu nutzen. Ein Pausenzeichen erscheint unten links im Fenster. Klicke die [Pause]-Taste ein weiteres Mal, um die Pause zu beenden.
 
-* [F8] - Zeigt eine Sprachauswahl an
-
-
 **Geburtstagsbenachrichtigung**
 
 Geburtstage erscheinen auf deiner Startseite für alle Freunde, die in den nächsten 6 Tagen Geburtstag haben. 
index 4764c287c8977c9d2efbbfcbcfb6a2afccf1f92b..a36e0bef22a8ab7bc40e646dd09e5dac97e3d960 100644 (file)
@@ -34,6 +34,7 @@ line to your .htconfig.php:
 * like_no_comment (Boolean) - Don't update the "commented" value of an item when it is liked.
 * local_block (Boolean) - Used in conjunction with "block_public".
 * local_search (Boolean) - Blocks the search for not logged in users to prevent crawlers from blocking your system.
+* max_connections - The poller process isn't started when 3/4 of the possible database connections are used. When the system can't detect the maximum numbers of connection then this value can be used.
 * max_contact_queue - Default value is 500.
 * max_batch_queue - Default value is 1000.
 * no_oembed (Boolean) - Don't use OEmbed to fetch more information about a link.
@@ -63,9 +64,6 @@ line to your .htconfig.php:
 * throttle_limit_week - Maximum number of posts that a user can send per week with the API.
 * throttle_limit_month - Maximum number of posts that a user can send per month with the API.
 * wall-to-wall_share (Boolean) - Displays forwarded posts like "wall-to-wall" posts.
-* worker (Boolean) - (Experimental) Use the worker system instead of calling several background processes. Reduces the overall load and speeds up item delivery.
-* worker_dont_fork (Boolean) - if enabled, the workers are only called from the poller process. Useful on systems that permit the use of "proc_open".
-* worker_queues - Number of parallel workers. Default value is 10 queues.
 * xrd_timeout - Timeout for fetching the XRD links. Default value is 20 seconds.
 
 ## service_class ##
diff --git a/doc/smarty3-templates.md b/doc/smarty3-templates.md
new file mode 100644 (file)
index 0000000..751ef20
--- /dev/null
@@ -0,0 +1,173 @@
+Friendica Templating Documentation
+==================================
+
+* [Home](help)
+
+Friendica uses [Smarty 3](http://www.smarty.net/) as PHP templating engine. The main templates are found in
+
+               /view/templates
+
+theme authors may overwrite the default templates by putting a files with the same name into the
+
+               /view/themes/$themename/templates
+
+directory.
+
+Templates that are only used by addons shall be placed in the
+
+               /addon/$addonname/templates
+
+directory.
+
+To render a template use the function *get_markup_template* to load the template and *replace_macros* to replace the macros/variables in the just loaded template file.
+
+               $tpl = get_markup_template('install_settings.tpl');
+        $o .= replace_macros($tpl, array( ... ));
+
+the array consists of an association of an identifier and the value for that identifier, i.e.
+
+               '$title' => $install_title,
+
+where the value may as well be an array by its own.
+
+Form Templates
+--------------
+
+To guarantee a consistent look and feel for input forms, i.e. in the settings sections, there are templates for the basic form fields. They are initialized with an array of data, depending on the tyle of the field.
+
+All of these take an array for holding the values, i.e. for an one line text input field, which is required and should be used to type email addesses use something along
+
+               '$adminmail' => array('adminmail', t('Site administrator email address'), $adminmail, t('Your account email address must match this in order to use the web admin panel.'), 'required', '', 'email'),
+
+To evaluate the input value, you can then use the $_POST array, more precisely the $_POST['adminemail'] variable.
+
+Listed below are the template file names, the general purpose of the template and their field parameters.
+
+### field_checkbox.tpl
+
+A checkbox. If the checkbox is checked its value is **1**. Field parameter:
+
+0. Name of the checkbox,
+1. Label for the checkbox,
+2. State checked? if true then the checkbox will be marked as checked,
+3. Help text for the checkbox.
+
+### field_combobox.tpl
+
+A combobox, combining a pull down selection and a textual input field. Field parameter:
+
+0. Name of the combobox,
+1. Label for the combobox,
+2. Current value of the variable,
+3. Help text for the combobox,
+4. Array holding the possible values for the textual input,
+5. Array holding the possible values for the pull down selection.
+
+### field_custom.tpl
+
+A customizeable template to include a custom element in the form with the usual surroundings, Field parameter:
+
+0. Name of the field,
+1. Label for the field,
+2. the field,
+3. Help text for the field.
+
+### field_input.tpl
+
+A single line input field for textual input. Field parameter:
+
+0. Name of the field,
+1. Label for the input box,
+2. Current value of the variable,
+3. Help text for the input box,
+4. if set to "required" modern browser will check that this input box is filled when submitting the form,
+5. if set to "autofocus" modern browser will put the cursur into this box once the page is loaded,
+6. if set to "email" or "url" modern browser will check that the filled in value corresponds to an email address or URL.
+
+### field_intcheckbox.tpl
+
+A checkbox (see above) but you can define the value of it. Field parameter:
+
+0. Name of the checkbox,
+1. Label for the checkbox,
+2. State checked? if true then the checkbox will be marked as checked,
+3. Value of the checkbox,
+4. Help text for the checkbox.
+
+### field_openid.tpl
+
+An input box (see above) but prepared for special CSS styling for openID input. Field parameter:
+
+0. Name of the field,
+1. Label for the input box,
+2. Current value of the variable,
+3. Help text for the input field.
+
+### field_password.tpl
+
+A single line input field (see above) for textual input. The characters typed in will not be shown by the browser. Field parameter:
+
+0. Name of the field,
+1. Label for the field,
+2. Value for the field, e.g. the old password,
+3. Help text for the input field,
+4. if set to "required" modern browser will check that this field is filled out,
+5. if set to "autofocus" modern browser will put the cursor automatically into this input field.
+
+### field_radio.tpl
+
+A radio button. Field parameter:
+
+0. Name of the radio button,
+1. Label for the radio button,
+2. Current value of the variable,
+3. Help text for the button,
+4. if set, the radio button will be checked.
+
+### field_richtext.tpl
+
+A multi-line input field for *rich* textual content. Field parameter:
+
+0. Name of the input field,
+1. Label for the input box,
+2. Current text for the box,
+3. Help text for the input box.
+
+### field_select.tpl
+
+A drop down selection box. Field parameter:
+
+0. Name of the field,
+1. Label of the selection box,
+2. Current selected value,
+3. Help text for the selection box,
+4. Array holding the possible values of the selection drop down.
+
+### field_select_raw.tpl
+
+A drop down selection box (see above) but you have to prepare the values yourself. Field parameter:
+
+0. Name of the field,
+1. Label of the selection box,
+2. Current selected value,
+3. Help text for the selection box,
+4. Possible values of the selection drop down.
+
+### field_textarea.tpl
+
+A multi-line input field for (plain) textual content. Field parameter:
+
+0. Name of the input field,
+1. Label for the input box,
+2. Current text for the box,
+3. Help text for the input box.
+
+### field_yesno.tpl
+
+A button that has two states *yes* or *no*. Field parameter:
+
+0. Name of the input field,
+1. Label for the button,
+2. Current value,
+3. Help text for the button
+4. if set to an array of two values, these two will be used, otherwise "off" and "on".
diff --git a/doc/snarty3-templates.md b/doc/snarty3-templates.md
deleted file mode 100644 (file)
index 751ef20..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-Friendica Templating Documentation
-==================================
-
-* [Home](help)
-
-Friendica uses [Smarty 3](http://www.smarty.net/) as PHP templating engine. The main templates are found in
-
-               /view/templates
-
-theme authors may overwrite the default templates by putting a files with the same name into the
-
-               /view/themes/$themename/templates
-
-directory.
-
-Templates that are only used by addons shall be placed in the
-
-               /addon/$addonname/templates
-
-directory.
-
-To render a template use the function *get_markup_template* to load the template and *replace_macros* to replace the macros/variables in the just loaded template file.
-
-               $tpl = get_markup_template('install_settings.tpl');
-        $o .= replace_macros($tpl, array( ... ));
-
-the array consists of an association of an identifier and the value for that identifier, i.e.
-
-               '$title' => $install_title,
-
-where the value may as well be an array by its own.
-
-Form Templates
---------------
-
-To guarantee a consistent look and feel for input forms, i.e. in the settings sections, there are templates for the basic form fields. They are initialized with an array of data, depending on the tyle of the field.
-
-All of these take an array for holding the values, i.e. for an one line text input field, which is required and should be used to type email addesses use something along
-
-               '$adminmail' => array('adminmail', t('Site administrator email address'), $adminmail, t('Your account email address must match this in order to use the web admin panel.'), 'required', '', 'email'),
-
-To evaluate the input value, you can then use the $_POST array, more precisely the $_POST['adminemail'] variable.
-
-Listed below are the template file names, the general purpose of the template and their field parameters.
-
-### field_checkbox.tpl
-
-A checkbox. If the checkbox is checked its value is **1**. Field parameter:
-
-0. Name of the checkbox,
-1. Label for the checkbox,
-2. State checked? if true then the checkbox will be marked as checked,
-3. Help text for the checkbox.
-
-### field_combobox.tpl
-
-A combobox, combining a pull down selection and a textual input field. Field parameter:
-
-0. Name of the combobox,
-1. Label for the combobox,
-2. Current value of the variable,
-3. Help text for the combobox,
-4. Array holding the possible values for the textual input,
-5. Array holding the possible values for the pull down selection.
-
-### field_custom.tpl
-
-A customizeable template to include a custom element in the form with the usual surroundings, Field parameter:
-
-0. Name of the field,
-1. Label for the field,
-2. the field,
-3. Help text for the field.
-
-### field_input.tpl
-
-A single line input field for textual input. Field parameter:
-
-0. Name of the field,
-1. Label for the input box,
-2. Current value of the variable,
-3. Help text for the input box,
-4. if set to "required" modern browser will check that this input box is filled when submitting the form,
-5. if set to "autofocus" modern browser will put the cursur into this box once the page is loaded,
-6. if set to "email" or "url" modern browser will check that the filled in value corresponds to an email address or URL.
-
-### field_intcheckbox.tpl
-
-A checkbox (see above) but you can define the value of it. Field parameter:
-
-0. Name of the checkbox,
-1. Label for the checkbox,
-2. State checked? if true then the checkbox will be marked as checked,
-3. Value of the checkbox,
-4. Help text for the checkbox.
-
-### field_openid.tpl
-
-An input box (see above) but prepared for special CSS styling for openID input. Field parameter:
-
-0. Name of the field,
-1. Label for the input box,
-2. Current value of the variable,
-3. Help text for the input field.
-
-### field_password.tpl
-
-A single line input field (see above) for textual input. The characters typed in will not be shown by the browser. Field parameter:
-
-0. Name of the field,
-1. Label for the field,
-2. Value for the field, e.g. the old password,
-3. Help text for the input field,
-4. if set to "required" modern browser will check that this field is filled out,
-5. if set to "autofocus" modern browser will put the cursor automatically into this input field.
-
-### field_radio.tpl
-
-A radio button. Field parameter:
-
-0. Name of the radio button,
-1. Label for the radio button,
-2. Current value of the variable,
-3. Help text for the button,
-4. if set, the radio button will be checked.
-
-### field_richtext.tpl
-
-A multi-line input field for *rich* textual content. Field parameter:
-
-0. Name of the input field,
-1. Label for the input box,
-2. Current text for the box,
-3. Help text for the input box.
-
-### field_select.tpl
-
-A drop down selection box. Field parameter:
-
-0. Name of the field,
-1. Label of the selection box,
-2. Current selected value,
-3. Help text for the selection box,
-4. Array holding the possible values of the selection drop down.
-
-### field_select_raw.tpl
-
-A drop down selection box (see above) but you have to prepare the values yourself. Field parameter:
-
-0. Name of the field,
-1. Label of the selection box,
-2. Current selected value,
-3. Help text for the selection box,
-4. Possible values of the selection drop down.
-
-### field_textarea.tpl
-
-A multi-line input field for (plain) textual content. Field parameter:
-
-0. Name of the input field,
-1. Label for the input box,
-2. Current text for the box,
-3. Help text for the input box.
-
-### field_yesno.tpl
-
-A button that has two states *yes* or *no*. Field parameter:
-
-0. Name of the input field,
-1. Label for the button,
-2. Current value,
-3. Help text for the button
-4. if set to an array of two values, these two will be used, otherwise "off" and "on".
index ec3a76ac289a472088350110335ee2d60a317062..add44c776bc5f130b6bc842891101e970de5dd9c 100644 (file)
@@ -59,19 +59,7 @@ The same rule applies to the JavaScript files found in
 
 they will be overwritten by files in
 
-    /view/theme/**your-theme-name**/js
-
-### Modules
-
-You have the freedom to override core modules found in
-
-    /mod
-
-They will be overwritten by files in
-
-    /view/theme/**your-theme-name**/mod
-
-Be aware that you can break things easily here if you don't know what you do. Also notice that you can override parts of the module – functions not defined in your theme module will be loaded from the core module.
+    /view/theme/**your-theme-name**/js.
 
 ## Expand an existing Theme
 
@@ -300,4 +288,4 @@ The default file is in
     /view/default.php
 
 if you want to change it, say adding a 4th column for banners of your favourite FLOSS projects, place a new default.php file in your theme directory.
-As with the theme.php file, you can use the properties of the $a variable with holds the friendica application to decide what content is displayed.
+As with the theme.php file, you can use the properties of the $a variable with holds the friendica application to decide what content is displayed.
\ No newline at end of file
index 3799e0b189a98830aec305d5da37d2122f7310f8..79a14ab581c84dd3de3ab61d9e3efe8174bf27c1 100644 (file)
@@ -129,7 +129,7 @@ function terminate_friendship($user,$self,$contact) {
        }
        elseif($contact['network'] === NETWORK_DIASPORA) {
                require_once('include/diaspora.php');
-               diaspora_unshare($user,$contact);
+               diaspora::send_unshare($user,$contact);
        }
        elseif($contact['network'] === NETWORK_DFRN) {
                require_once('include/dfrn.php');
@@ -555,60 +555,6 @@ function posts_from_gcontact($a, $gcontact_id) {
        return $o;
 }
 
-/**
- * @brief set the gcontact-id in all item entries
- *
- * This job has to be started multiple times until all entries are set.
- * It isn't started in the update function since it would consume too much time and can be done in the background.
- */
-function item_set_gcontact() {
-       define ('POST_UPDATE_VERSION', 1192);
-
-       // Was the script completed?
-       if (get_config("system", "post_update_version") >= POST_UPDATE_VERSION)
-               return;
-
-       // Check if the first step is done (Setting "gcontact-id" in the item table)
-       $r = q("SELECT `author-link`, `author-name`, `author-avatar`, `uid`, `network` FROM `item` WHERE `gcontact-id` = 0 LIMIT 1000");
-       if (!$r) {
-               // Are there unfinished entries in the thread table?
-               $r = q("SELECT COUNT(*) AS `total` FROM `thread`
-                       INNER JOIN `item` ON `item`.`id` =`thread`.`iid`
-                       WHERE `thread`.`gcontact-id` = 0 AND
-                               (`thread`.`uid` IN (SELECT `uid` from `user`) OR `thread`.`uid` = 0)");
-
-               if ($r AND ($r[0]["total"] == 0)) {
-                       set_config("system", "post_update_version", POST_UPDATE_VERSION);
-                       return false;
-               }
-
-               // Update the thread table from the item table
-               q("UPDATE `thread` INNER JOIN `item` ON `item`.`id`=`thread`.`iid`
-                               SET `thread`.`gcontact-id` = `item`.`gcontact-id`
-                       WHERE `thread`.`gcontact-id` = 0 AND
-                               (`thread`.`uid` IN (SELECT `uid` from `user`) OR `thread`.`uid` = 0)");
-
-               return false;
-       }
-
-       $item_arr = array();
-       foreach ($r AS $item) {
-               $index = $item["author-link"]."-".$item["uid"];
-               $item_arr[$index] = array("author-link" => $item["author-link"],
-                                               "uid" => $item["uid"],
-                                               "network" => $item["network"]);
-       }
-
-       // Set the "gcontact-id" in the item table and add a new gcontact entry if needed
-       foreach($item_arr AS $item) {
-               $gcontact_id = get_gcontact_id(array("url" => $item['author-link'], "network" => $item['network'],
-                                               "photo" => $item['author-avatar'], "name" => $item['author-name']));
-               q("UPDATE `item` SET `gcontact-id` = %d WHERE `uid` = %d AND `author-link` = '%s' AND `gcontact-id` = 0",
-                       intval($gcontact_id), intval($item["uid"]), dbesc($item["author-link"]));
-       }
-       return true;
-}
-
 /**
  * @brief Returns posts from a given contact
  *
index 7b5fb1c2f651397ef2140d5af45401ee052dff67..17a6b6730b6956c72e8d26aede56e108dc8a4f3f 100644 (file)
@@ -95,12 +95,12 @@ class ForumManager {
                                $selected = (($cid == $contact['id']) ? ' forum-selected' : '');
 
                                $entry = array(
-                                       'url' => z_root() . '/network?f=&cid=' . $contact['id'],
-                                       'external_url' => z_root() . '/redir/' . $contact['id'],
+                                       'url' => 'network?f=&cid=' . $contact['id'],
+                                       'external_url' => 'redir/' . $contact['id'],
                                        'name' => $contact['name'],
                                        'cid' => $contact['id'],
                                        'selected'      => $selected,
-                                       'micro' => proxy_url($contact['micro'], false, PROXY_SIZE_MICRO),
+                                       'micro' => App::remove_baseurl(proxy_url($contact['micro'], false, PROXY_SIZE_MICRO)),
                                        'id' => ++$id,
                                );
                                $entries[] = $entry;
diff --git a/include/NotificationsManager.php b/include/NotificationsManager.php
new file mode 100644 (file)
index 0000000..5f8211e
--- /dev/null
@@ -0,0 +1,136 @@
+<?php
+/**
+ * @file include/NotificationsManager.php
+ */
+require_once('include/html2plain.php');
+require_once("include/datetime.php");
+require_once("include/bbcode.php");
+
+/**
+ * @brief Read and write notifications from/to database
+ */
+class NotificationsManager {
+    private $a;
+    
+    public function __construct() {
+        $this->a = get_app();
+    }
+    
+       /**
+        * @brief set some extra note properties
+        *
+        * @param array $notes array of note arrays from db
+        * @return array Copy of input array with added properties
+        * 
+        * Set some extra properties to note array from db:
+        *  - timestamp as int in default TZ
+        *  - date_rel : relative date string
+        *  - msg_html: message as html string
+        *  - msg_plain: message as plain text string
+        */
+    private function _set_extra($notes) {
+        $rets = array();
+        foreach($notes as $n) {
+            $local_time = datetime_convert('UTC',date_default_timezone_get(),$n['date']);
+            $n['timestamp'] = strtotime($local_time);
+            $n['date_rel'] = relative_date($n['date']);
+                       $n['msg_html'] = bbcode($n['msg'], false, false, false, false);
+                       $n['msg_plain'] = explode("\n",trim(html2plain($n['msg_html'], 0)))[0];
+                       
+            $rets[] = $n;
+        }
+        return $rets;
+    }
+
+
+    /**
+     * @brief get all notifications for local_user()
+     *
+     * @param array $filter optional Array "column name"=>value: filter query by columns values
+     * @param string $order optional Space separated list of column to sort by. prepend name with "+" to sort ASC, "-" to sort DESC. Default to "-date"
+     * @param string $limit optional Query limits
+     *
+     * @return array of results or false on errors
+     */
+    public function getAll($filter = array(), $order="-date", $limit="") {
+        $filter_str = array();
+        $filter_sql = "";
+        foreach($filter as $column => $value) {
+            $filter_str[] = sprintf("`%s` = '%s'", $column, dbesc($value));
+        }
+        if (count($filter_str)>0) {
+            $filter_sql = "AND ".implode(" AND ", $filter_str);
+        }
+        
+        $aOrder = explode(" ", $order);
+        $asOrder = array();
+        foreach($aOrder as $o) {
+            $dir = "asc";
+            if ($o[0]==="-") {
+                $dir = "desc";
+                $o = substr($o,1);
+            }
+            if ($o[0]==="+") {
+                $dir = "asc";
+                $o = substr($o,1);
+            }
+            $asOrder[] = "$o $dir";
+        }
+        $order_sql = implode(", ", $asOrder);
+        
+        if ($limit!="") $limit = " LIMIT ".$limit;
+        
+               $r = q("SELECT * FROM `notify` WHERE `uid` = %d $filter_sql ORDER BY $order_sql $limit",
+                       intval(local_user())
+               );
+        if ($r!==false && count($r)>0) return $this->_set_extra($r);
+        return false;
+    }
+    
+    /**
+     * @brief get one note for local_user() by $id value
+     *
+     * @param int $id
+     * @return array note values or null if not found
+     */
+    public function getByID($id) {
+        $r = q("SELECT * FROM `notify` WHERE `id` = %d AND `uid` = %d LIMIT 1",
+                intval($id),
+                intval(local_user())
+        );
+        if($r!==false && count($r)>0) {
+            return $this->_set_extra($r)[0];
+        }
+        return null;
+    }
+    
+    /**
+     * @brief set seen state of $note of local_user()
+     *
+     * @param array $note
+     * @param bool $seen optional true or false, default true
+     * @return bool true on success, false on errors
+     */
+    public function setSeen($note, $seen = true) {
+        return q("UPDATE `notify` SET `seen` = %d WHERE ( `link` = '%s' OR ( `parent` != 0 AND `parent` = %d AND `otype` = '%s' )) AND `uid` = %d",
+            intval($seen),
+            dbesc($note['link']),
+            intval($note['parent']),
+            dbesc($note['otype']),
+            intval(local_user())
+        );
+    }
+       
+    /**
+     * @brief set seen state of all notifications of local_user()
+     *
+     * @param bool $seen optional true or false. default true
+     * @return bool true on success, false on error
+     */
+    public function setAllSeen($seen = true) {
+       return q("UPDATE `notify` SET `seen` = %d WHERE `uid` = %d",
+            intval($seen),
+                       intval(local_user())
+               );
+    }
+}
index ca6489b16ad37255311c4b327442824552413ac4..68926a997ec1aa9dc8afd888ce3d5249030b4bdb 100644 (file)
@@ -2,6 +2,7 @@
 
 require_once('library/HTML5/Parser.php');
 require_once('include/crypto.php');
+require_once('include/feed.php');
 
 if(! function_exists('scrape_dfrn')) {
 function scrape_dfrn($url, $dont_probe = false) {
@@ -12,9 +13,25 @@ function scrape_dfrn($url, $dont_probe = false) {
 
        logger('scrape_dfrn: url=' . $url);
 
+       // Try to fetch the data from noscrape. This is faster than parsing the HTML
+       $noscrape = str_replace("/hcard/", "/noscrape/", $url);
+       $noscrapejson = fetch_url($noscrape);
+       $noscrapedata = array();
+       if ($noscrapejson) {
+               $noscrapedata = json_decode($noscrapejson, true);
+
+               if (is_array($noscrapedata)) {
+                       if ($noscrapedata["nick"] != "")
+                               return($noscrapedata);
+                       else
+                               unset($noscrapedata["nick"]);
+               } else
+                       $noscrapedata = array();
+       }
+
        $s = fetch_url($url);
 
-       if(! $s)
+       if (!$s)
                return $ret;
 
        if (!$dont_probe) {
@@ -91,8 +108,7 @@ function scrape_dfrn($url, $dont_probe = false) {
                        }
                }
        }
-
-       return $ret;
+       return array_merge($ret, $noscrapedata);
 }}
 
 
@@ -342,7 +358,7 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
 
        $result = array();
 
-       if(! $url)
+       if (!$url)
                return $result;
 
        $result = Cache::get("probe_url:".$mode.":".$url);
@@ -351,6 +367,7 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
                return $result;
        }
 
+       $original_url = $url;
        $network = null;
        $diaspora = false;
        $diaspora_base = '';
@@ -366,8 +383,6 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
                $network = NETWORK_TWITTER;
        }
 
-       // Twitter is deactivated since twitter closed its old API
-       //$twitter = ((strpos($url,'twitter.com') !== false) ? true : false);
        $lastfm  = ((strpos($url,'last.fm/user') !== false) ? true : false);
 
        $at_addr = ((strpos($url,'@') !== false) ? true : false);
@@ -381,7 +396,12 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
                else
                        $links = lrdd($url);
 
-               if(count($links)) {
+               if ((count($links) == 0) AND strstr($url, "/index.php")) {
+                       $url = str_replace("/index.php", "", $url);
+                       $links = lrdd($url);
+               }
+
+               if (count($links)) {
                        $has_lrdd = true;
 
                        logger('probe_url: found lrdd links: ' . print_r($links,true), LOGGER_DATA);
@@ -428,12 +448,21 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
                        // aliases, let's hope we're lucky and get one that matches the feed author-uri because
                        // otherwise we're screwed.
 
+                       $backup_alias = "";
+
                        foreach($links as $link) {
                                if($link['@attributes']['rel'] === 'alias') {
                                        if(strpos($link['@attributes']['href'],'@') === false) {
                                                if(isset($profile)) {
-                                                       if($link['@attributes']['href'] !== $profile)
-                                                               $alias = unamp($link['@attributes']['href']);
+                                                       $alias_url = $link['@attributes']['href'];
+
+                                                       if(($alias_url !== $profile) AND ($backup_alias == "") AND
+                                                               ($alias_url !== str_replace("/index.php", "", $profile)))
+                                                               $backup_alias = $alias_url;
+
+                                                       if(($alias_url !== $profile) AND !strstr($alias_url, "index.php") AND
+                                                               ($alias_url !== str_replace("/index.php", "", $profile)))
+                                                               $alias = $alias_url;
                                                }
                                                else
                                                        $profile = unamp($link['@attributes']['href']);
@@ -441,6 +470,9 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
                                }
                        }
 
+                       if ($alias == "")
+                               $alias = $backup_alias;
+
                        // If the profile is different from the url then the url is abviously an alias
                        if (($alias == "") AND ($profile != "") AND !$at_addr AND (normalise_link($profile) != normalise_link($url)))
                                $alias = $url;
@@ -604,21 +636,6 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
                        $vcard['nick'] = $addr_parts[0];
                }
 
-               /* if($twitter) {
-                       logger('twitter: setup');
-                       $tid = basename($url);
-                       $tapi = 'https://api.twitter.com/1/statuses/user_timeline.rss';
-                       if(intval($tid))
-                               $poll = $tapi . '?user_id=' . $tid;
-                       else
-                               $poll = $tapi . '?screen_name=' . $tid;
-                       $profile = 'http://twitter.com/#!/' . $tid;
-                       //$vcard['photo'] = 'https://api.twitter.com/1/users/profile_image/' . $tid;
-                       $vcard['photo'] = 'https://api.twitter.com/1/users/profile_image?screen_name=' . $tid . '&size=bigger';
-                       $vcard['nick'] = $tid;
-                       $vcard['fn'] = $tid;
-               } */
-
                if($lastfm) {
                        $profile = $url;
                        $poll = str_replace(array('www.','last.fm/'),array('','ws.audioscrobbler.com/1.0/'),$url) . '/recenttracks.rss';
@@ -662,85 +679,41 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
 
                        if(x($feedret,'photo') && (! x($vcard,'photo')))
                                $vcard['photo'] = $feedret['photo'];
-                       require_once('library/simplepie/simplepie.inc');
-                       $feed = new SimplePie();
+
                        $cookiejar = tempnam(get_temppath(), 'cookiejar-scrape-feed-');
                        $xml = fetch_url($poll, false, $redirects, 0, Null, $cookiejar);
                        unlink($cookiejar);
 
                        logger('probe_url: fetch feed: ' . $poll . ' returns: ' . $xml, LOGGER_DATA);
-                       $a = get_app();
 
-                       logger('probe_url: scrape_feed: headers: ' . $a->get_curl_headers(), LOGGER_DATA);
+                       if ($xml == "") {
+                               logger("scrape_feed: XML is empty for feed ".$poll);
+                               $network = NETWORK_PHANTOM;
+                       } else {
+                               $data = feed_import($xml,$dummy1,$dummy2, $dummy3, true);
 
-                       // Don't try and parse an empty string
-                       $feed->set_raw_data(($xml) ? $xml : '<?xml version="1.0" encoding="utf-8" ?><xml></xml>');
+                               if (!is_array($data)) {
+                                       logger("scrape_feed: This doesn't seem to be a feed: ".$poll);
+                                       $network = NETWORK_PHANTOM;
+                               } else {
+                                       if (($vcard["photo"] == "") AND ($data["header"]["author-avatar"] != ""))
+                                               $vcard["photo"] = $data["header"]["author-avatar"];
 
-                       $feed->init();
-                       if($feed->error()) {
-                               logger('probe_url: scrape_feed: Error parsing XML: ' . $feed->error());
-                               $network = NETWORK_PHANTOM;
-                       }
+                                       if (($vcard["fn"] == "") AND ($data["header"]["author-name"] != ""))
+                                               $vcard["fn"] = $data["header"]["author-name"];
 
-                       if(! x($vcard,'photo'))
-                               $vcard['photo'] = $feed->get_image_url();
-                       $author = $feed->get_author();
-
-                       if($author) {
-                               $vcard['fn'] = unxmlify(trim($author->get_name()));
-                               if(! $vcard['fn'])
-                                       $vcard['fn'] = trim(unxmlify($author->get_email()));
-                               if(strpos($vcard['fn'],'@') !== false)
-                                       $vcard['fn'] = substr($vcard['fn'],0,strpos($vcard['fn'],'@'));
-
-                               $email = unxmlify($author->get_email());
-                               if(! $profile && $author->get_link())
-                                       $profile = trim(unxmlify($author->get_link()));
-                               if(! $vcard['photo']) {
-                                       $rawtags = $feed->get_feed_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
-                                       if($rawtags) {
-                                               $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10];
-                                               if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo'))
-                                                       $vcard['photo'] = $elems['link'][0]['attribs']['']['href'];
-                                       }
-                               }
-                               // Fetch fullname via poco:displayName
-                               $pocotags = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
-                               if ($pocotags) {
-                                       $elems = $pocotags[0]['child']['http://portablecontacts.net/spec/1.0'];
-                                       if (isset($elems["displayName"]))
-                                               $vcard['fn'] = $elems["displayName"][0]["data"];
-                                       if (isset($elems["preferredUsername"]))
-                                               $vcard['nick'] = $elems["preferredUsername"][0]["data"];
-                               }
-                       }
-                       else {
-                               $item = $feed->get_item(0);
-                               if($item) {
-                                       $author = $item->get_author();
-                                       if($author) {
-                                               $vcard['fn'] = trim(unxmlify($author->get_name()));
-                                               if(! $vcard['fn'])
-                                                       $vcard['fn'] = trim(unxmlify($author->get_email()));
-                                               if(strpos($vcard['fn'],'@') !== false)
-                                                       $vcard['fn'] = substr($vcard['fn'],0,strpos($vcard['fn'],'@'));
-                                               $email = unxmlify($author->get_email());
-                                               if(! $profile && $author->get_link())
-                                                       $profile = trim(unxmlify($author->get_link()));
-                                       }
-                                       if(! $vcard['photo']) {
-                                               $rawmedia = $item->get_item_tags('http://search.yahoo.com/mrss/','thumbnail');
-                                               if($rawmedia && $rawmedia[0]['attribs']['']['url'])
-                                                       $vcard['photo'] = unxmlify($rawmedia[0]['attribs']['']['url']);
-                                       }
-                                       if(! $vcard['photo']) {
-                                               $rawtags = $item->get_item_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
-                                               if($rawtags) {
-                                                       $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10];
-                                                       if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo'))
-                                                               $vcard['photo'] = $elems['link'][0]['attribs']['']['href'];
-                                               }
-                                       }
+                                       if (($vcard["nick"] == "") AND ($data["header"]["author-nick"] != ""))
+                                               $vcard["nick"] = $data["header"]["author-nick"];
+
+                                       if ($network == NETWORK_OSTATUS) {
+                                               if ($data["header"]["author-id"] != "")
+                                                       $alias = $data["header"]["author-id"];
+
+                                               if ($data["header"]["author-link"] != "")
+                                                       $profile = $data["header"]["author-link"];
+
+                                       } elseif(!$profile AND ($data["header"]["author-link"] != "") AND !in_array($network, array("", NETWORK_FEED)))
+                                               $profile = $data["header"]["author-link"];
                                }
                        }
 
@@ -783,27 +756,9 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
                                }
                        }
 
-                       if((! $vcard['photo']) && strlen($email))
-                               $vcard['photo'] = avatar_img($email);
-                       if($poll === $profile)
-                               $lnk = $feed->get_permalink();
-                       if(isset($lnk) && strlen($lnk))
-                               $profile = $lnk;
-
-                       if(! $network) {
+                       if(! $network)
                                $network = NETWORK_FEED;
-                               // If it is a feed, don't take the author name as feed name
-                               unset($vcard['fn']);
-                       }
-                       if(! (x($vcard,'fn')))
-                               $vcard['fn'] = notags($feed->get_title());
-                       if(! (x($vcard,'fn')))
-                               $vcard['fn'] = notags($feed->get_description());
-
-                       if(strpos($vcard['fn'],'Twitter / ') !== false) {
-                               $vcard['fn'] = substr($vcard['fn'],strpos($vcard['fn'],'/')+1);
-                               $vcard['fn'] = trim($vcard['fn']);
-                       }
+
                        if(! x($vcard,'nick')) {
                                $vcard['nick'] = strtolower(notags(unxmlify($vcard['fn'])));
                                if(strpos($vcard['nick'],' '))
@@ -816,7 +771,7 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
 
        if(! x($vcard,'photo')) {
                $a = get_app();
-               $vcard['photo'] = $a->get_baseurl() . '/images/person-175.jpg' ;
+               $vcard['photo'] = App::get_baseurl() . '/images/person-175.jpg' ;
        }
 
        if(! $profile)
@@ -828,18 +783,21 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
                $vcard['fn'] = $url;
 
        if (($notify != "") AND ($poll != "")) {
-               $baseurl = matching(normalise_link($notify), normalise_link($poll));
+               $baseurl = matching_url(normalise_link($notify), normalise_link($poll));
 
-               $baseurl2 = matching($baseurl, normalise_link($profile));
+               $baseurl2 = matching_url($baseurl, normalise_link($profile));
                if ($baseurl2 != "")
                        $baseurl = $baseurl2;
        }
 
        if (($baseurl == "") AND ($notify != ""))
-               $baseurl = matching(normalise_link($profile), normalise_link($notify));
+               $baseurl = matching_url(normalise_link($profile), normalise_link($notify));
 
        if (($baseurl == "") AND ($poll != ""))
-               $baseurl = matching(normalise_link($profile), normalise_link($poll));
+               $baseurl = matching_url(normalise_link($profile), normalise_link($poll));
+
+       if (substr($baseurl, -10) == "/index.php")
+               $baseurl = str_replace("/index.php", "", $baseurl);
 
        $baseurl = rtrim($baseurl, "/");
 
@@ -888,25 +846,82 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
        }
 
        // Only store into the cache if the value seems to be valid
-       if ($result['network'] != NETWORK_PHANTOM)
-               Cache::set("probe_url:".$mode.":".$url,serialize($result), CACHE_DAY);
+       if ($result['network'] != NETWORK_PHANTOM) {
+               Cache::set("probe_url:".$mode.":".$original_url,serialize($result), CACHE_DAY);
+
+               /// @todo temporary fix - we need a real contact update function that updates only changing fields
+               /// The biggest problem is the avatar picture that could have a reduced image size.
+               /// It should only be updated if the existing picture isn't existing anymore.
+               if (($result['network'] != NETWORK_FEED) AND ($mode == PROBE_NORMAL) AND
+                       $result["name"] AND $result["nick"] AND $result["url"] AND $result["addr"] AND $result["poll"])
+                       q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `url` = '%s', `addr` = '%s',
+                                       `notify` = '%s', `poll` = '%s', `alias` = '%s', `success_update` = '%s'
+                               WHERE `nurl` = '%s' AND NOT `self` AND `uid` = 0",
+                               dbesc($result["name"]),
+                               dbesc($result["nick"]),
+                               dbesc($result["url"]),
+                               dbesc($result["addr"]),
+                               dbesc($result["notify"]),
+                               dbesc($result["poll"]),
+                               dbesc($result["alias"]),
+                               dbesc(datetime_convert()),
+                               dbesc(normalise_link($result['url']))
+               );
+       }
 
        return $result;
 }
 
-function matching($part1, $part2) {
-       $len = min(strlen($part1), strlen($part2));
+/**
+ * @brief Find the matching part between two url
+ *
+ * @param string $url1
+ * @param string $url2
+ * @return string The matching part
+ */
+function matching_url($url1, $url2) {
+
+       if (($url1 == "") OR ($url2 == ""))
+               return "";
+
+       $url1 = normalise_link($url1);
+       $url2 = normalise_link($url2);
+
+       $parts1 = parse_url($url1);
+       $parts2 = parse_url($url2);
+
+       if (!isset($parts1["host"]) OR !isset($parts2["host"]))
+               return "";
+
+       if ($parts1["scheme"] != $parts2["scheme"])
+               return "";
+
+       if ($parts1["host"] != $parts2["host"])
+               return "";
+
+       if ($parts1["port"] != $parts2["port"])
+               return "";
+
+       $match = $parts1["scheme"]."://".$parts1["host"];
+
+       if ($parts1["port"])
+               $match .= ":".$parts1["port"];
+
+       $pathparts1 = explode("/", $parts1["path"]);
+       $pathparts2 = explode("/", $parts2["path"]);
 
-       $match = "";
-       $matching = true;
        $i = 0;
-       while (($i <= $len) AND $matching) {
-               if (substr($part1, $i, 1) == substr($part2, $i, 1))
-                       $match .= substr($part1, $i, 1);
-               else
-                       $matching = false;
+       $path = "";
+       do {
+               $path1 = $pathparts1[$i];
+               $path2 = $pathparts2[$i];
 
-               $i++;
-       }
-       return($match);
+               if ($path1 == $path2)
+                       $path .= $path1."/";
+
+       } while (($path1 == $path2) AND ($i++ <= count($pathparts1)));
+
+       $match .= $path;
+
+       return normalise_link($match);
 }
index 4d206da28e771d2de71a16df9e9d1a010b99ed30..a494e3cdd9ea23f704f91d7ae8a457e36371cbdf 100644 (file)
@@ -23,6 +23,7 @@
        require_once('include/message.php');
        require_once('include/group.php');
        require_once('include/like.php');
+       require_once('include/NotificationsManager.php');
 
 
        define('API_METHOD_ANY','*');
                if (!isset($_SERVER['PHP_AUTH_USER'])) {
                        logger('API_login: ' . print_r($_SERVER,true), LOGGER_DEBUG);
                        header('WWW-Authenticate: Basic realm="Friendica"');
-                       header('HTTP/1.0 401 Unauthorized');
-                       die((api_error($a, 'json', "This api requires login")));
-
-                       //die('This api requires login');
+                       throw new UnauthorizedException("This API requires login");
                }
 
                $user = $_SERVER['PHP_AUTH_USER'];
                if((! $record) || (! count($record))) {
                        logger('API_login failure: ' . print_r($_SERVER,true), LOGGER_DEBUG);
                        header('WWW-Authenticate: Basic realm="Friendica"');
-                       header('HTTP/1.0 401 Unauthorized');
-                       die('This api requires login');
+                       #header('HTTP/1.0 401 Unauthorized');
+                       #die('This api requires login');
+                       throw new UnauthorizedException("This API requires login");
                }
 
                authenticate_success($record); $_SESSION["allow_api"] = true;
         */
        function api_call(&$a){
                GLOBAL $API, $called_api;
-
+               
                $type="json";
                if (strpos($a->query_string, ".xml")>0) $type="xml";
                if (strpos($a->query_string, ".json")>0) $type="json";
         *
         * @param Api $a
         * @param string $type Return type (xml, json, rss, as)
-        * @param string $error Error message
+        * @param HTTPException $error Error object
+        * @return strin error message formatted as $type
         */
        function api_error(&$a, $type, $e) {
                $error = ($e->getMessage()!==""?$e->getMessage():$e->httpdesc);
        }
 
 
+       /**
+        * @brief transform $data array in xml without a template
+        *
+        * @param array $data
+        * @return string xml string
+        */
+       function api_array_to_xml($data, $ename="") {
+               $attrs="";
+               $childs="";
+               if (count($data)==1 && !is_array($data[0])) {
+                       $ename = array_keys($data)[0];
+                       $v = $data[$ename];
+                       return "<$ename>$v</$ename>";
+               }
+               foreach($data as $k=>$v) {
+                       $k=trim($k,'$');
+                       if (!is_array($v)) {
+                               $attrs .= sprintf('%s="%s" ', $k, $v);
+                       } else {
+                               if (is_numeric($k)) $k=trim($ename,'s');
+                               $childs.=api_array_to_xml($v, $k);
+                       }
+               }
+               $res = $childs;
+               if ($ename!="") $res = "<$ename $attrs>$res</$ename>";
+               return $res;
+       }
+
        /**
         *  load api $templatename for $type and replace $data array
         */
                        case "rss":
                        case "xml":
                                $data = array_xmlify($data);
-                               $tpl = get_markup_template("api_".$templatename."_".$type.".tpl");
-                               if(! $tpl) {
-                                       header ("Content-Type: text/xml");
-                                       echo '<?xml version="1.0" encoding="UTF-8"?>'."\n".'<status><error>not implemented</error></status>';
-                                       killme();
+                               if ($templatename==="<auto>") {
+                                       $ret = api_array_to_xml($data); 
+                               } else {
+                                       $tpl = get_markup_template("api_".$templatename."_".$type.".tpl");
+                                       if(! $tpl) {
+                                               header ("Content-Type: text/xml");
+                                               echo '<?xml version="1.0" encoding="UTF-8"?>'."\n".'<status><error>not implemented</error></status>';
+                                               killme();
+                                       }
+                                       $ret = replace_macros($tpl, $data);
                                }
-                               $ret = replace_macros($tpl, $data);
                                break;
                        case "json":
                                $ret = $data;
 
                if((strpos($txt,'<') !== false) || (strpos($txt,'>') !== false)) {
 
-                       require_once('library/HTMLPurifier.auto.php');
-
                        $txt = html2bb_video($txt);
                        $config = HTMLPurifier_Config::createDefault();
                        $config->set('Cache.DefinitionImpl', null);
                if(requestdata('htmlstatus')) {
                        $txt = requestdata('htmlstatus');
                        if((strpos($txt,'<') !== false) || (strpos($txt,'>') !== false)) {
-
-                               require_once('library/HTMLPurifier.auto.php');
-
                                $txt = html2bb_video($txt);
 
                                $config = HTMLPurifier_Config::createDefault();
 
                                if ($posts_day > $throttle_day) {
                                        logger('Daily posting limit reached for user '.api_user(), LOGGER_DEBUG);
-                                       die(api_error($a, $type, sprintf(t("Daily posting limit of %d posts reached. The post was rejected."), $throttle_day)));
+                                       #die(api_error($a, $type, sprintf(t("Daily posting limit of %d posts reached. The post was rejected."), $throttle_day)));
+                                       throw new TooManyRequestsException(sprintf(t("Daily posting limit of %d posts reached. The post was rejected."), $throttle_day));
                                }
                        }
 
 
                                if ($posts_week > $throttle_week) {
                                        logger('Weekly posting limit reached for user '.api_user(), LOGGER_DEBUG);
-                                       die(api_error($a, $type, sprintf(t("Weekly posting limit of %d posts reached. The post was rejected."), $throttle_week)));
+                                       #die(api_error($a, $type, sprintf(t("Weekly posting limit of %d posts reached. The post was rejected."), $throttle_week)));
+                                       throw new TooManyRequestsException(sprintf(t("Weekly posting limit of %d posts reached. The post was rejected."), $throttle_week));
+
                                }
                        }
 
 
                                if ($posts_month > $throttle_month) {
                                        logger('Monthly posting limit reached for user '.api_user(), LOGGER_DEBUG);
-                                       die(api_error($a, $type, sprintf(t("Monthly posting limit of %d posts reached. The post was rejected."), $throttle_month)));
+                                       #die(api_error($a, $type, sprintf(t("Monthly posting limit of %d posts reached. The post was rejected."), $throttle_month)));
+                                       throw new TooManyRequestsException(sprintf(t("Monthly posting limit of %d posts reached. The post was rejected."), $throttle_month));
                                }
                        }
 
                return api_apply_template("timeline", $type, $data);
        }
        api_register_func('api/conversation/show','api_conversation_show', true);
+       api_register_func('api/statusnet/conversation','api_conversation_show', true);
 
 
        /**
                        `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`,
                        `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`,
                        `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid`
-                       FROM `item`, `contact`
+                       FROM `item`  FORCE INDEX (`uid_id`), `contact`
                        WHERE `item`.`uid` = %d AND `verb` = '%s'
                        AND NOT (`item`.`author-link` IN ('https://%s', 'http://%s'))
-                       AND `item`.`visible` = 1 and `item`.`moderated` = 0 AND `item`.`deleted` = 0
+                       AND `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted`
                        AND `contact`.`id` = `item`.`contact-id`
-                       AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0
-                       AND `item`.`parent` IN (SELECT `iid` from thread where uid = %d AND `mention` AND !`ignored`)
+                       AND NOT `contact`.`blocked` AND NOT `contact`.`pending`
+                       AND `item`.`parent` IN (SELECT `iid` FROM `thread` WHERE `uid` = %d AND `mention` AND !`ignored`)
                        $sql_extra
                        AND `item`.`id`>%d
                        ORDER BY `item`.`id` DESC LIMIT %d ,%d ",
                $action_argv_id=2;
                if ($a->argv[1]=="1.1") $action_argv_id=3;
 
-               if ($a->argc<=$action_argv_id) die(api_error($a, $type, t("Invalid request.")));
+               if ($a->argc<=$action_argv_id) throw new BadRequestException("Invalid request.");
                $action = str_replace(".".$type,"",$a->argv[$action_argv_id]);
                if ($a->argc==$action_argv_id+2) {
                        $itemid = intval($a->argv[$action_argv_id+1]);
        api_register_func('api/friendica/activity/unattendno', 'api_friendica_activity', true, API_METHOD_POST);
        api_register_func('api/friendica/activity/unattendmaybe', 'api_friendica_activity', true, API_METHOD_POST);
 
+       /**
+        * @brief Returns notifications
+        *
+        * @param App $a
+        * @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
+        * @return string
+       */
+       function api_friendica_notification(&$a, $type) {
+               if (api_user()===false) throw new ForbiddenException();
+               if ($a->argc!==3) throw new BadRequestException("Invalid argument count");
+               $nm = new NotificationsManager();
+               
+               $notes = $nm->getAll(array(), "+seen -date", 50);
+               return api_apply_template("<auto>", $type, array('$notes' => $notes));
+       }
+       
+       /**
+        * @brief Set notification as seen and returns associated item (if possible)
+        *
+        * POST request with 'id' param as notification id
+        * 
+        * @param App $a
+        * @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
+        * @return string
+        */
+       function api_friendica_notification_seen(&$a, $type){
+               if (api_user()===false) throw new ForbiddenException();
+               if ($a->argc!==4) throw new BadRequestException("Invalid argument count");
+               
+               $id = (x($_REQUEST, 'id') ? intval($_REQUEST['id']) : 0);
+               
+               $nm = new NotificationsManager();               
+               $note = $nm->getByID($id);
+               if (is_null($note)) throw new BadRequestException("Invalid argument");
+               
+               $nm->setSeen($note);
+               if ($note['otype']=='item') {
+                       // would be really better with an ItemsManager and $im->getByID() :-P
+                       $r = q("SELECT * FROM `item` WHERE `id`=%d AND `uid`=%d",
+                               intval($note['iid']),
+                               intval(local_user())
+                       );
+                       if ($r!==false) {
+                               // we found the item, return it to the user
+                               $user_info = api_get_user($a);
+                               $ret = api_format_items($r,$user_info);
+                               $data = array('$statuses' => $ret);
+                               return api_apply_template("timeline", $type, $data);
+                       }
+                       // the item can't be found, but we set the note as seen, so we count this as a success
+               } 
+               return api_apply_template('<auto>', $type, array('status' => "success"));
+       }
+       
+       api_register_func('api/friendica/notification/seen', 'api_friendica_notification_seen', true, API_METHOD_POST);
+       api_register_func('api/friendica/notification', 'api_friendica_notification', true, API_METHOD_GET);
+       
+
 /*
 To.Do:
     [pagename] => api/1.1/statuses/lookup.json
index a5b6432fff37c35cc31070eb7ae585129accfae1..4abff1971008ec77f20d83f80cd59e6cc4442f45 100644 (file)
@@ -5,35 +5,14 @@ require_once('include/security.php');
 require_once('include/datetime.php');
 
 function nuke_session() {
-       if (get_config('system', 'disable_database_session')) {
-               session_unset();
-               return;
-       }
 
        new_cookie(0); // make sure cookie is deleted on browser close, as a security measure
-
-       unset($_SESSION['authenticated']);
-       unset($_SESSION['uid']);
-       unset($_SESSION['visitor_id']);
-       unset($_SESSION['administrator']);
-       unset($_SESSION['cid']);
-       unset($_SESSION['theme']);
-       unset($_SESSION['mobile-theme']);
-       unset($_SESSION['page_flags']);
-       unset($_SESSION['submanage']);
-       unset($_SESSION['my_url']);
-       unset($_SESSION['my_address']);
-       unset($_SESSION['addr']);
-       unset($_SESSION['return_url']);
-
+       session_unset();
 }
 
 
 // login/logout
 
-
-
-
 if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-params'))) || ($_POST['auth-params'] !== 'login'))) {
 
        if(((x($_POST,'auth-params')) && ($_POST['auth-params'] === 'logout')) || ($a->module === 'logout')) {
@@ -41,6 +20,7 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p
                // process logout request
                call_hooks("logging_out");
                nuke_session();
+               new_cookie(-1);
                info( t('Logged out.') . EOL);
                goaway(z_root());
        }
@@ -90,8 +70,7 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p
                }
                authenticate_success($r[0], false, false, $login_refresh);
        }
-}
-else {
+} else {
 
        if(isset($_SESSION)) {
                nuke_session();
@@ -209,13 +188,11 @@ else {
 }
 
 function new_cookie($time) {
-       if (!get_config('system', 'disable_database_session'))
-               $old_sid = session_id();
 
-       session_set_cookie_params($time);
+       if ($time != 0)
+               $time = $time + time();
 
-       if (!get_config('system', 'disable_database_session')) {
-               session_regenerate_id(false);
-               q("UPDATE session SET sid = '%s' WHERE sid = '%s'", dbesc(session_id()), dbesc($old_sid));
-       }
+       $params = session_get_cookie_params();
+       setcookie(session_name(), session_id(), $time, $params['path'], $params['domain'], $params['secure'], isset($params['httponly']));
+       return;
 }
diff --git a/include/autoloader.php b/include/autoloader.php
new file mode 100644 (file)
index 0000000..6caa082
--- /dev/null
@@ -0,0 +1,69 @@
+<?php\r
+/**\r
+ * @file include/autoloader.php\r
+ */\r
+\r
+/**\r
+ * @brief composer-derived autoloader init\r
+ **/\r
+class FriendicaAutoloaderInit\r
+{\r
+    private static $loader;\r
+\r
+    public static function loadClassLoader($class)\r
+    {\r
+        if ('Composer\Autoload\ClassLoader' === $class) {\r
+            require __DIR__ . '/autoloader/ClassLoader.php';\r
+        }\r
+    }\r
+\r
+    public static function getLoader()\r
+    {\r
+        if (null !== self::$loader) {\r
+            return self::$loader;\r
+        }\r
+\r
+        spl_autoload_register(array('FriendicaAutoloaderInit', 'loadClassLoader'), true, true);\r
+        self::$loader = $loader = new \Composer\Autoload\ClassLoader();\r
+        spl_autoload_unregister(array('FriendicaAutoloaderInit', 'loadClassLoader'));\r
+\r
+        // library \r
+        $map = require __DIR__ . '/autoloader/autoload_namespaces.php';\r
+        foreach ($map as $namespace => $path) {\r
+            $loader->set($namespace, $path);\r
+        }\r
+\r
+        $map = require __DIR__ . '/autoloader/autoload_psr4.php';\r
+        foreach ($map as $namespace => $path) {\r
+            $loader->setPsr4($namespace, $path);\r
+        }\r
+\r
+        $classMap = require __DIR__ . '/autoloader/autoload_classmap.php';\r
+        if ($classMap) {\r
+            $loader->addClassMap($classMap);\r
+        }\r
+        \r
+        $loader->register(true);\r
+        \r
+        $includeFiles = require __DIR__ . '/autoloader/autoload_files.php';\r
+        foreach ($includeFiles as $fileIdentifier => $file) {\r
+            friendicaRequire($fileIdentifier, $file);\r
+        }\r
+        \r
+\r
+        return $loader;\r
+    }\r
+}\r
+\r
+function friendicaRequire($fileIdentifier, $file)\r
+{\r
+    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {\r
+        require $file;\r
+\r
+        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;\r
+    }\r
+}\r
+\r
+\r
+\r
+return FriendicaAutoloaderInit::getLoader();\r
diff --git a/include/autoloader/ClassLoader.php b/include/autoloader/ClassLoader.php
new file mode 100644 (file)
index 0000000..d916d80
--- /dev/null
@@ -0,0 +1,413 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE.composer
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0 class loader
+ *
+ * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
+ *
+ *     $loader = new \Composer\Autoload\ClassLoader();
+ *
+ *     // register classes with namespaces
+ *     $loader->add('Symfony\Component', __DIR__.'/component');
+ *     $loader->add('Symfony',           __DIR__.'/framework');
+ *
+ *     // activate the autoloader
+ *     $loader->register();
+ *
+ *     // to enable searching the include path (eg. for PEAR packages)
+ *     $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class ClassLoader
+{
+    // PSR-4
+    private $prefixLengthsPsr4 = array();
+    private $prefixDirsPsr4 = array();
+    private $fallbackDirsPsr4 = array();
+
+    // PSR-0
+    private $prefixesPsr0 = array();
+    private $fallbackDirsPsr0 = array();
+
+    private $useIncludePath = false;
+    private $classMap = array();
+
+    private $classMapAuthoritative = false;
+
+    public function getPrefixes()
+    {
+        if (!empty($this->prefixesPsr0)) {
+            return call_user_func_array('array_merge', $this->prefixesPsr0);
+        }
+
+        return array();
+    }
+
+    public function getPrefixesPsr4()
+    {
+        return $this->prefixDirsPsr4;
+    }
+
+    public function getFallbackDirs()
+    {
+        return $this->fallbackDirsPsr0;
+    }
+
+    public function getFallbackDirsPsr4()
+    {
+        return $this->fallbackDirsPsr4;
+    }
+
+    public function getClassMap()
+    {
+        return $this->classMap;
+    }
+
+    /**
+     * @param array $classMap Class to filename map
+     */
+    public function addClassMap(array $classMap)
+    {
+        if ($this->classMap) {
+            $this->classMap = array_merge($this->classMap, $classMap);
+        } else {
+            $this->classMap = $classMap;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix, either
+     * appending or prepending to the ones previously set for this prefix.
+     *
+     * @param string       $prefix  The prefix
+     * @param array|string $paths   The PSR-0 root directories
+     * @param bool         $prepend Whether to prepend the directories
+     */
+    public function add($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            if ($prepend) {
+                $this->fallbackDirsPsr0 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr0
+                );
+            } else {
+                $this->fallbackDirsPsr0 = array_merge(
+                    $this->fallbackDirsPsr0,
+                    (array) $paths
+                );
+            }
+
+            return;
+        }
+
+        $first = $prefix[0];
+        if (!isset($this->prefixesPsr0[$first][$prefix])) {
+            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+            return;
+        }
+        if ($prepend) {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixesPsr0[$first][$prefix]
+            );
+        } else {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                $this->prefixesPsr0[$first][$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace, either
+     * appending or prepending to the ones previously set for this namespace.
+     *
+     * @param string       $prefix  The prefix/namespace, with trailing '\\'
+     * @param array|string $paths   The PSR-0 base directories
+     * @param bool         $prepend Whether to prepend the directories
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function addPsr4($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            // Register directories for the root namespace.
+            if ($prepend) {
+                $this->fallbackDirsPsr4 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr4
+                );
+            } else {
+                $this->fallbackDirsPsr4 = array_merge(
+                    $this->fallbackDirsPsr4,
+                    (array) $paths
+                );
+            }
+        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+            // Register directories for a new namespace.
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        } elseif ($prepend) {
+            // Prepend directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixDirsPsr4[$prefix]
+            );
+        } else {
+            // Append directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                $this->prefixDirsPsr4[$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix,
+     * replacing any others previously set for this prefix.
+     *
+     * @param string       $prefix The prefix
+     * @param array|string $paths  The PSR-0 base directories
+     */
+    public function set($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr0 = (array) $paths;
+        } else {
+            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace,
+     * replacing any others previously set for this namespace.
+     *
+     * @param string       $prefix The prefix/namespace, with trailing '\\'
+     * @param array|string $paths  The PSR-4 base directories
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function setPsr4($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr4 = (array) $paths;
+        } else {
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Turns on searching the include path for class files.
+     *
+     * @param bool $useIncludePath
+     */
+    public function setUseIncludePath($useIncludePath)
+    {
+        $this->useIncludePath = $useIncludePath;
+    }
+
+    /**
+     * Can be used to check if the autoloader uses the include path to check
+     * for classes.
+     *
+     * @return bool
+     */
+    public function getUseIncludePath()
+    {
+        return $this->useIncludePath;
+    }
+
+    /**
+     * Turns off searching the prefix and fallback directories for classes
+     * that have not been registered with the class map.
+     *
+     * @param bool $classMapAuthoritative
+     */
+    public function setClassMapAuthoritative($classMapAuthoritative)
+    {
+        $this->classMapAuthoritative = $classMapAuthoritative;
+    }
+
+    /**
+     * Should class lookup fail if not found in the current class map?
+     *
+     * @return bool
+     */
+    public function isClassMapAuthoritative()
+    {
+        return $this->classMapAuthoritative;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param bool $prepend Whether to prepend the autoloader or not
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+    }
+
+    /**
+     * Unregisters this instance as an autoloader.
+     */
+    public function unregister()
+    {
+        spl_autoload_unregister(array($this, 'loadClass'));
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param  string    $class The name of the class
+     * @return bool|null True if loaded, null otherwise
+     */
+    public function loadClass($class)
+    {
+        if ($file = $this->findFile($class)) {
+            includeFile($file);
+
+            return true;
+        }
+    }
+
+    /**
+     * Finds the path to the file where the class is defined.
+     *
+     * @param string $class The name of the class
+     *
+     * @return string|false The path if found, false otherwise
+     */
+    public function findFile($class)
+    {
+        // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
+        if ('\\' == $class[0]) {
+            $class = substr($class, 1);
+        }
+
+        // class map lookup
+        if (isset($this->classMap[$class])) {
+            return $this->classMap[$class];
+        }
+        if ($this->classMapAuthoritative) {
+            return false;
+        }
+
+        $file = $this->findFileWithExtension($class, '.php');
+
+        // Search for Hack files if we are running on HHVM
+        if ($file === null && defined('HHVM_VERSION')) {
+            $file = $this->findFileWithExtension($class, '.hh');
+        }
+
+        if ($file === null) {
+            // Remember that this class does not exist.
+            return $this->classMap[$class] = false;
+        }
+
+        return $file;
+    }
+
+    private function findFileWithExtension($class, $ext)
+    {
+        // PSR-4 lookup
+        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+        $first = $class[0];
+        if (isset($this->prefixLengthsPsr4[$first])) {
+            foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
+                if (0 === strpos($class, $prefix)) {
+                    foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
+                        if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-4 fallback dirs
+        foreach ($this->fallbackDirsPsr4 as $dir) {
+            if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 lookup
+        if (false !== $pos = strrpos($class, '\\')) {
+            // namespaced class name
+            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+        } else {
+            // PEAR-like class name
+            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+        }
+
+        if (isset($this->prefixesPsr0[$first])) {
+            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+                if (0 === strpos($class, $prefix)) {
+                    foreach ($dirs as $dir) {
+                        if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-0 fallback dirs
+        foreach ($this->fallbackDirsPsr0 as $dir) {
+            if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 include paths.
+        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+            return $file;
+        }
+    }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+    include $file;
+}
diff --git a/include/autoloader/LICENSE.composer b/include/autoloader/LICENSE.composer
new file mode 100644 (file)
index 0000000..b365b1f
--- /dev/null
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Nils Adermann, Jordi Boggiano\r
+\r
+Permission is hereby granted, free of charge, to any person obtaining a copy\r
+of this software and associated documentation files (the Software), to deal\r
+in the Software without restriction, including without limitation the rights\r
+to use, copy, modify, merge, publish, distribute, sublicense, andor sell\r
+copies of the Software, and to permit persons to whom the Software is furnished\r
+to do so, subject to the following conditions\r
+\r
+The above copyright notice and this permission notice shall be included in all\r
+copies or substantial portions of the Software.\r
+\r
+THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
+THE SOFTWARE.
\ No newline at end of file
diff --git a/include/autoloader/autoload_classmap.php b/include/autoloader/autoload_classmap.php
new file mode 100644 (file)
index 0000000..3efd09f
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+
+// autoload_classmap.php @generated by Composer
+
+$vendorDir = dirname(dirname(dirname(__FILE__)))."/library";
+$baseDir = dirname($vendorDir);
+
+return array(
+);
diff --git a/include/autoloader/autoload_files.php b/include/autoloader/autoload_files.php
new file mode 100644 (file)
index 0000000..859135c
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+
+// autoload_files.php @generated by Composer
+
+$vendorDir = dirname(dirname(dirname(__FILE__)))."/library";
+$baseDir = dirname($vendorDir);
+
+return array(
+    '2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
+);
diff --git a/include/autoloader/autoload_namespaces.php b/include/autoloader/autoload_namespaces.php
new file mode 100644 (file)
index 0000000..315a349
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+
+// autoload_namespaces.php @generated by Composer
+
+$vendorDir = dirname(dirname(dirname(__FILE__)))."/library";
+$baseDir = dirname($vendorDir);
+
+return array(
+    'HTMLPurifier' => array($vendorDir . '/ezyang/htmlpurifier/library'),
+);
diff --git a/include/autoloader/autoload_psr4.php b/include/autoloader/autoload_psr4.php
new file mode 100644 (file)
index 0000000..fe93afe
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(dirname(dirname(__FILE__)))."/library";
+$baseDir = dirname($vendorDir);
+
+return array(
+);
index 6a44e19ec446e87bcef9b83f53cab95d4af85299..8545b2ff8253c9b29c9b71d87b8003a383623e3b 100644 (file)
@@ -311,6 +311,9 @@ function tryoembed($match){
 
        $o = oembed_fetch_url($url);
 
+       if (!is_object($o))
+               return $match[0];
+
        if (isset($match[2]))
                $o->title = $match[2];
 
@@ -858,6 +861,8 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true, $simplehtml = fal
        $Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_spacefy',$Text);
        $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_spacefy',$Text);
 
+       // Remove the abstract element. It is a non visible element.
+       $Text = remove_abstract($Text);
 
        // Move all spaces out of the tags
        $Text = preg_replace("/\[(\w*)\](\s*)/ism", '$2[$1]', $Text);
@@ -1300,4 +1305,43 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true, $simplehtml = fal
 
        return trim($Text);
 }
+
+/**
+ * @brief Removes the "abstract" element from the text
+ *
+ * @param string $text The text with BBCode
+ * @return string The same text - but without "abstract" element
+ */
+function remove_abstract($text) {
+       $text = preg_replace("/[\s|\n]*\[abstract\].*?\[\/abstract\][\s|\n]*/ism", '', $text);
+       $text = preg_replace("/[\s|\n]*\[abstract=.*?\].*?\[\/abstract][\s|\n]*/ism", '', $text);
+
+       return $text;
+}
+
+/**
+ * @brief Returns the value of the "abstract" element
+ *
+ * @param string $text The text that maybe contains the element
+ * @param string $addon The addon for which the abstract is meant for
+ * @return string The abstract
+ */
+function fetch_abstract($text, $addon = "") {
+       $abstract = "";
+       $abstracts = array();
+       $addon = strtolower($addon);
+
+       if (preg_match_all("/\[abstract=(.*?)\](.*?)\[\/abstract\]/ism",$text, $results, PREG_SET_ORDER))
+               foreach ($results AS $result)
+                       $abstracts[strtolower($result[1])] = $result[2];
+
+       if (isset($abstracts[$addon]))
+               $abstract = $abstracts[$addon];
+
+       if ($abstract == "")
+               if (preg_match("/\[abstract\](.*?)\[\/abstract\]/ism",$text, $result))
+                       $abstract = $result[1];
+
+       return $abstract;
+}
 ?>
index f104866232d4326f36cc3669636986682770b67f..3bf68f764e5597abdfd72fac05c91d1e371fd178 100644 (file)
@@ -99,8 +99,16 @@ function network_to_name($s, $profile = "") {
 
        $networkname = str_replace($search,$replace,$s);
 
-       if (($s == NETWORK_DIASPORA) AND ($profile != "") AND diaspora_is_redmatrix($profile))
-               $networkname = t("Redmatrix");
+       if (($s == NETWORK_DIASPORA) AND ($profile != "") AND diaspora::is_redmatrix($profile)) {
+               $networkname = t("Hubzilla/Redmatrix");
+
+               $r = q("SELECT `gserver`.`platform` FROM `gcontact`
+                               INNER JOIN `gserver` ON `gserver`.`nurl` = `gcontact`.`server_url`
+                               WHERE `gcontact`.`nurl` = '%s' AND `platform` != ''",
+                               dbesc(normalise_link($profile)));
+               if ($r)
+                       $networkname = $r[0]["platform"];
+       }
 
        return $networkname;
 }
index 6c33be84fb33971d1f9a167e7607e244487268c0..a52502ec3923a226a4e7c2b4e830872f86e8838a 100644 (file)
@@ -614,7 +614,7 @@ function conversation(&$a, $items, $mode, $update, $preview = false) {
                                if(($normalised != 'mailbox') && (x($a->contacts[$normalised])))
                                        $profile_avatar = $a->contacts[$normalised]['thumb'];
                                else
-                                       $profile_avatar = ((strlen($item['author-avatar'])) ? $a->get_cached_avatar_image($item['author-avatar']) : $item['thumb']);
+                                       $profile_avatar = $a->remove_baseurl(((strlen($item['author-avatar'])) ? $item['author-avatar'] : $item['thumb']));
 
                                $locate = array('location' => $item['location'], 'coord' => $item['coord'], 'html' => '');
                                call_hooks('render_location',$locate);
@@ -707,8 +707,8 @@ function conversation(&$a, $items, $mode, $update, $preview = false) {
                                        'like' => '',
                                        'dislike' => '',
                                        'comment' => '',
-                                       //'conv' => (($preview) ? '' : array('href'=> $a->get_baseurl($ssl_state) . '/display/' . $nickname . '/' . $item['id'], 'title'=> t('View in context'))),
-                                       'conv' => (($preview) ? '' : array('href'=> $a->get_baseurl($ssl_state) . '/display/'.$item['guid'], 'title'=> t('View in context'))),
+                                       //'conv' => (($preview) ? '' : array('href'=> 'display/' . $nickname . '/' . $item['id'], 'title'=> t('View in context'))),
+                                       'conv' => (($preview) ? '' : array('href'=> 'display/'.$item['guid'], 'title'=> t('View in context'))),
                                        'previewing' => $previewing,
                                        'wait' => t('Please wait'),
                                        'thread_level' => 1,
@@ -868,7 +868,7 @@ function item_photo_menu($item){
                $status_link = $profile_link . "?url=status";
                $photos_link = $profile_link . "?url=photos";
                $profile_link = $profile_link . "?url=profile";
-               $pm_url = $a->get_baseurl($ssl_state) . '/message/new/' . $cid;
+               $pm_url = 'message/new/' . $cid;
                $zurl = '';
        }
        else {
@@ -882,23 +882,23 @@ function item_photo_menu($item){
                                $cid = $r[0]["id"];
 
                                if ($r[0]["network"] == NETWORK_DIASPORA)
-                                       $pm_url = $a->get_baseurl($ssl_state) . '/message/new/' . $cid;
+                                       $pm_url = 'message/new/' . $cid;
 
                        } else
                                $cid = 0;
                }
        }
        if(($cid) && (! $item['self'])) {
-               $poke_link = $a->get_baseurl($ssl_state) . '/poke/?f=&c=' . $cid;
-               $contact_url = $a->get_baseurl($ssl_state) . '/contacts/' . $cid;
-               $posts_link = $a->get_baseurl($ssl_state) . '/contacts/' . $cid . '/posts';
+               $poke_link = 'poke/?f=&c=' . $cid;
+               $contact_url = 'contacts/' . $cid;
+               $posts_link = 'contacts/' . $cid . '/posts';
 
                $clean_url = normalise_link($item['author-link']);
 
                if((local_user()) && (local_user() == $item['uid'])) {
                        if(isset($a->contacts) && x($a->contacts,$clean_url)) {
                                if($a->contacts[$clean_url]['network'] === NETWORK_DIASPORA) {
-                                       $pm_url = $a->get_baseurl($ssl_state) . '/message/new/' . $cid;
+                                       $pm_url = 'message/new/' . $cid;
                                }
                        }
                }
@@ -921,7 +921,7 @@ function item_photo_menu($item){
 
                if ((($cid == 0) OR ($a->contacts[$clean_url]['rel'] == CONTACT_IS_FOLLOWER)) AND
                        in_array($item['network'], array(NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_DIASPORA)))
-                       $menu[t("Connect/Follow")] = $a->get_baseurl($ssl_state)."/follow?url=".urlencode($item['author-link']);
+                       $menu[t("Connect/Follow")] = "follow?url=".urlencode($item['author-link']);
        } else
                $menu = array(t("View Profile") => $item['author-link']);
 
@@ -980,7 +980,7 @@ function builtin_activity_puller($item, &$conv_responses) {
                if((activity_match($item['verb'], $verb)) && ($item['id'] != $item['parent'])) {
                        $url = $item['author-link'];
                        if((local_user()) && (local_user() == $item['uid']) && ($item['network'] === NETWORK_DFRN) && (! $item['self']) && (link_compare($item['author-link'],$item['url']))) {
-                               $url = z_root(true) . '/redir/' . $item['contact-id'];
+                               $url = 'redir/' . $item['contact-id'];
                                $sparkle = ' class="sparkle" ';
                        }
                        else
@@ -1178,7 +1178,7 @@ function status_editor($a,$x, $notes_cid = 0, $popup=false) {
 
        $o .= replace_macros($tpl,array(
                '$return_path' => $query_str,
-               '$action' =>  $a->get_baseurl(true) . '/item',
+               '$action' =>  'item',
                '$share' => (x($x,'button') ? $x['button'] : t('Share')),
                '$upload' => t('Upload photo'),
                '$shortupload' => t('upload photo'),
index 3acf711dd163056234b822a1eb27f39387108a7b..00dd500704a55a327dd9551f8ae9d71289a6e2c4 100644 (file)
@@ -34,22 +34,18 @@ function cron_run(&$argv, &$argc){
        require_once('include/Contact.php');
        require_once('include/email.php');
        require_once('include/socgraph.php');
-       require_once('include/pidfile.php');
        require_once('mod/nodeinfo.php');
+       require_once('include/post_update.php');
 
        load_config('config');
        load_config('system');
 
-       $maxsysload = intval(get_config('system','maxloadavg'));
-       if($maxsysload < 1)
-               $maxsysload = 50;
-
-       $load = current_load();
-       if($load) {
-               if(intval($load) > $maxsysload) {
-                       logger('system: load ' . $load . ' too high. cron deferred to next scheduled run.');
+       // Don't check this stuff if the function is called by the poller
+       if (App::callstack() != "poller_run") {
+               if (App::maxload_reached())
+                       return;
+               if (App::is_already_running('cron', 'include/cron.php', 540))
                        return;
-               }
        }
 
        $last = get_config('system','last_cron');
@@ -66,23 +62,6 @@ function cron_run(&$argv, &$argc){
                }
        }
 
-       $lockpath = get_lockpath();
-       if ($lockpath != '') {
-               $pidfile = new pidfile($lockpath, 'cron');
-               if($pidfile->is_already_running()) {
-                       logger("cron: Already running");
-                       if ($pidfile->running_time() > 9*60) {
-                               $pidfile->kill();
-                               logger("cron: killed stale process");
-                               // Calling a new instance
-                               proc_run('php','include/cron.php');
-                       }
-                       exit;
-               }
-       }
-
-
-
        $a->set_baseurl(get_config('system','url'));
 
        load_hooks();
@@ -93,10 +72,6 @@ function cron_run(&$argv, &$argc){
 
        proc_run('php',"include/queue.php");
 
-       // run diaspora photo queue process in the background
-
-       proc_run('php',"include/dsprphotoq.php");
-
        // run the process to discover global contacts in the background
 
        proc_run('php',"include/discover_poco.php");
@@ -127,13 +102,14 @@ function cron_run(&$argv, &$argc){
 
        // Check OStatus conversations
        // Check only conversations with mentions (for a longer time)
-       check_conversations(true);
+       ostatus::check_conversations(true);
 
        // Check every conversation
-       check_conversations(false);
+       ostatus::check_conversations(false);
 
-       // Set the gcontact-id in the item table if missing
-       item_set_gcontact();
+       // Call possible post update functions
+       // see include/post_update.php for more details
+       post_update();
 
        // update nodeinfo data
        nodeinfo_cron();
@@ -361,35 +337,37 @@ function cron_clear_cache(&$a) {
        if ($max_tablesize == 0)
                $max_tablesize = 100 * 1000000; // Default are 100 MB
 
-       // Minimum fragmentation level in percent
-       $fragmentation_level = intval(get_config('system','optimize_fragmentation')) / 100;
-       if ($fragmentation_level == 0)
-               $fragmentation_level = 0.3; // Default value is 30%
+       if ($max_tablesize > 0) {
+               // Minimum fragmentation level in percent
+               $fragmentation_level = intval(get_config('system','optimize_fragmentation')) / 100;
+               if ($fragmentation_level == 0)
+                       $fragmentation_level = 0.3; // Default value is 30%
 
-       // Optimize some tables that need to be optimized
-       $r = q("SHOW TABLE STATUS");
-       foreach($r as $table) {
+               // Optimize some tables that need to be optimized
+               $r = q("SHOW TABLE STATUS");
+               foreach($r as $table) {
 
-               // Don't optimize tables that are too large
-               if ($table["Data_length"] > $max_tablesize)
-                       continue;
+                       // Don't optimize tables that are too large
+                       if ($table["Data_length"] > $max_tablesize)
+                               continue;
 
-               // Don't optimize empty tables
-               if ($table["Data_length"] == 0)
-                       continue;
+                       // Don't optimize empty tables
+                       if ($table["Data_length"] == 0)
+                               continue;
 
-               // Calculate fragmentation
-               $fragmentation = $table["Data_free"] / $table["Data_length"];
+                       // Calculate fragmentation
+                       $fragmentation = $table["Data_free"] / ($table["Data_length"] + $table["Index_length"]);
 
-               logger("Table ".$table["Name"]." - Fragmentation level: ".round($fragmentation * 100, 2), LOGGER_DEBUG);
+                       logger("Table ".$table["Name"]." - Fragmentation level: ".round($fragmentation * 100, 2), LOGGER_DEBUG);
 
-               // Don't optimize tables that needn't to be optimized
-               if ($fragmentation < $fragmentation_level)
-                       continue;
+                       // Don't optimize tables that needn't to be optimized
+                       if ($fragmentation < $fragmentation_level)
+                               continue;
 
-               // So optimize it
-               logger("Optimize Table ".$table["Name"], LOGGER_DEBUG);
-               q("OPTIMIZE TABLE `%s`", dbesc($table["Name"]));
+                       // So optimize it
+                       logger("Optimize Table ".$table["Name"], LOGGER_DEBUG);
+                       q("OPTIMIZE TABLE `%s`", dbesc($table["Name"]));
+               }
        }
 
        set_config('system','cache_last_cleared', time());
@@ -429,6 +407,9 @@ function cron_repair_database() {
        // This call is very "cheap" so we can do it at any time without a problem
        q("UPDATE `item` INNER JOIN `item` AS `parent` ON `parent`.`uri` = `item`.`parent-uri` AND `parent`.`uid` = `item`.`uid` SET `item`.`parent` = `parent`.`id` WHERE `item`.`parent` = 0");
 
+       // There was an issue where the nick vanishes from the contact table
+       q("UPDATE `contact` INNER JOIN `user` ON `contact`.`uid` = `user`.`uid` SET `nick` = `nickname` WHERE `self` AND `nick`=''");
+
        /// @todo
        /// - remove thread entries without item
        /// - remove sign entries without item
index 8c70008e453c913e9318184ed859e161ed68eb6a..b6cf0e72378763d6b3e5ac697672dce67438ea02 100644 (file)
@@ -19,21 +19,16 @@ function cronhooks_run(&$argv, &$argc){
 
        require_once('include/session.php');
        require_once('include/datetime.php');
-       require_once('include/pidfile.php');
 
        load_config('config');
        load_config('system');
 
-       $maxsysload = intval(get_config('system','maxloadavg'));
-       if($maxsysload < 1)
-               $maxsysload = 50;
-
-       $load = current_load();
-       if($load) {
-               if(intval($load) > $maxsysload) {
-                       logger('system: load ' . $load . ' too high. Cronhooks deferred to next scheduled run.');
+       // Don't check this stuff if the function is called by the poller
+       if (App::callstack() != "poller_run") {
+               if (App::maxload_reached())
+                       return;
+               if (App::is_already_running('cronhooks', 'include/cronhooks.php', 1140))
                        return;
-               }
        }
 
        $last = get_config('system','last_cronhook');
@@ -50,21 +45,6 @@ function cronhooks_run(&$argv, &$argc){
                }
        }
 
-       $lockpath = get_lockpath();
-       if ($lockpath != '') {
-               $pidfile = new pidfile($lockpath, 'cronhooks');
-               if($pidfile->is_already_running()) {
-                       logger("cronhooks: Already running");
-                       if ($pidfile->running_time() > 19*60) {
-                               $pidfile->kill();
-                               logger("cronhooks: killed stale process");
-                               // Calling a new instance
-                               proc_run('php','include/cronhooks.php');
-                       }
-                       exit;
-               }
-       }
-
        $a->set_baseurl(get_config('system','url'));
 
        load_hooks();
index 96d18cd78959fcd01e5a6091c3faa033cd2dbf2f..e34e409023dc5918905eb3373951a5ebddd71402 100644 (file)
@@ -537,17 +537,6 @@ function db_definition() {
                                        "PRIMARY" => array("id"),
                                        )
                        );
-       $database["dsprphotoq"] = array(
-                       "fields" => array(
-                                       "id" => array("type" => "int(10) unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1"),
-                                       "uid" => array("type" => "int(11)", "not null" => "1", "default" => "0"),
-                                       "msg" => array("type" => "mediumtext", "not null" => "1"),
-                                       "attempt" => array("type" => "tinyint(4)", "not null" => "1", "default" => "0"),
-                                       ),
-                       "indexes" => array(
-                                       "PRIMARY" => array("id"),
-                                       )
-                       );
        $database["event"] = array(
                        "fields" => array(
                                        "id" => array("type" => "int(11)", "not null" => "1", "extra" => "auto_increment", "primary" => "1"),
@@ -748,21 +737,6 @@ function db_definition() {
                                        "nurl" => array("nurl"),
                                        )
                        );
-       $database["guid"] = array(
-                       "fields" => array(
-                                       "id" => array("type" => "int(10) unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1"),
-                                       "guid" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
-                                       "plink" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
-                                       "uri" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
-                                       "network" => array("type" => "varchar(32)", "not null" => "1", "default" => ""),
-                                       ),
-                       "indexes" => array(
-                                       "PRIMARY" => array("id"),
-                                       "guid" => array("guid"),
-                                       "plink" => array("plink"),
-                                       "uri" => array("uri"),
-                                       )
-                       );
        $database["hook"] = array(
                        "fields" => array(
                                        "id" => array("type" => "int(11)", "not null" => "1", "extra" => "auto_increment", "primary" => "1"),
@@ -1261,7 +1235,6 @@ function db_definition() {
                        "fields" => array(
                                        "id" => array("type" => "int(10) unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1"),
                                        "iid" => array("type" => "int(10) unsigned", "not null" => "1", "default" => "0"),
-                                       "retract_iid" => array("type" => "int(10) unsigned", "not null" => "1", "default" => "0"),
                                        "signed_text" => array("type" => "mediumtext", "not null" => "1"),
                                        "signature" => array("type" => "text", "not null" => "1"),
                                        "signer" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
@@ -1269,7 +1242,6 @@ function db_definition() {
                        "indexes" => array(
                                        "PRIMARY" => array("id"),
                                        "iid" => array("iid"),
-                                       "retract_iid" => array("retract_iid"),
                                        )
                        );
        $database["spam"] = array(
index 021ceb9968a8496e738685021af242f60102aaa4..fe337743820dcb85940262fefdb4abe099c4d530 100644 (file)
@@ -10,11 +10,11 @@ require_once("include/dfrn.php");
 function delivery_run(&$argv, &$argc){
        global $a, $db;
 
-       if(is_null($a)){
+       if (is_null($a)){
                $a = new App;
        }
 
-       if(is_null($db)) {
+       if (is_null($db)) {
                @include(".htconfig.php");
                require_once("include/dba.php");
                $db = new dba($db_host, $db_user, $db_pass, $db_data);
@@ -32,12 +32,12 @@ function delivery_run(&$argv, &$argc){
 
        load_hooks();
 
-       if($argc < 3)
+       if ($argc < 3)
                return;
 
        $a->set_baseurl(get_config('system','url'));
 
-       logger('delivery: invoked: ' . print_r($argv,true), LOGGER_DEBUG);
+       logger('delivery: invoked: '. print_r($argv,true), LOGGER_DEBUG);
 
        $cmd        = $argv[1];
        $item_id    = intval($argv[2]);
@@ -53,21 +53,12 @@ function delivery_run(&$argv, &$argc){
                        dbesc($item_id),
                        dbesc($contact_id)
                );
-               if(! count($r)) {
+               if (!count($r)) {
                        continue;
                }
 
-               $maxsysload = intval(get_config('system','maxloadavg'));
-               if($maxsysload < 1)
-                       $maxsysload = 50;
-
-               $load = current_load();
-               if($load) {
-                       if(intval($load) > $maxsysload) {
-                               logger('system: load ' . $load . ' too high. Delivery deferred to next queue run.');
-                               return;
-                       }
-               }
+               if (App::maxload_reached())
+                       return;
 
                // It's ours to deliver. Remove it from the queue.
 
@@ -77,7 +68,7 @@ function delivery_run(&$argv, &$argc){
                        dbesc($contact_id)
                );
 
-               if((! $item_id) || (! $contact_id))
+               if (!$item_id || !$contact_id)
                        continue;
 
                $expire = false;
@@ -93,20 +84,20 @@ function delivery_run(&$argv, &$argc){
 
                $recipients[] = $contact_id;
 
-               if($cmd === 'mail') {
+               if ($cmd === 'mail') {
                        $normal_mode = false;
                        $mail = true;
                        $message = q("SELECT * FROM `mail` WHERE `id` = %d LIMIT 1",
                                        intval($item_id)
                        );
-                       if(! count($message)){
+                       if (!count($message)){
                                return;
                        }
                        $uid = $message[0]['uid'];
                        $recipients[] = $message[0]['contact-id'];
                        $item = $message[0];
                }
-               elseif($cmd === 'expire') {
+               elseif ($cmd === 'expire') {
                        $normal_mode = false;
                        $expire = true;
                        $items = q("SELECT * FROM `item` WHERE `uid` = %d AND `wall` = 1
@@ -115,22 +106,22 @@ function delivery_run(&$argv, &$argc){
                        );
                        $uid = $item_id;
                        $item_id = 0;
-                       if(! count($items))
+                       if (!count($items))
                                continue;
                }
-               elseif($cmd === 'suggest') {
+               elseif ($cmd === 'suggest') {
                        $normal_mode = false;
                        $fsuggest = true;
 
                        $suggest = q("SELECT * FROM `fsuggest` WHERE `id` = %d LIMIT 1",
                                intval($item_id)
                        );
-                       if(! count($suggest))
+                       if (!count($suggest))
                                return;
                        $uid = $suggest[0]['uid'];
                        $recipients[] = $suggest[0]['cid'];
                        $item = $suggest[0];
-               } elseif($cmd === 'relocate') {
+               } elseif ($cmd === 'relocate') {
                        $normal_mode = false;
                        $relocate = true;
                        $uid = $item_id;
@@ -140,7 +131,7 @@ function delivery_run(&$argv, &$argc){
                                intval($item_id)
                        );
 
-                       if((! count($r)) || (! intval($r[0]['parent']))) {
+                       if ((!count($r)) || (!intval($r[0]['parent']))) {
                                continue;
                        }
 
@@ -154,32 +145,32 @@ function delivery_run(&$argv, &$argc){
                                intval($parent_id)
                        );
 
-                       if(! count($items)) {
+                       if (!count($items)) {
                                continue;
                        }
 
                        $icontacts = null;
                        $contacts_arr = array();
                        foreach($items as $item)
-                               if(! in_array($item['contact-id'],$contacts_arr))
+                               if (!in_array($item['contact-id'],$contacts_arr))
                                        $contacts_arr[] = intval($item['contact-id']);
-                       if(count($contacts_arr)) {
+                       if (count($contacts_arr)) {
                                $str_contacts = implode(',',$contacts_arr);
                                $icontacts = q("SELECT * FROM `contact`
                                        WHERE `id` IN ( $str_contacts ) "
                                );
                        }
-                       if( ! ($icontacts && count($icontacts)))
+                       if ( !($icontacts && count($icontacts)))
                                continue;
 
                        // avoid race condition with deleting entries
 
-                       if($items[0]['deleted']) {
+                       if ($items[0]['deleted']) {
                                foreach($items as $item)
                                        $item['deleted'] = 1;
                        }
 
-                       if((count($items) == 1) && ($items[0]['uri'] === $items[0]['parent-uri'])) {
+                       if ((count($items) == 1) && ($items[0]['uri'] === $items[0]['parent-uri'])) {
                                logger('delivery: top level post');
                                $top_level = true;
                        }
@@ -193,7 +184,7 @@ function delivery_run(&$argv, &$argc){
                        intval($uid)
                );
 
-               if(! count($r))
+               if (!count($r))
                        continue;
 
                $owner = $r[0];
@@ -202,7 +193,7 @@ function delivery_run(&$argv, &$argc){
 
                $public_message = true;
 
-               if(! ($mail || $fsuggest || $relocate)) {
+               if (!($mail || $fsuggest || $relocate)) {
                        require_once('include/group.php');
 
                        $parent = $items[0];
@@ -226,7 +217,7 @@ function delivery_run(&$argv, &$argc){
 
 
                        $localhost = $a->get_hostname();
-                       if(strpos($localhost,':'))
+                       if (strpos($localhost,':'))
                                $localhost = substr($localhost,0,strpos($localhost,':'));
 
                        /**
@@ -239,20 +230,21 @@ function delivery_run(&$argv, &$argc){
 
                        $relay_to_owner = false;
 
-                       if((! $top_level) && ($parent['wall'] == 0) && (! $expire) && (stristr($target_item['uri'],$localhost))) {
+                       if (!$top_level && ($parent['wall'] == 0) && !$expire && stristr($target_item['uri'],$localhost)) {
                                $relay_to_owner = true;
                        }
 
-                       if($relay_to_owner) {
+                       if ($relay_to_owner) {
                                logger('followup '.$target_item["guid"], LOGGER_DEBUG);
                                // local followup to remote post
                                $followup = true;
                        }
 
-                       if((strlen($parent['allow_cid']))
+                       if ((strlen($parent['allow_cid']))
                                || (strlen($parent['allow_gid']))
                                || (strlen($parent['deny_cid']))
-                               || (strlen($parent['deny_gid']))) {
+                               || (strlen($parent['deny_gid']))
+                               || $parent["private"]) {
                                $public_message = false; // private recipients, not public
                        }
 
@@ -262,10 +254,10 @@ function delivery_run(&$argv, &$argc){
                        intval($contact_id)
                );
 
-               if(count($r))
+               if (count($r))
                        $contact = $r[0];
 
-               if($contact['self'])
+               if ($contact['self'])
                        continue;
 
                $deliver_status = 0;
@@ -275,7 +267,7 @@ function delivery_run(&$argv, &$argc){
                switch($contact['network']) {
 
                        case NETWORK_DFRN:
-                               logger('notifier: '.$target_item["guid"].' dfrndelivery: ' . $contact['name']);
+                               logger('notifier: '.$target_item["guid"].' dfrndelivery: '.$contact['name']);
 
                                if ($mail) {
                                        $item['body'] = fix_private_photos($item['body'],$owner['uid'],null,$message[0]['contact-id']);
@@ -285,13 +277,13 @@ function delivery_run(&$argv, &$argc){
                                        q("DELETE FROM `fsuggest` WHERE `id` = %d LIMIT 1", intval($item['id']));
                                } elseif ($relocate)
                                        $atom = dfrn::relocate($owner, $uid);
-                               elseif($followup) {
+                               elseif ($followup) {
                                        $msgitems = array();
                                        foreach($items as $item) {  // there is only one item
-                                               if(!$item['parent'])
+                                               if (!$item['parent'])
                                                        continue;
-                                               if($item['id'] == $item_id) {
-                                                       logger('followup: item: ' . print_r($item,true), LOGGER_DATA);
+                                               if ($item['id'] == $item_id) {
+                                                       logger('followup: item: '. print_r($item,true), LOGGER_DATA);
                                                        $msgitems[] = $item;
                                                }
                                        }
@@ -299,19 +291,19 @@ function delivery_run(&$argv, &$argc){
                                } else {
                                        $msgitems = array();
                                        foreach($items as $item) {
-                                               if(!$item['parent'])
+                                               if (!$item['parent'])
                                                        continue;
 
                                                // private emails may be in included in public conversations. Filter them.
-                                               if(($public_message) && $item['private'])
+                                               if ($public_message && $item['private'])
                                                        continue;
 
                                                $item_contact = get_item_contact($item,$icontacts);
-                                               if(!$item_contact)
+                                               if (!$item_contact)
                                                        continue;
 
-                                               if($normal_mode) {
-                                                       if($item_id == $item['id'] || $item['id'] == $item['parent']) {
+                                               if ($normal_mode) {
+                                                       if ($item_id == $item['id'] || $item['id'] == $item['parent']) {
                                                                $item["entry:comment-allow"] = true;
                                                                $item["entry:cid"] = (($top_level) ? $contact['id'] : 0);
                                                                $msgitems[] = $item;
@@ -326,15 +318,15 @@ function delivery_run(&$argv, &$argc){
 
                                logger('notifier entry: '.$contact["url"].' '.$target_item["guid"].' entry: '.$atom, LOGGER_DEBUG);
 
-                               logger('notifier: ' . $atom, LOGGER_DATA);
+                               logger('notifier: '.$atom, LOGGER_DATA);
                                $basepath =  implode('/', array_slice(explode('/',$contact['url']),0,3));
 
                                // perform local delivery if we are on the same site
 
-                               if(link_compare($basepath,$a->get_baseurl())) {
+                               if (link_compare($basepath,$a->get_baseurl())) {
 
                                        $nickname = basename($contact['url']);
-                                       if($contact['issued-id'])
+                                       if ($contact['issued-id'])
                                                $sql_extra = sprintf(" AND `dfrn-id` = '%s' ", dbesc($contact['issued-id']));
                                        else
                                                $sql_extra = sprintf(" AND `issued-id` = '%s' ", dbesc($contact['dfrn-id']));
@@ -356,10 +348,10 @@ function delivery_run(&$argv, &$argc){
                                                dbesc($nickname)
                                        );
 
-                                       if($x && count($x)) {
+                                       if ($x && count($x)) {
                                                $write_flag = ((($x[0]['rel']) && ($x[0]['rel'] != CONTACT_IS_SHARING)) ? true : false);
-                                               if((($owner['page-flags'] == PAGE_COMMUNITY) || ($write_flag)) && (! $x[0]['writable'])) {
-                                                       q("update contact set writable = 1 where id = %d",
+                                               if ((($owner['page-flags'] == PAGE_COMMUNITY) || $write_flag) && !$x[0]['writable']) {
+                                                       q("UPDATE `contact` SET `writable` = 1 WHERE `id` = %d",
                                                                intval($x[0]['id'])
                                                        );
                                                        $x[0]['writable'] = 1;
@@ -379,14 +371,14 @@ function delivery_run(&$argv, &$argc){
                                        }
                                }
 
-                               if(! was_recently_delayed($contact['id']))
+                               if (!was_recently_delayed($contact['id']))
                                        $deliver_status = dfrn::deliver($owner,$contact,$atom);
                                else
                                        $deliver_status = (-1);
 
                                logger('notifier: dfrn_delivery to '.$contact["url"].' with guid '.$target_item["guid"].' returns '.$deliver_status);
 
-                               if($deliver_status == (-1)) {
+                               if ($deliver_status == (-1)) {
                                        logger('notifier: delivery failed: queuing message');
                                        add_to_queue($contact['id'],NETWORK_DFRN,$atom);
                                }
@@ -394,9 +386,9 @@ function delivery_run(&$argv, &$argc){
 
                        case NETWORK_OSTATUS:
                                // Do not send to otatus if we are not configured to send to public networks
-                               if($owner['prvnets'])
+                               if ($owner['prvnets'])
                                        break;
-                               if(get_config('system','ostatus_disabled') || get_config('system','dfrn_only'))
+                               if (get_config('system','ostatus_disabled') || get_config('system','dfrn_only'))
                                        break;
 
                                // There is currently no code here to distribute anything to OStatus.
@@ -406,67 +398,67 @@ function delivery_run(&$argv, &$argc){
                        case NETWORK_MAIL:
                        case NETWORK_MAIL2:
 
-                               if(get_config('system','dfrn_only'))
+                               if (get_config('system','dfrn_only'))
                                        break;
                                // WARNING: does not currently convert to RFC2047 header encodings, etc.
 
                                $addr = $contact['addr'];
-                               if(! strlen($addr))
+                               if (!strlen($addr))
                                        break;
 
-                               if($cmd === 'wall-new' || $cmd === 'comment-new') {
+                               if ($cmd === 'wall-new' || $cmd === 'comment-new') {
 
                                        $it = null;
-                                       if($cmd === 'wall-new')
+                                       if ($cmd === 'wall-new')
                                                $it = $items[0];
                                        else {
                                                $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
                                                        intval($argv[2]),
                                                        intval($uid)
                                                );
-                                               if(count($r))
+                                               if (count($r))
                                                        $it = $r[0];
                                        }
-                                       if(! $it)
+                                       if (!$it)
                                                break;
 
 
                                        $local_user = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1",
                                                intval($uid)
                                        );
-                                       if(! count($local_user))
+                                       if (!count($local_user))
                                                break;
 
                                        $reply_to = '';
                                        $r1 = q("SELECT * FROM `mailacct` WHERE `uid` = %d LIMIT 1",
                                                intval($uid)
                                        );
-                                       if($r1 && $r1[0]['reply_to'])
+                                       if ($r1 && $r1[0]['reply_to'])
                                                $reply_to = $r1[0]['reply_to'];
 
                                        $subject  = (($it['title']) ? email_header_encode($it['title'],'UTF-8') : t("\x28no subject\x29")) ;
 
                                        // only expose our real email address to true friends
 
-                                       if(($contact['rel'] == CONTACT_IS_FRIEND) && (! $contact['blocked'])) {
-                                               if($reply_to) {
+                                       if (($contact['rel'] == CONTACT_IS_FRIEND) && !$contact['blocked']) {
+                                               if ($reply_to) {
                                                        $headers  = 'From: '.email_header_encode($local_user[0]['username'],'UTF-8').' <'.$reply_to.'>'."\n";
                                                        $headers .= 'Sender: '.$local_user[0]['email']."\n";
                                                } else
                                                        $headers  = 'From: '.email_header_encode($local_user[0]['username'],'UTF-8').' <'.$local_user[0]['email'].'>'."\n";
                                        } else
-                                               $headers  = 'From: ' . email_header_encode($local_user[0]['username'],'UTF-8') . ' <' . t('noreply') . '@' . $a->get_hostname() . '>' . "\n";
+                                               $headers  = 'From: '. email_header_encode($local_user[0]['username'],'UTF-8') .' <'. t('noreply') .'@'.$a->get_hostname() .'>'. "\n";
 
-                                       //if($reply_to)
-                                       //      $headers .= 'Reply-to: ' . $reply_to . "\n";
+                                       //if ($reply_to)
+                                       //      $headers .= 'Reply-to: '.$reply_to . "\n";
 
-                                       $headers .= 'Message-Id: <' . iri2msgid($it['uri']). '>' . "\n";
+                                       $headers .= 'Message-Id: <'. iri2msgid($it['uri']).'>'. "\n";
 
                                        //logger("Mail: uri: ".$it['uri']." parent-uri ".$it['parent-uri'], LOGGER_DEBUG);
                                        //logger("Mail: Data: ".print_r($it, true), LOGGER_DEBUG);
                                        //logger("Mail: Data: ".print_r($it, true), LOGGER_DATA);
 
-                                       if($it['uri'] !== $it['parent-uri']) {
+                                       if ($it['uri'] !== $it['parent-uri']) {
                                                $headers .= "References: <".iri2msgid($it["parent-uri"]).">";
 
                                                // If Threading is enabled, write down the correct parent
@@ -474,23 +466,23 @@ function delivery_run(&$argv, &$argc){
                                                        $headers .= " <".iri2msgid($it["thr-parent"]).">";
                                                $headers .= "\n";
 
-                                               if(!$it['title']) {
+                                               if (!$it['title']) {
                                                        $r = q("SELECT `title` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
                                                                dbesc($it['parent-uri']),
                                                                intval($uid));
 
-                                                       if(count($r) AND ($r[0]['title'] != ''))
+                                                       if (count($r) AND ($r[0]['title'] != ''))
                                                                $subject = $r[0]['title'];
                                                        else {
                                                                $r = q("SELECT `title` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d LIMIT 1",
                                                                        dbesc($it['parent-uri']),
                                                                        intval($uid));
 
-                                                               if(count($r) AND ($r[0]['title'] != ''))
+                                                               if (count($r) AND ($r[0]['title'] != ''))
                                                                        $subject = $r[0]['title'];
                                                        }
                                                }
-                                               if(strncasecmp($subject,'RE:',3))
+                                               if (strncasecmp($subject,'RE:',3))
                                                        $subject = 'Re: '.$subject;
                                        }
                                        email_send($addr, $subject, $headers, $it);
@@ -498,60 +490,59 @@ function delivery_run(&$argv, &$argc){
                                break;
 
                        case NETWORK_DIASPORA:
-                               if($public_message)
-                                       $loc = 'public batch ' . $contact['batch'];
+                               if ($public_message)
+                                       $loc = 'public batch '.$contact['batch'];
                                else
                                        $loc = $contact['name'];
 
-                               logger('delivery: diaspora batch deliver: ' . $loc);
+                               logger('delivery: diaspora batch deliver: '.$loc);
 
-                               if(get_config('system','dfrn_only') || (!get_config('system','diaspora_enabled')))
+                               if (get_config('system','dfrn_only') || (!get_config('system','diaspora_enabled')))
                                        break;
 
-                               if($mail) {
-                                       diaspora_send_mail($item,$owner,$contact);
+                               if ($mail) {
+                                       diaspora::send_mail($item,$owner,$contact);
                                        break;
                                }
 
-                               if(!$normal_mode)
+                               if (!$normal_mode)
                                        break;
 
-                               if((! $contact['pubkey']) && (! $public_message))
+                               if (!$contact['pubkey'] && !$public_message)
                                        break;
 
                                $unsupported_activities = array(ACTIVITY_DISLIKE, ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE);
 
                                //don't transmit activities which are not supported by diaspora
                                foreach($unsupported_activities as $act) {
-                                       if(activity_match($target_item['verb'],$act)) {
+                                       if (activity_match($target_item['verb'],$act)) {
                                                break 2;
                                        }
                                }
 
-                               if(($target_item['deleted']) && (($target_item['uri'] === $target_item['parent-uri']) || $followup)) {
+                               if (($target_item['deleted']) && (($target_item['uri'] === $target_item['parent-uri']) || $followup)) {
                                        // top-level retraction
-                                       logger('delivery: diaspora retract: ' . $loc);
-
-                                       diaspora_send_retraction($target_item,$owner,$contact,$public_message);
+                                       logger('diaspora retract: '.$loc);
+                                       diaspora::send_retraction($target_item,$owner,$contact,$public_message);
                                        break;
-                               } elseif($followup) {
+                               } elseif ($followup) {
                                        // send comments and likes to owner to relay
-                                       diaspora_send_followup($target_item,$owner,$contact,$public_message);
+                                       logger('diaspora followup: '.$loc);
+                                       diaspora::send_followup($target_item,$owner,$contact,$public_message);
                                        break;
-                               } elseif($target_item['uri'] !== $target_item['parent-uri']) {
+                               } elseif ($target_item['uri'] !== $target_item['parent-uri']) {
                                        // we are the relay - send comments, likes and relayable_retractions to our conversants
-                                       logger('delivery: diaspora relay: ' . $loc);
-
-                                       diaspora_send_relay($target_item,$owner,$contact,$public_message);
+                                       logger('diaspora relay: '.$loc);
+                                       diaspora::send_relay($target_item,$owner,$contact,$public_message);
                                        break;
-                               } elseif(($top_level) && (! $walltowall)) {
+                               } elseif ($top_level && !$walltowall) {
                                        // currently no workable solution for sending walltowall
-                                       logger('delivery: diaspora status: ' . $loc);
-                                       diaspora_send_status($target_item,$owner,$contact,$public_message);
+                                       logger('diaspora status: '.$loc);
+                                       diaspora::send_status($target_item,$owner,$contact,$public_message);
                                        break;
                                }
 
-                               logger('delivery: diaspora unknown mode: ' . $contact['name']);
+                               logger('delivery: diaspora unknown mode: '.$contact['name']);
 
                                break;
 
index c6c8deef58b0508ee16b33b6faa18432692d6034..14be7473056dbe173b307902ffa478bf570abf66 100644 (file)
@@ -18,7 +18,8 @@ require_once("include/event.php");
 require_once("include/text.php");
 require_once("include/oembed.php");
 require_once("include/html2bbcode.php");
-require_once("library/HTMLPurifier.auto.php");
+require_once("include/bbcode.php");
+require_once("include/xml.php");
 
 /**
  * @brief This class contain functions to create and send DFRN XML files
@@ -85,7 +86,7 @@ class dfrn {
                                        $converse = true;
                                if($a->argv[$x] == 'starred')
                                        $starred = true;
-                               if($a->argv[$x] === 'category' && $a->argc > ($x + 1) && strlen($a->argv[$x+1]))
+                               if($a->argv[$x] == 'category' && $a->argc > ($x + 1) && strlen($a->argv[$x+1]))
                                        $category = $a->argv[$x+1];
                        }
                }
@@ -96,7 +97,7 @@ class dfrn {
 
                $sql_extra = " AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid`  = '' AND `item`.`deny_gid`  = '' ";
 
-               $r = q("SELECT `contact`.*, `user`.`uid` AS `user_uid`, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags`
+               $r = q("SELECT `contact`.*, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags`
                        FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid`
                        WHERE `contact`.`self` = 1 AND `user`.`nickname` = '%s' LIMIT 1",
                        dbesc($owner_nick)
@@ -106,7 +107,7 @@ class dfrn {
                        killme();
 
                $owner = $r[0];
-               $owner_id = $owner['user_uid'];
+               $owner_id = $owner['uid'];
                $owner_nick = $owner['nickname'];
 
                $sql_post_table = "";
@@ -244,7 +245,7 @@ class dfrn {
                foreach($items as $item) {
 
                        // prevent private email from leaking.
-                       if($item['network'] === NETWORK_MAIL)
+                       if($item['network'] == NETWORK_MAIL)
                                continue;
 
                        // public feeds get html, our own nodes use bbcode
@@ -286,17 +287,17 @@ class dfrn {
                $mail = $doc->createElement("dfrn:mail");
                $sender = $doc->createElement("dfrn:sender");
 
-               xml_add_element($doc, $sender, "dfrn:name", $owner['name']);
-               xml_add_element($doc, $sender, "dfrn:uri", $owner['url']);
-               xml_add_element($doc, $sender, "dfrn:avatar", $owner['thumb']);
+               xml::add_element($doc, $sender, "dfrn:name", $owner['name']);
+               xml::add_element($doc, $sender, "dfrn:uri", $owner['url']);
+               xml::add_element($doc, $sender, "dfrn:avatar", $owner['thumb']);
 
                $mail->appendChild($sender);
 
-               xml_add_element($doc, $mail, "dfrn:id", $item['uri']);
-               xml_add_element($doc, $mail, "dfrn:in-reply-to", $item['parent-uri']);
-               xml_add_element($doc, $mail, "dfrn:sentdate", datetime_convert('UTC', 'UTC', $item['created'] . '+00:00' , ATOM_TIME));
-               xml_add_element($doc, $mail, "dfrn:subject", $item['title']);
-               xml_add_element($doc, $mail, "dfrn:content", $item['body']);
+               xml::add_element($doc, $mail, "dfrn:id", $item['uri']);
+               xml::add_element($doc, $mail, "dfrn:in-reply-to", $item['parent-uri']);
+               xml::add_element($doc, $mail, "dfrn:sentdate", datetime_convert('UTC', 'UTC', $item['created'] . '+00:00' , ATOM_TIME));
+               xml::add_element($doc, $mail, "dfrn:subject", $item['title']);
+               xml::add_element($doc, $mail, "dfrn:content", $item['body']);
 
                $root->appendChild($mail);
 
@@ -319,11 +320,11 @@ class dfrn {
 
                $suggest = $doc->createElement("dfrn:suggest");
 
-               xml_add_element($doc, $suggest, "dfrn:url", $item['url']);
-               xml_add_element($doc, $suggest, "dfrn:name", $item['name']);
-               xml_add_element($doc, $suggest, "dfrn:photo", $item['photo']);
-               xml_add_element($doc, $suggest, "dfrn:request", $item['request']);
-               xml_add_element($doc, $suggest, "dfrn:note", $item['note']);
+               xml::add_element($doc, $suggest, "dfrn:url", $item['url']);
+               xml::add_element($doc, $suggest, "dfrn:name", $item['name']);
+               xml::add_element($doc, $suggest, "dfrn:photo", $item['photo']);
+               xml::add_element($doc, $suggest, "dfrn:request", $item['request']);
+               xml::add_element($doc, $suggest, "dfrn:note", $item['note']);
 
                $root->appendChild($suggest);
 
@@ -365,16 +366,16 @@ class dfrn {
 
                $relocate = $doc->createElement("dfrn:relocate");
 
-               xml_add_element($doc, $relocate, "dfrn:url", $owner['url']);
-               xml_add_element($doc, $relocate, "dfrn:name", $owner['name']);
-               xml_add_element($doc, $relocate, "dfrn:photo", $photos[4]);
-               xml_add_element($doc, $relocate, "dfrn:thumb", $photos[5]);
-               xml_add_element($doc, $relocate, "dfrn:micro", $photos[6]);
-               xml_add_element($doc, $relocate, "dfrn:request", $owner['request']);
-               xml_add_element($doc, $relocate, "dfrn:confirm", $owner['confirm']);
-               xml_add_element($doc, $relocate, "dfrn:notify", $owner['notify']);
-               xml_add_element($doc, $relocate, "dfrn:poll", $owner['poll']);
-               xml_add_element($doc, $relocate, "dfrn:sitepubkey", get_config('system','site_pubkey'));
+               xml::add_element($doc, $relocate, "dfrn:url", $owner['url']);
+               xml::add_element($doc, $relocate, "dfrn:name", $owner['name']);
+               xml::add_element($doc, $relocate, "dfrn:photo", $photos[4]);
+               xml::add_element($doc, $relocate, "dfrn:thumb", $photos[5]);
+               xml::add_element($doc, $relocate, "dfrn:micro", $photos[6]);
+               xml::add_element($doc, $relocate, "dfrn:request", $owner['request']);
+               xml::add_element($doc, $relocate, "dfrn:confirm", $owner['confirm']);
+               xml::add_element($doc, $relocate, "dfrn:notify", $owner['notify']);
+               xml::add_element($doc, $relocate, "dfrn:poll", $owner['poll']);
+               xml::add_element($doc, $relocate, "dfrn:sitepubkey", get_config('system','site_pubkey'));
 
                $root->appendChild($relocate);
 
@@ -410,39 +411,39 @@ class dfrn {
                $root->setAttribute("xmlns:ostatus", NAMESPACE_OSTATUS);
                $root->setAttribute("xmlns:statusnet", NAMESPACE_STATUSNET);
 
-               xml_add_element($doc, $root, "id", app::get_baseurl()."/profile/".$owner["nick"]);
-               xml_add_element($doc, $root, "title", $owner["name"]);
+               xml::add_element($doc, $root, "id", app::get_baseurl()."/profile/".$owner["nick"]);
+               xml::add_element($doc, $root, "title", $owner["name"]);
 
                $attributes = array("uri" => "https://friendi.ca", "version" => FRIENDICA_VERSION."-".DB_UPDATE_VERSION);
-               xml_add_element($doc, $root, "generator", FRIENDICA_PLATFORM, $attributes);
+               xml::add_element($doc, $root, "generator", FRIENDICA_PLATFORM, $attributes);
 
                $attributes = array("rel" => "license", "href" => "http://creativecommons.org/licenses/by/3.0/");
-               xml_add_element($doc, $root, "link", "", $attributes);
+               xml::add_element($doc, $root, "link", "", $attributes);
 
                $attributes = array("rel" => "alternate", "type" => "text/html", "href" => $alternatelink);
-               xml_add_element($doc, $root, "link", "", $attributes);
+               xml::add_element($doc, $root, "link", "", $attributes);
 
 
                if ($public) {
                        // DFRN itself doesn't uses this. But maybe someone else wants to subscribe to the public feed.
-                       ostatus_hublinks($doc, $root);
+                       ostatus::hublinks($doc, $root);
 
                        $attributes = array("rel" => "salmon", "href" => app::get_baseurl()."/salmon/".$owner["nick"]);
-                       xml_add_element($doc, $root, "link", "", $attributes);
+                       xml::add_element($doc, $root, "link", "", $attributes);
 
                        $attributes = array("rel" => "http://salmon-protocol.org/ns/salmon-replies", "href" => app::get_baseurl()."/salmon/".$owner["nick"]);
-                       xml_add_element($doc, $root, "link", "", $attributes);
+                       xml::add_element($doc, $root, "link", "", $attributes);
 
                        $attributes = array("rel" => "http://salmon-protocol.org/ns/salmon-mention", "href" => app::get_baseurl()."/salmon/".$owner["nick"]);
-                       xml_add_element($doc, $root, "link", "", $attributes);
+                       xml::add_element($doc, $root, "link", "", $attributes);
                }
 
                if ($owner['page-flags'] == PAGE_COMMUNITY)
-                       xml_add_element($doc, $root, "dfrn:community", 1);
+                       xml::add_element($doc, $root, "dfrn:community", 1);
 
                /// @todo We need a way to transmit the different page flags like "PAGE_PRVGROUP"
 
-               xml_add_element($doc, $root, "updated", datetime_convert("UTC", "UTC", "now", ATOM_TIME));
+               xml::add_element($doc, $root, "updated", datetime_convert("UTC", "UTC", "now", ATOM_TIME));
 
                $author = self::add_author($doc, $owner, $authorelement, $public);
                $root->appendChild($author);
@@ -468,26 +469,26 @@ class dfrn {
                $picdate = datetime_convert('UTC', 'UTC', $owner['avatar-date'].'+00:00', ATOM_TIME);
 
                $attributes = array("dfrn:updated" => $namdate);
-               xml_add_element($doc, $author, "name", $owner["name"], $attributes);
+               xml::add_element($doc, $author, "name", $owner["name"], $attributes);
 
                $attributes = array("dfrn:updated" => $namdate);
-               xml_add_element($doc, $author, "uri", app::get_baseurl().'/profile/'.$owner["nickname"], $attributes);
+               xml::add_element($doc, $author, "uri", app::get_baseurl().'/profile/'.$owner["nickname"], $attributes);
 
                $attributes = array("dfrn:updated" => $namdate);
-               xml_add_element($doc, $author, "dfrn:handle", $owner["addr"], $attributes);
+               xml::add_element($doc, $author, "dfrn:handle", $owner["addr"], $attributes);
 
                $attributes = array("rel" => "photo", "type" => "image/jpeg", "dfrn:updated" => $picdate,
                                        "media:width" => 175, "media:height" => 175, "href" => $owner['photo']);
-               xml_add_element($doc, $author, "link", "", $attributes);
+               xml::add_element($doc, $author, "link", "", $attributes);
 
                $attributes = array("rel" => "avatar", "type" => "image/jpeg", "dfrn:updated" => $picdate,
                                        "media:width" => 175, "media:height" => 175, "href" => $owner['photo']);
-               xml_add_element($doc, $author, "link", "", $attributes);
+               xml::add_element($doc, $author, "link", "", $attributes);
 
-               $birthday = feed_birthday($owner['user_uid'], $owner['timezone']);
+               $birthday = feed_birthday($owner['uid'], $owner['timezone']);
 
                if ($birthday)
-                       xml_add_element($doc, $author, "dfrn:birthday", $birthday);
+                       xml::add_element($doc, $author, "dfrn:birthday", $birthday);
 
                // The following fields will only be generated if this isn't for a public feed
                if ($public)
@@ -499,28 +500,28 @@ class dfrn {
                        FROM `profile`
                                INNER JOIN `user` ON `user`.`uid` = `profile`.`uid`
                                WHERE `profile`.`is-default` AND NOT `user`.`hidewall` AND `user`.`uid` = %d",
-                       intval($owner['user_uid']));
+                       intval($owner['uid']));
                if ($r) {
                        $profile = $r[0];
-                       xml_add_element($doc, $author, "poco:displayName", $profile["name"]);
-                       xml_add_element($doc, $author, "poco:updated", $namdate);
+                       xml::add_element($doc, $author, "poco:displayName", $profile["name"]);
+                       xml::add_element($doc, $author, "poco:updated", $namdate);
 
                        if (trim($profile["dob"]) != "0000-00-00")
-                               xml_add_element($doc, $author, "poco:birthday", "0000-".date("m-d", strtotime($profile["dob"])));
+                               xml::add_element($doc, $author, "poco:birthday", "0000-".date("m-d", strtotime($profile["dob"])));
 
-                       xml_add_element($doc, $author, "poco:note", $profile["about"]);
-                       xml_add_element($doc, $author, "poco:preferredUsername", $profile["nickname"]);
+                       xml::add_element($doc, $author, "poco:note", $profile["about"]);
+                       xml::add_element($doc, $author, "poco:preferredUsername", $profile["nickname"]);
 
                        $savetz = date_default_timezone_get();
                        date_default_timezone_set($profile["timezone"]);
-                       xml_add_element($doc, $author, "poco:utcOffset", date("P"));
+                       xml::add_element($doc, $author, "poco:utcOffset", date("P"));
                        date_default_timezone_set($savetz);
 
                        if (trim($profile["homepage"]) != "") {
                                $urls = $doc->createElement("poco:urls");
-                               xml_add_element($doc, $urls, "poco:type", "homepage");
-                               xml_add_element($doc, $urls, "poco:value", $profile["homepage"]);
-                               xml_add_element($doc, $urls, "poco:primary", "true");
+                               xml::add_element($doc, $urls, "poco:type", "homepage");
+                               xml::add_element($doc, $urls, "poco:value", $profile["homepage"]);
+                               xml::add_element($doc, $urls, "poco:primary", "true");
                                $author->appendChild($urls);
                        }
 
@@ -528,7 +529,7 @@ class dfrn {
                                $keywords = explode(",", $profile["pub_keywords"]);
 
                                foreach ($keywords AS $keyword)
-                                       xml_add_element($doc, $author, "poco:tags", trim($keyword));
+                                       xml::add_element($doc, $author, "poco:tags", trim($keyword));
 
                        }
 
@@ -536,25 +537,25 @@ class dfrn {
                        $xmpp = "";
                        if (trim($xmpp) != "") {
                                $ims = $doc->createElement("poco:ims");
-                               xml_add_element($doc, $ims, "poco:type", "xmpp");
-                               xml_add_element($doc, $ims, "poco:value", $xmpp);
-                               xml_add_element($doc, $ims, "poco:primary", "true");
+                               xml::add_element($doc, $ims, "poco:type", "xmpp");
+                               xml::add_element($doc, $ims, "poco:value", $xmpp);
+                               xml::add_element($doc, $ims, "poco:primary", "true");
                                $author->appendChild($ims);
                        }
 
                        if (trim($profile["locality"].$profile["region"].$profile["country-name"]) != "") {
                                $element = $doc->createElement("poco:address");
 
-                               xml_add_element($doc, $element, "poco:formatted", formatted_location($profile));
+                               xml::add_element($doc, $element, "poco:formatted", formatted_location($profile));
 
                                if (trim($profile["locality"]) != "")
-                                       xml_add_element($doc, $element, "poco:locality", $profile["locality"]);
+                                       xml::add_element($doc, $element, "poco:locality", $profile["locality"]);
 
                                if (trim($profile["region"]) != "")
-                                       xml_add_element($doc, $element, "poco:region", $profile["region"]);
+                                       xml::add_element($doc, $element, "poco:region", $profile["region"]);
 
                                if (trim($profile["country-name"]) != "")
-                                       xml_add_element($doc, $element, "poco:country", $profile["country-name"]);
+                                       xml::add_element($doc, $element, "poco:country", $profile["country-name"]);
 
                                $author->appendChild($element);
                        }
@@ -578,9 +579,9 @@ class dfrn {
                $contact = get_contact_details_by_url($contact_url, $item["uid"]);
 
                $author = $doc->createElement($element);
-               xml_add_element($doc, $author, "name", $contact["name"]);
-               xml_add_element($doc, $author, "uri", $contact["url"]);
-               xml_add_element($doc, $author, "dfrn:handle", $contact["addr"]);
+               xml::add_element($doc, $author, "name", $contact["name"]);
+               xml::add_element($doc, $author, "uri", $contact["url"]);
+               xml::add_element($doc, $author, "dfrn:handle", $contact["addr"]);
 
                /// @Todo
                /// - Check real image type and image size
@@ -591,7 +592,7 @@ class dfrn {
                                "media:width" => 80,
                                "media:height" => 80,
                                "href" => $contact["photo"]);
-               xml_add_element($doc, $author, "link", "", $attributes);
+               xml::add_element($doc, $author, "link", "", $attributes);
 
                $attributes = array(
                                "rel" => "avatar",
@@ -599,7 +600,7 @@ class dfrn {
                                "media:width" => 80,
                                "media:height" => 80,
                                "href" => $contact["photo"]);
-               xml_add_element($doc, $author, "link", "", $attributes);
+               xml::add_element($doc, $author, "link", "", $attributes);
 
                return $author;
        }
@@ -622,28 +623,35 @@ class dfrn {
                        if(!$r)
                                return false;
                        if($r->type)
-                               xml_add_element($doc, $entry, "activity:object-type", $r->type);
+                               xml::add_element($doc, $entry, "activity:object-type", $r->type);
                        if($r->id)
-                               xml_add_element($doc, $entry, "id", $r->id);
+                               xml::add_element($doc, $entry, "id", $r->id);
                        if($r->title)
-                               xml_add_element($doc, $entry, "title", $r->title);
+                               xml::add_element($doc, $entry, "title", $r->title);
                        if($r->link) {
-                               if(substr($r->link,0,1) === '<') {
+                               if(substr($r->link,0,1) == '<') {
                                        if(strstr($r->link,'&') && (! strstr($r->link,'&amp;')))
                                                $r->link = str_replace('&','&amp;', $r->link);
 
                                        $r->link = preg_replace('/\<link(.*?)\"\>/','<link$1"/>',$r->link);
 
-                                       $data = parse_xml_string($r->link, false);
-                                       foreach ($data->attributes() AS $parameter => $value)
-                                               $attributes[$parameter] = $value;
-                               } else
+                                       // XML does need a single element as root element so we add a dummy element here
+                                       $data = parse_xml_string("<dummy>".$r->link."</dummy>", false);
+                                       if (is_object($data)) {
+                                               foreach ($data->link AS $link) {
+                                                       $attributes = array();
+                                                       foreach ($link->attributes() AS $parameter => $value)
+                                                               $attributes[$parameter] = $value;
+                                                       xml::add_element($doc, $entry, "link", "", $attributes);
+                                               }
+                                       }
+                               } else {
                                        $attributes = array("rel" => "alternate", "type" => "text/html", "href" => $r->link);
-
-                               xml_add_element($doc, $entry, "link", "", $attributes);
+                                       xml::add_element($doc, $entry, "link", "", $attributes);
+                               }
                        }
                        if($r->content)
-                               xml_add_element($doc, $entry, "content", bbcode($r->content), array("type" => "html"));
+                               xml::add_element($doc, $entry, "content", bbcode($r->content), array("type" => "html"));
 
                        return $entry;
                }
@@ -677,7 +685,7 @@ class dfrn {
                                        if(trim($matches[4]) != "")
                                                $attributes["title"] = trim($matches[4]);
 
-                                       xml_add_element($doc, $root, "link", "", $attributes);
+                                       xml::add_element($doc, $root, "link", "", $attributes);
                                }
                        }
                }
@@ -704,7 +712,7 @@ class dfrn {
 
                if($item['deleted']) {
                        $attributes = array("ref" => $item['uri'], "when" => datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME));
-                       return xml_create_element($doc, "at:deleted-entry", "", $attributes);
+                       return xml::create_element($doc, "at:deleted-entry", "", $attributes);
                }
 
                $entry = $doc->createElement("entry");
@@ -714,6 +722,9 @@ class dfrn {
                else
                        $body = $item['body'];
 
+               // Remove the abstract element. It is only locally important.
+               $body = remove_abstract($body);
+
                if ($type == 'html') {
                        $htmlbody = $body;
 
@@ -735,66 +746,66 @@ class dfrn {
                        $attributes = array("ref" => $parent_item, "type" => "text/html",
                                                "href" => app::get_baseurl().'/display/'.$parent[0]['guid'],
                                                "dfrn:diaspora_guid" => $parent[0]['guid']);
-                       xml_add_element($doc, $entry, "thr:in-reply-to", "", $attributes);
+                       xml::add_element($doc, $entry, "thr:in-reply-to", "", $attributes);
                }
 
-               xml_add_element($doc, $entry, "id", $item["uri"]);
-               xml_add_element($doc, $entry, "title", $item["title"]);
+               xml::add_element($doc, $entry, "id", $item["uri"]);
+               xml::add_element($doc, $entry, "title", $item["title"]);
 
-               xml_add_element($doc, $entry, "published", datetime_convert("UTC","UTC",$item["created"]."+00:00",ATOM_TIME));
-               xml_add_element($doc, $entry, "updated", datetime_convert("UTC","UTC",$item["edited"]."+00:00",ATOM_TIME));
+               xml::add_element($doc, $entry, "published", datetime_convert("UTC","UTC",$item["created"]."+00:00",ATOM_TIME));
+               xml::add_element($doc, $entry, "updated", datetime_convert("UTC","UTC",$item["edited"]."+00:00",ATOM_TIME));
 
                // "dfrn:env" is used to read the content
-               xml_add_element($doc, $entry, "dfrn:env", base64url_encode($body, true));
+               xml::add_element($doc, $entry, "dfrn:env", base64url_encode($body, true));
 
                // The "content" field is not read by the receiver. We could remove it when the type is "text"
                // We keep it at the moment, maybe there is some old version that doesn't read "dfrn:env"
-               xml_add_element($doc, $entry, "content", (($type === 'html') ? $htmlbody : $body), array("type" => $type));
+               xml::add_element($doc, $entry, "content", (($type == 'html') ? $htmlbody : $body), array("type" => $type));
 
                // We save this value in "plink". Maybe we should read it from there as well?
-               xml_add_element($doc, $entry, "link", "", array("rel" => "alternate", "type" => "text/html",
+               xml::add_element($doc, $entry, "link", "", array("rel" => "alternate", "type" => "text/html",
                                                                "href" => app::get_baseurl()."/display/".$item["guid"]));
 
                // "comment-allow" is some old fashioned stuff for old Friendica versions.
                // It is included in the rewritten code for completeness
                if ($comment)
-                       xml_add_element($doc, $entry, "dfrn:comment-allow", intval($item['last-child']));
+                       xml::add_element($doc, $entry, "dfrn:comment-allow", intval($item['last-child']));
 
                if($item['location'])
-                       xml_add_element($doc, $entry, "dfrn:location", $item['location']);
+                       xml::add_element($doc, $entry, "dfrn:location", $item['location']);
 
                if($item['coord'])
-                       xml_add_element($doc, $entry, "georss:point", $item['coord']);
+                       xml::add_element($doc, $entry, "georss:point", $item['coord']);
 
                if(($item['private']) || strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid']))
-                       xml_add_element($doc, $entry, "dfrn:private", (($item['private']) ? $item['private'] : 1));
+                       xml::add_element($doc, $entry, "dfrn:private", (($item['private']) ? $item['private'] : 1));
 
                if($item['extid'])
-                       xml_add_element($doc, $entry, "dfrn:extid", $item['extid']);
+                       xml::add_element($doc, $entry, "dfrn:extid", $item['extid']);
 
                if($item['bookmark'])
-                       xml_add_element($doc, $entry, "dfrn:bookmark", "true");
+                       xml::add_element($doc, $entry, "dfrn:bookmark", "true");
 
                if($item['app'])
-                       xml_add_element($doc, $entry, "statusnet:notice_info", "", array("local_id" => $item['id'], "source" => $item['app']));
+                       xml::add_element($doc, $entry, "statusnet:notice_info", "", array("local_id" => $item['id'], "source" => $item['app']));
 
-               xml_add_element($doc, $entry, "dfrn:diaspora_guid", $item["guid"]);
+               xml::add_element($doc, $entry, "dfrn:diaspora_guid", $item["guid"]);
 
                // The signed text contains the content in Markdown, the sender handle and the signatur for the content
                // It is needed for relayed comments to Diaspora.
                if($item['signed_text']) {
                        $sign = base64_encode(json_encode(array('signed_text' => $item['signed_text'],'signature' => $item['signature'],'signer' => $item['signer'])));
-                       xml_add_element($doc, $entry, "dfrn:diaspora_signature", $sign);
+                       xml::add_element($doc, $entry, "dfrn:diaspora_signature", $sign);
                }
 
-               xml_add_element($doc, $entry, "activity:verb", construct_verb($item));
+               xml::add_element($doc, $entry, "activity:verb", construct_verb($item));
 
                if ($item['object-type'] != "")
-                       xml_add_element($doc, $entry, "activity:object-type", $item['object-type']);
+                       xml::add_element($doc, $entry, "activity:object-type", $item['object-type']);
                elseif ($item['id'] == $item['parent'])
-                       xml_add_element($doc, $entry, "activity:object-type", ACTIVITY_OBJ_NOTE);
+                       xml::add_element($doc, $entry, "activity:object-type", ACTIVITY_OBJ_NOTE);
                else
-                       xml_add_element($doc, $entry, "activity:object-type", ACTIVITY_OBJ_COMMENT);
+                       xml::add_element($doc, $entry, "activity:object-type", ACTIVITY_OBJ_COMMENT);
 
                $actobj = self::create_activity($doc, "activity:object", $item['object']);
                if ($actobj)
@@ -809,7 +820,7 @@ class dfrn {
                if(count($tags)) {
                        foreach($tags as $t)
                                if (($type != 'html') OR ($t[0] != "@"))
-                                       xml_add_element($doc, $entry, "category", "", array("scheme" => "X-DFRN:".$t[0].":".$t[1], "term" => $t[2]));
+                                       xml::add_element($doc, $entry, "category", "", array("scheme" => "X-DFRN:".$t[0].":".$t[1], "term" => $t[2]));
                }
 
                if(count($tags))
@@ -822,11 +833,11 @@ class dfrn {
                                intval($owner["uid"]),
                                dbesc(normalise_link($mention)));
                        if ($r[0]["forum"] OR $r[0]["prv"])
-                               xml_add_element($doc, $entry, "link", "", array("rel" => "mentioned",
+                               xml::add_element($doc, $entry, "link", "", array("rel" => "mentioned",
                                                                                        "ostatus:object-type" => ACTIVITY_OBJ_GROUP,
                                                                                        "href" => $mention));
                        else
-                               xml_add_element($doc, $entry, "link", "", array("rel" => "mentioned",
+                               xml::add_element($doc, $entry, "link", "", array("rel" => "mentioned",
                                                                                        "ostatus:object-type" => ACTIVITY_OBJ_PERSON,
                                                                                        "href" => $mention));
                }
@@ -1108,13 +1119,13 @@ class dfrn {
         *
         * @return Returns an array with relevant data of the author
         */
-       private function fetchauthor($xpath, $context, $importer, $element, $onlyfetch) {
+       private function fetchauthor($xpath, $context, $importer, $element, $onlyfetch, $xml = "") {
 
                $author = array();
                $author["name"] = $xpath->evaluate($element."/atom:name/text()", $context)->item(0)->nodeValue;
                $author["link"] = $xpath->evaluate($element."/atom:uri/text()", $context)->item(0)->nodeValue;
 
-               $r = q("SELECT `id`, `uid`, `network`, `avatar-date`, `name-date`, `uri-date`, `addr`,
+               $r = q("SELECT `id`, `uid`, `url`, `network`, `avatar-date`, `name-date`, `uri-date`, `addr`,
                                `name`, `nick`, `about`, `location`, `keywords`, `bdyear`, `bd`
                                FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `network` != '%s'",
                        intval($importer["uid"]), dbesc(normalise_link($author["link"])), dbesc(NETWORK_STATUSNET));
@@ -1123,6 +1134,9 @@ class dfrn {
                        $author["contact-id"] = $r[0]["id"];
                        $author["network"] = $r[0]["network"];
                } else {
+                       if (!$onlyfetch)
+                               logger("Contact ".$author["link"]." wasn't found for user ".$importer["uid"]." XML: ".$xml, LOGGER_DEBUG);
+
                        $author["contact-id"] = $importer["id"];
                        $author["network"] = $importer["network"];
                        $onlyfetch = true;
@@ -1152,38 +1166,41 @@ class dfrn {
                }
 
                if ($r AND !$onlyfetch) {
+                       logger("Check if contact details for contact ".$r[0]["id"]." (".$r[0]["nick"].") have to be updated.", LOGGER_DEBUG);
+
+                       $poco = array("url" => $contact["url"]);
 
                        // When was the last change to name or uri?
                        $name_element = $xpath->query($element."/atom:name", $context)->item(0);
                        foreach($name_element->attributes AS $attributes)
                                if ($attributes->name == "updated")
-                                       $contact["name-date"] = $attributes->textContent;
+                                       $poco["name-date"] = $attributes->textContent;
 
                        $link_element = $xpath->query($element."/atom:link", $context)->item(0);
                        foreach($link_element->attributes AS $attributes)
                                if ($attributes->name == "updated")
-                                       $contact["uri-date"] = $attributes->textContent;
+                                       $poco["uri-date"] = $attributes->textContent;
 
                        // Update contact data
                        $value = $xpath->evaluate($element."/dfrn:handle/text()", $context)->item(0)->nodeValue;
                        if ($value != "")
-                               $contact["addr"] = $value;
+                               $poco["addr"] = $value;
 
                        $value = $xpath->evaluate($element."/poco:displayName/text()", $context)->item(0)->nodeValue;
                        if ($value != "")
-                               $contact["name"] = $value;
+                               $poco["name"] = $value;
 
                        $value = $xpath->evaluate($element."/poco:preferredUsername/text()", $context)->item(0)->nodeValue;
                        if ($value != "")
-                               $contact["nick"] = $value;
+                               $poco["nick"] = $value;
 
                        $value = $xpath->evaluate($element."/poco:note/text()", $context)->item(0)->nodeValue;
                        if ($value != "")
-                               $contact["about"] = $value;
+                               $poco["about"] = $value;
 
                        $value = $xpath->evaluate($element."/poco:address/poco:formatted/text()", $context)->item(0)->nodeValue;
                        if ($value != "")
-                               $contact["location"] = $value;
+                               $poco["location"] = $value;
 
                        /// @todo Add support for the following fields that we don't support by now in the contact table:
                        /// - poco:utcOffset
@@ -1200,7 +1217,7 @@ class dfrn {
                                $tags[$tag->nodeValue] = $tag->nodeValue;
 
                        if (count($tags))
-                               $contact["keywords"] = implode(", ", $tags);
+                               $poco["keywords"] = implode(", ", $tags);
 
                        // "dfrn:birthday" contains the birthday converted to UTC
                        $old_bdyear = $contact["bdyear"];
@@ -1210,7 +1227,7 @@ class dfrn {
                        if (strtotime($birthday) > time()) {
                                $bd_timestamp = strtotime($birthday);
 
-                               $contact["bdyear"] = date("Y", $bd_timestamp);
+                               $poco["bdyear"] = date("Y", $bd_timestamp);
                        }
 
                        // "poco:birthday" is the birthday in the format "yyyy-mm-dd"
@@ -1225,9 +1242,11 @@ class dfrn {
                                        $bdyear = $bdyear + 1;
                                }
 
-                               $contact["bd"] = $value;
+                               $poco["bd"] = $value;
                        }
 
+                       $contact = array_merge($contact, $poco);
+
                        if ($old_bdyear != $contact["bdyear"])
                                self::birthday_event($contact, $birthday);
 
@@ -1238,6 +1257,7 @@ class dfrn {
 
                        unset($fields["id"]);
                        unset($fields["uid"]);
+                       unset($fields["url"]);
                        unset($fields["avatar-date"]);
                        unset($fields["name-date"]);
                        unset($fields["uri-date"]);
@@ -1245,8 +1265,10 @@ class dfrn {
                         // Update check for this field has to be done differently
                        $datefields = array("name-date", "uri-date");
                        foreach ($datefields AS $field)
-                               if (strtotime($contact[$field]) > strtotime($r[0][$field]))
+                               if (strtotime($contact[$field]) > strtotime($r[0][$field])) {
+                                       logger("Difference for contact ".$contact["id"]." in field '".$field."'. Old value: '".$contact[$field]."', new value '".$r[0][$field]."'", LOGGER_DEBUG);
                                        $update = true;
+                               }
 
                        foreach ($fields AS $field => $data)
                                if ($contact[$field] != $r[0][$field]) {
@@ -1255,7 +1277,7 @@ class dfrn {
                                }
 
                        if ($update) {
-                               logger("Update contact data for contact ".$contact["id"], LOGGER_DEBUG);
+                               logger("Update contact data for contact ".$contact["id"]." (".$contact["nick"].")", LOGGER_DEBUG);
 
                                q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `about` = '%s', `location` = '%s',
                                        `addr` = '%s', `keywords` = '%s', `bdyear` = '%s', `bd` = '%s',
@@ -1274,9 +1296,10 @@ class dfrn {
                        // It is used in the socgraph.php to prevent that old contact data
                        // that was relayed over several servers can overwrite contact
                        // data that we received directly.
-                       $contact["generation"] = 2;
-                       $contact["photo"] = $author["avatar"];
-                       update_gcontact($contact);
+
+                       $poco["generation"] = 2;
+                       $poco["photo"] = $author["avatar"];
+                       update_gcontact($poco);
                }
 
                return($author);
@@ -1301,7 +1324,7 @@ class dfrn {
                $obj_element = $obj_doc->createElementNS(NAMESPACE_ATOM1, $element);
 
                $activity_type = $xpath->query("activity:object-type/text()", $activity)->item(0)->nodeValue;
-               xml_add_element($obj_doc, $obj_element, "type", $activity_type);
+               xml::add_element($obj_doc, $obj_element, "type", $activity_type);
 
                $id = $xpath->query("atom:id", $activity)->item(0);
                if (is_object($id))
@@ -1311,9 +1334,10 @@ class dfrn {
                if (is_object($title))
                        $obj_element->appendChild($obj_doc->importNode($title, true));
 
-               $link = $xpath->query("atom:link", $activity)->item(0);
-               if (is_object($link))
-                       $obj_element->appendChild($obj_doc->importNode($link, true));
+               $links = $xpath->query("atom:link", $activity);
+               if (is_object($links))
+                       foreach ($links AS $link)
+                               $obj_element->appendChild($obj_doc->importNode($link, true));
 
                $content = $xpath->query("atom:content", $activity)->item(0);
                if (is_object($content))
@@ -1750,6 +1774,9 @@ class dfrn {
         * @return bool Should the processing of the entries be continued?
         */
        private function process_verbs($entrytype, $importer, &$item, &$is_like) {
+
+               logger("Process verb ".$item["verb"]." and object-type ".$item["object-type"]." for entrytype ".$entrytype, LOGGER_DEBUG);
+
                if (($entrytype == DFRN_TOP_LEVEL)) {
                        // The filling of the the "contact" variable is done for legcy reasons
                        // The functions below are partly used by ostatus.php as well - where we have this variable
@@ -1780,11 +1807,11 @@ class dfrn {
                                return false;
                        }
                } else {
-                       if(($item["verb"] === ACTIVITY_LIKE)
-                               || ($item["verb"] === ACTIVITY_DISLIKE)
-                               || ($item["verb"] === ACTIVITY_ATTEND)
-                               || ($item["verb"] === ACTIVITY_ATTENDNO)
-                               || ($item["verb"] === ACTIVITY_ATTENDMAYBE)) {
+                       if(($item["verb"] == ACTIVITY_LIKE)
+                               || ($item["verb"] == ACTIVITY_DISLIKE)
+                               || ($item["verb"] == ACTIVITY_ATTEND)
+                               || ($item["verb"] == ACTIVITY_ATTENDNO)
+                               || ($item["verb"] == ACTIVITY_ATTENDMAYBE)) {
                                $is_like = true;
                                $item["type"] = "activity";
                                $item["gravity"] = GRAVITY_LIKE;
@@ -1810,7 +1837,7 @@ class dfrn {
                        } else
                                $is_like = false;
 
-                       if(($item["verb"] === ACTIVITY_TAG) && ($item["object-type"] === ACTIVITY_OBJ_TAGTERM)) {
+                       if(($item["verb"] == ACTIVITY_TAG) && ($item["object-type"] == ACTIVITY_OBJ_TAGTERM)) {
 
                                $xo = parse_xml_string($item["object"],false);
                                $xt = parse_xml_string($item["target"],false);
@@ -1945,6 +1972,8 @@ class dfrn {
                        $item['body'] = @html2bbcode($item['body']);
                }
 
+               /// @todo We should check for a repeated post and if we know the repeated author.
+
                // We don't need the content element since "dfrn:env" is always present
                //$item["body"] = $xpath->query("atom:content/text()", $entry)->item(0)->nodeValue;
 
@@ -1997,14 +2026,28 @@ class dfrn {
                $categories = $xpath->query("atom:category", $entry);
                if ($categories) {
                        foreach ($categories AS $category) {
-                               foreach($category->attributes AS $attributes)
-                                       if ($attributes->name == "term") {
+                               $term = "";
+                               $scheme = "";
+                               foreach($category->attributes AS $attributes) {
+                                       if ($attributes->name == "term")
                                                $term = $attributes->textContent;
+
+                                       if ($attributes->name == "scheme")
+                                               $scheme = $attributes->textContent;
+                               }
+
+                               if (($term != "") AND ($scheme != "")) {
+                                       $parts = explode(":", $scheme);
+                                       if ((count($parts) >= 4) AND (array_shift($parts) == "X-DFRN")) {
+                                               $termhash = array_shift($parts);
+                                               $termurl = implode(":", $parts);
+
                                                if(strlen($item["tag"]))
                                                        $item["tag"] .= ",";
 
-                                               $item["tag"] .= "#[url=".App::get_baseurl()."/search?tag=".$term."]".$term."[/url]";
+                                               $item["tag"] .= $termhash."[url=".$termurl."]".$term."[/url]";
                                        }
+                               }
                        }
                }
 
@@ -2043,10 +2086,14 @@ class dfrn {
                        if (($item["network"] != $author["network"]) AND ($author["network"] != ""))
                                $item["network"] = $author["network"];
 
-                       if($importer["rel"] == CONTACT_IS_FOLLOWER) {
-                               logger("Contact ".$importer["id"]." is only follower. Quitting", LOGGER_DEBUG);
-                               return;
-                       }
+                       // This code was taken from the old DFRN code
+                       // When activated, forums don't work.
+                       // And: Why should we disallow commenting by followers?
+                       // the behaviour is now similar to the Diaspora part.
+                       //if($importer["rel"] == CONTACT_IS_FOLLOWER) {
+                       //      logger("Contact ".$importer["id"]." is only follower. Quitting", LOGGER_DEBUG);
+                       //      return;
+                       //}
                }
 
                if ($entrytype == DFRN_REPLY_RC) {
@@ -2218,15 +2265,17 @@ class dfrn {
                        else
                                return;
 
-                       if($item["object-type"] === ACTIVITY_OBJ_EVENT) {
+                       if($item["object-type"] == ACTIVITY_OBJ_EVENT) {
                                logger("Deleting event ".$item["event-id"], LOGGER_DEBUG);
                                event_delete($item["event-id"]);
                        }
 
-                       if(($item["verb"] === ACTIVITY_TAG) && ($item["object-type"] === ACTIVITY_OBJ_TAGTERM)) {
+                       if(($item["verb"] == ACTIVITY_TAG) && ($item["object-type"] == ACTIVITY_OBJ_TAGTERM)) {
+
                                $xo = parse_xml_string($item["object"],false);
                                $xt = parse_xml_string($item["target"],false);
-                               if($xt->type === ACTIVITY_OBJ_NOTE) {
+
+                               if($xt->type == ACTIVITY_OBJ_NOTE) {
                                        $i = q("SELECT `id`, `contact-id`, `tag` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
                                                dbesc($xt->id),
                                                intval($importer["importer_uid"])
@@ -2355,8 +2404,14 @@ class dfrn {
                $header["contact-id"] = $importer["id"];
 
                // Update the contact table if the data has changed
+
+               // The "atom:author" is only present in feeds
+               if ($xpath->query("/atom:feed/atom:author")->length > 0)
+                       self::fetchauthor($xpath, $doc->firstChild, $importer, "atom:author", false, $xml);
+
                // Only the "dfrn:owner" in the head section contains all data
-               self::fetchauthor($xpath, $doc->firstChild, $importer, "dfrn:owner", false);
+               if ($xpath->query("/atom:feed/dfrn:owner")->length > 0)
+                       self::fetchauthor($xpath, $doc->firstChild, $importer, "dfrn:owner", false, $xml);
 
                logger("Import DFRN message for user ".$importer["uid"]." from contact ".$importer["id"], LOGGER_DEBUG);
 
index 93fe2a472feb47268375709fd4f771614c8e7634..e3a3dcd78c2e05d37d177b9f5b4a6e78db2cceb5 100644 (file)
 <?php
-
 /**
  * @file include/diaspora.php
- * 
- * @todo GET /people/9aed8882b9f64896/stream
+ * @brief The implementation of the diaspora protocol
+ *
+ * Checklist:
+ *
+ * Checked:
+ * - send status
+ * - send comment
+ * - send like
+ * - send mail
+ * - send status retraction
+ * - send comment retraction on own post
+ * - send like retraction on own post
+ * - send comment retraction on diaspora post
+ * - send like retraction on diaspora post
+ * - receive status
+ * - receive reshare
+ * - receive comment
+ * - receive like
+ * - receive connect request
+ * - receive profile data
+ * - receive mail
+ * - receive comment retraction
+ * - receive like retraction
+ * - relay comment
+ * - relay like
+ * - relay comment retraction from diaspora
+ * - relay comment retraction from friendica
+ * - relay like retraction from diaspora
+ * - relay like retraction from friendica
+ * - send share
+ *
+ * Should work:
+ * - receive account deletion
+ * - send unshare
+ *
+ * Unchecked:
  */
 
-require_once('include/crypto.php');
-require_once('include/items.php');
-require_once('include/bb2diaspora.php');
-require_once('include/contact_selectors.php');
-require_once('include/queue_fn.php');
-require_once('include/lock.php');
-require_once('include/threads.php');
-require_once('mod/share.php');
-require_once('include/enotify.php');
-
-function diaspora_dispatch_public($msg) {
-
-       $enabled = intval(get_config('system','diaspora_enabled'));
-       if(! $enabled) {
-               logger('mod-diaspora: disabled');
-               return;
-       }
-
-       // Use a dummy importer to import the data for the public copy
-       $importer = array("uid" => 0, "page-flags" => PAGE_FREELOVE);
-       $result = diaspora_dispatch($importer,$msg);
-       logger("Dispatcher reported ".$result, LOGGER_DEBUG);
-
-       // Now distribute it to the followers
-       $r = q("SELECT `user`.* FROM `user` WHERE `user`.`uid` IN
-               ( SELECT `contact`.`uid` FROM `contact` WHERE `contact`.`network` = '%s' AND `contact`.`addr` = '%s' )
-               AND `account_expired` = 0 AND `account_removed` = 0 ",
-               dbesc(NETWORK_DIASPORA),
-               dbesc($msg['author'])
-       );
-       if(count($r)) {
-               foreach($r as $rr) {
-                       logger('diaspora_public: delivering to: ' . $rr['username']);
-                       diaspora_dispatch($rr,$msg);
-               }
-       }
-       else
-               logger('diaspora_public: no subscribers for '.$msg["author"].' '.print_r($msg, true));
-}
-
-
-
-function diaspora_dispatch($importer,$msg,$attempt=1) {
-
-       $ret = 0;
-
-       $enabled = intval(get_config('system','diaspora_enabled'));
-       if(! $enabled) {
-               logger('mod-diaspora: disabled');
-               return;
-       }
+require_once("include/items.php");
+require_once("include/bb2diaspora.php");
+require_once("include/Scrape.php");
+require_once("include/Contact.php");
+require_once("include/Photo.php");
+require_once("include/socgraph.php");
+require_once("include/group.php");
+require_once("include/xml.php");
+require_once("include/datetime.php");
+require_once("include/queue_fn.php");
 
-       // php doesn't like dashes in variable names
-
-       $msg['message'] = str_replace(
-                       array('<activity_streams-photo>','</activity_streams-photo>'),
-                       array('<asphoto>','</asphoto>'),
-                       $msg['message']);
+/**
+ * @brief This class contain functions to create and send Diaspora XML files
+ *
+ */
+class diaspora {
 
+       /**
+        * @brief Return a list of relay servers
+        *
+        * This is an experimental Diaspora feature.
+        *
+        * @return array of relay servers
+        */
+       public static function relay_list() {
 
-       $parsed_xml = parse_xml_string($msg['message'],false);
+               $serverdata = get_config("system", "relay_server");
+               if ($serverdata == "")
+                       return array();
 
-       $xmlbase = $parsed_xml->post;
+               $relay = array();
 
-       logger('diaspora_dispatch: ' . print_r($xmlbase,true), LOGGER_DEBUG);
+               $servers = explode(",", $serverdata);
 
+               foreach($servers AS $server) {
+                       $server = trim($server);
+                       $batch = $server."/receive/public";
 
-       if($xmlbase->request) {
-               $ret = diaspora_request($importer,$xmlbase->request);
-       }
-       elseif($xmlbase->status_message) {
-               $ret = diaspora_post($importer,$xmlbase->status_message,$msg);
-       }
-       elseif($xmlbase->profile) {
-               $ret = diaspora_profile($importer,$xmlbase->profile,$msg);
-       }
-       elseif($xmlbase->comment) {
-               $ret = diaspora_comment($importer,$xmlbase->comment,$msg);
-       }
-       elseif($xmlbase->like) {
-               $ret = diaspora_like($importer,$xmlbase->like,$msg);
-       }
-       elseif($xmlbase->asphoto) {
-               $ret = diaspora_asphoto($importer,$xmlbase->asphoto,$msg);
-       }
-       elseif($xmlbase->reshare) {
-               $ret = diaspora_reshare($importer,$xmlbase->reshare,$msg);
-       }
-       elseif($xmlbase->retraction) {
-               $ret = diaspora_retraction($importer,$xmlbase->retraction,$msg);
-       }
-       elseif($xmlbase->signed_retraction) {
-               $ret = diaspora_signed_retraction($importer,$xmlbase->signed_retraction,$msg);
-       }
-       elseif($xmlbase->relayable_retraction) {
-               $ret = diaspora_signed_retraction($importer,$xmlbase->relayable_retraction,$msg);
-       }
-       elseif($xmlbase->photo) {
-               $ret = diaspora_photo($importer,$xmlbase->photo,$msg,$attempt);
-       }
-       elseif($xmlbase->conversation) {
-               $ret = diaspora_conversation($importer,$xmlbase->conversation,$msg);
-       }
-       elseif($xmlbase->message) {
-               $ret = diaspora_message($importer,$xmlbase->message,$msg);
-       }
-       elseif($xmlbase->participation) {
-               $ret = diaspora_participation($importer,$xmlbase->participation);
-       }
-       else {
-               logger('diaspora_dispatch: unknown message type: ' . print_r($xmlbase,true));
-       }
-       return $ret;
-}
-
-function diaspora_handle_from_contact($contact_id) {
-       $handle = False;
-
-       logger("diaspora_handle_from_contact: contact id is " . $contact_id, LOGGER_DEBUG);
-
-       $r = q("SELECT network, addr, self, url, nick FROM contact WHERE id = %d",
-              intval($contact_id)
-       );
-       if($r) {
-               $contact = $r[0];
-
-               logger("diaspora_handle_from_contact: contact 'self' = " . $contact['self'] . " 'url' = " . $contact['url'], LOGGER_DEBUG);
+                       $relais = q("SELECT `batch`, `id`, `name`,`network` FROM `contact` WHERE `uid` = 0 AND `batch` = '%s' LIMIT 1", dbesc($batch));
 
-               if($contact['network'] === NETWORK_DIASPORA) {
-                       $handle = $contact['addr'];
+                       if (!$relais) {
+                               $addr = "relay@".str_replace("http://", "", normalise_link($server));
+
+                               $r = q("INSERT INTO `contact` (`uid`, `created`, `name`, `nick`, `addr`, `url`, `nurl`, `batch`, `network`, `rel`, `blocked`, `pending`, `writable`, `name-date`, `uri-date`, `avatar-date`)
+                                       VALUES (0, '%s', '%s', 'relay', '%s', '%s', '%s', '%s', '%s', %d, 0, 0, 1, '%s', '%s', '%s')",
+                                       datetime_convert(),
+                                       dbesc($addr),
+                                       dbesc($addr),
+                                       dbesc($server),
+                                       dbesc(normalise_link($server)),
+                                       dbesc($batch),
+                                       dbesc(NETWORK_DIASPORA),
+                                       intval(CONTACT_IS_FOLLOWER),
+                                       dbesc(datetime_convert()),
+                                       dbesc(datetime_convert()),
+                                       dbesc(datetime_convert())
+                               );
 
-//                     logger("diaspora_handle_from_contact: contact id is a Diaspora person, handle = " . $handle, LOGGER_DEBUG);
+                               $relais = q("SELECT `batch`, `id`, `name`,`network` FROM `contact` WHERE `uid` = 0 AND `batch` = '%s' LIMIT 1", dbesc($batch));
+                               if ($relais)
+                                       $relay[] = $relais[0];
+                       } else
+                               $relay[] = $relais[0];
                }
-               elseif(($contact['network'] === NETWORK_DFRN) || ($contact['self'] == 1)) {
-                       $baseurl_start = strpos($contact['url'],'://') + 3;
-                       $baseurl_length = strpos($contact['url'],'/profile') - $baseurl_start; // allows installations in a subdirectory--not sure how Diaspora will handle
-                       $baseurl = substr($contact['url'], $baseurl_start, $baseurl_length);
-                       $handle = $contact['nick'] . '@' . $baseurl;
 
-//                     logger("diaspora_handle_from_contact: contact id is a DFRN person, handle = " . $handle, LOGGER_DEBUG);
-               }
+               return $relay;
        }
 
-       return $handle;
-}
-
-function diaspora_get_contact_by_handle($uid,$handle) {
-       $r = q("SELECT * FROM `contact` WHERE `network` = '%s' AND `uid` = %d AND `addr` = '%s' LIMIT 1",
-               dbesc(NETWORK_DIASPORA),
-               intval($uid),
-               dbesc($handle)
-       );
-       if($r && count($r))
-               return $r[0];
-
-       $handle_parts = explode("@", $handle);
-       $nurl_sql = '%%://' . $handle_parts[1] . '%%/profile/' . $handle_parts[0];
-       $r = q("SELECT * FROM contact WHERE network = '%s' AND uid = %d AND nurl LIKE '%s' LIMIT 1",
-              dbesc(NETWORK_DFRN),
-              intval($uid),
-              dbesc($nurl_sql)
-       );
-       if($r && count($r))
-               return $r[0];
-
-       return false;
-}
-
-function find_diaspora_person_by_handle($handle) {
-
-       $person = false;
-       $update = false;
-       $got_lock = false;
-
-       $endlessloop = 0;
-       $maxloops = 10;
-
-       do {
-               $r = q("select * from fcontact where network = '%s' and addr = '%s' limit 1",
-                       dbesc(NETWORK_DIASPORA),
-                       dbesc($handle)
-               );
-               if(count($r)) {
-                       $person = $r[0];
-                       logger('find_diaspora_person_by handle: in cache ' . print_r($r,true), LOGGER_DEBUG);
-
-                       // update record occasionally so it doesn't get stale
-                       $d = strtotime($person['updated'] . ' +00:00');
-                       if($d < strtotime('now - 14 days'))
-                               $update = true;
-               }
+       /**
+        * @brief repairs a signature that was double encoded
+        *
+        * The function is unused at the moment. It was copied from the old implementation.
+        *
+        * @param string $signature The signature
+        * @param string $handle The handle of the signature owner
+        * @param integer $level This value is only set inside this function to avoid endless loops
+        *
+        * @return string the repaired signature
+        */
+       private function repair_signature($signature, $handle = "", $level = 1) {
 
+               if ($signature == "")
+                       return ($signature);
 
-               // FETCHING PERSON INFORMATION FROM REMOTE SERVER
-               //
-               // If the person isn't in our 'fcontact' table, or if he/she is but
-               // his/her information hasn't been updated for more than 14 days, then
-               // we want to fetch the person's information from the remote server.
-               //
-               // Note that $person isn't changed by this block of code unless the
-               // person's information has been successfully fetched from the remote
-               // server. So if $person was 'false' to begin with (because he/she wasn't
-               // in the local cache), it'll stay false, and if $person held the local
-               // cache information to begin with, it'll keep that information. That way
-               // if there's a problem with the remote fetch, we can at least use our
-               // cached information--it's better than nothing.
-
-               if((! $person) || ($update))  {
-                       // Lock the function to prevent race conditions if multiple items
-                       // come in at the same time from a person who doesn't exist in
-                       // fcontact
-                       //
-                       // Don't loop forever. On the last loop, try to create the contact
-                       // whether the function is locked or not. Maybe the locking thread
-                       // has died or something. At any rate, a duplicate in 'fcontact'
-                       // is a much smaller problem than a deadlocked thread
-                       $got_lock = lock_function('find_diaspora_person_by_handle', false);
-                       if(($endlessloop + 1) >= $maxloops)
-                               $got_lock = true;
-
-                       if($got_lock) {
-                               logger('find_diaspora_person_by_handle: create or refresh', LOGGER_DEBUG);
-                               require_once('include/Scrape.php');
-                               $r = probe_url($handle, PROBE_DIASPORA);
-
-                               // Note that Friendica contacts can return a "Diaspora person"
-                               // if Diaspora connectivity is enabled on their server
-                               if((count($r)) && ($r['network'] === NETWORK_DIASPORA)) {
-                                       add_fcontact($r,$update);
-                                       $person = ($r);
-                               }
+               if (base64_encode(base64_decode(base64_decode($signature))) == base64_decode($signature)) {
+                       $signature = base64_decode($signature);
+                       logger("Repaired double encoded signature from Diaspora/Hubzilla handle ".$handle." - level ".$level, LOGGER_DEBUG);
 
-                               unlock_function('find_diaspora_person_by_handle');
-                       }
-                       else {
-                               logger('find_diaspora_person_by_handle: couldn\'t lock function', LOGGER_DEBUG);
-                               if(! $person)
-                                       block_on_function_lock('find_diaspora_person_by_handle');
-                       }
+                       // Do a recursive call to be able to fix even multiple levels
+                       if ($level < 10)
+                               $signature = self::repair_signature($signature, $handle, ++$level);
                }
-       } while((! $person) && (! $got_lock) && (++$endlessloop < $maxloops));
-       // We need to try again if the person wasn't in 'fcontact' but the function was locked.
-       // The fact that the function was locked may mean that another process was creating the
-       // person's record. It could also mean another process was creating or updating an unrelated
-       // person.
-       //
-       // At any rate, we need to keep trying until we've either got the person or had a chance to
-       // try to fetch his/her remote information. But we don't want to block on locking the
-       // function, because if the other process is creating the record, then when we acquire the lock
-       // we'll dive right into creating another, duplicate record. We DO want to at least wait
-       // until the lock is released, so we don't flood the database with requests.
-       //
-       // If the person was in the 'fcontact' table, don't try again. It's not worth the time, since
-       // we do have some information for the person
-
-       return $person;
-}
-
 
-function get_diaspora_key($uri) {
-       logger('Fetching diaspora key for: ' . $uri);
-
-       $r = find_diaspora_person_by_handle($uri);
-       if($r)
-               return $r['pubkey'];
-       return '';
-}
+               return($signature);
+       }
 
+       /**
+        * @brief: Decodes incoming Diaspora message
+        *
+        * @param array $importer Array of the importer user
+        * @param string $xml urldecoded Diaspora salmon
+        *
+        * @return array
+        * 'message' -> decoded Diaspora XML message
+        * 'author' -> author diaspora handle
+        * 'key' -> author public key (converted to pkcs#8)
+        */
+       public static function decode($importer, $xml) {
 
-function diaspora_pubmsg_build($msg,$user,$contact,$prvkey,$pubkey) {
-       $a = get_app();
+               $public = false;
+               $basedom = parse_xml_string($xml);
 
-       logger('diaspora_pubmsg_build: ' . $msg, LOGGER_DATA);
+               if (!is_object($basedom))
+                       return false;
 
+               $children = $basedom->children('https://joindiaspora.com/protocol');
 
-       $handle = $user['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
+               if($children->header) {
+                       $public = true;
+                       $author_link = str_replace('acct:','',$children->header->author_id);
+               } else {
 
-//     $b64_data = base64_encode($msg);
-//     $b64url_data = base64url_encode($b64_data);
+                       $encrypted_header = json_decode(base64_decode($children->encrypted_header));
 
-       $b64url_data = base64url_encode($msg);
+                       $encrypted_aes_key_bundle = base64_decode($encrypted_header->aes_key);
+                       $ciphertext = base64_decode($encrypted_header->ciphertext);
 
-       $data = str_replace(array("\n","\r"," ","\t"),array('','','',''),$b64url_data);
+                       $outer_key_bundle = '';
+                       openssl_private_decrypt($encrypted_aes_key_bundle,$outer_key_bundle,$importer['prvkey']);
 
-       $type = 'application/xml';
-       $encoding = 'base64url';
-       $alg = 'RSA-SHA256';
+                       $j_outer_key_bundle = json_decode($outer_key_bundle);
 
-       $signable_data = $data  . '.' . base64url_encode($type) . '.'
-               . base64url_encode($encoding) . '.' . base64url_encode($alg) ;
+                       $outer_iv = base64_decode($j_outer_key_bundle->iv);
+                       $outer_key = base64_decode($j_outer_key_bundle->key);
 
-       $signature = rsa_sign($signable_data,$prvkey);
-       $sig = base64url_encode($signature);
+                       $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $outer_key, $ciphertext, MCRYPT_MODE_CBC, $outer_iv);
 
-$magic_env = <<< EOT
-<?xml version='1.0' encoding='UTF-8'?>
-<diaspora xmlns="https://joindiaspora.com/protocol" xmlns:me="http://salmon-protocol.org/ns/magic-env" >
-  <header>
-    <author_id>$handle</author_id>
-  </header>
-  <me:env>
-    <me:encoding>base64url</me:encoding>
-    <me:alg>RSA-SHA256</me:alg>
-    <me:data type="application/xml">$data</me:data>
-    <me:sig>$sig</me:sig>
-  </me:env>
-</diaspora>
-EOT;
 
-       logger('diaspora_pubmsg_build: magic_env: ' . $magic_env, LOGGER_DATA);
-       return $magic_env;
+                       $decrypted = pkcs5_unpad($decrypted);
 
-}
+                       logger('decrypted: '.$decrypted, LOGGER_DEBUG);
+                       $idom = parse_xml_string($decrypted,false);
 
+                       $inner_iv = base64_decode($idom->iv);
+                       $inner_aes_key = base64_decode($idom->aes_key);
 
+                       $author_link = str_replace('acct:','',$idom->author_id);
+               }
 
+               $dom = $basedom->children(NAMESPACE_SALMON_ME);
 
-function diaspora_msg_build($msg,$user,$contact,$prvkey,$pubkey,$public = false) {
-       $a = get_app();
+               // figure out where in the DOM tree our data is hiding
 
-       if($public)
-               return diaspora_pubmsg_build($msg,$user,$contact,$prvkey,$pubkey);
+               if($dom->provenance->data)
+                       $base = $dom->provenance;
+               elseif($dom->env->data)
+                       $base = $dom->env;
+               elseif($dom->data)
+                       $base = $dom;
 
-       logger('diaspora_msg_build: ' . $msg, LOGGER_DATA);
+               if (!$base) {
+                       logger('unable to locate salmon data in xml');
+                       http_status_exit(400);
+               }
 
-       // without a public key nothing will work
 
-       if(! $pubkey) {
-               logger('diaspora_msg_build: pubkey missing: contact id: ' . $contact['id']);
-               return '';
-       }
+               // Stash the signature away for now. We have to find their key or it won't be good for anything.
+               $signature = base64url_decode($base->sig);
 
-       $inner_aes_key = random_string(32);
-       $b_inner_aes_key = base64_encode($inner_aes_key);
-       $inner_iv = random_string(16);
-       $b_inner_iv = base64_encode($inner_iv);
+               // unpack the  data
 
-       $outer_aes_key = random_string(32);
-       $b_outer_aes_key = base64_encode($outer_aes_key);
-       $outer_iv = random_string(16);
-       $b_outer_iv = base64_encode($outer_iv);
+               // strip whitespace so our data element will return to one big base64 blob
+               $data = str_replace(array(" ","\t","\r","\n"),array("","","",""),$base->data);
 
-       $handle = $user['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
 
-       $padded_data = pkcs5_pad($msg,16);
-       $inner_encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $inner_aes_key, $padded_data, MCRYPT_MODE_CBC, $inner_iv);
+               // stash away some other stuff for later
 
-       $b64_data = base64_encode($inner_encrypted);
+               $type = $base->data[0]->attributes()->type[0];
+               $keyhash = $base->sig[0]->attributes()->keyhash[0];
+               $encoding = $base->encoding;
+               $alg = $base->alg;
 
 
-       $b64url_data = base64url_encode($b64_data);
-       $data = str_replace(array("\n","\r"," ","\t"),array('','','',''),$b64url_data);
+               $signed_data = $data.'.'.base64url_encode($type).'.'.base64url_encode($encoding).'.'.base64url_encode($alg);
 
-       $type = 'application/xml';
-       $encoding = 'base64url';
-       $alg = 'RSA-SHA256';
 
-       $signable_data = $data  . '.' . base64url_encode($type) . '.'
-               . base64url_encode($encoding) . '.' . base64url_encode($alg) ;
+               // decode the data
+               $data = base64url_decode($data);
 
-       $signature = rsa_sign($signable_data,$prvkey);
-       $sig = base64url_encode($signature);
 
-$decrypted_header = <<< EOT
-<decrypted_header>
-  <iv>$b_inner_iv</iv>
-  <aes_key>$b_inner_aes_key</aes_key>
-  <author_id>$handle</author_id>
-</decrypted_header>
-EOT;
+               if($public)
+                       $inner_decrypted = $data;
+               else {
 
-       $decrypted_header = pkcs5_pad($decrypted_header,16);
+                       // Decode the encrypted blob
 
-       $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $outer_aes_key, $decrypted_header, MCRYPT_MODE_CBC, $outer_iv);
+                       $inner_encrypted = base64_decode($data);
+                       $inner_decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $inner_aes_key, $inner_encrypted, MCRYPT_MODE_CBC, $inner_iv);
+                       $inner_decrypted = pkcs5_unpad($inner_decrypted);
+               }
 
-       $outer_json = json_encode(array('iv' => $b_outer_iv,'key' => $b_outer_aes_key));
+               if (!$author_link) {
+                       logger('Could not retrieve author URI.');
+                       http_status_exit(400);
+               }
+               // Once we have the author URI, go to the web and try to find their public key
+               // (first this will look it up locally if it is in the fcontact cache)
+               // This will also convert diaspora public key from pkcs#1 to pkcs#8
 
-       $encrypted_outer_key_bundle = '';
-       openssl_public_encrypt($outer_json,$encrypted_outer_key_bundle,$pubkey);
+               logger('Fetching key for '.$author_link);
+               $key = self::key($author_link);
 
-       $b64_encrypted_outer_key_bundle = base64_encode($encrypted_outer_key_bundle);
+               if (!$key) {
+                       logger('Could not retrieve author key.');
+                       http_status_exit(400);
+               }
 
-       logger('outer_bundle: ' . $b64_encrypted_outer_key_bundle . ' key: ' . $pubkey, LOGGER_DATA);
+               $verify = rsa_verify($signed_data,$signature,$key);
 
-       $encrypted_header_json_object = json_encode(array('aes_key' => base64_encode($encrypted_outer_key_bundle), 
-               'ciphertext' => base64_encode($ciphertext)));
-       $cipher_json = base64_encode($encrypted_header_json_object);
+               if (!$verify) {
+                       logger('Message did not verify. Discarding.');
+                       http_status_exit(400);
+               }
 
-       $encrypted_header = '<encrypted_header>' . $cipher_json . '</encrypted_header>';
+               logger('Message verified.');
 
-$magic_env = <<< EOT
-<?xml version='1.0' encoding='UTF-8'?>
-<diaspora xmlns="https://joindiaspora.com/protocol" xmlns:me="http://salmon-protocol.org/ns/magic-env" >
-  $encrypted_header
-  <me:env>
-    <me:encoding>base64url</me:encoding>
-    <me:alg>RSA-SHA256</me:alg>
-    <me:data type="application/xml">$data</me:data>
-    <me:sig>$sig</me:sig>
-  </me:env>
-</diaspora>
-EOT;
+               return array('message' => (string)$inner_decrypted,
+                               'author' => unxmlify($author_link),
+                               'key' => (string)$key);
 
-       logger('diaspora_msg_build: magic_env: ' . $magic_env, LOGGER_DATA);
-       return $magic_env;
+       }
 
-}
 
-/**
- *
- * diaspora_decode($importer,$xml)
- *   array $importer -> from user table
- *   string $xml -> urldecoded Diaspora salmon 
- *
- * Returns array
- * 'message' -> decoded Diaspora XML message
- * 'author' -> author diaspora handle
- * 'key' -> author public key (converted to pkcs#8)
- *
- * Author and key are used elsewhere to save a lookup for verifying replies and likes
- */
+       /**
+        * @brief Dispatches public messages and find the fitting receivers
+        *
+        * @param array $msg The post that will be dispatched
+        *
+        * @return int The message id of the generated message, "true" or "false" if there was an error
+        */
+       public static function dispatch_public($msg) {
 
+               $enabled = intval(get_config("system", "diaspora_enabled"));
+               if (!$enabled) {
+                       logger("diaspora is disabled");
+                       return false;
+               }
 
-function diaspora_decode($importer,$xml) {
-
-       $public = false;
-       $basedom = parse_xml_string($xml);
+               // Use a dummy importer to import the data for the public copy
+               $importer = array("uid" => 0, "page-flags" => PAGE_FREELOVE);
+               $message_id = self::dispatch($importer,$msg);
 
-       $children = $basedom->children('https://joindiaspora.com/protocol');
+               // Now distribute it to the followers
+               $r = q("SELECT `user`.* FROM `user` WHERE `user`.`uid` IN
+                       (SELECT `contact`.`uid` FROM `contact` WHERE `contact`.`network` = '%s' AND `contact`.`addr` = '%s')
+                       AND NOT `account_expired` AND NOT `account_removed`",
+                       dbesc(NETWORK_DIASPORA),
+                       dbesc($msg["author"])
+               );
+               if($r) {
+                       foreach($r as $rr) {
+                               logger("delivering to: ".$rr["username"]);
+                               self::dispatch($rr,$msg);
+                       }
+               } else
+                       logger("No subscribers for ".$msg["author"]." ".print_r($msg, true));
 
-       if($children->header) {
-               $public = true;
-               $author_link = str_replace('acct:','',$children->header->author_id);
+               return $message_id;
        }
-       else {
 
-               $encrypted_header = json_decode(base64_decode($children->encrypted_header));
+       /**
+        * @brief Dispatches the different message types to the different functions
+        *
+        * @param array $importer Array of the importer user
+        * @param array $msg The post that will be dispatched
+        *
+        * @return int The message id of the generated message, "true" or "false" if there was an error
+        */
+       public static function dispatch($importer, $msg) {
 
-               $encrypted_aes_key_bundle = base64_decode($encrypted_header->aes_key);
-               $ciphertext = base64_decode($encrypted_header->ciphertext);
+               // The sender is the handle of the contact that sent the message.
+               // This will often be different with relayed messages (for example "like" and "comment")
+               $sender = $msg["author"];
 
-               $outer_key_bundle = '';
-               openssl_private_decrypt($encrypted_aes_key_bundle,$outer_key_bundle,$importer['prvkey']);
-
-               $j_outer_key_bundle = json_decode($outer_key_bundle);
-
-               $outer_iv = base64_decode($j_outer_key_bundle->iv);
-               $outer_key = base64_decode($j_outer_key_bundle->key);
-
-               $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $outer_key, $ciphertext, MCRYPT_MODE_CBC, $outer_iv);
+               if (!diaspora::valid_posting($msg, $fields)) {
+                       logger("Invalid posting");
+                       return false;
+               }
 
+               $type = $fields->getName();
 
-               $decrypted = pkcs5_unpad($decrypted);
+               logger("Received message type ".$type." from ".$sender." for user ".$importer["uid"], LOGGER_DEBUG);
 
-               /**
-                * $decrypted now contains something like
-                *
-                *  <decrypted_header>
-                *     <iv>8e+G2+ET8l5BPuW0sVTnQw==</iv>
-                *     <aes_key>UvSMb4puPeB14STkcDWq+4QE302Edu15oaprAQSkLKU=</aes_key>
+               switch ($type) {
+                       case "account_deletion":
+                               return self::receive_account_deletion($importer, $fields);
 
-***** OBSOLETE
+                       case "comment":
+                               return self::receive_comment($importer, $sender, $fields, $msg["message"]);
 
-                *     <author>
-                *       <name>Ryan Hughes</name>
-                *       <uri>acct:galaxor@diaspora.pirateship.org</uri>
-                *     </author>
+                       case "contact":
+                               return self::receive_contact_request($importer, $fields);
 
-***** CURRENT
+                       case "conversation":
+                               return self::receive_conversation($importer, $msg, $fields);
 
-                *     <author_id>galaxor@diaspora.priateship.org</author_id>
+                       case "like":
+                               return self::receive_like($importer, $sender, $fields);
 
-***** END DIFFS
+                       case "message":
+                               return self::receive_message($importer, $fields);
 
-                *  </decrypted_header>
-                */
+                       case "participation": // Not implemented
+                               return self::receive_participation($importer, $fields);
 
-               logger('decrypted: ' . $decrypted, LOGGER_DEBUG);
-               $idom = parse_xml_string($decrypted,false);
+                       case "photo": // Not implemented
+                               return self::receive_photo($importer, $fields);
 
-               $inner_iv = base64_decode($idom->iv);
-               $inner_aes_key = base64_decode($idom->aes_key);
+                       case "poll_participation": // Not implemented
+                               return self::receive_poll_participation($importer, $fields);
 
-               $author_link = str_replace('acct:','',$idom->author_id);
+                       case "profile":
+                               return self::receive_profile($importer, $fields);
 
-       }
+                       case "reshare":
+                               return self::receive_reshare($importer, $fields, $msg["message"]);
 
-       $dom = $basedom->children(NAMESPACE_SALMON_ME);
+                       case "retraction":
+                               return self::receive_retraction($importer, $sender, $fields);
 
-       // figure out where in the DOM tree our data is hiding
+                       case "status_message":
+                               return self::receive_status_message($importer, $fields, $msg["message"]);
 
-       if($dom->provenance->data)
-               $base = $dom->provenance;
-       elseif($dom->env->data)
-               $base = $dom->env;
-       elseif($dom->data)
-               $base = $dom;
+                       default:
+                               logger("Unknown message type ".$type);
+                               return false;
+               }
 
-       if(! $base) {
-               logger('mod-diaspora: unable to locate salmon data in xml ');
-               http_status_exit(400);
+               return true;
        }
 
+       /**
+        * @brief Checks if a posting is valid and fetches the data fields.
+        *
+        * This function does not only check the signature.
+        * It also does the conversion between the old and the new diaspora format.
+        *
+        * @param array $msg Array with the XML, the sender handle and the sender signature
+        * @param object $fields SimpleXML object that contains the posting when it is valid
+        *
+        * @return bool Is the posting valid?
+        */
+       private function valid_posting($msg, &$fields) {
 
-       // Stash the signature away for now. We have to find their key or it won't be good for anything.
-       $signature = base64url_decode($base->sig);
+               $data = parse_xml_string($msg["message"], false);
 
-       // unpack the  data
-
-       // strip whitespace so our data element will return to one big base64 blob
-       $data = str_replace(array(" ","\t","\r","\n"),array("","","",""),$base->data);
+               if (!is_object($data))
+                       return false;
 
+               $first_child = $data->getName();
 
-       // stash away some other stuff for later
+               // Is this the new or the old version?
+               if ($data->getName() == "XML") {
+                       $oldXML = true;
+                       foreach ($data->post->children() as $child)
+                               $element = $child;
+               } else {
+                       $oldXML = false;
+                       $element = $data;
+               }
 
-       $type = $base->data[0]->attributes()->type[0];
-       $keyhash = $base->sig[0]->attributes()->keyhash[0];
-       $encoding = $base->encoding;
-       $alg = $base->alg;
+               $type = $element->getName();
+               $orig_type = $type;
 
+               // All retractions are handled identically from now on.
+               // In the new version there will only be "retraction".
+               if (in_array($type, array("signed_retraction", "relayable_retraction")))
+                       $type = "retraction";
 
-       $signed_data = $data  . '.' . base64url_encode($type) . '.' . base64url_encode($encoding) . '.' . base64url_encode($alg);
+               if ($type == "request")
+                       $type = "contact";
 
+               $fields = new SimpleXMLElement("<".$type."/>");
 
-       // decode the data
-       $data = base64url_decode($data);
+               $signed_data = "";
 
+               foreach ($element->children() AS $fieldname => $entry) {
+                       if ($oldXML) {
+                               // Translation for the old XML structure
+                               if ($fieldname == "diaspora_handle")
+                                       $fieldname = "author";
 
-       if($public) {
-               $inner_decrypted = $data;
-       }
-       else {
+                               if ($fieldname == "participant_handles")
+                                       $fieldname = "participants";
 
-               // Decode the encrypted blob
+                               if (in_array($type, array("like", "participation"))) {
+                                       if ($fieldname == "target_type")
+                                               $fieldname = "parent_type";
+                               }
 
-               $inner_encrypted = base64_decode($data);
-               $inner_decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $inner_aes_key, $inner_encrypted, MCRYPT_MODE_CBC, $inner_iv);
-               $inner_decrypted = pkcs5_unpad($inner_decrypted);
-       }
+                               if ($fieldname == "sender_handle")
+                                       $fieldname = "author";
 
-       if(! $author_link) {
-               logger('mod-diaspora: Could not retrieve author URI.');
-               http_status_exit(400);
-       }
+                               if ($fieldname == "recipient_handle")
+                                       $fieldname = "recipient";
 
-       // Once we have the author URI, go to the web and try to find their public key
-       // (first this will look it up locally if it is in the fcontact cache)
-       // This will also convert diaspora public key from pkcs#1 to pkcs#8
+                               if ($fieldname == "root_diaspora_id")
+                                       $fieldname = "root_author";
 
-       logger('mod-diaspora: Fetching key for ' . $author_link );
-       $key = get_diaspora_key($author_link);
+                               if ($type == "retraction") {
+                                       if ($fieldname == "post_guid")
+                                               $fieldname = "target_guid";
 
-       if(! $key) {
-               logger('mod-diaspora: Could not retrieve author key.');
-               http_status_exit(400);
-       }
+                                       if ($fieldname == "type")
+                                               $fieldname = "target_type";
+                               }
+                       }
 
-       $verify = rsa_verify($signed_data,$signature,$key);
+                       if ($fieldname == "author_signature")
+                               $author_signature = base64_decode($entry);
+                       elseif ($fieldname == "parent_author_signature")
+                               $parent_author_signature = base64_decode($entry);
+                       elseif ($fieldname != "target_author_signature") {
+                               if ($signed_data != "") {
+                                       $signed_data .= ";";
+                                       $signed_data_parent .= ";";
+                               }
 
-       if(! $verify) {
-               logger('mod-diaspora: Message did not verify. Discarding.');
-               http_status_exit(400);
-       }
+                               $signed_data .= $entry;
+                       }
+                       if (!in_array($fieldname, array("parent_author_signature", "target_author_signature")) OR
+                               ($orig_type == "relayable_retraction"))
+                               xml::copy($entry, $fields, $fieldname);
+               }
 
-       logger('mod-diaspora: Message verified.');
+               // This is something that shouldn't happen at all.
+               if (in_array($type, array("status_message", "reshare", "profile")))
+                       if ($msg["author"] != $fields->author) {
+                               logger("Message handle is not the same as envelope sender. Quitting this message.");
+                               return false;
+                       }
 
-       return array('message' => $inner_decrypted, 'author' => $author_link, 'key' => $key);
+               // Only some message types have signatures. So we quit here for the other types.
+               if (!in_array($type, array("comment", "message", "like")))
+                       return true;
 
-}
+               // No author_signature? This is a must, so we quit.
+               if (!isset($author_signature))
+                       return false;
 
+               if (isset($parent_author_signature)) {
+                       $key = self::key($msg["author"]);
 
-function diaspora_request($importer,$xml) {
+                       if (!rsa_verify($signed_data, $parent_author_signature, $key, "sha256"))
+                               return false;
+               }
 
-       $a = get_app();
+               $key = self::key($fields->author);
 
-       $sender_handle = unxmlify($xml->sender_handle);
-       $recipient_handle = unxmlify($xml->recipient_handle);
+               return rsa_verify($signed_data, $author_signature, $key, "sha256");
+       }
 
-       if(! $sender_handle || ! $recipient_handle)
-               return;
+       /**
+        * @brief Fetches the public key for a given handle
+        *
+        * @param string $handle The handle
+        *
+        * @return string The public key
+        */
+       private function key($handle) {
+               $handle = strval($handle);
 
-       $contact = diaspora_get_contact_by_handle($importer['uid'],$sender_handle);
+               logger("Fetching diaspora key for: ".$handle);
 
-       if($contact) {
+               $r = self::person_by_handle($handle);
+               if($r)
+                       return $r["pubkey"];
 
-               // perhaps we were already sharing with this person. Now they're sharing with us.
-               // That makes us friends.
+               return "";
+       }
 
-               if($contact['rel'] == CONTACT_IS_FOLLOWER && in_array($importer['page-flags'], array(PAGE_FREELOVE))) {
-                       q("UPDATE `contact` SET `rel` = %d, `writable` = 1 WHERE `id` = %d AND `uid` = %d",
-                               intval(CONTACT_IS_FRIEND),
-                               intval($contact['id']),
-                               intval($importer['uid'])
-                       );
-               }
-               // send notification
+       /**
+        * @brief Fetches data for a given handle
+        *
+        * @param string $handle The handle
+        *
+        * @return array the queried data
+        */
+       private function person_by_handle($handle) {
 
-               $r = q("SELECT `hide-friends` FROM `profile` WHERE `uid` = %d AND `is-default` = 1 LIMIT 1",
-                       intval($importer['uid'])
+               $r = q("SELECT * FROM `fcontact` WHERE `network` = '%s' AND `addr` = '%s' LIMIT 1",
+                       dbesc(NETWORK_DIASPORA),
+                       dbesc($handle)
                );
+               if ($r) {
+                       $person = $r[0];
+                       logger("In cache ".print_r($r,true), LOGGER_DEBUG);
 
-               if((count($r)) && (!$r[0]['hide-friends']) && (!$contact['hidden']) && intval(get_pconfig($importer['uid'],'system','post_newfriend'))) {
-                       require_once('include/items.php');
-
-                       $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
-                               intval($importer['uid'])
-                       );
-
-                       // they are not CONTACT_IS_FOLLOWER anymore but that's what we have in the array
-
-                       if(count($self) && $contact['rel'] == CONTACT_IS_FOLLOWER) {
-
-                               $arr = array();
-                               $arr['uri'] = $arr['parent-uri'] = item_new_uri($a->get_hostname(), $importer['uid']);
-                               $arr['uid'] = $importer['uid'];
-                               $arr['contact-id'] = $self[0]['id'];
-                               $arr['wall'] = 1;
-                               $arr['type'] = 'wall';
-                               $arr['gravity'] = 0;
-                               $arr['origin'] = 1;
-                               $arr['author-name'] = $arr['owner-name'] = $self[0]['name'];
-                               $arr['author-link'] = $arr['owner-link'] = $self[0]['url'];
-                               $arr['author-avatar'] = $arr['owner-avatar'] = $self[0]['thumb'];
-                               $arr['verb'] = ACTIVITY_FRIEND;
-                               $arr['object-type'] = ACTIVITY_OBJ_PERSON;
-
-                               $A = '[url=' . $self[0]['url'] . ']' . $self[0]['name'] . '[/url]';
-                               $B = '[url=' . $contact['url'] . ']' . $contact['name'] . '[/url]';
-                               $BPhoto = '[url=' . $contact['url'] . ']' . '[img]' . $contact['thumb'] . '[/img][/url]';
-                               $arr['body'] =  sprintf( t('%1$s is now friends with %2$s'), $A, $B)."\n\n\n".$Bphoto;
-
-                               $arr['object'] = '<object><type>' . ACTIVITY_OBJ_PERSON . '</type><title>' . $contact['name'] . '</title>'
-                                       . '<id>' . $contact['url'] . '/' . $contact['name'] . '</id>';
-                               $arr['object'] .= '<link>' . xmlify('<link rel="alternate" type="text/html" href="' . $contact['url'] . '" />' . "\n");
-                               $arr['object'] .= xmlify('<link rel="photo" type="image/jpeg" href="' . $contact['thumb'] . '" />' . "\n");
-                               $arr['object'] .= '</link></object>' . "\n";
-                               $arr['last-child'] = 1;
-
-                               $arr['allow_cid'] = $user[0]['allow_cid'];
-                               $arr['allow_gid'] = $user[0]['allow_gid'];
-                               $arr['deny_cid']  = $user[0]['deny_cid'];
-                               $arr['deny_gid']  = $user[0]['deny_gid'];
+                       // update record occasionally so it doesn't get stale
+                       $d = strtotime($person["updated"]." +00:00");
+                       if ($d < strtotime("now - 14 days"))
+                               $update = true;
+               }
 
-                               $i = item_store($arr);
-                               if($i)
-                               proc_run('php',"include/notifier.php","activity","$i");
+               if (!$person OR $update) {
+                       logger("create or refresh", LOGGER_DEBUG);
+                       $r = probe_url($handle, PROBE_DIASPORA);
 
+                       // Note that Friendica contacts will return a "Diaspora person"
+                       // if Diaspora connectivity is enabled on their server
+                       if ($r AND ($r["network"] === NETWORK_DIASPORA)) {
+                               self::add_fcontact($r, $update);
+                               $person = $r;
                        }
-
+               }
+               return $person;
+       }
+
+       /**
+        * @brief Updates the fcontact table
+        *
+        * @param array $arr The fcontact data
+        * @param bool $update Update or insert?
+        *
+        * @return string The id of the fcontact entry
+        */
+       private function add_fcontact($arr, $update = false) {
+
+               if($update) {
+                       $r = q("UPDATE `fcontact` SET
+                                       `name` = '%s',
+                                       `photo` = '%s',
+                                       `request` = '%s',
+                                       `nick` = '%s',
+                                       `addr` = '%s',
+                                       `batch` = '%s',
+                                       `notify` = '%s',
+                                       `poll` = '%s',
+                                       `confirm` = '%s',
+                                       `alias` = '%s',
+                                       `pubkey` = '%s',
+                                       `updated` = '%s'
+                               WHERE `url` = '%s' AND `network` = '%s'",
+                                       dbesc($arr["name"]),
+                                       dbesc($arr["photo"]),
+                                       dbesc($arr["request"]),
+                                       dbesc($arr["nick"]),
+                                       dbesc($arr["addr"]),
+                                       dbesc($arr["batch"]),
+                                       dbesc($arr["notify"]),
+                                       dbesc($arr["poll"]),
+                                       dbesc($arr["confirm"]),
+                                       dbesc($arr["alias"]),
+                                       dbesc($arr["pubkey"]),
+                                       dbesc(datetime_convert()),
+                                       dbesc($arr["url"]),
+                                       dbesc($arr["network"])
+                               );
+               } else {
+                       $r = q("INSERT INTO `fcontact` (`url`,`name`,`photo`,`request`,`nick`,`addr`,
+                                       `batch`, `notify`,`poll`,`confirm`,`network`,`alias`,`pubkey`,`updated`)
+                               VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')",
+                                       dbesc($arr["url"]),
+                                       dbesc($arr["name"]),
+                                       dbesc($arr["photo"]),
+                                       dbesc($arr["request"]),
+                                       dbesc($arr["nick"]),
+                                       dbesc($arr["addr"]),
+                                       dbesc($arr["batch"]),
+                                       dbesc($arr["notify"]),
+                                       dbesc($arr["poll"]),
+                                       dbesc($arr["confirm"]),
+                                       dbesc($arr["network"]),
+                                       dbesc($arr["alias"]),
+                                       dbesc($arr["pubkey"]),
+                                       dbesc(datetime_convert())
+                               );
                }
 
-               return;
-       }
-
-       $ret = find_diaspora_person_by_handle($sender_handle);
-
-
-       if((! count($ret)) || ($ret['network'] != NETWORK_DIASPORA)) {
-               logger('diaspora_request: Cannot resolve diaspora handle ' . $sender_handle . ' for ' . $recipient_handle);
-               return;
+               return $r;
        }
 
-       $batch = (($ret['batch']) ? $ret['batch'] : implode('/', array_slice(explode('/',$ret['url']),0,3)) . '/receive/public');
+       /**
+        * @brief get a handle (user@domain.tld) from a given contact id or gcontact id
+        *
+        * @param int $contact_id The id in the contact table
+        * @param int $gcontact_id The id in the gcontact table
+        *
+        * @return string the handle
+        */
+       public static function handle_from_contact($contact_id, $gcontact_id = 0) {
+               $handle = False;
 
+               logger("contact id is ".$contact_id." - gcontact id is ".$gcontact_id, LOGGER_DEBUG);
 
+               if ($gcontact_id != 0) {
+                       $r = q("SELECT `addr` FROM `gcontact` WHERE `id` = %d AND `addr` != ''",
+                               intval($gcontact_id));
+                       if ($r)
+                               return $r[0]["addr"];
+               }
 
-       $r = q("INSERT INTO `contact` (`uid`, `network`,`addr`,`created`,`url`,`nurl`,`batch`,`name`,`nick`,`photo`,`pubkey`,`notify`,`poll`,`blocked`,`priority`)
-               VALUES ( %d, '%s', '%s', '%s', '%s','%s','%s','%s','%s','%s','%s','%s','%s',%d,%d) ",
-               intval($importer['uid']),
-               dbesc($ret['network']),
-               dbesc($ret['addr']),
-               datetime_convert(),
-               dbesc($ret['url']),
-               dbesc(normalise_link($ret['url'])),
-               dbesc($batch),
-               dbesc($ret['name']),
-               dbesc($ret['nick']),
-               dbesc($ret['photo']),
-               dbesc($ret['pubkey']),
-               dbesc($ret['notify']),
-               dbesc($ret['poll']),
-               1,
-               2
-       );
-
-       // find the contact record we just created
+               $r = q("SELECT `network`, `addr`, `self`, `url`, `nick` FROM `contact` WHERE `id` = %d",
+                       intval($contact_id));
+               if ($r) {
+                       $contact = $r[0];
 
-       $contact_record = diaspora_get_contact_by_handle($importer['uid'],$sender_handle);
+                       logger("contact 'self' = ".$contact['self']." 'url' = ".$contact['url'], LOGGER_DEBUG);
 
-       if(! $contact_record) {
-               logger('diaspora_request: unable to locate newly created contact record.');
-               return;
-       }
+                       if($contact['addr'] != "")
+                               $handle = $contact['addr'];
+                       else {
+                               $baseurl_start = strpos($contact['url'],'://') + 3;
+                               $baseurl_length = strpos($contact['url'],'/profile') - $baseurl_start; // allows installations in a subdirectory--not sure how Diaspora will handle
+                               $baseurl = substr($contact['url'], $baseurl_start, $baseurl_length);
+                               $handle = $contact['nick'].'@'.$baseurl;
+                       }
+               }
 
-       $g = q("select def_gid from user where uid = %d limit 1",
-               intval($importer['uid'])
-       );
-       if($g && intval($g[0]['def_gid'])) {
-               require_once('include/group.php');
-               group_add_member($importer['uid'],'',$contact_record['id'],$g[0]['def_gid']);
+               return $handle;
        }
 
-       if($importer['page-flags'] == PAGE_NORMAL) {
-
-               $hash = random_string() . (string) time();   // Generate a confirm_key
-
-               $ret = q("INSERT INTO `intro` ( `uid`, `contact-id`, `blocked`, `knowyou`, `note`, `hash`, `datetime` )
-                       VALUES ( %d, %d, %d, %d, '%s', '%s', '%s' )",
-                       intval($importer['uid']),
-                       intval($contact_record['id']),
-                       0,
-                       0,
-                       dbesc( t('Sharing notification from Diaspora network')),
-                       dbesc($hash),
-                       dbesc(datetime_convert())
+       /**
+        * @brief Get a contact id for a given handle
+        *
+        * @param int $uid The user id
+        * @param string $handle The handle in the format user@domain.tld
+        *
+        * @return The contact id
+        */
+       private function contact_by_handle($uid, $handle) {
+               $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `addr` = '%s' LIMIT 1",
+                       intval($uid),
+                       dbesc($handle)
                );
-       }
-       else {
-
-               // automatic friend approval
-
-               require_once('include/Photo.php');
 
-               update_contact_avatar($contact_record['photo'],$importer['uid'],$contact_record['id']);
+               if ($r)
+                       return $r[0];
 
-               // technically they are sharing with us (CONTACT_IS_SHARING),
-               // but if our page-type is PAGE_COMMUNITY or PAGE_SOAPBOX
-               // we are going to change the relationship and make them a follower.
-
-               if($importer['page-flags'] == PAGE_FREELOVE)
-                       $new_relation = CONTACT_IS_FRIEND;
-               else
-                       $new_relation = CONTACT_IS_FOLLOWER;
-
-               $r = q("UPDATE `contact` SET `rel` = %d,
-                       `name-date` = '%s',
-                       `uri-date` = '%s',
-                       `blocked` = 0,
-                       `pending` = 0,
-                       `writable` = 1
-                       WHERE `id` = %d
-                       ",
-                       intval($new_relation),
-                       dbesc(datetime_convert()),
-                       dbesc(datetime_convert()),
-                       intval($contact_record['id'])
+               $handle_parts = explode("@", $handle);
+               $nurl_sql = "%%://".$handle_parts[1]."%%/profile/".$handle_parts[0];
+               $r = q("SELECT * FROM `contact` WHERE `network` = '%s' AND `uid` = %d AND `nurl` LIKE '%s' LIMIT 1",
+                       dbesc(NETWORK_DFRN),
+                       intval($uid),
+                       dbesc($nurl_sql)
                );
+               if($r)
+                       return $r[0];
 
-               $u = q("select * from user where uid = %d limit 1",intval($importer['uid']));
-               if($u)
-                       $ret = diaspora_share($u[0],$contact_record);
+               return false;
        }
 
-       return;
-}
-
-function diaspora_post_allow($importer,$contact, $is_comment = false) {
+       /**
+        * @brief Check if posting is allowed for this contact
+        *
+        * @param array $importer Array of the importer user
+        * @param array $contact The contact that is checked
+        * @param bool $is_comment Is the check for a comment?
+        *
+        * @return bool is the contact allowed to post?
+        */
+       private function post_allow($importer, $contact, $is_comment = false) {
 
-       // perhaps we were already sharing with this person. Now they're sharing with us.
-       // That makes us friends.
-       // Normally this should have handled by getting a request - but this could get lost
-       if($contact['rel'] == CONTACT_IS_FOLLOWER && in_array($importer['page-flags'], array(PAGE_FREELOVE))) {
-               q("UPDATE `contact` SET `rel` = %d, `writable` = 1 WHERE `id` = %d AND `uid` = %d",
-                       intval(CONTACT_IS_FRIEND),
-                       intval($contact['id']),
-                       intval($importer['uid'])
-               );
-               $contact['rel'] = CONTACT_IS_FRIEND;
-               logger('diaspora_post_allow: defining user '.$contact["nick"].' as friend');
-       }
+               // perhaps we were already sharing with this person. Now they're sharing with us.
+               // That makes us friends.
+               // Normally this should have handled by getting a request - but this could get lost
+               if($contact["rel"] == CONTACT_IS_FOLLOWER && in_array($importer["page-flags"], array(PAGE_FREELOVE))) {
+                       q("UPDATE `contact` SET `rel` = %d, `writable` = 1 WHERE `id` = %d AND `uid` = %d",
+                               intval(CONTACT_IS_FRIEND),
+                               intval($contact["id"]),
+                               intval($importer["uid"])
+                       );
+                       $contact["rel"] = CONTACT_IS_FRIEND;
+                       logger("defining user ".$contact["nick"]." as friend");
+               }
 
-       if(($contact['blocked']) || ($contact['readonly']) || ($contact['archive']))
-               return false;
-       if($contact['rel'] == CONTACT_IS_SHARING || $contact['rel'] == CONTACT_IS_FRIEND)
-               return true;
-       if($contact['rel'] == CONTACT_IS_FOLLOWER)
-               if(($importer['page-flags'] == PAGE_COMMUNITY) OR $is_comment)
+               if(($contact["blocked"]) || ($contact["readonly"]) || ($contact["archive"]))
+                       return false;
+               if($contact["rel"] == CONTACT_IS_SHARING || $contact["rel"] == CONTACT_IS_FRIEND)
                        return true;
+               if($contact["rel"] == CONTACT_IS_FOLLOWER)
+                       if(($importer["page-flags"] == PAGE_COMMUNITY) OR $is_comment)
+                               return true;
 
-       // Messages for the global users are always accepted
-       if ($importer['uid'] == 0)
-               return true;
-
-       return false;
-}
-
-function diaspora_is_redmatrix($url) {
-       return(strstr($url, "/channel/"));
-}
-
-function diaspora_plink($addr, $guid) {
-       $r = q("SELECT `url`, `nick`, `network` FROM `fcontact` WHERE `addr`='%s' LIMIT 1", dbesc($addr));
-
-       // Fallback
-       if (!$r)
-               return 'https://'.substr($addr,strpos($addr,'@')+1).'/posts/'.$guid;
-
-       // Friendica contacts are often detected as Diaspora contacts in the "fcontact" table
-       // So we try another way as well.
-       $s = q("SELECT `network` FROM `gcontact` WHERE `nurl`='%s' LIMIT 1", dbesc(normalise_link($r[0]["url"])));
-       if ($s)
-               $r[0]["network"] = $s[0]["network"];
-
-       if ($r[0]["network"] == NETWORK_DFRN)
-               return(str_replace("/profile/".$r[0]["nick"]."/", "/display/".$guid, $r[0]["url"]."/"));
-
-       if (diaspora_is_redmatrix($r[0]["url"]))
-               return $r[0]["url"]."/?f=&mid=".$guid;
-
-       return 'https://'.substr($addr,strpos($addr,'@')+1).'/posts/'.$guid;
-}
-
-function diaspora_repair_signature($signature, $handle = "", $level = 1) {
-
-       if ($signature == "")
-               return($signature);
-
-       if (base64_encode(base64_decode(base64_decode($signature))) == base64_decode($signature)) {
-               $signature = base64_decode($signature);
-               logger("Repaired double encoded signature from Diaspora/Hubzilla handle ".$handle." - level ".$level, LOGGER_DEBUG);
+               // Messages for the global users are always accepted
+               if ($importer["uid"] == 0)
+                       return true;
 
-               // Do a recursive call to be able to fix even multiple levels
-               if ($level < 10)
-                       $signature = diaspora_repair_signature($signature, $handle, ++$level);
+               return false;
        }
 
-       return($signature);
-}
+       /**
+        * @brief Fetches the contact id for a handle and checks if posting is allowed
+        *
+        * @param array $importer Array of the importer user
+        * @param string $handle The checked handle in the format user@domain.tld
+        * @param bool $is_comment Is the check for a comment?
+        *
+        * @return array The contact data
+        */
+       private function allowed_contact_by_handle($importer, $handle, $is_comment = false) {
+               $contact = self::contact_by_handle($importer["uid"], $handle);
+               if (!$contact) {
+                       logger("A Contact for handle ".$handle." and user ".$importer["uid"]." was not found");
+                       return false;
+               }
 
-function diaspora_post($importer,$xml,$msg) {
+               if (!self::post_allow($importer, $contact, $is_comment)) {
+                       logger("The handle: ".$handle." is not allowed to post to user ".$importer["uid"]);
+                       return false;
+               }
+               return $contact;
+       }
+
+       /**
+        * @brief Does the message already exists on the system?
+        *
+        * @param int $uid The user id
+        * @param string $guid The guid of the message
+        *
+        * @return int|bool message id if the message already was stored into the system - or false.
+        */
+       private function message_exists($uid, $guid) {
+               $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
+                       intval($uid),
+                       dbesc($guid)
+               );
 
-       $a = get_app();
-       $guid = notags(unxmlify($xml->guid));
-       $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
+               if($r) {
+                       logger("message ".$guid." already exists for user ".$uid);
+                       return $r[0]["id"];
+               }
 
-       if($diaspora_handle != $msg['author']) {
-               logger('diaspora_post: Potential forgery. Message handle is not the same as envelope sender.');
-               return 202;
+               return false;
        }
 
-       $contact = diaspora_get_contact_by_handle($importer['uid'],$diaspora_handle);
-       if(! $contact) {
-               logger('diaspora_post: A Contact for handle '.$diaspora_handle.' and user '.$importer['uid'].' was not found');
-               return 203;
-       }
+       /**
+        * @brief Checks for links to posts in a message
+        *
+        * @param array $item The item array
+        */
+       private function fetch_guid($item) {
+               preg_replace_callback("&\[url=/posts/([^\[\]]*)\](.*)\[\/url\]&Usi",
+                       function ($match) use ($item){
+                               return(self::fetch_guid_sub($match, $item));
+                       },$item["body"]);
+       }
+
+       /**
+        * @brief sub function of "fetch_guid" which checks for links in messages
+        *
+        * @param array $match array containing a link that has to be checked for a message link
+        * @param array $item The item array
+        */
+       private function fetch_guid_sub($match, $item) {
+               if (!self::store_by_guid($match[1], $item["author-link"]))
+                       self::store_by_guid($match[1], $item["owner-link"]);
+       }
+
+       /**
+        * @brief Fetches an item with a given guid from a given server
+        *
+        * @param string $guid the message guid
+        * @param string $server The server address
+        * @param int $uid The user id of the user
+        *
+        * @return int the message id of the stored message or false
+        */
+       private function store_by_guid($guid, $server, $uid = 0) {
+               $serverparts = parse_url($server);
+               $server = $serverparts["scheme"]."://".$serverparts["host"];
+
+               logger("Trying to fetch item ".$guid." from ".$server, LOGGER_DEBUG);
+
+               $msg = self::message($guid, $server);
+
+               if (!$msg)
+                       return false;
 
-       if(! diaspora_post_allow($importer,$contact, false)) {
-               logger('diaspora_post: Ignoring this author.');
-               return 202;
-       }
+               logger("Successfully fetched item ".$guid." from ".$server, LOGGER_DEBUG);
 
-       $message_id = $diaspora_handle . ':' . $guid;
-       $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
-               intval($importer['uid']),
-               dbesc($guid)
-       );
-       if(count($r)) {
-               logger('diaspora_post: message exists: ' . $guid);
-               return 208;
+               // Now call the dispatcher
+               return self::dispatch_public($msg);
        }
 
-       $created = unxmlify($xml->created_at);
-       $private = ((unxmlify($xml->public) == 'false') ? 1 : 0);
+       /**
+        * @brief Fetches a message from a server
+        *
+        * @param string $guid message guid
+        * @param string $server The url of the server
+        * @param int $level Endless loop prevention
+        *
+        * @return array
+        *      'message' => The message XML
+        *      'author' => The author handle
+        *      'key' => The public key of the author
+        */
+       private function message($guid, $server, $level = 0) {
 
-       $body = diaspora2bb($xml->raw_message);
-
-       $datarray = array();
+               if ($level > 5)
+                       return false;
 
-       $datarray["object"] = json_encode($xml);
+               // This will work for Diaspora and newer Friendica servers
+               $source_url = $server."/p/".$guid.".xml";
+               $x = fetch_url($source_url);
+               if(!$x)
+                       return false;
 
-       if($xml->photo->remote_photo_path AND $xml->photo->remote_photo_name)
-               $datarray["object-type"] = ACTIVITY_OBJ_PHOTO;
-       else {
-               $datarray['object-type'] = ACTIVITY_OBJ_NOTE;
-               // Add OEmbed and other information to the body
-               if (!diaspora_is_redmatrix($contact['url']))
-                       $body = add_page_info_to_body($body, false, true);
-       }
+               $source_xml = parse_xml_string($x, false);
 
-       $str_tags = '';
+               if (!is_object($source_xml))
+                       return false;
 
-       $cnt = preg_match_all('/@\[url=(.*?)\[\/url\]/ism',$body,$matches,PREG_SET_ORDER);
-       if($cnt) {
-               foreach($matches as $mtch) {
-                       if(strlen($str_tags))
-                               $str_tags .= ',';
-                       $str_tags .= '@[url=' . $mtch[1] . '[/url]';
+               if ($source_xml->post->reshare) {
+                       // Reshare of a reshare - old Diaspora version
+                       return self::message($source_xml->post->reshare->root_guid, $server, ++$level);
+               } elseif ($source_xml->getName() == "reshare") {
+                       // Reshare of a reshare - new Diaspora version
+                       return self::message($source_xml->root_guid, $server, ++$level);
                }
-       }
-
-       $plink = diaspora_plink($diaspora_handle, $guid);
-
-       $datarray['uid'] = $importer['uid'];
-       $datarray['contact-id'] = $contact['id'];
-       $datarray['wall'] = 0;
-       $datarray['network'] = NETWORK_DIASPORA;
-       $datarray['verb'] = ACTIVITY_POST;
-       $datarray['guid'] = $guid;
-       $datarray['uri'] = $datarray['parent-uri'] = $message_id;
-       $datarray['changed'] = $datarray['created'] = $datarray['edited'] = datetime_convert('UTC','UTC',$created);
-       $datarray['private'] = $private;
-       $datarray['parent'] = 0;
-       $datarray['plink'] = $plink;
-       $datarray['owner-name'] = $contact['name'];
-       $datarray['owner-link'] = $contact['url'];
-       //$datarray['owner-avatar'] = $contact['thumb'];
-       $datarray['owner-avatar'] = ((x($contact,'thumb')) ? $contact['thumb'] : $contact['photo']);
-       $datarray['author-name'] = $contact['name'];
-       $datarray['author-link'] = $contact['url'];
-       $datarray['author-avatar'] = $contact['thumb'];
-       $datarray['body'] = $body;
-       $datarray['tag'] = $str_tags;
-       if ($xml->provider_display_name)
-               $datarray["app"] = unxmlify($xml->provider_display_name);
-       else
-               $datarray['app']  = 'Diaspora';
-
-       // if empty content it might be a photo that hasn't arrived yet. If a photo arrives, we'll make it visible.
-
-       $datarray['visible'] = ((strlen($body)) ? 1 : 0);
-
-       DiasporaFetchGuid($datarray);
-       $message_id = item_store($datarray);
-
-       logger("Stored item with message id ".$message_id, LOGGER_DEBUG);
-
-       return 201;
-
-}
-
-function DiasporaFetchGuid($item) {
-       preg_replace_callback("&\[url=/posts/([^\[\]]*)\](.*)\[\/url\]&Usi",
-               function ($match) use ($item){
-                       return(DiasporaFetchGuidSub($match, $item));
-               },$item["body"]);
-}
-
-function DiasporaFetchGuidSub($match, $item) {
-       $a = get_app();
-
-       if (!diaspora_store_by_guid($match[1], $item["author-link"]))
-               diaspora_store_by_guid($match[1], $item["owner-link"]);
-}
-
-function diaspora_store_by_guid($guid, $server, $uid = 0) {
-       require_once("include/Contact.php");
-
-       $serverparts = parse_url($server);
-       $server = $serverparts["scheme"]."://".$serverparts["host"];
-
-       logger("Trying to fetch item ".$guid." from ".$server, LOGGER_DEBUG);
-
-       $item = diaspora_fetch_message($guid, $server);
-
-       if (!$item)
-               return false;
 
-       logger("Successfully fetched item ".$guid." from ".$server, LOGGER_DEBUG);
+               $author = "";
 
-       $body = $item["body"];
-       $str_tags = $item["tag"];
-       $app = $item["app"];
-       $created = $item["created"];
-       $author = $item["author"];
-       $guid = $item["guid"];
-       $private = $item["private"];
-       $object = $item["object"];
-       $objecttype = $item["object-type"];
+               // Fetch the author - for the old and the new Diaspora version
+               if ($source_xml->post->status_message->diaspora_handle)
+                       $author = (string)$source_xml->post->status_message->diaspora_handle;
+               elseif ($source_xml->author AND ($source_xml->getName() == "status_message"))
+                       $author = (string)$source_xml->author;
 
-       $message_id = $author.':'.$guid;
-       $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
-               intval($uid),
-               dbesc($guid)
-       );
-       if(count($r))
-               return $r[0]["id"];
-
-       $person = find_diaspora_person_by_handle($author);
-
-       $contact_id = get_contact($person['url'], $uid);
-
-       $contacts = q("SELECT * FROM `contact` WHERE `id` = %d", intval($contact_id));
-       $importers = q("SELECT * FROM `user` WHERE `uid` = %d", intval($uid));
-
-       if ($contacts AND $importers)
-               if(!diaspora_post_allow($importers[0],$contacts[0], false)) {
-                       logger('Ignoring author '.$person['url'].' for uid '.$uid);
+               // If this isn't a "status_message" then quit
+               if (!$author)
                        return false;
-               } else
-                       logger('Author '.$person['url'].' is allowed for uid '.$uid);
-
-       $datarray = array();
-       $datarray['uid'] = $uid;
-       $datarray['contact-id'] = $contact_id;
-       $datarray['wall'] = 0;
-       $datarray['network']  = NETWORK_DIASPORA;
-       $datarray['guid'] = $guid;
-       $datarray['uri'] = $datarray['parent-uri'] = $message_id;
-       $datarray['changed'] = $datarray['created'] = $datarray['edited'] = datetime_convert('UTC','UTC',$created);
-       $datarray['private'] = $private;
-       $datarray['parent'] = 0;
-       $datarray['plink'] = diaspora_plink($author, $guid);
-       $datarray['author-name'] = $person['name'];
-       $datarray['author-link'] = $person['url'];
-       $datarray['author-avatar'] = ((x($person,'thumb')) ? $person['thumb'] : $person['photo']);
-       $datarray['owner-name'] = $datarray['author-name'];
-       $datarray['owner-link'] = $datarray['author-link'];
-       $datarray['owner-avatar'] = $datarray['author-avatar'];
-       $datarray['body'] = $body;
-       $datarray['tag'] = $str_tags;
-       $datarray['app']  = $app;
-       $datarray['visible'] = ((strlen($body)) ? 1 : 0);
-       $datarray['object'] = $object;
-       $datarray['object-type'] = $objecttype;
-
-       if ($datarray['contact-id'] == 0)
-               return false;
-
-       DiasporaFetchGuid($datarray);
-       $message_id = item_store($datarray);
-
-       /// @TODO
-       /// Looking if there is some subscribe mechanism in Diaspora to get all comments for this post
-
-       return $message_id;
-}
-
-function diaspora_fetch_message($guid, $server, $level = 0) {
-
-       if ($level > 5)
-               return false;
-
-       $a = get_app();
-
-       // This will not work if the server is not a Diaspora server
-       $source_url = $server.'/p/'.$guid.'.xml';
-       $x = fetch_url($source_url);
-       if(!$x)
-               return false;
 
-       $x = str_replace(array('<activity_streams-photo>','</activity_streams-photo>'),array('<asphoto>','</asphoto>'),$x);
-       $source_xml = parse_xml_string($x,false);
+               $msg = array("message" => $x, "author" => $author);
 
-       $item = array();
-       $item["app"] = 'Diaspora';
-       $item["guid"] = $guid;
-       $body = "";
+               $msg["key"] = self::key($msg["author"]);
 
-       if ($source_xml->post->status_message->created_at)
-               $item["created"] = unxmlify($source_xml->post->status_message->created_at);
-
-       if ($source_xml->post->status_message->provider_display_name)
-               $item["app"] = unxmlify($source_xml->post->status_message->provider_display_name);
-
-       if ($source_xml->post->status_message->diaspora_handle)
-               $item["author"] = unxmlify($source_xml->post->status_message->diaspora_handle);
-
-       if ($source_xml->post->status_message->guid)
-               $item["guid"] = unxmlify($source_xml->post->status_message->guid);
-
-       $item["private"] = (unxmlify($source_xml->post->status_message->public) == 'false');
-       $item["object"] = json_encode($source_xml->post);
-
-       if(strlen($source_xml->post->asphoto->objectId) && ($source_xml->post->asphoto->objectId != 0) && ($source_xml->post->asphoto->image_url)) {
-               $item["object-type"] = ACTIVITY_OBJ_PHOTO;
-               $body = '[url=' . notags(unxmlify($source_xml->post->asphoto->image_url)) . '][img]' . notags(unxmlify($source_xml->post->asphoto->objectId)) . '[/img][/url]' . "\n";
-               $body = scale_external_images($body,false);
-       } elseif($source_xml->post->asphoto->image_url) {
-               $item["object-type"] = ACTIVITY_OBJ_PHOTO;
-               $body = '[img]' . notags(unxmlify($source_xml->post->asphoto->image_url)) . '[/img]' . "\n";
-               $body = scale_external_images($body);
-       } elseif($source_xml->post->status_message) {
-               $body = diaspora2bb($source_xml->post->status_message->raw_message);
-
-               // Checking for embedded pictures
-               if($source_xml->post->status_message->photo->remote_photo_path AND
-                       $source_xml->post->status_message->photo->remote_photo_name) {
-
-                       $item["object-type"] = ACTIVITY_OBJ_PHOTO;
-
-                       $remote_photo_path = notags(unxmlify($source_xml->post->status_message->photo->remote_photo_path));
-                       $remote_photo_name = notags(unxmlify($source_xml->post->status_message->photo->remote_photo_name));
-
-                       $body = '[img]'.$remote_photo_path.$remote_photo_name.'[/img]'."\n".$body;
-
-                       logger('embedded picture link found: '.$body, LOGGER_DEBUG);
-               } else
-                       $item["object-type"] = ACTIVITY_OBJ_NOTE;
-
-               $body = scale_external_images($body);
-
-               // Add OEmbed and other information to the body
-               /// @TODO It could be a repeated redmatrix item
-               /// Then we shouldn't add further data to it
-               if ($item["object-type"] == ACTIVITY_OBJ_NOTE)
-                       $body = add_page_info_to_body($body, false, true);
-
-       } elseif($source_xml->post->reshare) {
-               // Reshare of a reshare
-               return diaspora_fetch_message($source_xml->post->reshare->root_guid, $server, ++$level);
-       } else {
-               // Maybe it is a reshare of a photo that will be delivered at a later time (testing)
-               logger('no content found: '.print_r($source_xml,true));
-               return false;
+               return $msg;
        }
 
-       if (trim($body) == "")
-               return false;
-
-       $item["tag"] = '';
-       $item["body"] = $body;
+       /**
+        * @brief Fetches the item record of a given guid
+        *
+        * @param int $uid The user id
+        * @param string $guid message guid
+        * @param string $author The handle of the item
+        * @param array $contact The contact of the item owner
+        *
+        * @return array the item record
+        */
+       private function parent_item($uid, $guid, $author, $contact) {
+               $r = q("SELECT `id`, `body`, `wall`, `uri`, `private`, `origin`,
+                               `author-name`, `author-link`, `author-avatar`,
+                               `owner-name`, `owner-link`, `owner-avatar`
+                       FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
+                       intval($uid), dbesc($guid));
 
-       return $item;
-}
+               if(!$r) {
+                       $result = self::store_by_guid($guid, $contact["url"], $uid);
 
-function diaspora_reshare($importer,$xml,$msg) {
-
-       logger('diaspora_reshare: init: ' . print_r($xml,true));
-
-       $a = get_app();
-       $guid = notags(unxmlify($xml->guid));
-       $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
-
-
-       if($diaspora_handle != $msg['author']) {
-               logger('diaspora_post: Potential forgery. Message handle is not the same as envelope sender.');
-               return 202;
-       }
-
-       $contact = diaspora_get_contact_by_handle($importer['uid'],$diaspora_handle);
-       if(! $contact)
-               return;
+                       if (!$result) {
+                               $person = self::person_by_handle($author);
+                               $result = self::store_by_guid($guid, $person["url"], $uid);
+                       }
 
-       if(! diaspora_post_allow($importer,$contact, false)) {
-               logger('diaspora_reshare: Ignoring this author: ' . $diaspora_handle . ' ' . print_r($xml,true));
-               return 202;
-       }
+                       if ($result) {
+                               logger("Fetched missing item ".$guid." - result: ".$result, LOGGER_DEBUG);
 
-       $message_id = $diaspora_handle . ':' . $guid;
-       $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
-               intval($importer['uid']),
-               dbesc($guid)
-       );
-       if(count($r)) {
-               logger('diaspora_reshare: message exists: ' . $guid);
-               return;
-       }
+                               $r = q("SELECT `id`, `body`, `wall`, `uri`, `private`, `origin`,
+                                               `author-name`, `author-link`, `author-avatar`,
+                                               `owner-name`, `owner-link`, `owner-avatar`
+                                       FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
+                                       intval($uid), dbesc($guid));
+                       }
+               }
 
-       $orig_author = notags(unxmlify($xml->root_diaspora_id));
-       $orig_guid = notags(unxmlify($xml->root_guid));
-       $orig_url = $a->get_baseurl()."/display/".$orig_guid;
-
-       $create_original_post = false;
-
-       // Do we already have this item?
-       $r = q("SELECT `body`, `tag`, `app`, `created`, `plink`, `object`, `object-type`, `uri` FROM `item` WHERE `guid` = '%s' AND `visible` AND NOT `deleted` AND `body` != '' LIMIT 1",
-               dbesc($orig_guid),
-               dbesc(NETWORK_DIASPORA)
-       );
-       if(count($r)) {
-               logger('reshared message '.$orig_guid." reshared by ".$guid.' already exists on system.');
-
-               // Maybe it is already a reshared item?
-               // Then refetch the content, since there can be many side effects with reshared posts from other networks or reshares from reshares
-               require_once('include/api.php');
-               if (api_share_as_retweet($r[0]))
-                       $r = array();
-               else {
-                       $body = $r[0]["body"];
-                       $str_tags = $r[0]["tag"];
-                       $app = $r[0]["app"];
-                       $orig_created = $r[0]["created"];
-                       $orig_plink = $r[0]["plink"];
-                       $orig_uri = $r[0]["uri"];
-                       $object = $r[0]["object"];
-                       $objecttype = $r[0]["object-type"];
+               if (!$r) {
+                       logger("parent item not found: parent: ".$guid." - user: ".$uid);
+                       return false;
+               } else {
+                       logger("parent item found: parent: ".$guid." - user: ".$uid);
+                       return $r[0];
                }
        }
 
-       if (!count($r)) {
-               $body = "";
-               $str_tags = "";
-               $app = "";
-
-               $server = 'https://'.substr($orig_author,strpos($orig_author,'@')+1);
-               logger('1st try: reshared message '.$orig_guid." reshared by ".$guid.' will be fetched from original server: '.$server);
-               $item = diaspora_fetch_message($orig_guid, $server);
-
-               if (!$item) {
-                       $server = 'https://'.substr($diaspora_handle,strpos($diaspora_handle,'@')+1);
-                       logger('2nd try: reshared message '.$orig_guid." reshared by ".$guid." will be fetched from sharer's server: ".$server);
-                       $item = diaspora_fetch_message($orig_guid, $server);
-               }
-               if (!$item) {
-                       $server = 'http://'.substr($orig_author,strpos($orig_author,'@')+1);
-                       logger('3rd try: reshared message '.$orig_guid." reshared by ".$guid.' will be fetched from original server: '.$server);
-                       $item = diaspora_fetch_message($orig_guid, $server);
-               }
-               if (!$item) {
-                       $server = 'http://'.substr($diaspora_handle,strpos($diaspora_handle,'@')+1);
-                       logger('4th try: reshared message '.$orig_guid." reshared by ".$guid." will be fetched from sharer's server: ".$server);
-                       $item = diaspora_fetch_message($orig_guid, $server);
+       /**
+        * @brief returns contact details
+        *
+        * @param array $contact The default contact if the person isn't found
+        * @param array $person The record of the person
+        * @param int $uid The user id
+        *
+        * @return array
+        *      'cid' => contact id
+        *      'network' => network type
+        */
+       private function author_contact_by_url($contact, $person, $uid) {
+
+               $r = q("SELECT `id`, `network` FROM `contact` WHERE `nurl` = '%s' AND `uid` = %d LIMIT 1",
+                       dbesc(normalise_link($person["url"])), intval($uid));
+               if ($r) {
+                       $cid = $r[0]["id"];
+                       $network = $r[0]["network"];
+               } else {
+                       $cid = $contact["id"];
+                       $network = NETWORK_DIASPORA;
                }
 
-               if ($item) {
-                       $body = $item["body"];
-                       $str_tags = $item["tag"];
-                       $app = $item["app"];
-                       $orig_created = $item["created"];
-                       $orig_author = $item["author"];
-                       $orig_guid = $item["guid"];
-                       $orig_plink = diaspora_plink($orig_author, $orig_guid);
-                       $orig_uri = $orig_author.':'.$orig_guid;
-                       $create_original_post = ($body != "");
-                       $object = $item["object"];
-                       $objecttype = $item["object-type"];
+               return (array("cid" => $cid, "network" => $network));
+       }
+
+       /**
+        * @brief Is the profile a hubzilla profile?
+        *
+        * @param string $url The profile link
+        *
+        * @return bool is it a hubzilla server?
+        */
+       public static function is_redmatrix($url) {
+               return(strstr($url, "/channel/"));
+       }
+
+       /**
+        * @brief Generate a post link with a given handle and message guid
+        *
+        * @param string $addr The user handle
+        * @param string $guid message guid
+        *
+        * @return string the post link
+        */
+       private function plink($addr, $guid) {
+               $r = q("SELECT `url`, `nick`, `network` FROM `fcontact` WHERE `addr`='%s' LIMIT 1", dbesc($addr));
+
+               // Fallback
+               if (!$r)
+                       return "https://".substr($addr,strpos($addr,"@")+1)."/posts/".$guid;
+
+               // Friendica contacts are often detected as Diaspora contacts in the "fcontact" table
+               // So we try another way as well.
+               $s = q("SELECT `network` FROM `gcontact` WHERE `nurl`='%s' LIMIT 1", dbesc(normalise_link($r[0]["url"])));
+               if ($s)
+                       $r[0]["network"] = $s[0]["network"];
+
+               if ($r[0]["network"] == NETWORK_DFRN)
+                       return(str_replace("/profile/".$r[0]["nick"]."/", "/display/".$guid, $r[0]["url"]."/"));
+
+               if (self::is_redmatrix($r[0]["url"]))
+                       return $r[0]["url"]."/?f=&mid=".$guid;
+
+               return "https://".substr($addr,strpos($addr,"@")+1)."/posts/".$guid;
+       }
+
+       /**
+        * @brief Processes an account deletion
+        *
+        * @param array $importer Array of the importer user
+        * @param object $data The message object
+        *
+        * @return bool Success
+        */
+       private function receive_account_deletion($importer, $data) {
+               $author = notags(unxmlify($data->author));
+
+               $contact = self::contact_by_handle($importer["uid"], $author);
+               if (!$contact) {
+                       logger("cannot find contact for author: ".$author);
+                       return false;
                }
-       }
 
-       $plink = diaspora_plink($diaspora_handle, $guid);
-
-       $person = find_diaspora_person_by_handle($orig_author);
-
-       $created = unxmlify($xml->created_at);
-       $private = ((unxmlify($xml->public) == 'false') ? 1 : 0);
-
-       $datarray = array();
-
-       $datarray['uid'] = $importer['uid'];
-       $datarray['contact-id'] = $contact['id'];
-       $datarray['wall'] = 0;
-       $datarray['network']  = NETWORK_DIASPORA;
-       $datarray['guid'] = $guid;
-       $datarray['uri'] = $datarray['parent-uri'] = $message_id;
-       $datarray['changed'] = $datarray['created'] = $datarray['edited'] = datetime_convert('UTC','UTC',$created);
-       $datarray['private'] = $private;
-       $datarray['parent'] = 0;
-       $datarray['plink'] = $plink;
-       $datarray['owner-name'] = $contact['name'];
-       $datarray['owner-link'] = $contact['url'];
-       $datarray['owner-avatar'] = ((x($contact,'thumb')) ? $contact['thumb'] : $contact['photo']);
-       if (!intval(get_config('system','wall-to-wall_share'))) {
-               $prefix = share_header($person['name'], $person['url'], ((x($person,'thumb')) ? $person['thumb'] : $person['photo']), $orig_guid, $orig_created, $orig_url);
-
-               $datarray['author-name'] = $contact['name'];
-               $datarray['author-link'] = $contact['url'];
-               $datarray['author-avatar'] = $contact['thumb'];
-               $datarray['body'] = $prefix.$body."[/share]";
-       } else {
-               // Let reshared messages look like wall-to-wall posts
-               $datarray['author-name'] = $person['name'];
-               $datarray['author-link'] = $person['url'];
-               $datarray['author-avatar'] = ((x($person,'thumb')) ? $person['thumb'] : $person['photo']);
-               $datarray['body'] = $body;
+               // We now remove the contact
+               contact_remove($contact["id"]);
+               return true;
        }
 
-       $datarray["object"] = json_encode($xml);
-       $datarray['object-type'] = $objecttype;
-
-       $datarray['tag'] = $str_tags;
-       $datarray['app']  = $app;
-
-       // if empty content it might be a photo that hasn't arrived yet. If a photo arrives, we'll make it visible. (testing)
-       $datarray['visible'] = ((strlen($body)) ? 1 : 0);
-
-       // Store the original item of a reshare
-       if ($create_original_post) {
-               require_once("include/Contact.php");
+       /**
+        * @brief Processes an incoming comment
+        *
+        * @param array $importer Array of the importer user
+        * @param string $sender The sender of the message
+        * @param object $data The message object
+        * @param string $xml The original XML of the message
+        *
+        * @return int The message id of the generated comment or "false" if there was an error
+        */
+       private function receive_comment($importer, $sender, $data, $xml) {
+               $guid = notags(unxmlify($data->guid));
+               $parent_guid = notags(unxmlify($data->parent_guid));
+               $text = unxmlify($data->text);
+               $author = notags(unxmlify($data->author));
+
+               $contact = self::allowed_contact_by_handle($importer, $sender, true);
+               if (!$contact)
+                       return false;
 
-               $datarray2 = $datarray;
+               $message_id = self::message_exists($importer["uid"], $guid);
+               if ($message_id)
+                       return $message_id;
 
-               $datarray2['uid'] = 0;
-               $datarray2['contact-id'] = get_contact($person['url'], 0);
-               $datarray2['guid'] = $orig_guid;
-               $datarray2['uri'] = $datarray2['parent-uri'] = $orig_uri;
-               $datarray2['changed'] = $datarray2['created'] = $datarray2['edited'] = $datarray2['commented'] = $datarray2['received'] = datetime_convert('UTC','UTC',$orig_created);
-               $datarray2['parent'] = 0;
-               $datarray2['plink'] = $orig_plink;
+               $parent_item = self::parent_item($importer["uid"], $parent_guid, $author, $contact);
+               if (!$parent_item)
+                       return false;
 
-               $datarray2['author-name'] = $person['name'];
-               $datarray2['author-link'] = $person['url'];
-               $datarray2['author-avatar'] = ((x($person,'thumb')) ? $person['thumb'] : $person['photo']);
-               $datarray2['owner-name'] = $datarray2['author-name'];
-               $datarray2['owner-link'] = $datarray2['author-link'];
-               $datarray2['owner-avatar'] = $datarray2['author-avatar'];
-               $datarray2['body'] = $body;
-               $datarray2["object"] = $object;
+               $person = self::person_by_handle($author);
+               if (!is_array($person)) {
+                       logger("unable to find author details");
+                       return false;
+               }
 
-               DiasporaFetchGuid($datarray2);
-               $message_id = item_store($datarray2);
+               // Fetch the contact id - if we know this contact
+               $author_contact = self::author_contact_by_url($contact, $person, $importer["uid"]);
 
-               logger("Store original item ".$orig_guid." under message id ".$message_id);
-       }
+               $datarray = array();
 
-       DiasporaFetchGuid($datarray);
-       $message_id = item_store($datarray);
+               $datarray["uid"] = $importer["uid"];
+               $datarray["contact-id"] = $author_contact["cid"];
+               $datarray["network"]  = $author_contact["network"];
 
-       return;
+               $datarray["author-name"] = $person["name"];
+               $datarray["author-link"] = $person["url"];
+               $datarray["author-avatar"] = ((x($person,"thumb")) ? $person["thumb"] : $person["photo"]);
 
-}
+               $datarray["owner-name"] = $contact["name"];
+               $datarray["owner-link"] = $contact["url"];
+               $datarray["owner-avatar"] = ((x($contact,"thumb")) ? $contact["thumb"] : $contact["photo"]);
 
+               $datarray["guid"] = $guid;
+               $datarray["uri"] = $author.":".$guid;
 
-function diaspora_asphoto($importer,$xml,$msg) {
-       logger('diaspora_asphoto called');
+               $datarray["type"] = "remote-comment";
+               $datarray["verb"] = ACTIVITY_POST;
+               $datarray["gravity"] = GRAVITY_COMMENT;
+               $datarray["parent-uri"] = $parent_item["uri"];
 
-       $a = get_app();
-       $guid = notags(unxmlify($xml->guid));
-       $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
+               $datarray["object-type"] = ACTIVITY_OBJ_COMMENT;
+               $datarray["object"] = $xml;
 
-       if($diaspora_handle != $msg['author']) {
-               logger('diaspora_post: Potential forgery. Message handle is not the same as envelope sender.');
-               return 202;
-       }
+               $datarray["body"] = diaspora2bb($text);
 
-       $contact = diaspora_get_contact_by_handle($importer['uid'],$diaspora_handle);
-       if(! $contact)
-               return;
+               self::fetch_guid($datarray);
 
-       if(! diaspora_post_allow($importer,$contact, false)) {
-               logger('diaspora_asphoto: Ignoring this author.');
-               return 202;
-       }
+               $message_id = item_store($datarray);
 
-       $message_id = $diaspora_handle . ':' . $guid;
-       $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
-               intval($importer['uid']),
-               dbesc($guid)
-       );
-       if(count($r)) {
-               logger('diaspora_asphoto: message exists: ' . $guid);
-               return;
-       }
+               if ($message_id)
+                       logger("Stored comment ".$datarray["guid"]." with message id ".$message_id, LOGGER_DEBUG);
 
-       $created = unxmlify($xml->created_at);
-       $private = ((unxmlify($xml->public) == 'false') ? 1 : 0);
+               // If we are the origin of the parent we store the original data and notify our followers
+               if($message_id AND $parent_item["origin"]) {
 
-       if(strlen($xml->objectId) && ($xml->objectId != 0) && ($xml->image_url)) {
-               $body = '[url=' . notags(unxmlify($xml->image_url)) . '][img]' . notags(unxmlify($xml->objectId)) . '[/img][/url]' . "\n";
-               $body = scale_external_images($body,false);
-       }
-       elseif($xml->image_url) {
-               $body = '[img]' . notags(unxmlify($xml->image_url)) . '[/img]' . "\n";
-               $body = scale_external_images($body);
-       }
-       else {
-               logger('diaspora_asphoto: no photo url found.');
-               return;
-       }
+                       // Formerly we stored the signed text, the signature and the author in different fields.
+                       // We now store the raw data so that we are more flexible.
+                       q("INSERT INTO `sign` (`iid`,`signed_text`) VALUES (%d,'%s')",
+                               intval($message_id),
+                               dbesc(json_encode($data))
+                       );
 
-       $plink = diaspora_plink($diaspora_handle, $guid);
-
-       $datarray = array();
-
-       $datarray['uid'] = $importer['uid'];
-       $datarray['contact-id'] = $contact['id'];
-       $datarray['wall'] = 0;
-       $datarray['network']  = NETWORK_DIASPORA;
-       $datarray['guid'] = $guid;
-       $datarray['uri'] = $datarray['parent-uri'] = $message_id;
-       $datarray['changed'] = $datarray['created'] = $datarray['edited'] = datetime_convert('UTC','UTC',$created);
-       $datarray['private'] = $private;
-       $datarray['parent'] = 0;
-       $datarray['plink'] = $plink;
-       $datarray['owner-name'] = $contact['name'];
-       $datarray['owner-link'] = $contact['url'];
-       //$datarray['owner-avatar'] = $contact['thumb'];
-       $datarray['owner-avatar'] = ((x($contact,'thumb')) ? $contact['thumb'] : $contact['photo']);
-       $datarray['author-name'] = $contact['name'];
-       $datarray['author-link'] = $contact['url'];
-       $datarray['author-avatar'] = $contact['thumb'];
-       $datarray['body'] = $body;
-       $datarray["object"] = json_encode($xml);
-       $datarray['object-type'] = ACTIVITY_OBJ_PHOTO;
-
-       $datarray['app']  = 'Diaspora/Cubbi.es';
-
-       DiasporaFetchGuid($datarray);
-       $message_id = item_store($datarray);
-
-       //if($message_id) {
-       //      q("update item set plink = '%s' where id = %d",
-       //              dbesc($a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $message_id),
-       //              intval($message_id)
-       //      );
-       //}
-
-       return;
+                       // notify others
+                       proc_run("php", "include/notifier.php", "comment-import", $message_id);
+               }
 
-}
+               return $message_id;
+       }
+
+       /**
+        * @brief processes and stores private messages
+        *
+        * @param array $importer Array of the importer user
+        * @param array $contact The contact of the message
+        * @param object $data The message object
+        * @param array $msg Array of the processed message, author handle and key
+        * @param object $mesg The private message
+        * @param array $conversation The conversation record to which this message belongs
+        *
+        * @return bool "true" if it was successful
+        */
+       private function receive_conversation_message($importer, $contact, $data, $msg, $mesg, $conversation) {
+               $guid = notags(unxmlify($data->guid));
+               $subject = notags(unxmlify($data->subject));
+               $author = notags(unxmlify($data->author));
 
-function diaspora_comment($importer,$xml,$msg) {
+               $reply = 0;
 
-       $a = get_app();
-       $guid = notags(unxmlify($xml->guid));
-       $parent_guid = notags(unxmlify($xml->parent_guid));
-       $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
-       $target_type = notags(unxmlify($xml->target_type));
-       $text = unxmlify($xml->text);
-       $author_signature = notags(unxmlify($xml->author_signature));
+               $msg_guid = notags(unxmlify($mesg->guid));
+               $msg_parent_guid = notags(unxmlify($mesg->parent_guid));
+               $msg_parent_author_signature = notags(unxmlify($mesg->parent_author_signature));
+               $msg_author_signature = notags(unxmlify($mesg->author_signature));
+               $msg_text = unxmlify($mesg->text);
+               $msg_created_at = datetime_convert("UTC", "UTC", notags(unxmlify($mesg->created_at)));
+
+               // "diaspora_handle" is the element name from the old version
+               // "author" is the element name from the new version
+               if ($mesg->author)
+                       $msg_author = notags(unxmlify($mesg->author));
+               elseif ($mesg->diaspora_handle)
+                       $msg_author = notags(unxmlify($mesg->diaspora_handle));
+               else
+                       return false;
 
-       $parent_author_signature = (($xml->parent_author_signature) ? notags(unxmlify($xml->parent_author_signature)) : '');
+               $msg_conversation_guid = notags(unxmlify($mesg->conversation_guid));
 
-       $contact = diaspora_get_contact_by_handle($importer['uid'],$msg['author']);
-       if(! $contact) {
-               logger('diaspora_comment: cannot find contact: ' . $msg['author']);
-               return;
-       }
+               if($msg_conversation_guid != $guid) {
+                       logger("message conversation guid does not belong to the current conversation.");
+                       return false;
+               }
 
-       if(! diaspora_post_allow($importer,$contact, true)) {
-               logger('diaspora_comment: Ignoring this author.');
-               return 202;
-       }
+               $body = diaspora2bb($msg_text);
+               $message_uri = $msg_author.":".$msg_guid;
 
-       $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
-               intval($importer['uid']),
-               dbesc($guid)
-       );
-       if(count($r)) {
-               logger('diaspora_comment: our comment just got relayed back to us (or there was a guid collision) : ' . $guid);
-               return;
-       }
+               $author_signed_data = $msg_guid.";".$msg_parent_guid.";".$msg_text.";".unxmlify($mesg->created_at).";".$msg_author.";".$msg_conversation_guid;
 
-       $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
-               intval($importer['uid']),
-               dbesc($parent_guid)
-       );
+               $author_signature = base64_decode($msg_author_signature);
 
-       if(!count($r)) {
-               $result = diaspora_store_by_guid($parent_guid, $contact['url'], $importer['uid']);
+               if(strcasecmp($msg_author,$msg["author"]) == 0) {
+                       $person = $contact;
+                       $key = $msg["key"];
+               } else {
+                       $person = self::person_by_handle($msg_author);
 
-               if (!$result) {
-                       $person = find_diaspora_person_by_handle($diaspora_handle);
-                       $result = diaspora_store_by_guid($parent_guid, $person['url'], $importer['uid']);
+                       if (is_array($person) && x($person, "pubkey"))
+                               $key = $person["pubkey"];
+                       else {
+                               logger("unable to find author details");
+                                       return false;
+                       }
                }
 
-               if ($result) {
-                       logger("Fetched missing item ".$parent_guid." - result: ".$result, LOGGER_DEBUG);
-
-                       $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
-                               intval($importer['uid']),
-                               dbesc($parent_guid)
-                       );
+               if (!rsa_verify($author_signed_data, $author_signature, $key, "sha256")) {
+                       logger("verification failed.");
+                       return false;
                }
-       }
 
-       if(! count($r)) {
-               logger('diaspora_comment: parent item not found: parent: ' . $parent_guid . ' item: ' . $guid);
-               return;
-       }
-       $parent_item = $r[0];
+               if($msg_parent_author_signature) {
+                       $owner_signed_data = $msg_guid.";".$msg_parent_guid.";".$msg_text.";".unxmlify($mesg->created_at).";".$msg_author.";".$msg_conversation_guid;
 
+                       $parent_author_signature = base64_decode($msg_parent_author_signature);
 
-       /* How Diaspora performs comment signature checking:
+                       $key = $msg["key"];
 
-          - If an item has been sent by the comment author to the top-level post owner to relay on
-            to the rest of the contacts on the top-level post, the top-level post owner should check
-            the author_signature, then create a parent_author_signature before relaying the comment on
-          - If an item has been relayed on by the top-level post owner, the contacts who receive it
-            check only the parent_author_signature. Basically, they trust that the top-level post
-            owner has already verified the authenticity of anything he/she sends out
-          - In either case, the signature that get checked is the signature created by the person
-            who sent the salmon
-       */
+                       if (!rsa_verify($owner_signed_data, $parent_author_signature, $key, "sha256")) {
+                               logger("owner verification failed.");
+                               return false;
+                       }
+               }
 
-       $signed_data = $guid . ';' . $parent_guid . ';' . $text . ';' . $diaspora_handle;
-       $key = $msg['key'];
+               $r = q("SELECT `id` FROM `mail` WHERE `uri` = '%s' LIMIT 1",
+                       dbesc($message_uri)
+               );
+               if($r) {
+                       logger("duplicate message already delivered.", LOGGER_DEBUG);
+                       return false;
+               }
 
-       if($parent_author_signature) {
-               // If a parent_author_signature exists, then we've received the comment
-               // relayed from the top-level post owner. There's no need to check the
-               // author_signature if the parent_author_signature is valid
+               q("INSERT INTO `mail` (`uid`, `guid`, `convid`, `from-name`,`from-photo`,`from-url`,`contact-id`,`title`,`body`,`seen`,`reply`,`uri`,`parent-uri`,`created`)
+                       VALUES (%d, '%s', %d, '%s', '%s', '%s', %d, '%s', '%s', %d, %d, '%s','%s','%s')",
+                       intval($importer["uid"]),
+                       dbesc($msg_guid),
+                       intval($conversation["id"]),
+                       dbesc($person["name"]),
+                       dbesc($person["photo"]),
+                       dbesc($person["url"]),
+                       intval($contact["id"]),
+                       dbesc($subject),
+                       dbesc($body),
+                       0,
+                       0,
+                       dbesc($message_uri),
+                       dbesc($author.":".$guid),
+                       dbesc($msg_created_at)
+               );
 
-               $parent_author_signature = base64_decode($parent_author_signature);
+               q("UPDATE `conv` SET `updated` = '%s' WHERE `id` = %d",
+                       dbesc(datetime_convert()),
+                       intval($conversation["id"])
+               );
 
-               if(! rsa_verify($signed_data,$parent_author_signature,$key,'sha256')) {
-                       logger('diaspora_comment: top-level owner verification failed.');
-                       return;
-               }
+               notification(array(
+                       "type" => NOTIFY_MAIL,
+                       "notify_flags" => $importer["notify-flags"],
+                       "language" => $importer["language"],
+                       "to_name" => $importer["username"],
+                       "to_email" => $importer["email"],
+                       "uid" =>$importer["uid"],
+                       "item" => array("subject" => $subject, "body" => $body),
+                       "source_name" => $person["name"],
+                       "source_link" => $person["url"],
+                       "source_photo" => $person["thumb"],
+                       "verb" => ACTIVITY_POST,
+                       "otype" => "mail"
+               ));
+               return true;
        }
-       else {
-               // If there's no parent_author_signature, then we've received the comment
-               // from the comment creator. In that case, the person is commenting on
-               // our post, so he/she must be a contact of ours and his/her public key
-               // should be in $msg['key']
-
-               $author_signature = base64_decode($author_signature);
 
-               if(! rsa_verify($signed_data,$author_signature,$key,'sha256')) {
-                       logger('diaspora_comment: comment author verification failed.');
-                       return;
+       /**
+        * @brief Processes new private messages (answers to private messages are processed elsewhere)
+        *
+        * @param array $importer Array of the importer user
+        * @param array $msg Array of the processed message, author handle and key
+        * @param object $data The message object
+        *
+        * @return bool Success
+        */
+       private function receive_conversation($importer, $msg, $data) {
+               $guid = notags(unxmlify($data->guid));
+               $subject = notags(unxmlify($data->subject));
+               $created_at = datetime_convert("UTC", "UTC", notags(unxmlify($data->created_at)));
+               $author = notags(unxmlify($data->author));
+               $participants = notags(unxmlify($data->participants));
+
+               $messages = $data->message;
+
+               if (!count($messages)) {
+                       logger("empty conversation");
+                       return false;
                }
-       }
 
-       // Phew! Everything checks out. Now create an item.
+               $contact = self::allowed_contact_by_handle($importer, $msg["author"], true);
+               if (!$contact)
+                       return false;
+
+               $conversation = null;
 
-       // Find the original comment author information.
-       // We need this to make sure we display the comment author
-       // information (name and avatar) correctly.
-       if(strcasecmp($diaspora_handle,$msg['author']) == 0)
-               $person = $contact;
-       else {
-               $person = find_diaspora_person_by_handle($diaspora_handle);
+               $c = q("SELECT * FROM `conv` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
+                       intval($importer["uid"]),
+                       dbesc($guid)
+               );
+               if($c)
+                       $conversation = $c[0];
+               else {
+                       $r = q("INSERT INTO `conv` (`uid`, `guid`, `creator`, `created`, `updated`, `subject`, `recips`)
+                               VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s')",
+                               intval($importer["uid"]),
+                               dbesc($guid),
+                               dbesc($author),
+                               dbesc(datetime_convert("UTC", "UTC", $created_at)),
+                               dbesc(datetime_convert()),
+                               dbesc($subject),
+                               dbesc($participants)
+                       );
+                       if($r)
+                               $c = q("SELECT * FROM `conv` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
+                                       intval($importer["uid"]),
+                                       dbesc($guid)
+                               );
 
-               if(! is_array($person)) {
-                       logger('diaspora_comment: unable to find author details');
+                       if($c)
+                               $conversation = $c[0];
+               }
+               if (!$conversation) {
+                       logger("unable to create conversation.");
                        return;
                }
-       }
 
-       // Fetch the contact id - if we know this contact
-       $r = q("SELECT `id`, `network` FROM `contact` WHERE `nurl` = '%s' AND `uid` = %d LIMIT 1",
-               dbesc(normalise_link($person['url'])), intval($importer['uid']));
-       if ($r) {
-               $cid = $r[0]['id'];
-               $network = $r[0]['network'];
-       } else {
-               $cid = $contact['id'];
-               $network = NETWORK_DIASPORA;
+               foreach($messages as $mesg)
+                       self::receive_conversation_message($importer, $contact, $data, $msg, $mesg, $conversation);
+
+               return true;
        }
 
-       $body = diaspora2bb($text);
-       $message_id = $diaspora_handle . ':' . $guid;
-
-       $datarray = array();
-
-       $datarray['uid'] = $importer['uid'];
-       $datarray['contact-id'] = $cid;
-       $datarray['type'] = 'remote-comment';
-       $datarray['wall'] = $parent_item['wall'];
-       $datarray['network']  = $network;
-       $datarray['verb'] = ACTIVITY_POST;
-       $datarray['gravity'] = GRAVITY_COMMENT;
-       $datarray['guid'] = $guid;
-       $datarray['uri'] = $message_id;
-       $datarray['parent-uri'] = $parent_item['uri'];
-
-       // No timestamps for comments? OK, we'll the use current time.
-       $datarray['changed'] = $datarray['created'] = $datarray['edited'] = datetime_convert();
-       $datarray['private'] = $parent_item['private'];
-
-       $datarray['owner-name'] = $parent_item['owner-name'];
-       $datarray['owner-link'] = $parent_item['owner-link'];
-       $datarray['owner-avatar'] = $parent_item['owner-avatar'];
-
-       $datarray['author-name'] = $person['name'];
-       $datarray['author-link'] = $person['url'];
-       $datarray['author-avatar'] = ((x($person,'thumb')) ? $person['thumb'] : $person['photo']);
-       $datarray['body'] = $body;
-       $datarray["object"] = json_encode($xml);
-       $datarray["object-type"] = ACTIVITY_OBJ_COMMENT;
-
-       // We can't be certain what the original app is if the message is relayed.
-       if(($parent_item['origin']) && (! $parent_author_signature))
-               $datarray['app']  = 'Diaspora';
-
-       DiasporaFetchGuid($datarray);
-       $message_id = item_store($datarray);
-
-       $datarray['id'] = $message_id;
-
-       //if($message_id) {
-               //q("update item set plink = '%s' where id = %d",
-               //      //dbesc($a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $message_id),
-               //      dbesc($a->get_baseurl().'/display/'.$datarray['guid']),
-               //      intval($message_id)
-               //);
-       //}
+       /**
+        * @brief Creates the body for a "like" message
+        *
+        * @param array $contact The contact that send us the "like"
+        * @param array $parent_item The item array of the parent item
+        * @param string $guid message guid
+        *
+        * @return string the body
+        */
+       private function construct_like_body($contact, $parent_item, $guid) {
+               $bodyverb = t('%1$s likes %2$s\'s %3$s');
+
+               $ulink = "[url=".$contact["url"]."]".$contact["name"]."[/url]";
+               $alink = "[url=".$parent_item["author-link"]."]".$parent_item["author-name"]."[/url]";
+               $plink = "[url=".App::get_baseurl()."/display/".urlencode($guid)."]".t("status")."[/url]";
+
+               return sprintf($bodyverb, $ulink, $alink, $plink);
+       }
+
+       /**
+        * @brief Creates a XML object for a "like"
+        *
+        * @param array $importer Array of the importer user
+        * @param array $parent_item The item array of the parent item
+        *
+        * @return string The XML
+        */
+       private function construct_like_object($importer, $parent_item) {
+               $objtype = ACTIVITY_OBJ_NOTE;
+               $link = '<link rel="alternate" type="text/html" href="'.App::get_baseurl()."/display/".$importer["nickname"]."/".$parent_item["id"].'" />';
+               $parent_body = $parent_item["body"];
+
+               $xmldata = array("object" => array("type" => $objtype,
+                                               "local" => "1",
+                                               "id" => $parent_item["uri"],
+                                               "link" => $link,
+                                               "title" => "",
+                                               "content" => $parent_body));
+
+               return xml::from_array($xmldata, $xml, true);
+       }
+
+       /**
+        * @brief Processes "like" messages
+        *
+        * @param array $importer Array of the importer user
+        * @param string $sender The sender of the message
+        * @param object $data The message object
+        *
+        * @return int The message id of the generated like or "false" if there was an error
+        */
+       private function receive_like($importer, $sender, $data) {
+               $positive = notags(unxmlify($data->positive));
+               $guid = notags(unxmlify($data->guid));
+               $parent_type = notags(unxmlify($data->parent_type));
+               $parent_guid = notags(unxmlify($data->parent_guid));
+               $author = notags(unxmlify($data->author));
+
+               // likes on comments aren't supported by Diaspora - only on posts
+               // But maybe this will be supported in the future, so we will accept it.
+               if (!in_array($parent_type, array("Post", "Comment")))
+                       return false;
 
-       // If we are the origin of the parent we store the original signature and notify our followers
-       if($parent_item['origin']) {
-               $author_signature_base64 = base64_encode($author_signature);
-               $author_signature_base64 = diaspora_repair_signature($author_signature_base64, $diaspora_handle);
+               $contact = self::allowed_contact_by_handle($importer, $sender, true);
+               if (!$contact)
+                       return false;
 
-               q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
-                       intval($message_id),
-                       dbesc($signed_data),
-                       dbesc($author_signature_base64),
-                       dbesc($diaspora_handle)
-               );
+               $message_id = self::message_exists($importer["uid"], $guid);
+               if ($message_id)
+                       return $message_id;
 
-               // notify others
-               proc_run('php','include/notifier.php','comment-import',$message_id);
-       }
+               $parent_item = self::parent_item($importer["uid"], $parent_guid, $author, $contact);
+               if (!$parent_item)
+                       return false;
 
-       return;
-}
+               $person = self::person_by_handle($author);
+               if (!is_array($person)) {
+                       logger("unable to find author details");
+                       return false;
+               }
 
+               // Fetch the contact id - if we know this contact
+               $author_contact = self::author_contact_by_url($contact, $person, $importer["uid"]);
 
+               // "positive" = "false" would be a Dislike - wich isn't currently supported by Diaspora
+               // We would accept this anyhow.
+               if ($positive == "true")
+                       $verb = ACTIVITY_LIKE;
+               else
+                       $verb = ACTIVITY_DISLIKE;
 
+               $datarray = array();
 
-function diaspora_conversation($importer,$xml,$msg) {
+               $datarray["uid"] = $importer["uid"];
+               $datarray["contact-id"] = $author_contact["cid"];
+               $datarray["network"]  = $author_contact["network"];
 
-       $a = get_app();
+               $datarray["author-name"] = $person["name"];
+               $datarray["author-link"] = $person["url"];
+               $datarray["author-avatar"] = ((x($person,"thumb")) ? $person["thumb"] : $person["photo"]);
 
-       $guid = notags(unxmlify($xml->guid));
-       $subject = notags(unxmlify($xml->subject));
-       $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
-       $participant_handles = notags(unxmlify($xml->participant_handles));
-       $created_at = datetime_convert('UTC','UTC',notags(unxmlify($xml->created_at)));
+               $datarray["owner-name"] = $contact["name"];
+               $datarray["owner-link"] = $contact["url"];
+               $datarray["owner-avatar"] = ((x($contact,"thumb")) ? $contact["thumb"] : $contact["photo"]);
 
-       $parent_uri = $diaspora_handle . ':' . $guid;
+               $datarray["guid"] = $guid;
+               $datarray["uri"] = $author.":".$guid;
 
-       $messages = $xml->message;
+               $datarray["type"] = "activity";
+               $datarray["verb"] = $verb;
+               $datarray["gravity"] = GRAVITY_LIKE;
+               $datarray["parent-uri"] = $parent_item["uri"];
 
-       if(! count($messages)) {
-               logger('diaspora_conversation: empty conversation');
-               return;
-       }
+               $datarray["object-type"] = ACTIVITY_OBJ_NOTE;
+               $datarray["object"] = self::construct_like_object($importer, $parent_item);
 
-       $contact = diaspora_get_contact_by_handle($importer['uid'],$msg['author']);
-       if(! $contact) {
-               logger('diaspora_conversation: cannot find contact: ' . $msg['author']);
-               return;
-       }
+               $datarray["body"] = self::construct_like_body($contact, $parent_item, $guid);
 
-       if(($contact['rel'] == CONTACT_IS_FOLLOWER) || ($contact['blocked']) || ($contact['readonly'])) {
-               logger('diaspora_conversation: Ignoring this author.');
-               return 202;
-       }
+               $message_id = item_store($datarray);
 
-       $conversation = null;
-
-       $c = q("select * from conv where uid = %d and guid = '%s' limit 1",
-               intval($importer['uid']),
-               dbesc($guid)
-       );
-       if(count($c))
-               $conversation = $c[0];
-       else {
-               $r = q("insert into conv (uid,guid,creator,created,updated,subject,recips) values(%d, '%s', '%s', '%s', '%s', '%s', '%s') ",
-                       intval($importer['uid']),
-                       dbesc($guid),
-                       dbesc($diaspora_handle),
-                       dbesc(datetime_convert('UTC','UTC',$created_at)),
-                       dbesc(datetime_convert()),
-                       dbesc($subject),
-                       dbesc($participant_handles)
-               );
-               if($r)
-                       $c = q("select * from conv where uid = %d and guid = '%s' limit 1",
-               intval($importer['uid']),
-           dbesc($guid)
-       );
-           if(count($c))
-           $conversation = $c[0];
-       }
-       if(! $conversation) {
-               logger('diaspora_conversation: unable to create conversation.');
-               return;
-       }
+               if ($message_id)
+                       logger("Stored like ".$datarray["guid"]." with message id ".$message_id, LOGGER_DEBUG);
 
-       foreach($messages as $mesg) {
+               // If we are the origin of the parent we store the original data and notify our followers
+               if($message_id AND $parent_item["origin"]) {
 
-               $reply = 0;
+                       // Formerly we stored the signed text, the signature and the author in different fields.
+                       // We now store the raw data so that we are more flexible.
+                       q("INSERT INTO `sign` (`iid`,`signed_text`) VALUES (%d,'%s')",
+                               intval($message_id),
+                               dbesc(json_encode($data))
+                       );
 
-               $msg_guid = notags(unxmlify($mesg->guid));
-               $msg_parent_guid = notags(unxmlify($mesg->parent_guid));
-               $msg_parent_author_signature = notags(unxmlify($mesg->parent_author_signature));
-               $msg_author_signature = notags(unxmlify($mesg->author_signature));
-               $msg_text = unxmlify($mesg->text);
-               $msg_created_at = datetime_convert('UTC','UTC',notags(unxmlify($mesg->created_at)));
-               $msg_diaspora_handle = notags(unxmlify($mesg->diaspora_handle));
-               $msg_conversation_guid = notags(unxmlify($mesg->conversation_guid));
-               if($msg_conversation_guid != $guid) {
-                       logger('diaspora_conversation: message conversation guid does not belong to the current conversation. ' . $xml);
-                       continue;
+                       // notify others
+                       proc_run("php", "include/notifier.php", "comment-import", $message_id);
                }
 
-               $body = diaspora2bb($msg_text);
-               $message_id = $msg_diaspora_handle . ':' . $msg_guid;
-
-               $author_signed_data = $msg_guid . ';' . $msg_parent_guid . ';' . $msg_text . ';' . unxmlify($mesg->created_at) . ';' . $msg_diaspora_handle . ';' . $msg_conversation_guid;
+               return $message_id;
+       }
+
+       /**
+        * @brief Processes private messages
+        *
+        * @param array $importer Array of the importer user
+        * @param object $data The message object
+        *
+        * @return bool Success?
+        */
+       private function receive_message($importer, $data) {
+               $guid = notags(unxmlify($data->guid));
+               $parent_guid = notags(unxmlify($data->parent_guid));
+               $text = unxmlify($data->text);
+               $created_at = datetime_convert("UTC", "UTC", notags(unxmlify($data->created_at)));
+               $author = notags(unxmlify($data->author));
+               $conversation_guid = notags(unxmlify($data->conversation_guid));
+
+               $contact = self::allowed_contact_by_handle($importer, $author, true);
+               if (!$contact)
+                       return false;
 
-               $author_signature = base64_decode($msg_author_signature);
+               $conversation = null;
 
-               if(strcasecmp($msg_diaspora_handle,$msg['author']) == 0) {
-                       $person = $contact;
-                       $key = $msg['key'];
-               }
+               $c = q("SELECT * FROM `conv` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
+                       intval($importer["uid"]),
+                       dbesc($conversation_guid)
+               );
+               if($c)
+                       $conversation = $c[0];
                else {
-                       $person = find_diaspora_person_by_handle($msg_diaspora_handle); 
-
-                       if(is_array($person) && x($person,'pubkey'))
-                               $key = $person['pubkey'];
-                       else {
-                               logger('diaspora_conversation: unable to find author details');
-                               continue;
-                       }
-               }
-
-               if(! rsa_verify($author_signed_data,$author_signature,$key,'sha256')) {
-                       logger('diaspora_conversation: verification failed.');
-                       continue;
+                       logger("conversation not available.");
+                       return false;
                }
 
-               if($msg_parent_author_signature) {
-                       $owner_signed_data = $msg_guid . ';' . $msg_parent_guid . ';' . $msg_text . ';' . unxmlify($mesg->created_at) . ';' . $msg_diaspora_handle . ';' . $msg_conversation_guid;
-
-                       $parent_author_signature = base64_decode($msg_parent_author_signature);
+               $reply = 0;
 
-                       $key = $msg['key'];
+               $body = diaspora2bb($text);
+               $message_uri = $author.":".$guid;
 
-                       if(! rsa_verify($owner_signed_data,$parent_author_signature,$key,'sha256')) {
-                               logger('diaspora_conversation: owner verification failed.');
-                               continue;
-                       }
+               $person = self::person_by_handle($author);
+               if (!$person) {
+                       logger("unable to find author details");
+                       return false;
                }
 
-               $r = q("select id from mail where `uri` = '%s' limit 1",
-                       dbesc($message_id)
+               $r = q("SELECT `id` FROM `mail` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
+                       dbesc($message_uri),
+                       intval($importer["uid"])
                );
-               if(count($r)) {
-                       logger('diaspora_conversation: duplicate message already delivered.', LOGGER_DEBUG);
-                       continue;
+               if($r) {
+                       logger("duplicate message already delivered.", LOGGER_DEBUG);
+                       return false;
                }
 
-               q("insert into mail ( `uid`, `guid`, `convid`, `from-name`,`from-photo`,`from-url`,`contact-id`,`title`,`body`,`seen`,`reply`,`uri`,`parent-uri`,`created`) values ( %d, '%s', %d, '%s', '%s', '%s', %d, '%s', '%s', %d, %d, '%s','%s','%s')",
-                       intval($importer['uid']),
-                       dbesc($msg_guid),
-                       intval($conversation['id']),
-                       dbesc($person['name']),
-                       dbesc($person['photo']),
-                       dbesc($person['url']),
-                       intval($contact['id']),
-                       dbesc($subject),
+               q("INSERT INTO `mail` (`uid`, `guid`, `convid`, `from-name`,`from-photo`,`from-url`,`contact-id`,`title`,`body`,`seen`,`reply`,`uri`,`parent-uri`,`created`)
+                               VALUES ( %d, '%s', %d, '%s', '%s', '%s', %d, '%s', '%s', %d, %d, '%s','%s','%s')",
+                       intval($importer["uid"]),
+                       dbesc($guid),
+                       intval($conversation["id"]),
+                       dbesc($person["name"]),
+                       dbesc($person["photo"]),
+                       dbesc($person["url"]),
+                       intval($contact["id"]),
+                       dbesc($conversation["subject"]),
                        dbesc($body),
                        0,
-                       0,
-                       dbesc($message_id),
-                       dbesc($parent_uri),
-                       dbesc($msg_created_at)
+                       1,
+                       dbesc($message_uri),
+                       dbesc($author.":".$parent_guid),
+                       dbesc($created_at)
                );
 
-               q("update conv set updated = '%s' where id = %d",
+               q("UPDATE `conv` SET `updated` = '%s' WHERE `id` = %d",
                        dbesc(datetime_convert()),
-                       intval($conversation['id'])
+                       intval($conversation["id"])
                );
 
-               notification(array(
-                       'type' => NOTIFY_MAIL,
-                       'notify_flags' => $importer['notify-flags'],
-                       'language' => $importer['language'],
-                       'to_name' => $importer['username'],
-                       'to_email' => $importer['email'],
-                       'uid' =>$importer['uid'],
-                       'item' => array('subject' => $subject, 'body' => $body),
-                       'source_name' => $person['name'],
-                       'source_link' => $person['url'],
-                       'source_photo' => $person['thumb'],
-                       'verb' => ACTIVITY_POST,
-                       'otype' => 'mail'
-               ));
+               return true;
        }
 
-       return;
-}
+       /**
+        * @brief Processes participations - unsupported by now
+        *
+        * @param array $importer Array of the importer user
+        * @param object $data The message object
+        *
+        * @return bool always true
+        */
+       private function receive_participation($importer, $data) {
+               // I'm not sure if we can fully support this message type
+               return true;
+       }
 
-function diaspora_message($importer,$xml,$msg) {
+       /**
+        * @brief Processes photos - unneeded
+        *
+        * @param array $importer Array of the importer user
+        * @param object $data The message object
+        *
+        * @return bool always true
+        */
+       private function receive_photo($importer, $data) {
+               // There doesn't seem to be a reason for this function, since the photo data is transmitted in the status message as well
+               return true;
+       }
 
-       $a = get_app();
+       /**
+        * @brief Processes poll participations - unssupported
+        *
+        * @param array $importer Array of the importer user
+        * @param object $data The message object
+        *
+        * @return bool always true
+        */
+       private function receive_poll_participation($importer, $data) {
+               // We don't support polls by now
+               return true;
+       }
 
-       $msg_guid = notags(unxmlify($xml->guid));
-       $msg_parent_guid = notags(unxmlify($xml->parent_guid));
-       $msg_parent_author_signature = notags(unxmlify($xml->parent_author_signature));
-       $msg_author_signature = notags(unxmlify($xml->author_signature));
-       $msg_text = unxmlify($xml->text);
-       $msg_created_at = datetime_convert('UTC','UTC',notags(unxmlify($xml->created_at)));
-       $msg_diaspora_handle = notags(unxmlify($xml->diaspora_handle));
-       $msg_conversation_guid = notags(unxmlify($xml->conversation_guid));
+       /**
+        * @brief Processes incoming profile updates
+        *
+        * @param array $importer Array of the importer user
+        * @param object $data The message object
+        *
+        * @return bool Success
+        */
+       private function receive_profile($importer, $data) {
+               $author = notags(unxmlify($data->author));
 
-       $parent_uri = $msg_diaspora_handle . ':' . $msg_parent_guid;
+               $contact = self::contact_by_handle($importer["uid"], $author);
+               if (!$contact)
+                       return false;
 
-       $contact = diaspora_get_contact_by_handle($importer['uid'],$msg_diaspora_handle);
-       if(! $contact) {
-               logger('diaspora_message: cannot find contact: ' . $msg_diaspora_handle);
-               return;
-       }
+               $name = unxmlify($data->first_name).((strlen($data->last_name)) ? " ".unxmlify($data->last_name) : "");
+               $image_url = unxmlify($data->image_url);
+               $birthday = unxmlify($data->birthday);
+               $location = diaspora2bb(unxmlify($data->location));
+               $about = diaspora2bb(unxmlify($data->bio));
+               $gender = unxmlify($data->gender);
+               $searchable = (unxmlify($data->searchable) == "true");
+               $nsfw = (unxmlify($data->nsfw) == "true");
+               $tags = unxmlify($data->tag_string);
+
+               $tags = explode("#", $tags);
+
+               $keywords = array();
+               foreach ($tags as $tag) {
+                       $tag = trim(strtolower($tag));
+                       if ($tag != "")
+                               $keywords[] = $tag;
+               }
 
-       if(($contact['rel'] == CONTACT_IS_FOLLOWER) || ($contact['blocked']) || ($contact['readonly'])) {
-               logger('diaspora_message: Ignoring this author.');
-               return 202;
-       }
+               $keywords = implode(", ", $keywords);
 
-       $conversation = null;
-
-       $c = q("select * from conv where uid = %d and guid = '%s' limit 1",
-               intval($importer['uid']),
-               dbesc($msg_conversation_guid)
-       );
-       if(count($c))
-               $conversation = $c[0];
-       else {
-               logger('diaspora_message: conversation not available.');
-               return;
-       }
+               $handle_parts = explode("@", $author);
+               $nick = $handle_parts[0];
 
-       $reply = 0;
+               if($name === "")
+                       $name = $handle_parts[0];
 
-       $body = diaspora2bb($msg_text);
-       $message_id = $msg_diaspora_handle . ':' . $msg_guid;
+               if( preg_match("|^https?://|", $image_url) === 0)
+                       $image_url = "http://".$handle_parts[1].$image_url;
 
-       $author_signed_data = $msg_guid . ';' . $msg_parent_guid . ';' . $msg_text . ';' . unxmlify($xml->created_at) . ';' . $msg_diaspora_handle . ';' . $msg_conversation_guid;
+               update_contact_avatar($image_url, $importer["uid"], $contact["id"]);
 
+               // Generic birthday. We don't know the timezone. The year is irrelevant.
 
-       $author_signature = base64_decode($msg_author_signature);
+               $birthday = str_replace("1000", "1901", $birthday);
 
-       $person = find_diaspora_person_by_handle($msg_diaspora_handle);
-       if(is_array($person) && x($person,'pubkey'))
-               $key = $person['pubkey'];
-       else {
-               logger('diaspora_message: unable to find author details');
-               return;
-       }
+               if ($birthday != "")
+                       $birthday = datetime_convert("UTC", "UTC", $birthday, "Y-m-d");
 
-       if(! rsa_verify($author_signed_data,$author_signature,$key,'sha256')) {
-               logger('diaspora_message: verification failed.');
-               return;
-       }
+               // this is to prevent multiple birthday notifications in a single year
+               // if we already have a stored birthday and the 'm-d' part hasn't changed, preserve the entry, which will preserve the notify year
 
-       $r = q("select id from mail where `uri` = '%s' and uid = %d limit 1",
-               dbesc($message_id),
-               intval($importer['uid'])
-       );
-       if(count($r)) {
-               logger('diaspora_message: duplicate message already delivered.', LOGGER_DEBUG);
-               return;
-       }
+               if(substr($birthday,5) === substr($contact["bd"],5))
+                       $birthday = $contact["bd"];
 
-       q("insert into mail ( `uid`, `guid`, `convid`, `from-name`,`from-photo`,`from-url`,`contact-id`,`title`,`body`,`seen`,`reply`,`uri`,`parent-uri`,`created`) values ( %d, '%s', %d, '%s', '%s', '%s', %d, '%s', '%s', %d, %d, '%s','%s','%s')",
-               intval($importer['uid']),
-               dbesc($msg_guid),
-               intval($conversation['id']),
-               dbesc($person['name']),
-               dbesc($person['photo']),
-               dbesc($person['url']),
-               intval($contact['id']),
-               dbesc($conversation['subject']),
-               dbesc($body),
-               0,
-               1,
-               dbesc($message_id),
-               dbesc($parent_uri),
-               dbesc($msg_created_at)
-       );
-
-       q("update conv set updated = '%s' where id = %d",
-               dbesc(datetime_convert()),
-               intval($conversation['id'])
-       );
-
-       return;
-}
+               $r = q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `addr` = '%s', `name-date` = '%s', `bd` = '%s',
+                               `location` = '%s', `about` = '%s', `keywords` = '%s', `gender` = '%s' WHERE `id` = %d AND `uid` = %d",
+                       dbesc($name),
+                       dbesc($nick),
+                       dbesc($author),
+                       dbesc(datetime_convert()),
+                       dbesc($birthday),
+                       dbesc($location),
+                       dbesc($about),
+                       dbesc($keywords),
+                       dbesc($gender),
+                       intval($contact["id"]),
+                       intval($importer["uid"])
+               );
 
-function diaspora_participation($importer,$xml) {
-       logger("Unsupported message type 'participation' ".print_r($xml, true));
-}
+               if ($searchable) {
+                       poco_check($contact["url"], $name, NETWORK_DIASPORA, $image_url, $about, $location, $gender, $keywords, "",
+                               datetime_convert(), 2, $contact["id"], $importer["uid"]);
+               }
 
-function diaspora_photo($importer,$xml,$msg,$attempt=1) {
+               $gcontact = array("url" => $contact["url"], "network" => NETWORK_DIASPORA, "generation" => 2,
+                                       "photo" => $image_url, "name" => $name, "location" => $location,
+                                       "about" => $about, "birthday" => $birthday, "gender" => $gender,
+                                       "addr" => $author, "nick" => $nick, "keywords" => $keywords,
+                                       "hide" => !$searchable, "nsfw" => $nsfw);
 
-       $a = get_app();
+               update_gcontact($gcontact);
 
-       logger('diaspora_photo: init',LOGGER_DEBUG);
+               logger("Profile of contact ".$contact["id"]." stored for user ".$importer["uid"], LOGGER_DEBUG);
 
-       $remote_photo_path = notags(unxmlify($xml->remote_photo_path));
+               return true;
+       }
 
-       $remote_photo_name = notags(unxmlify($xml->remote_photo_name));
+       /**
+        * @brief Processes incoming friend requests
+        *
+        * @param array $importer Array of the importer user
+        * @param array $contact The contact that send the request
+        */
+       private function receive_request_make_friend($importer, $contact) {
 
-       $status_message_guid = notags(unxmlify($xml->status_message_guid));
+               $a = get_app();
 
-       $guid = notags(unxmlify($xml->guid));
+               if($contact["rel"] == CONTACT_IS_FOLLOWER && in_array($importer["page-flags"], array(PAGE_FREELOVE))) {
+                       q("UPDATE `contact` SET `rel` = %d, `writable` = 1 WHERE `id` = %d AND `uid` = %d",
+                               intval(CONTACT_IS_FRIEND),
+                               intval($contact["id"]),
+                               intval($importer["uid"])
+                       );
+               }
+               // send notification
 
-       $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
+               $r = q("SELECT `hide-friends` FROM `profile` WHERE `uid` = %d AND `is-default` = 1 LIMIT 1",
+                       intval($importer["uid"])
+               );
 
-       $public = notags(unxmlify($xml->public));
+               if($r && !$r[0]["hide-friends"] && !$contact["hidden"] && intval(get_pconfig($importer["uid"], "system", "post_newfriend"))) {
 
-       $created_at = notags(unxmlify($xml_created_at));
+                       $self = q("SELECT * FROM `contact` WHERE `self` AND `uid` = %d LIMIT 1",
+                               intval($importer["uid"])
+                       );
 
-       logger('diaspora_photo: status_message_guid: ' . $status_message_guid, LOGGER_DEBUG);
+                       // they are not CONTACT_IS_FOLLOWER anymore but that's what we have in the array
 
-       $contact = diaspora_get_contact_by_handle($importer['uid'],$msg['author']);
-       if(! $contact) {
-               logger('diaspora_photo: contact record not found: ' . $msg['author'] . ' handle: ' . $diaspora_handle);
-               return;
-       }
+                       if($self && $contact["rel"] == CONTACT_IS_FOLLOWER) {
 
-       if(! diaspora_post_allow($importer,$contact, false)) {
-               logger('diaspora_photo: Ignoring this author.');
-               return 202;
+                               $arr = array();
+                               $arr["uri"] = $arr["parent-uri"] = item_new_uri($a->get_hostname(), $importer["uid"]);
+                               $arr["uid"] = $importer["uid"];
+                               $arr["contact-id"] = $self[0]["id"];
+                               $arr["wall"] = 1;
+                               $arr["type"] = 'wall';
+                               $arr["gravity"] = 0;
+                               $arr["origin"] = 1;
+                               $arr["author-name"] = $arr["owner-name"] = $self[0]["name"];
+                               $arr["author-link"] = $arr["owner-link"] = $self[0]["url"];
+                               $arr["author-avatar"] = $arr["owner-avatar"] = $self[0]["thumb"];
+                               $arr["verb"] = ACTIVITY_FRIEND;
+                               $arr["object-type"] = ACTIVITY_OBJ_PERSON;
+
+                               $A = "[url=".$self[0]["url"]."]".$self[0]["name"]."[/url]";
+                               $B = "[url=".$contact["url"]."]".$contact["name"]."[/url]";
+                               $BPhoto = "[url=".$contact["url"]."][img]".$contact["thumb"]."[/img][/url]";
+                               $arr["body"] = sprintf(t("%1$s is now friends with %2$s"), $A, $B)."\n\n\n".$Bphoto;
+
+                               $arr["object"] = self::construct_new_friend_object($contact);
+
+                               $arr["last-child"] = 1;
+
+                               $arr["allow_cid"] = $user[0]["allow_cid"];
+                               $arr["allow_gid"] = $user[0]["allow_gid"];
+                               $arr["deny_cid"]  = $user[0]["deny_cid"];
+                               $arr["deny_gid"]  = $user[0]["deny_gid"];
+
+                               $i = item_store($arr);
+                               if($i)
+                                       proc_run("php", "include/notifier.php", "activity", $i);
+                       }
+               }
        }
 
-       $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
-               intval($importer['uid']),
-               dbesc($status_message_guid)
-       );
+       /**
+        * @brief Creates a XML object for a "new friend" message
+        *
+        * @param array $contact Array of the contact
+        *
+        * @return string The XML
+        */
+        private function construct_new_friend_object($contact) {
+                $objtype = ACTIVITY_OBJ_PERSON;
+                $link = '<link rel="alternate" type="text/html" href="'.$contact["url"].'" />'."\n".
+                        '<link rel="photo" type="image/jpeg" href="'.$contact["thumb"].'" />'."\n";
+
+                $xmldata = array("object" => array("type" => $objtype,
+                                                "title" => $contact["name"],
+                                                "id" => $contact["url"]."/".$contact["name"],
+                                                "link" => $link));
+
+                return xml::from_array($xmldata, $xml, true);
+        }
+
+       /**
+        * @brief Processes incoming sharing notification
+        *
+        * @param array $importer Array of the importer user
+        * @param object $data The message object
+        *
+        * @return bool Success
+        */
+       private function receive_contact_request($importer, $data) {
+               $author = unxmlify($data->author);
+               $recipient = unxmlify($data->recipient);
+
+               if (!$author || !$recipient)
+                       return false;
 
-/*     deactivated by now since it can lead to multiplicated pictures in posts.
-       if(!count($r)) {
-               $result = diaspora_store_by_guid($status_message_guid, $contact['url'], $importer['uid']);
+               // the current protocol version doesn't know these fields
+               // That means that we will assume their existance
+               if (isset($data->following))
+                       $following = (unxmlify($data->following) == "true");
+               else
+                       $following = true;
 
-               if (!$result) {
-                       $person = find_diaspora_person_by_handle($diaspora_handle);
-                       $result = diaspora_store_by_guid($status_message_guid, $person['url'], $importer['uid']);
-               }
+               if (isset($data->sharing))
+                       $sharing = (unxmlify($data->sharing) == "true");
+               else
+                       $sharing = true;
 
-               if ($result) {
-                       logger("Fetched missing item ".$status_message_guid." - result: ".$result, LOGGER_DEBUG);
+               $contact = self::contact_by_handle($importer["uid"],$author);
 
-                       $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
-                               intval($importer['uid']),
-                               dbesc($status_message_guid)
-                       );
+               // perhaps we were already sharing with this person. Now they're sharing with us.
+               // That makes us friends.
+               if ($contact) {
+                       if ($following AND $sharing) {
+                               self::receive_request_make_friend($importer, $contact);
+                               return true;
+                       } else /// @todo Handle all possible variations of adding and retracting of permissions
+                               return false;
                }
-       }
-*/
-       if(!count($r)) {
-               if($attempt <= 3) {
-                       q("INSERT INTO dsprphotoq (uid, msg, attempt) VALUES (%d, '%s', %d)",
-                          intval($importer['uid']),
-                          dbesc(serialize($msg)),
-                          intval($attempt + 1)
-                       );
+
+               if (!$following AND $sharing AND in_array($importer["page-flags"], array(PAGE_SOAPBOX, PAGE_NORMAL))) {
+                       logger("Author ".$author." wants to share with us - but doesn't want to listen. Request is ignored.", LOGGER_DEBUG);
+                       return false;
+               } elseif (!$following AND !$sharing) {
+                       logger("Author ".$author." doesn't want anything - and we don't know the author. Request is ignored.", LOGGER_DEBUG);
+                       return false;
                }
 
-               logger('diaspora_photo: attempt = ' . $attempt . '; status message not found: ' . $status_message_guid . ' for photo: ' . $guid);
-               return;
-       }
+               $ret = self::person_by_handle($author);
 
-       $parent_item = $r[0];
+               if (!$ret || ($ret["network"] != NETWORK_DIASPORA)) {
+                       logger("Cannot resolve diaspora handle ".$author." for ".$recipient);
+                       return false;
+               }
 
-       $link_text = '[img]' . $remote_photo_path . $remote_photo_name . '[/img]' . "\n";
+               $batch = (($ret["batch"]) ? $ret["batch"] : implode("/", array_slice(explode("/", $ret["url"]), 0, 3))."/receive/public");
+
+               $r = q("INSERT INTO `contact` (`uid`, `network`,`addr`,`created`,`url`,`nurl`,`batch`,`name`,`nick`,`photo`,`pubkey`,`notify`,`poll`,`blocked`,`priority`)
+                       VALUES (%d, '%s', '%s', '%s', '%s','%s','%s','%s','%s','%s','%s','%s','%s',%d,%d)",
+                       intval($importer["uid"]),
+                       dbesc($ret["network"]),
+                       dbesc($ret["addr"]),
+                       datetime_convert(),
+                       dbesc($ret["url"]),
+                       dbesc(normalise_link($ret["url"])),
+                       dbesc($batch),
+                       dbesc($ret["name"]),
+                       dbesc($ret["nick"]),
+                       dbesc($ret["photo"]),
+                       dbesc($ret["pubkey"]),
+                       dbesc($ret["notify"]),
+                       dbesc($ret["poll"]),
+                       1,
+                       2
+               );
 
-       $link_text = scale_external_images($link_text, true,
-                                          array($remote_photo_name, 'scaled_full_' . $remote_photo_name));
+               // find the contact record we just created
 
-       if(strpos($parent_item['body'],$link_text) === false) {
+               $contact_record = self::contact_by_handle($importer["uid"],$author);
 
-               $parent_item['body'] = $link_text . $parent_item['body'];
+               if (!$contact_record) {
+                       logger("unable to locate newly created contact record.");
+                       return;
+               }
 
-               $r = q("UPDATE `item` SET `body` = '%s', `visible` = 1 WHERE `id` = %d AND `uid` = %d",
-                       dbesc($parent_item['body']),
-                       intval($parent_item['id']),
-                       intval($parent_item['uid'])
+               $g = q("SELECT `def_gid` FROM `user` WHERE `uid` = %d LIMIT 1",
+                       intval($importer["uid"])
                );
-               put_item_in_cache($parent_item, true);
-               update_thread($parent_item['id']);
-       }
-
-       return;
-}
-
-
 
+               if($g && intval($g[0]["def_gid"]))
+                       group_add_member($importer["uid"], "", $contact_record["id"], $g[0]["def_gid"]);
 
-function diaspora_like($importer,$xml,$msg) {
+               if($importer["page-flags"] == PAGE_NORMAL) {
 
-       $a = get_app();
-       $guid = notags(unxmlify($xml->guid));
-       $parent_guid = notags(unxmlify($xml->parent_guid));
-       $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
-       $target_type = notags(unxmlify($xml->target_type));
-       $positive = notags(unxmlify($xml->positive));
-       $author_signature = notags(unxmlify($xml->author_signature));
+                       $hash = random_string().(string)time();   // Generate a confirm_key
 
-       $parent_author_signature = (($xml->parent_author_signature) ? notags(unxmlify($xml->parent_author_signature)) : '');
+                       $ret = q("INSERT INTO `intro` (`uid`, `contact-id`, `blocked`, `knowyou`, `note`, `hash`, `datetime`)
+                               VALUES (%d, %d, %d, %d, '%s', '%s', '%s')",
+                               intval($importer["uid"]),
+                               intval($contact_record["id"]),
+                               0,
+                               0,
+                               dbesc(t("Sharing notification from Diaspora network")),
+                               dbesc($hash),
+                               dbesc(datetime_convert())
+                       );
+               } else {
 
-       // likes on comments not supported here and likes on photos not supported by Diaspora
+                       // automatic friend approval
+
+                       update_contact_avatar($contact_record["photo"],$importer["uid"],$contact_record["id"]);
+
+                       // technically they are sharing with us (CONTACT_IS_SHARING),
+                       // but if our page-type is PAGE_COMMUNITY or PAGE_SOAPBOX
+                       // we are going to change the relationship and make them a follower.
+
+                       if (($importer["page-flags"] == PAGE_FREELOVE) AND $sharing AND $following)
+                               $new_relation = CONTACT_IS_FRIEND;
+                       elseif (($importer["page-flags"] == PAGE_FREELOVE) AND $sharing)
+                               $new_relation = CONTACT_IS_SHARING;
+                       else
+                               $new_relation = CONTACT_IS_FOLLOWER;
+
+                       $r = q("UPDATE `contact` SET `rel` = %d,
+                               `name-date` = '%s',
+                               `uri-date` = '%s',
+                               `blocked` = 0,
+                               `pending` = 0,
+                               `writable` = 1
+                               WHERE `id` = %d
+                               ",
+                               intval($new_relation),
+                               dbesc(datetime_convert()),
+                               dbesc(datetime_convert()),
+                               intval($contact_record["id"])
+                       );
 
-//     if($target_type !== 'Post')
-//             return;
+                       $u = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", intval($importer["uid"]));
+                       if($u)
+                               $ret = self::send_share($u[0], $contact_record);
+               }
 
-       $contact = diaspora_get_contact_by_handle($importer['uid'],$msg['author']);
-       if(! $contact) {
-               logger('diaspora_like: cannot find contact: ' . $msg['author']);
-               return;
+               return true;
        }
 
-       if(! diaspora_post_allow($importer,$contact, false)) {
-               logger('diaspora_like: Ignoring this author.');
-               return 202;
-       }
+       /**
+        * @brief Fetches a message with a given guid
+        *
+        * @param string $guid message guid
+        * @param string $orig_author handle of the original post
+        * @param string $author handle of the sharer
+        *
+        * @return array The fetched item
+        */
+       private function original_item($guid, $orig_author, $author) {
+
+               // Do we already have this item?
+               $r = q("SELECT `body`, `tag`, `app`, `created`, `object-type`, `uri`, `guid`,
+                               `author-name`, `author-link`, `author-avatar`
+                               FROM `item` WHERE `guid` = '%s' AND `visible` AND NOT `deleted` AND `body` != '' LIMIT 1",
+                       dbesc($guid));
+
+               if($r) {
+                       logger("reshared message ".$guid." already exists on system.");
+
+                       // Maybe it is already a reshared item?
+                       // Then refetch the content, if it is a reshare from a reshare.
+                       // If it is a reshared post from another network then reformat to avoid display problems with two share elements
+                       if (self::is_reshare($r[0]["body"], true))
+                               $r = array();
+                       elseif (self::is_reshare($r[0]["body"], false)) {
+                               $r[0]["body"] = diaspora2bb(bb2diaspora($r[0]["body"]));
+
+                               // Add OEmbed and other information to the body
+                               $r[0]["body"] = add_page_info_to_body($r[0]["body"], false, true);
+
+                               return $r[0];
+                       } else
+                               return $r[0];
+               }
 
-       $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
-               intval($importer['uid']),
-               dbesc($parent_guid)
-       );
+               if (!$r) {
+                       $server = "https://".substr($orig_author, strpos($orig_author, "@") + 1);
+                       logger("1st try: reshared message ".$guid." will be fetched from original server: ".$server);
+                       $item_id = self::store_by_guid($guid, $server);
 
-       if(!count($r)) {
-               $result = diaspora_store_by_guid($parent_guid, $contact['url'], $importer['uid']);
+                       if (!$item_id) {
+                               $server = "http://".substr($orig_author, strpos($orig_author, "@") + 1);
+                               logger("2nd try: reshared message ".$guid." will be fetched from original server: ".$server);
+                               $item_id = self::store_by_guid($guid, $server);
+                       }
 
-               if (!$result) {
-                       $person = find_diaspora_person_by_handle($diaspora_handle);
-                       $result = diaspora_store_by_guid($parent_guid, $person['url'], $importer['uid']);
-               }
+                       // Deactivated by now since there is a risk that someone could manipulate postings through this method
+/*                     if (!$item_id) {
+                               $server = "https://".substr($author, strpos($author, "@") + 1);
+                               logger("3rd try: reshared message ".$guid." will be fetched from sharer's server: ".$server);
+                               $item_id = self::store_by_guid($guid, $server);
+                       }
+                       if (!$item_id) {
+                               $server = "http://".substr($author, strpos($author, "@") + 1);
+                               logger("4th try: reshared message ".$guid." will be fetched from sharer's server: ".$server);
+                               $item_id = self::store_by_guid($guid, $server);
+                       }
+*/
+                       if ($item_id) {
+                               $r = q("SELECT `body`, `tag`, `app`, `created`, `object-type`, `uri`, `guid`,
+                                               `author-name`, `author-link`, `author-avatar`
+                                       FROM `item` WHERE `id` = %d AND `visible` AND NOT `deleted` AND `body` != '' LIMIT 1",
+                                       intval($item_id));
 
-               if ($result) {
-                       logger("Fetched missing item ".$parent_guid." - result: ".$result, LOGGER_DEBUG);
+                               if ($r)
+                                       return $r[0];
 
-                       $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
-                               intval($importer['uid']),
-                               dbesc($parent_guid)
-                       );
+                       }
                }
+               return false;
        }
 
-       if(! count($r)) {
-               logger('diaspora_like: parent item not found: ' . $guid);
-               return;
-       }
-
-       $parent_item = $r[0];
+       /**
+        * @brief Processes a reshare message
+        *
+        * @param array $importer Array of the importer user
+        * @param object $data The message object
+        * @param string $xml The original XML of the message
+        *
+        * @return int the message id
+        */
+       private function receive_reshare($importer, $data, $xml) {
+               $root_author = notags(unxmlify($data->root_author));
+               $root_guid = notags(unxmlify($data->root_guid));
+               $guid = notags(unxmlify($data->guid));
+               $author = notags(unxmlify($data->author));
+               $public = notags(unxmlify($data->public));
+               $created_at = notags(unxmlify($data->created_at));
+
+               $contact = self::allowed_contact_by_handle($importer, $author, false);
+               if (!$contact)
+                       return false;
 
-       $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
-               intval($importer['uid']),
-               dbesc($guid)
-       );
-       if(count($r)) {
-               if($positive === 'true') {
-                       logger('diaspora_like: duplicate like: ' . $guid);
-                       return;
-               }
-               // Note: I don't think "Like" objects with positive = "false" are ever actually used
-               // It looks like "RelayableRetractions" are used for "unlike" instead
-               if($positive === 'false') {
-                       logger('diaspora_like: received a like with positive set to "false"...ignoring');
-/*                     q("UPDATE `item` SET `deleted` = 1 WHERE `id` = %d AND `uid` = %d",
-                               intval($r[0]['id']),
-                               intval($importer['uid'])
-                       );*/
-                       // FIXME--actually don't unless it turns out that Diaspora does indeed send out "false" likes
-                       //  send notification via proc_run()
-                       return;
-               }
-       }
-       // Note: I don't think "Like" objects with positive = "false" are ever actually used
-       // It looks like "RelayableRetractions" are used for "unlike" instead
-       if($positive === 'false') {
-               logger('diaspora_like: received a like with positive set to "false"');
-               logger('diaspora_like: unlike received with no corresponding like...ignoring');
-               return;
-       }
+               $message_id = self::message_exists($importer["uid"], $guid);
+               if ($message_id)
+                       return $message_id;
 
+               $original_item = self::original_item($root_guid, $root_author, $author);
+               if (!$original_item)
+                       return false;
 
-       /* How Diaspora performs "like" signature checking:
+               $orig_url = App::get_baseurl()."/display/".$original_item["guid"];
 
-          - If an item has been sent by the like author to the top-level post owner to relay on
-            to the rest of the contacts on the top-level post, the top-level post owner should check
-            the author_signature, then create a parent_author_signature before relaying the like on
-          - If an item has been relayed on by the top-level post owner, the contacts who receive it
-            check only the parent_author_signature. Basically, they trust that the top-level post
-            owner has already verified the authenticity of anything he/she sends out
-          - In either case, the signature that get checked is the signature created by the person
-            who sent the salmon
-       */
+               $datarray = array();
 
-       // Diaspora has changed the way they are signing the likes.
-       // Just to make sure that we don't miss any likes we will check the old and the current way.
-       $old_signed_data = $guid . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $diaspora_handle;
+               $datarray["uid"] = $importer["uid"];
+               $datarray["contact-id"] = $contact["id"];
+               $datarray["network"]  = NETWORK_DIASPORA;
 
-       $signed_data = $positive . ';' . $guid . ';' . $target_type . ';' . $parent_guid . ';' . $diaspora_handle;
+               $datarray["author-name"] = $contact["name"];
+               $datarray["author-link"] = $contact["url"];
+               $datarray["author-avatar"] = ((x($contact,"thumb")) ? $contact["thumb"] : $contact["photo"]);
 
-       $key = $msg['key'];
+               $datarray["owner-name"] = $datarray["author-name"];
+               $datarray["owner-link"] = $datarray["author-link"];
+               $datarray["owner-avatar"] = $datarray["author-avatar"];
 
-       if ($parent_author_signature) {
-               // If a parent_author_signature exists, then we've received the like
-               // relayed from the top-level post owner. There's no need to check the
-               // author_signature if the parent_author_signature is valid
+               $datarray["guid"] = $guid;
+               $datarray["uri"] = $datarray["parent-uri"] = $author.":".$guid;
 
-               $parent_author_signature = base64_decode($parent_author_signature);
+               $datarray["verb"] = ACTIVITY_POST;
+               $datarray["gravity"] = GRAVITY_PARENT;
 
-               if (!rsa_verify($signed_data,$parent_author_signature,$key,'sha256') AND
-                       !rsa_verify($old_signed_data,$parent_author_signature,$key,'sha256')) {
+               $datarray["object"] = $xml;
 
-                       logger('diaspora_like: top-level owner verification failed.');
-                       return;
-               }
-       } else {
-               // If there's no parent_author_signature, then we've received the like
-               // from the like creator. In that case, the person is "like"ing
-               // our post, so he/she must be a contact of ours and his/her public key
-               // should be in $msg['key']
+               $prefix = share_header($original_item["author-name"], $original_item["author-link"], $original_item["author-avatar"],
+                                       $original_item["guid"], $original_item["created"], $orig_url);
+               $datarray["body"] = $prefix.$original_item["body"]."[/share]";
 
-               $author_signature = base64_decode($author_signature);
+               $datarray["tag"] = $original_item["tag"];
+               $datarray["app"]  = $original_item["app"];
 
-               if (!rsa_verify($signed_data,$author_signature,$key,'sha256') AND
-                       !rsa_verify($old_signed_data,$author_signature,$key,'sha256')) {
+               $datarray["plink"] = self::plink($author, $guid);
+               $datarray["private"] = (($public == "false") ? 1 : 0);
+               $datarray["changed"] = $datarray["created"] = $datarray["edited"] = datetime_convert("UTC", "UTC", $created_at);
 
-                       logger('diaspora_like: like creator verification failed.');
-                       return;
-               }
-       }
+               $datarray["object-type"] = $original_item["object-type"];
 
-       // Phew! Everything checks out. Now create an item.
+               self::fetch_guid($datarray);
+               $message_id = item_store($datarray);
 
-       // Find the original comment author information.
-       // We need this to make sure we display the comment author
-       // information (name and avatar) correctly.
-       if(strcasecmp($diaspora_handle,$msg['author']) == 0)
-               $person = $contact;
-       else {
-               $person = find_diaspora_person_by_handle($diaspora_handle);
+               if ($message_id)
+                       logger("Stored reshare ".$datarray["guid"]." with message id ".$message_id, LOGGER_DEBUG);
 
-               if(! is_array($person)) {
-                       logger('diaspora_like: unable to find author details');
-                       return;
-               }
+               return $message_id;
        }
 
-       $uri = $diaspora_handle . ':' . $guid;
-
-       $activity = ACTIVITY_LIKE;
-       $post_type = (($parent_item['resource-id']) ? t('photo') : t('status'));
-       $objtype = (($parent_item['resource-id']) ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE );
-       $link = xmlify('<link rel="alternate" type="text/html" href="' . $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $parent_item['id'] . '" />' . "\n") ;
-       $body = $parent_item['body'];
-
-       $obj = <<< EOT
-
-       <object>
-               <type>$objtype</type>
-               <local>1</local>
-               <id>{$parent_item['uri']}</id>
-               <link>$link</link>
-               <title></title>
-               <content>$body</content>
-       </object>
-EOT;
-       $bodyverb = t('%1$s likes %2$s\'s %3$s');
-
-       // Fetch the contact id - if we know this contact
-       $r = q("SELECT `id`, `network` FROM `contact` WHERE `nurl` = '%s' AND `uid` = %d LIMIT 1",
-               dbesc(normalise_link($person['url'])), intval($importer['uid']));
-       if ($r) {
-               $cid = $r[0]['id'];
-               $network = $r[0]['network'];
-       } else {
-               $cid = $contact['id'];
-               $network = NETWORK_DIASPORA;
-       }
+       /**
+        * @brief Processes retractions
+        *
+        * @param array $importer Array of the importer user
+        * @param array $contact The contact of the item owner
+        * @param object $data The message object
+        *
+        * @return bool success
+        */
+       private function item_retraction($importer, $contact, $data) {
+               $target_type = notags(unxmlify($data->target_type));
+               $target_guid = notags(unxmlify($data->target_guid));
+               $author = notags(unxmlify($data->author));
 
-       $arr = array();
-
-       $arr['uri'] = $uri;
-       $arr['uid'] = $importer['uid'];
-       $arr['guid'] = $guid;
-       $arr['network']  = $network;
-       $arr['contact-id'] = $cid;
-       $arr['type'] = 'activity';
-       $arr['wall'] = $parent_item['wall'];
-       $arr['gravity'] = GRAVITY_LIKE;
-       $arr['parent'] = $parent_item['id'];
-       $arr['parent-uri'] = $parent_item['uri'];
-
-       $arr['owner-name'] = $parent_item['name'];
-       $arr['owner-link'] = $parent_item['url'];
-       //$arr['owner-avatar'] = $parent_item['thumb'];
-       $arr['owner-avatar'] = ((x($parent_item,'thumb')) ? $parent_item['thumb'] : $parent_item['photo']);
-
-       $arr['author-name'] = $person['name'];
-       $arr['author-link'] = $person['url'];
-       $arr['author-avatar'] = ((x($person,'thumb')) ? $person['thumb'] : $person['photo']);
-
-       $ulink = '[url=' . $contact['url'] . ']' . $contact['name'] . '[/url]';
-       $alink = '[url=' . $parent_item['author-link'] . ']' . $parent_item['author-name'] . '[/url]';
-       //$plink = '[url=' . $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $parent_item['id'] . ']' . $post_type . '[/url]';
-       $plink = '[url='.$a->get_baseurl().'/display/'.urlencode($guid).']'.$post_type.'[/url]';
-       $arr['body'] =  sprintf( $bodyverb, $ulink, $alink, $plink );
-
-       $arr['app']  = 'Diaspora';
-
-       $arr['private'] = $parent_item['private'];
-       $arr['verb'] = $activity;
-       $arr['object-type'] = $objtype;
-       $arr['object'] = $obj;
-       $arr['visible'] = 1;
-       $arr['unseen'] = 1;
-       $arr['last-child'] = 0;
-
-       $message_id = item_store($arr);
-
-
-       //if($message_id) {
-       //      q("update item set plink = '%s' where id = %d",
-       //              //dbesc($a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $message_id),
-       //              dbesc($a->get_baseurl().'/display/'.$guid),
-       //              intval($message_id)
-       //      );
-       //}
-
-       // If we are the origin of the parent we store the original signature and notify our followers
-       if($parent_item['origin']) {
-               $author_signature_base64 = base64_encode($author_signature);
-               $author_signature_base64 = diaspora_repair_signature($author_signature_base64, $diaspora_handle);
-
-               q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
-                       intval($message_id),
-                       dbesc($signed_data),
-                       dbesc($author_signature_base64),
-                       dbesc($diaspora_handle)
+               $person = self::person_by_handle($author);
+               if (!is_array($person)) {
+                       logger("unable to find author detail for ".$author);
+                       return false;
+               }
+
+               $r = q("SELECT `id`, `parent`, `parent-uri`, `author-link` FROM `item` WHERE `guid` = '%s' AND `uid` = %d AND NOT `file` LIKE '%%[%%' LIMIT 1",
+                       dbesc($target_guid),
+                       intval($importer["uid"])
                );
+               if (!$r)
+                       return false;
 
-               // notify others
-               proc_run('php','include/notifier.php','comment-import',$message_id);
-       }
+               // Only delete it if the author really fits
+               if (!link_compare($r[0]["author-link"], $person["url"])) {
+                       logger("Item author ".$r[0]["author-link"]." doesn't fit to expected contact ".$person["url"], LOGGER_DEBUG);
+                       return false;
+               }
 
-       return;
-}
+               // Check if the sender is the thread owner
+               $p = q("SELECT `id`, `author-link`, `origin` FROM `item` WHERE `id` = %d",
+                       intval($r[0]["parent"]));
 
-function diaspora_retraction($importer,$xml) {
+               // Only delete it if the parent author really fits
+               if (!link_compare($p[0]["author-link"], $contact["url"]) AND !link_compare($r[0]["author-link"], $contact["url"])) {
+                       logger("Thread author ".$p[0]["author-link"]." and item author ".$r[0]["author-link"]." don't fit to expected contact ".$contact["url"], LOGGER_DEBUG);
+                       return false;
+               }
 
+               // Currently we don't have a central deletion function that we could use in this case. The function "item_drop" doesn't work for that case
+               q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', `body` = '' , `title` = '' WHERE `id` = %d",
+                       dbesc(datetime_convert()),
+                       dbesc(datetime_convert()),
+                       intval($r[0]["id"])
+               );
+               delete_thread($r[0]["id"], $r[0]["parent-uri"]);
 
-       $guid = notags(unxmlify($xml->guid));
-       $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
-       $type = notags(unxmlify($xml->type));
+               logger("Deleted target ".$target_guid." (".$r[0]["id"].") from user ".$importer["uid"]." parent: ".$p[0]["id"], LOGGER_DEBUG);
 
-       $contact = diaspora_get_contact_by_handle($importer['uid'],$diaspora_handle);
-       if(! $contact)
-               return;
+               // Now check if the retraction needs to be relayed by us
+               if($p[0]["origin"]) {
+                       // notify others
+                       proc_run("php", "include/notifier.php", "drop", $r[0]["id"]);
+               }
 
-       if($type === 'Person') {
-               require_once('include/Contact.php');
-               contact_remove($contact['id']);
-       } elseif($type === 'StatusMessage') {
-               $guid = notags(unxmlify($xml->post_guid));
+               return true;
+       }
 
-               $r = q("SELECT * FROM `item` WHERE `guid` = '%s' AND `uid` = %d AND NOT `file` LIKE '%%[%%' LIMIT 1",
-                       dbesc($guid),
-                       intval($importer['uid'])
-               );
-               if(count($r)) {
-                       if(link_compare($r[0]['author-link'],$contact['url'])) {
-                               q("UPDATE `item` SET `deleted` = 1, `changed` = '%s' WHERE `id` = %d",
-                                       dbesc(datetime_convert()),
-                                       intval($r[0]['id'])
-                               );
-                               delete_thread($r[0]['id'], $r[0]['parent-uri']);
-                       }
+       /**
+        * @brief Receives retraction messages
+        *
+        * @param array $importer Array of the importer user
+        * @param string $sender The sender of the message
+        * @param object $data The message object
+        *
+        * @return bool Success
+        */
+       private function receive_retraction($importer, $sender, $data) {
+               $target_type = notags(unxmlify($data->target_type));
+
+               $contact = self::contact_by_handle($importer["uid"], $sender);
+               if (!$contact) {
+                       logger("cannot find contact for sender: ".$sender." and user ".$importer["uid"]);
+                       return false;
                }
-       } elseif($type === 'Post') {
-               $r = q("select * from item where guid = '%s' and uid = %d and not file like '%%[%%' limit 1",
-                       dbesc('guid'),
-                       intval($importer['uid'])
-               );
-               if(count($r)) {
-                       if(link_compare($r[0]['author-link'],$contact['url'])) {
-                               q("update item set `deleted` = 1, `changed` = '%s' where `id` = %d",
-                                       dbesc(datetime_convert()),
-                                       intval($r[0]['id'])
-                               );
-                               delete_thread($r[0]['id'], $r[0]['parent-uri']);
-                       }
+
+               logger("Got retraction for ".$target_type.", sender ".$sender." and user ".$importer["uid"], LOGGER_DEBUG);
+
+               switch ($target_type) {
+                       case "Comment":
+                       case "Like":
+                       case "Post": // "Post" will be supported in a future version
+                       case "Reshare":
+                       case "StatusMessage":
+                               return self::item_retraction($importer, $contact, $data);;
+
+                       case "Person":
+                               /// @todo What should we do with an "unshare"?
+                               // Removing the contact isn't correct since we still can read the public items
+                               //contact_remove($contact["id"]);
+                               return true;
+
+                       default:
+                               logger("Unknown target type ".$target_type);
+                               return false;
                }
+               return true;
        }
 
-       return 202;
-       // NOTREACHED
-}
+       /**
+        * @brief Receives status messages
+        *
+        * @param array $importer Array of the importer user
+        * @param object $data The message object
+        * @param string $xml The original XML of the message
+        *
+        * @return int The message id of the newly created item
+        */
+       private function receive_status_message($importer, $data, $xml) {
+
+               $raw_message = unxmlify($data->raw_message);
+               $guid = notags(unxmlify($data->guid));
+               $author = notags(unxmlify($data->author));
+               $public = notags(unxmlify($data->public));
+               $created_at = notags(unxmlify($data->created_at));
+               $provider_display_name = notags(unxmlify($data->provider_display_name));
+
+               /// @todo enable support for polls
+               //if ($data->poll) {
+               //      foreach ($data->poll AS $poll)
+               //              print_r($poll);
+               //      die("poll!\n");
+               //}
+               $contact = self::allowed_contact_by_handle($importer, $author, false);
+               if (!$contact)
+                       return false;
 
-function diaspora_signed_retraction($importer,$xml,$msg) {
+               $message_id = self::message_exists($importer["uid"], $guid);
+               if ($message_id)
+                       return $message_id;
 
+               $address = array();
+               if ($data->location)
+                       foreach ($data->location->children() AS $fieldname => $data)
+                               $address[$fieldname] = notags(unxmlify($data));
 
-       $guid = notags(unxmlify($xml->target_guid));
-       $diaspora_handle = notags(unxmlify($xml->sender_handle));
-       $type = notags(unxmlify($xml->target_type));
-       $sig = notags(unxmlify($xml->target_author_signature));
+               $body = diaspora2bb($raw_message);
 
-       $parent_author_signature = (($xml->parent_author_signature) ? notags(unxmlify($xml->parent_author_signature)) : '');
+               $datarray = array();
 
-       $contact = diaspora_get_contact_by_handle($importer['uid'],$diaspora_handle);
-       if(! $contact) {
-               logger('diaspora_signed_retraction: no contact ' . $diaspora_handle . ' for ' . $importer['uid']);
-               return;
-       }
+               // Attach embedded pictures to the body
+               if ($data->photo) {
+                       foreach ($data->photo AS $photo)
+                               $body = "[img]".unxmlify($photo->remote_photo_path).
+                                       unxmlify($photo->remote_photo_name)."[/img]\n".$body;
 
+                       $datarray["object-type"] = ACTIVITY_OBJ_PHOTO;
+               } else {
+                       $datarray["object-type"] = ACTIVITY_OBJ_NOTE;
 
-       $signed_data = $guid . ';' . $type ;
-       $key = $msg['key'];
+                       // Add OEmbed and other information to the body
+                       if (!self::is_redmatrix($contact["url"]))
+                               $body = add_page_info_to_body($body, false, true);
+               }
 
-       /* How Diaspora performs relayable_retraction signature checking:
+               $datarray["uid"] = $importer["uid"];
+               $datarray["contact-id"] = $contact["id"];
+               $datarray["network"] = NETWORK_DIASPORA;
 
-          - If an item has been sent by the item author to the top-level post owner to relay on
-            to the rest of the contacts on the top-level post, the top-level post owner checks
-            the author_signature, then creates a parent_author_signature before relaying the item on
-          - If an item has been relayed on by the top-level post owner, the contacts who receive it
-            check only the parent_author_signature. Basically, they trust that the top-level post
-            owner has already verified the authenticity of anything he/she sends out
-          - In either case, the signature that get checked is the signature created by the person
-            who sent the salmon
-       */
+               $datarray["author-name"] = $contact["name"];
+               $datarray["author-link"] = $contact["url"];
+               $datarray["author-avatar"] = ((x($contact,"thumb")) ? $contact["thumb"] : $contact["photo"]);
 
-       if($parent_author_signature) {
+               $datarray["owner-name"] = $datarray["author-name"];
+               $datarray["owner-link"] = $datarray["author-link"];
+               $datarray["owner-avatar"] = $datarray["author-avatar"];
 
-               $parent_author_signature = base64_decode($parent_author_signature);
+               $datarray["guid"] = $guid;
+               $datarray["uri"] = $datarray["parent-uri"] = $author.":".$guid;
 
-               if(! rsa_verify($signed_data,$parent_author_signature,$key,'sha256')) {
-                       logger('diaspora_signed_retraction: top-level post owner verification failed');
-                       return;
-               }
+               $datarray["verb"] = ACTIVITY_POST;
+               $datarray["gravity"] = GRAVITY_PARENT;
 
-       } else {
+               $datarray["object"] = $xml;
 
-               $sig_decode = base64_decode($sig);
+               $datarray["body"] = $body;
 
-               if(! rsa_verify($signed_data,$sig_decode,$key,'sha256')) {
-                       logger('diaspora_signed_retraction: retraction owner verification failed.' . print_r($msg,true));
-                       return;
-               }
-       }
+               if ($provider_display_name != "")
+                       $datarray["app"] = $provider_display_name;
 
-       if($type === 'StatusMessage' || $type === 'Comment' || $type === 'Like') {
-               $r = q("select * from item where guid = '%s' and uid = %d and not file like '%%[%%' limit 1",
-                       dbesc($guid),
-                       intval($importer['uid'])
-               );
-               if(count($r)) {
-                       if(link_compare($r[0]['author-link'],$contact['url'])) {
-                               q("update item set `deleted` = 1, `edited` = '%s', `changed` = '%s', `body` = '' , `title` = '' where `id` = %d",
-                                       dbesc(datetime_convert()),
-                                       dbesc(datetime_convert()),
-                                       intval($r[0]['id'])
-                               );
-                               delete_thread($r[0]['id'], $r[0]['parent-uri']);
-
-                               // Now check if the retraction needs to be relayed by us
-                               //
-                               // The first item in the `item` table with the parent id is the parent. However, MySQL doesn't always
-                               // return the items ordered by `item`.`id`, in which case the wrong item is chosen as the parent.
-                               // The only item with `parent` and `id` as the parent id is the parent item.
-                               $p = q("SELECT `origin` FROM `item` WHERE `parent` = %d AND `id` = %d LIMIT 1",
-                                       intval($r[0]['parent']),
-                                       intval($r[0]['parent'])
-                               );
-                               if(count($p)) {
-                                       if($p[0]['origin']) {
-                                               q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
-                                                       $r[0]['id'],
-                                                       dbesc($signed_data),
-                                                       dbesc($sig),
-                                                       dbesc($diaspora_handle)
-                                               );
-
-                                               // the existence of parent_author_signature would have meant the parent_author or owner
-                                               // is already relaying.
-                                               logger('diaspora_signed_retraction: relaying relayable_retraction');
-
-                                               proc_run('php','include/notifier.php','drop',$r[0]['id']);
-                                       }
-                               }
-                       }
-               }
-       }
-       else
-               logger('diaspora_signed_retraction: unknown type: ' . $type);
+               $datarray["plink"] = self::plink($author, $guid);
+               $datarray["private"] = (($public == "false") ? 1 : 0);
+               $datarray["changed"] = $datarray["created"] = $datarray["edited"] = datetime_convert("UTC", "UTC", $created_at);
 
-       return 202;
-       // NOTREACHED
-}
+               if (isset($address["address"]))
+                       $datarray["location"] = $address["address"];
 
-function diaspora_profile($importer,$xml,$msg) {
+               if (isset($address["lat"]) AND isset($address["lng"]))
+                       $datarray["coord"] = $address["lat"]." ".$address["lng"];
 
-       $a = get_app();
-       $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
+               self::fetch_guid($datarray);
+               $message_id = item_store($datarray);
 
+               if ($message_id)
+                       logger("Stored item ".$datarray["guid"]." with message id ".$message_id, LOGGER_DEBUG);
 
-       if($diaspora_handle != $msg['author']) {
-               logger('diaspora_post: Potential forgery. Message handle is not the same as envelope sender.');
-               return 202;
+               return $message_id;
        }
 
-       $contact = diaspora_get_contact_by_handle($importer['uid'],$diaspora_handle);
-       if(! $contact)
-               return;
-
-       //if($contact['blocked']) {
-       //      logger('diaspora_post: Ignoring this author.');
-       //      return 202;
-       //}
-
-       $name = unxmlify($xml->first_name) . ((strlen($xml->last_name)) ? ' ' . unxmlify($xml->last_name) : '');
-       $image_url = unxmlify($xml->image_url);
-       $birthday = unxmlify($xml->birthday);
-       $location = diaspora2bb(unxmlify($xml->location));
-       $about = diaspora2bb(unxmlify($xml->bio));
-       $gender = unxmlify($xml->gender);
-       $searchable = (unxmlify($xml->searchable) == "true");
-       $nsfw = (unxmlify($xml->nsfw) == "true");
-       $tags = unxmlify($xml->tag_string);
-
-       $tags = explode("#", $tags);
-
-       $keywords = array();
-       foreach ($tags as $tag) {
-               $tag = trim(strtolower($tag));
-               if ($tag != "")
-                       $keywords[] = $tag;
-       }
+       /* ************************************************************************************** *
+        * Here are all the functions that are needed to transmit data with the Diaspora protocol *
+        * ************************************************************************************** */
 
-       $keywords = implode(", ", $keywords);
+       /**
+        * @brief returnes the handle of a contact
+        *
+        * @param array $me contact array
+        *
+        * @return string the handle in the format user@domain.tld
+        */
+       private function my_handle($contact) {
+               if ($contact["addr"] != "")
+                       return $contact["addr"];
 
-       $handle_parts = explode("@", $diaspora_handle);
-       $nick = $handle_parts[0];
+               // Normally we should have a filled "addr" field - but in the past this wasn't the case
+               // So - just in case - we build the the address here.
+               if ($contact["nickname"] != "")
+                       $nick = $contact["nickname"];
+               else
+                       $nick = $contact["nick"];
 
-       if($name === '') {
-               $name = $handle_parts[0];
+               return $nick."@".substr(App::get_baseurl(), strpos(App::get_baseurl(),"://") + 3);
        }
 
-       if( preg_match("|^https?://|", $image_url) === 0) {
-               $image_url = "http://" . $handle_parts[1] . $image_url;
-       }
+       /**
+        * @brief Creates the envelope for a public message
+        *
+        * @param string $msg The message that is to be transmitted
+        * @param array $user The record of the sender
+        * @param array $contact Target of the communication
+        * @param string $prvkey The private key of the sender
+        * @param string $pubkey The public key of the receiver
+        *
+        * @return string The envelope
+        */
+       private function build_public_message($msg, $user, $contact, $prvkey, $pubkey) {
 
-/*     $r = q("SELECT DISTINCT ( `resource-id` ) FROM `photo` WHERE  `uid` = %d AND `contact-id` = %d AND `album` = 'Contact Photos' ",
-               intval($importer['uid']),
-               intval($contact['id'])
-       );
-       $oldphotos = ((count($r)) ? $r : null);*/
+               logger("Message: ".$msg, LOGGER_DATA);
 
-       require_once('include/Photo.php');
+               $handle = self::my_handle($user);
 
-       update_contact_avatar($image_url,$importer['uid'],$contact['id']);
+               $b64url_data = base64url_encode($msg);
 
-       // Generic birthday. We don't know the timezone. The year is irrelevant.
+               $data = str_replace(array("\n", "\r", " ", "\t"), array("", "", "", ""), $b64url_data);
 
-       $birthday = str_replace('1000','1901',$birthday);
+               $type = "application/xml";
+               $encoding = "base64url";
+               $alg = "RSA-SHA256";
 
-       if ($birthday != "")
-               $birthday = datetime_convert('UTC','UTC',$birthday,'Y-m-d');
+               $signable_data = $data.".".base64url_encode($type).".".base64url_encode($encoding).".".base64url_encode($alg);
 
-       // this is to prevent multiple birthday notifications in a single year
-       // if we already have a stored birthday and the 'm-d' part hasn't changed, preserve the entry, which will preserve the notify year
+               $signature = rsa_sign($signable_data,$prvkey);
+               $sig = base64url_encode($signature);
 
-       if(substr($birthday,5) === substr($contact['bd'],5))
-               $birthday = $contact['bd'];
+               $xmldata = array("diaspora" => array("header" => array("author_id" => $handle),
+                                               "me:env" => array("me:encoding" => "base64url",
+                                                               "me:alg" => "RSA-SHA256",
+                                                               "me:data" => $data,
+                                                               "@attributes" => array("type" => "application/xml"),
+                                                               "me:sig" => $sig)));
 
-       /// @TODO Update name on item['author-name'] if the name changed. See consume_feed()
-       /// (Not doing this currently because D* protocol is scheduled for revision soon).
+               $namespaces = array("" => "https://joindiaspora.com/protocol",
+                               "me" => "http://salmon-protocol.org/ns/magic-env");
 
-       $r = q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `addr` = '%s', `name-date` = '%s', `bd` = '%s',
-                       `location` = '%s', `about` = '%s', `keywords` = '%s', `gender` = '%s' WHERE `id` = %d AND `uid` = %d",
-               dbesc($name),
-               dbesc($nick),
-               dbesc($diaspora_handle),
-               dbesc(datetime_convert()),
-               dbesc($birthday),
-               dbesc($location),
-               dbesc($about),
-               dbesc($keywords),
-               dbesc($gender),
-               intval($contact['id']),
-               intval($importer['uid'])
-       );
+               $magic_env = xml::from_array($xmldata, $xml, false, $namespaces);
 
-       if ($searchable) {
-               require_once('include/socgraph.php');
-               poco_check($contact['url'], $name, NETWORK_DIASPORA, $image_url, $about, $location, $gender, $keywords, "",
-                       datetime_convert(), 2, $contact['id'], $importer['uid']);
+               logger("magic_env: ".$magic_env, LOGGER_DATA);
+               return $magic_env;
        }
 
-       update_gcontact(array("url" => $contact['url'], "network" => NETWORK_DIASPORA, "generation" => 2,
-                               "photo" => $image_url, "name" => $name, "location" => $location,
-                               "about" => $about, "birthday" => $birthday, "gender" => $gender,
-                               "addr" => $diaspora_handle, "nick" => $nick, "keywords" => $keywords,
-                               "hide" => !$searchable, "nsfw" => $nsfw));
-
-/*     if($r) {
-               if($oldphotos) {
-                       foreach($oldphotos as $ph) {
-                               q("DELETE FROM `photo` WHERE `uid` = %d AND `contact-id` = %d AND `album` = 'Contact Photos' AND `resource-id` = '%s' ",
-                                       intval($importer['uid']),
-                                       intval($contact['id']),
-                                       dbesc($ph['resource-id'])
-                               );
-                       }
+       /**
+        * @brief Creates the envelope for a private message
+        *
+        * @param string $msg The message that is to be transmitted
+        * @param array $user The record of the sender
+        * @param array $contact Target of the communication
+        * @param string $prvkey The private key of the sender
+        * @param string $pubkey The public key of the receiver
+        *
+        * @return string The envelope
+        */
+       private function build_private_message($msg, $user, $contact, $prvkey, $pubkey) {
+
+               logger("Message: ".$msg, LOGGER_DATA);
+
+               // without a public key nothing will work
+
+               if (!$pubkey) {
+                       logger("pubkey missing: contact id: ".$contact["id"]);
+                       return false;
                }
-       }       */
 
-       return;
+               $inner_aes_key = random_string(32);
+               $b_inner_aes_key = base64_encode($inner_aes_key);
+               $inner_iv = random_string(16);
+               $b_inner_iv = base64_encode($inner_iv);
 
-}
+               $outer_aes_key = random_string(32);
+               $b_outer_aes_key = base64_encode($outer_aes_key);
+               $outer_iv = random_string(16);
+               $b_outer_iv = base64_encode($outer_iv);
 
-function diaspora_share($me,$contact) {
-       $a = get_app();
-       $myaddr = $me['nickname'] . '@' .  substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
-       $theiraddr = $contact['addr'];
+               $handle = self::my_handle($user);
 
-       $tpl = get_markup_template('diaspora_share.tpl');
-       $msg = replace_macros($tpl, array(
-               '$sender' => $myaddr,
-               '$recipient' => $theiraddr
-       ));
+               $padded_data = pkcs5_pad($msg,16);
+               $inner_encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $inner_aes_key, $padded_data, MCRYPT_MODE_CBC, $inner_iv);
 
-       $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$me,$contact,$me['prvkey'],$contact['pubkey'])));
-       //$slap = 'xml=' . urlencode(diaspora_msg_build($msg,$me,$contact,$me['prvkey'],$contact['pubkey']));
+               $b64_data = base64_encode($inner_encrypted);
 
-       return(diaspora_transmit($owner,$contact,$slap, false));
-}
 
-function diaspora_unshare($me,$contact) {
+               $b64url_data = base64url_encode($b64_data);
+               $data = str_replace(array("\n", "\r", " ", "\t"), array("", "", "", ""), $b64url_data);
 
-       $a = get_app();
-       $myaddr = $me['nickname'] . '@' .  substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
+               $type = "application/xml";
+               $encoding = "base64url";
+               $alg = "RSA-SHA256";
 
-       $tpl = get_markup_template('diaspora_retract.tpl');
-       $msg = replace_macros($tpl, array(
-               '$guid'   => $me['guid'],
-               '$type'   => 'Person',
-               '$handle' => $myaddr
-       ));
+               $signable_data = $data.".".base64url_encode($type).".".base64url_encode($encoding).".".base64url_encode($alg);
 
-       $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$me,$contact,$me['prvkey'],$contact['pubkey'])));
-       //$slap = 'xml=' . urlencode(diaspora_msg_build($msg,$me,$contact,$me['prvkey'],$contact['pubkey']));
+               $signature = rsa_sign($signable_data,$prvkey);
+               $sig = base64url_encode($signature);
 
-       return(diaspora_transmit($owner,$contact,$slap, false));
+               $xmldata = array("decrypted_header" => array("iv" => $b_inner_iv,
+                                                       "aes_key" => $b_inner_aes_key,
+                                                       "author_id" => $handle));
 
-}
+               $decrypted_header = xml::from_array($xmldata, $xml, true);
+               $decrypted_header = pkcs5_pad($decrypted_header,16);
 
+               $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $outer_aes_key, $decrypted_header, MCRYPT_MODE_CBC, $outer_iv);
 
-function diaspora_send_status($item,$owner,$contact,$public_batch = false) {
+               $outer_json = json_encode(array("iv" => $b_outer_iv, "key" => $b_outer_aes_key));
 
-       $a = get_app();
-       $myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
-       $theiraddr = $contact['addr'];
+               $encrypted_outer_key_bundle = "";
+               openssl_public_encrypt($outer_json, $encrypted_outer_key_bundle, $pubkey);
 
-       $images = array();
+               $b64_encrypted_outer_key_bundle = base64_encode($encrypted_outer_key_bundle);
 
-       $title = $item['title'];
-       $body = $item['body'];
+               logger("outer_bundle: ".$b64_encrypted_outer_key_bundle." key: ".$pubkey, LOGGER_DATA);
 
-/*
-       // We're trying to match Diaspora's split message/photo protocol but
-       // all the photos are displayed on D* as links and not img's - even
-       // though we're sending pretty much precisely what they send us when
-       // doing the same operation.  
-       // Commented out for now, we'll use bb2diaspora to convert photos to markdown
-       // which seems to get through intact.
+               $encrypted_header_json_object = json_encode(array("aes_key" => base64_encode($encrypted_outer_key_bundle),
+                                                               "ciphertext" => base64_encode($ciphertext)));
+               $cipher_json = base64_encode($encrypted_header_json_object);
 
-       $cnt = preg_match_all('|\[img\](.*?)\[\/img\]|',$body,$matches,PREG_SET_ORDER);
-       if($cnt) {
-               foreach($matches as $mtch) {
-                       $detail = array();
-                       $detail['str'] = $mtch[0];
-                       $detail['path'] = dirname($mtch[1]) . '/';
-                       $detail['file'] = basename($mtch[1]);
-                       $detail['guid'] = $item['guid'];
-                       $detail['handle'] = $myaddr;
-                       $images[] = $detail;
-                       $body = str_replace($detail['str'],$mtch[1],$body);
-               }
+               $xmldata = array("diaspora" => array("encrypted_header" => $cipher_json,
+                                               "me:env" => array("me:encoding" => "base64url",
+                                                               "me:alg" => "RSA-SHA256",
+                                                               "me:data" => $data,
+                                                               "@attributes" => array("type" => "application/xml"),
+                                                               "me:sig" => $sig)));
+
+               $namespaces = array("" => "https://joindiaspora.com/protocol",
+                               "me" => "http://salmon-protocol.org/ns/magic-env");
+
+               $magic_env = xml::from_array($xmldata, $xml, false, $namespaces);
+
+               logger("magic_env: ".$magic_env, LOGGER_DATA);
+               return $magic_env;
        }
-*/
 
-       //if(strlen($title))
-       //      $body = "[b]".html_entity_decode($title)."[/b]\n\n".$body;
+       /**
+        * @brief Create the envelope for a message
+        *
+        * @param string $msg The message that is to be transmitted
+        * @param array $user The record of the sender
+        * @param array $contact Target of the communication
+        * @param string $prvkey The private key of the sender
+        * @param string $pubkey The public key of the receiver
+        * @param bool $public Is the message public?
+        *
+        * @return string The message that will be transmitted to other servers
+        */
+       private function build_message($msg, $user, $contact, $prvkey, $pubkey, $public = false) {
 
-       // convert to markdown
-       $body = xmlify(html_entity_decode(bb2diaspora($body)));
-       //$body = bb2diaspora($body);
+               if ($public)
+                       $magic_env =  self::build_public_message($msg,$user,$contact,$prvkey,$pubkey);
+               else
+                       $magic_env =  self::build_private_message($msg,$user,$contact,$prvkey,$pubkey);
+
+               // The data that will be transmitted is double encoded via "urlencode", strange ...
+               $slap = "xml=".urlencode(urlencode($magic_env));
+               return $slap;
+       }
+
+       /**
+        * @brief Creates a signature for a message
+        *
+        * @param array $owner the array of the owner of the message
+        * @param array $message The message that is to be signed
+        *
+        * @return string The signature
+        */
+       private function signature($owner, $message) {
+               $sigmsg = $message;
+               unset($sigmsg["author_signature"]);
+               unset($sigmsg["parent_author_signature"]);
+
+               $signed_text = implode(";", $sigmsg);
+
+               return base64_encode(rsa_sign($signed_text, $owner["uprvkey"], "sha256"));
+       }
+
+       /**
+        * @brief Transmit a message to a target server
+        *
+        * @param array $owner the array of the item owner
+        * @param array $contact Target of the communication
+        * @param string $slap The message that is to be transmitted
+        * @param bool $public_batch Is it a public post?
+        * @param bool $queue_run Is the transmission called from the queue?
+        * @param string $guid message guid
+        *
+        * @return int Result of the transmission
+        */
+       public static function transmit($owner, $contact, $slap, $public_batch, $queue_run=false, $guid = "") {
+
+               $a = get_app();
+
+               $enabled = intval(get_config("system", "diaspora_enabled"));
+               if(!$enabled)
+                       return 200;
+
+               $logid = random_string(4);
+               $dest_url = (($public_batch) ? $contact["batch"] : $contact["notify"]);
+               if (!$dest_url) {
+                       logger("no url for contact: ".$contact["id"]." batch mode =".$public_batch);
+                       return 0;
+               }
 
-       // Adding the title
-       if(strlen($title))
-               $body = "## ".html_entity_decode($title)."\n\n".$body;
+               logger("transmit: ".$logid."-".$guid." ".$dest_url);
 
-       if($item['attach']) {
-               $cnt = preg_match_all('/href=\"(.*?)\"(.*?)title=\"(.*?)\"/ism',$item['attach'],$matches,PREG_SET_ORDER);
-               if(cnt) {
-                       $body .= "\n" . t('Attachments:') . "\n";
-                       foreach($matches as $mtch) {
-                               $body .= '[' . $mtch[3] . '](' . $mtch[1] . ')' . "\n";
+               if (!$queue_run && was_recently_delayed($contact["id"])) {
+                       $return_code = 0;
+               } else {
+                       if (!intval(get_config("system", "diaspora_test"))) {
+                               post_url($dest_url."/", $slap);
+                               $return_code = $a->get_curl_code();
+                       } else {
+                               logger("test_mode");
+                               return 200;
                        }
                }
-       }
 
+               logger("transmit: ".$logid."-".$guid." returns: ".$return_code);
 
-       $public = (($item['private']) ? 'false' : 'true');
+               if(!$return_code || (($return_code == 503) && (stristr($a->get_curl_headers(), "retry-after")))) {
+                       logger("queue message");
 
-       require_once('include/datetime.php');
-       $created = datetime_convert('UTC','UTC',$item['created'],'Y-m-d H:i:s \U\T\C');
+                       $r = q("SELECT `id` FROM `queue` WHERE `cid` = %d AND `network` = '%s' AND `content` = '%s' AND `batch` = %d LIMIT 1",
+                               intval($contact["id"]),
+                               dbesc(NETWORK_DIASPORA),
+                               dbesc($slap),
+                               intval($public_batch)
+                       );
+                       if($r) {
+                               logger("add_to_queue ignored - identical item already in queue");
+                       } else {
+                               // queue message for redelivery
+                               add_to_queue($contact["id"], NETWORK_DIASPORA, $slap, $public_batch);
+                       }
+               }
 
-       // Detect a share element and do a reshare
-       // see: https://github.com/Raven24/diaspora-federation/blob/master/lib/diaspora-federation/entities/reshare.rb
-       if (!$item['private'] AND ($ret = diaspora_is_reshare($item["body"]))) {
-               $tpl = get_markup_template('diaspora_reshare.tpl');
-               $msg = replace_macros($tpl, array(
-                       '$root_handle' => xmlify($ret['root_handle']),
-                       '$root_guid' => $ret['root_guid'],
-                       '$guid' => $item['guid'],
-                       '$handle' => xmlify($myaddr),
-                       '$public' => $public,
-                       '$created' => $created,
-                       '$provider' => $item["app"]
-               ));
-       } else {
-               $tpl = get_markup_template('diaspora_post.tpl');
-               $msg = replace_macros($tpl, array(
-                       '$body' => $body,
-                       '$guid' => $item['guid'],
-                       '$handle' => xmlify($myaddr),
-                       '$public' => $public,
-                       '$created' => $created,
-                       '$provider' => $item["app"]
-               ));
+               return(($return_code) ? $return_code : (-1));
        }
 
-       logger('diaspora_send_status: '.$owner['username'].' -> '.$contact['name'].' base message: '.$msg, LOGGER_DATA);
-       logger('send guid '.$item['guid'], LOGGER_DEBUG);
 
-       $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch)));
-       //$slap = 'xml=' . urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch));
+       /**
+        * @brief Builds and transmit messages
+        *
+        * @param array $owner the array of the item owner
+        * @param array $contact Target of the communication
+        * @param string $type The message type
+        * @param array $message The message data
+        * @param bool $public_batch Is it a public post?
+        * @param string $guid message guid
+        * @param bool $spool Should the transmission be spooled or transmitted?
+        *
+        * @return int Result of the transmission
+        */
+       private function build_and_transmit($owner, $contact, $type, $message, $public_batch = false, $guid = "", $spool = false) {
 
-       $return_code = diaspora_transmit($owner,$contact,$slap,$public_batch,false,$item['guid']);
+               $data = array("XML" => array("post" => array($type => $message)));
 
-       logger('diaspora_send_status: guid: '.$item['guid'].' result '.$return_code, LOGGER_DEBUG);
+               $msg = xml::from_array($data, $xml);
 
-       if(count($images)) {
-               diaspora_send_images($item,$owner,$contact,$images,$public_batch);
-       }
-
-       return $return_code;
-}
+               logger('message: '.$msg, LOGGER_DATA);
+               logger('send guid '.$guid, LOGGER_DEBUG);
 
-function diaspora_is_reshare($body) {
-       $body = trim($body);
+               $slap = self::build_message($msg, $owner, $contact, $owner['uprvkey'], $contact['pubkey'], $public_batch);
 
-       // Skip if it isn't a pure repeated messages
-       // Does it start with a share?
-       if (strpos($body, "[share") > 0)
-               return(false);
+               if ($spool) {
+                       add_to_queue($contact['id'], NETWORK_DIASPORA, $slap, $public_batch);
+                       return true;
+               } else
+                       $return_code = self::transmit($owner, $contact, $slap, $public_batch, false, $guid);
 
-       // Does it end with a share?
-       if (strlen($body) > (strrpos($body, "[/share]") + 8))
-               return(false);
+               logger("guid: ".$item["guid"]." result ".$return_code, LOGGER_DEBUG);
 
-       $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
-       // Skip if there is no shared message in there
-       if ($body == $attributes)
-               return(false);
+               return $return_code;
+       }
 
-       $guid = "";
-       preg_match("/guid='(.*?)'/ism", $attributes, $matches);
-       if ($matches[1] != "")
-               $guid = $matches[1];
+       /**
+        * @brief Sends a "share" message
+        *
+        * @param array $owner the array of the item owner
+        * @param array $contact Target of the communication
+        *
+        * @return int The result of the transmission
+        */
+       public static function send_share($owner,$contact) {
 
-       preg_match('/guid="(.*?)"/ism', $attributes, $matches);
-       if ($matches[1] != "")
-               $guid = $matches[1];
+               $message = array("sender_handle" => self::my_handle($owner),
+                               "recipient_handle" => $contact["addr"]);
 
-       if ($guid != "") {
-               $r = q("SELECT `contact-id` FROM `item` WHERE `guid` = '%s' AND `network` IN ('%s', '%s') LIMIT 1",
-                       dbesc($guid), NETWORK_DFRN, NETWORK_DIASPORA);
-               if ($r) {
-                       $ret= array();
-                       $ret["root_handle"] = diaspora_handle_from_contact($r[0]["contact-id"]);
-                       $ret["root_guid"] = $guid;
-                       return($ret);
-               }
+               return self::build_and_transmit($owner, $contact, "request", $message);
        }
 
-       $profile = "";
-       preg_match("/profile='(.*?)'/ism", $attributes, $matches);
-       if ($matches[1] != "")
-               $profile = $matches[1];
+       /**
+        * @brief sends an "unshare"
+        *
+        * @param array $owner the array of the item owner
+        * @param array $contact Target of the communication
+        *
+        * @return int The result of the transmission
+        */
+       public static function send_unshare($owner,$contact) {
 
-       preg_match('/profile="(.*?)"/ism', $attributes, $matches);
-       if ($matches[1] != "")
-               $profile = $matches[1];
+               $message = array("post_guid" => $owner["guid"],
+                               "diaspora_handle" => self::my_handle($owner),
+                               "type" => "Person");
 
-       $ret= array();
+               return self::build_and_transmit($owner, $contact, "retraction", $message);
+       }
 
-       $ret["root_handle"] = preg_replace("=https?://(.*)/u/(.*)=ism", "$2@$1", $profile);
-       if (($ret["root_handle"] == $profile) OR ($ret["root_handle"] == ""))
-               return(false);
+       /**
+        * @brief Checks a message body if it is a reshare
+        *
+        * @param string $body The message body that is to be check
+        * @param bool $complete Should it be a complete check or a simple check?
+        *
+        * @return array|bool Reshare details or "false" if no reshare
+        */
+       public static function is_reshare($body, $complete = true) {
+               $body = trim($body);
 
-       $link = "";
-       preg_match("/link='(.*?)'/ism", $attributes, $matches);
-       if ($matches[1] != "")
-               $link = $matches[1];
+               // Skip if it isn't a pure repeated messages
+               // Does it start with a share?
+               if (strpos($body, "[share") > 0)
+                       return(false);
 
-       preg_match('/link="(.*?)"/ism', $attributes, $matches);
-       if ($matches[1] != "")
-               $link = $matches[1];
+               // Does it end with a share?
+               if (strlen($body) > (strrpos($body, "[/share]") + 8))
+                       return(false);
 
-       $ret["root_guid"] = preg_replace("=https?://(.*)/posts/(.*)=ism", "$2", $link);
-       if (($ret["root_guid"] == $link) OR ($ret["root_guid"] == ""))
-               return(false);
+               $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
+               // Skip if there is no shared message in there
+               if ($body == $attributes)
+                       return(false);
 
-       return($ret);
-}
+               // If we don't do the complete check we quit here
+               if (!$complete)
+                       return true;
 
-function diaspora_send_images($item,$owner,$contact,$images,$public_batch = false) {
-       $a = get_app();
-       if(! count($images))
-               return;
-       $mysite = substr($a->get_baseurl(),strpos($a->get_baseurl(),'://') + 3) . '/photo';
-
-       $tpl = get_markup_template('diaspora_photo.tpl');
-       foreach($images as $image) {
-               if(! stristr($image['path'],$mysite))
-                       continue;
-               $resource = str_replace('.jpg','',$image['file']);
-               $resource = substr($resource,0,strpos($resource,'-'));
-
-               $r = q("select * from photo where `resource-id` = '%s' and `uid` = %d limit 1",
-                       dbesc($resource),
-                       intval($owner['uid'])
-               );
-               if(! count($r))
-                       continue;
-               $public = (($r[0]['allow_cid'] || $r[0]['allow_gid'] || $r[0]['deny_cid'] || $r[0]['deny_gid']) ? 'false' : 'true' );
-               $msg = replace_macros($tpl,array(
-                       '$path' => xmlify($image['path']),
-                       '$filename' => xmlify($image['file']),
-                       '$msg_guid' => xmlify($image['guid']),
-                       '$guid' => xmlify($r[0]['guid']),
-                       '$handle' => xmlify($image['handle']),
-                       '$public' => xmlify($public),
-                       '$created_at' => xmlify(datetime_convert('UTC','UTC',$r[0]['created'],'Y-m-d H:i:s \U\T\C'))
-               ));
+               $guid = "";
+               preg_match("/guid='(.*?)'/ism", $attributes, $matches);
+               if ($matches[1] != "")
+                       $guid = $matches[1];
+
+               preg_match('/guid="(.*?)"/ism', $attributes, $matches);
+               if ($matches[1] != "")
+                       $guid = $matches[1];
+
+               if ($guid != "") {
+                       $r = q("SELECT `contact-id` FROM `item` WHERE `guid` = '%s' AND `network` IN ('%s', '%s') LIMIT 1",
+                               dbesc($guid), NETWORK_DFRN, NETWORK_DIASPORA);
+                       if ($r) {
+                               $ret= array();
+                               $ret["root_handle"] = self::handle_from_contact($r[0]["contact-id"]);
+                               $ret["root_guid"] = $guid;
+                               return($ret);
+                       }
+               }
 
+               $profile = "";
+               preg_match("/profile='(.*?)'/ism", $attributes, $matches);
+               if ($matches[1] != "")
+                       $profile = $matches[1];
 
-               logger('diaspora_send_photo: base message: ' . $msg, LOGGER_DATA);
-               logger('send guid '.$r[0]['guid'], LOGGER_DEBUG);
+               preg_match('/profile="(.*?)"/ism', $attributes, $matches);
+               if ($matches[1] != "")
+                       $profile = $matches[1];
 
-               $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch)));
-               //$slap = 'xml=' . urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch));
+               $ret= array();
 
-               diaspora_transmit($owner,$contact,$slap,$public_batch,false,$r[0]['guid']);
-       }
+               $ret["root_handle"] = preg_replace("=https?://(.*)/u/(.*)=ism", "$2@$1", $profile);
+               if (($ret["root_handle"] == $profile) OR ($ret["root_handle"] == ""))
+                       return(false);
 
-}
+               $link = "";
+               preg_match("/link='(.*?)'/ism", $attributes, $matches);
+               if ($matches[1] != "")
+                       $link = $matches[1];
 
-function diaspora_send_followup($item,$owner,$contact,$public_batch = false) {
+               preg_match('/link="(.*?)"/ism', $attributes, $matches);
+               if ($matches[1] != "")
+                       $link = $matches[1];
 
-       $a = get_app();
-       $myaddr = $owner['nickname'] . '@' .  substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
-//     $theiraddr = $contact['addr'];
+               $ret["root_guid"] = preg_replace("=https?://(.*)/posts/(.*)=ism", "$2", $link);
+               if (($ret["root_guid"] == $link) OR (trim($ret["root_guid"]) == ""))
+                       return(false);
 
-       // Diaspora doesn't support threaded comments, but some
-       // versions of Diaspora (i.e. Diaspora-pistos) support
-       // likes on comments
-       if($item['verb'] === ACTIVITY_LIKE && $item['thr-parent']) {
-               $p = q("select guid, type, uri, `parent-uri` from item where uri = '%s' limit 1",
-                       dbesc($item['thr-parent'])
-                     );
-       }
-       else {
-               // The first item in the `item` table with the parent id is the parent. However, MySQL doesn't always
-               // return the items ordered by `item`.`id`, in which case the wrong item is chosen as the parent.
-               // The only item with `parent` and `id` as the parent id is the parent item.
-               $p = q("select guid, type, uri, `parent-uri` from item where parent = %d and id = %d limit 1",
-                       intval($item['parent']),
-                       intval($item['parent'])
-               );
-       }
-       if(count($p))
-               $parent = $p[0];
-       else
-               return;
-
-       if($item['verb'] === ACTIVITY_LIKE) {
-               $tpl = get_markup_template('diaspora_like.tpl');
-               $like = true;
-               $target_type = ( $parent['uri'] === $parent['parent-uri']  ? 'Post' : 'Comment');
-//             $target_type = (strpos($parent['type'], 'comment') ? 'Comment' : 'Post');
-//             $positive = (($item['deleted']) ? 'false' : 'true');
-               $positive = 'true';
-
-               if(($item['deleted']))
-                       logger('diaspora_send_followup: received deleted "like". Those should go to diaspora_send_retraction');
-       }
-       else {
-               $tpl = get_markup_template('diaspora_comment.tpl');
-               $like = false;
+               return($ret);
        }
 
-       $text = html_entity_decode(bb2diaspora($item['body']));
+       /**
+        * @brief Sends a post
+        *
+        * @param array $item The item that will be exported
+        * @param array $owner the array of the item owner
+        * @param array $contact Target of the communication
+        * @param bool $public_batch Is it a public post?
+        *
+        * @return int The result of the transmission
+        */
+       public static function send_status($item, $owner, $contact, $public_batch = false) {
 
-       // sign it
+               $myaddr = self::my_handle($owner);
 
-       if($like)
-               $signed_text =  $positive . ';' . $item['guid'] . ';' . $target_type . ';' . $parent['guid'] . ';' . $myaddr;
-       else
-               $signed_text = $item['guid'] . ';' . $parent['guid'] . ';' . $text . ';' . $myaddr;
+               $public = (($item["private"]) ? "false" : "true");
 
-       $authorsig = base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256'));
+               $created = datetime_convert("UTC", "UTC", $item["created"], 'Y-m-d H:i:s \U\T\C');
 
-       $msg = replace_macros($tpl,array(
-               '$guid' => xmlify($item['guid']),
-               '$parent_guid' => xmlify($parent['guid']),
-               '$target_type' =>xmlify($target_type),
-               '$authorsig' => xmlify($authorsig),
-               '$body' => xmlify($text),
-               '$positive' => xmlify($positive),
-               '$handle' => xmlify($myaddr)
-       ));
+               // Detect a share element and do a reshare
+               if (!$item['private'] AND ($ret = self::is_reshare($item["body"]))) {
+                       $message = array("root_diaspora_id" => $ret["root_handle"],
+                                       "root_guid" => $ret["root_guid"],
+                                       "guid" => $item["guid"],
+                                       "diaspora_handle" => $myaddr,
+                                       "public" => $public,
+                                       "created_at" => $created,
+                                       "provider_display_name" => $item["app"]);
 
-       logger('diaspora_followup: base message: ' . $msg, LOGGER_DATA);
-       logger('send guid '.$item['guid'], LOGGER_DEBUG);
+                       $type = "reshare";
+               } else {
+                       $title = $item["title"];
+                       $body = $item["body"];
 
-       $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch)));
-       //$slap = 'xml=' . urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch));
+                       // convert to markdown
+                       $body = html_entity_decode(bb2diaspora($body));
 
-       return(diaspora_transmit($owner,$contact,$slap,$public_batch,false,$item['guid']));
-}
+                       // Adding the title
+                       if(strlen($title))
+                               $body = "## ".html_entity_decode($title)."\n\n".$body;
 
+                       if ($item["attach"]) {
+                               $cnt = preg_match_all('/href=\"(.*?)\"(.*?)title=\"(.*?)\"/ism', $item["attach"], $matches, PREG_SET_ORDER);
+                               if(cnt) {
+                                       $body .= "\n".t("Attachments:")."\n";
+                                       foreach($matches as $mtch)
+                                               $body .= "[".$mtch[3]."](".$mtch[1].")\n";
+                               }
+                       }
 
-function diaspora_send_relay($item,$owner,$contact,$public_batch = false) {
+                       $location = array();
 
+                       if ($item["location"] != "")
+                               $location["address"] = $item["location"];
 
-       $a = get_app();
-       $myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
-//     $theiraddr = $contact['addr'];
+                       if ($item["coord"] != "") {
+                               $coord = explode(" ", $item["coord"]);
+                               $location["lat"] = $coord[0];
+                               $location["lng"] = $coord[1];
+                       }
 
-       // Diaspora doesn't support threaded comments, but some
-       // versions of Diaspora (i.e. Diaspora-pistos) support
-       // likes on comments
-       if($item['verb'] === ACTIVITY_LIKE && $item['thr-parent']) {
-               $p = q("select guid, type, uri, `parent-uri` from item where uri = '%s' limit 1",
-                       dbesc($item['thr-parent'])
-                     );
-       }
-       else {
-               // The first item in the `item` table with the parent id is the parent. However, MySQL doesn't always
-               // return the items ordered by `item`.`id`, in which case the wrong item is chosen as the parent.
-               // The only item with `parent` and `id` as the parent id is the parent item.
-               $p = q("select guid, type, uri, `parent-uri` from item where parent = %d and id = %d limit 1",
-                      intval($item['parent']),
-                      intval($item['parent'])
-                     );
-       }
-       if(count($p))
-               $parent = $p[0];
-       else
-               return;
+                       $message = array("raw_message" => $body,
+                                       "location" => $location,
+                                       "guid" => $item["guid"],
+                                       "diaspora_handle" => $myaddr,
+                                       "public" => $public,
+                                       "created_at" => $created,
+                                       "provider_display_name" => $item["app"]);
 
-       $like = false;
-       $relay_retract = false;
-       $sql_sign_id = 'iid';
-       if( $item['deleted']) {
-               $relay_retract = true;
+                       if (count($location) == 0)
+                               unset($message["location"]);
 
-               $target_type = ( ($item['verb'] === ACTIVITY_LIKE) ? 'Like' : 'Comment');
+                       $type = "status_message";
+               }
 
-               $sql_sign_id = 'retract_iid';
-               $tpl = get_markup_template('diaspora_relayable_retraction.tpl');
+               return self::build_and_transmit($owner, $contact, $type, $message, $public_batch, $item["guid"]);
        }
-       elseif($item['verb'] === ACTIVITY_LIKE) {
-               $like = true;
 
-               $target_type = ( $parent['uri'] === $parent['parent-uri']  ? 'Post' : 'Comment');
-//             $positive = (($item['deleted']) ? 'false' : 'true');
-               $positive = 'true';
+       /**
+        * @brief Creates a "like" object
+        *
+        * @param array $item The item that will be exported
+        * @param array $owner the array of the item owner
+        *
+        * @return array The data for a "like"
+        */
+       private function construct_like($item, $owner) {
 
-               $tpl = get_markup_template('diaspora_like_relay.tpl');
-       }
-       else { // item is a comment
-               $tpl = get_markup_template('diaspora_comment_relay.tpl');
-       }
+               $p = q("SELECT `guid`, `uri`, `parent-uri` FROM `item` WHERE `uri` = '%s' LIMIT 1",
+                       dbesc($item["thr-parent"]));
+               if(!$p)
+                       return false;
 
+               $parent = $p[0];
 
-       // fetch the original signature if the relayable was created by a Diaspora
-       // or DFRN user. Relayables for other networks are not supported.
+               $target_type = ($parent["uri"] === $parent["parent-uri"] ? "Post" : "Comment");
+               $positive = "true";
+
+               return(array("positive" => $positive,
+                               "guid" => $item["guid"],
+                               "target_type" => $target_type,
+                               "parent_guid" => $parent["guid"],
+                               "author_signature" => "",
+                               "diaspora_handle" => self::my_handle($owner)));
+       }
+
+       /**
+        * @brief Creates the object for a comment
+        *
+        * @param array $item The item that will be exported
+        * @param array $owner the array of the item owner
+        *
+        * @return array The data for a comment
+        */
+       private function construct_comment($item, $owner) {
+
+               $p = q("SELECT `guid` FROM `item` WHERE `parent` = %d AND `id` = %d LIMIT 1",
+                       intval($item["parent"]),
+                       intval($item["parent"])
+               );
 
-       $r = q("SELECT `signed_text`, `signature`, `signer` FROM `sign` WHERE " . $sql_sign_id . " = %d LIMIT 1",
-               intval($item['id'])
-       );
-       if(count($r)) {
-               $orig_sign = $r[0];
-               $signed_text = $orig_sign['signed_text'];
-               $authorsig = $orig_sign['signature'];
-               $handle = $orig_sign['signer'];
+               if (!$p)
+                       return false;
 
-               // Split the signed text
-               $signed_parts = explode(";", $signed_text);
+               $parent = $p[0];
 
-               // Remove the parent guid
-               array_shift($signed_parts);
+               $text = html_entity_decode(bb2diaspora($item["body"]));
+
+               return(array("guid" => $item["guid"],
+                               "parent_guid" => $parent["guid"],
+                               "author_signature" => "",
+                               "text" => $text,
+                               "diaspora_handle" => self::my_handle($owner)));
+       }
+
+       /**
+        * @brief Send a like or a comment
+        *
+        * @param array $item The item that will be exported
+        * @param array $owner the array of the item owner
+        * @param array $contact Target of the communication
+        * @param bool $public_batch Is it a public post?
+        *
+        * @return int The result of the transmission
+        */
+       public static function send_followup($item,$owner,$contact,$public_batch = false) {
+
+               if($item['verb'] === ACTIVITY_LIKE) {
+                       $message = self::construct_like($item, $owner);
+                       $type = "like";
+               } else {
+                       $message = self::construct_comment($item, $owner);
+                       $type = "comment";
+               }
 
-               // Remove the comment guid
-               array_shift($signed_parts);
+               if (!$message)
+                       return false;
 
-               // Remove the handle
-               array_pop($signed_parts);
+               $message["author_signature"] = self::signature($owner, $message);
 
-               // Glue the parts together
-               $text = implode(";", $signed_parts);
+               return self::build_and_transmit($owner, $contact, $type, $message, $public_batch, $item["guid"]);
        }
-       else {
-               // This part is meant for cases where we don't have the signatur. (Which shouldn't happen with posts from Diaspora and Friendica)
-               // This means that the comment won't be accepted by newer Diaspora servers
-
-               $body = $item['body'];
-               $text = html_entity_decode(bb2diaspora($body));
 
-               $handle = diaspora_handle_from_contact($item['contact-id']);
-               if(! $handle)
-                       return;
-
-               if($relay_retract)
-                       $signed_text = $item['guid'] . ';' . $target_type;
-               elseif($like)
-                       $signed_text = $item['guid'] . ';' . $target_type . ';' . $parent['guid'] . ';' . $positive . ';' . $handle;
-               else
-                       $signed_text = $item['guid'] . ';' . $parent['guid'] . ';' . $text . ';' . $handle;
+       /**
+        * @brief Creates a message from a signature record entry
+        *
+        * @param array $item The item that will be exported
+        * @param array $signature The entry of the "sign" record
+        *
+        * @return string The message
+        */
+       private function message_from_signature($item, $signature) {
 
-               $authorsig = base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256'));
-       }
+               // Split the signed text
+               $signed_parts = explode(";", $signature['signed_text']);
+
+               if ($item["deleted"])
+                       $message = array("parent_author_signature" => "",
+                                       "target_guid" => $signed_parts[0],
+                                       "target_type" => $signed_parts[1],
+                                       "sender_handle" => $signature['signer'],
+                                       "target_author_signature" => $signature['signature']);
+               elseif ($item['verb'] === ACTIVITY_LIKE)
+                       $message = array("positive" => $signed_parts[0],
+                                       "guid" => $signed_parts[1],
+                                       "target_type" => $signed_parts[2],
+                                       "parent_guid" => $signed_parts[3],
+                                       "parent_author_signature" => "",
+                                       "author_signature" => $signature['signature'],
+                                       "diaspora_handle" => $signed_parts[4]);
+               else {
+                       // Remove the comment guid
+                       $guid = array_shift($signed_parts);
 
-       // Sign the relayable with the top-level owner's signature
-       $parentauthorsig = base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256'));
+                       // Remove the parent guid
+                       $parent_guid = array_shift($signed_parts);
 
-       $msg = replace_macros($tpl,array(
-               '$guid' => xmlify($item['guid']),
-               '$parent_guid' => xmlify($parent['guid']),
-               '$target_type' =>xmlify($target_type),
-               '$authorsig' => xmlify($authorsig),
-               '$parentsig' => xmlify($parentauthorsig),
-               '$body' => xmlify($text),
-               '$positive' => xmlify($positive),
-               '$handle' => xmlify($handle)
-       ));
+                       // Remove the handle
+                       $handle = array_pop($signed_parts);
 
-       logger('diaspora_send_relay: base message: ' . $msg, LOGGER_DATA);
-       logger('send guid '.$item['guid'], LOGGER_DEBUG);
+                       // Glue the parts together
+                       $text = implode(";", $signed_parts);
 
-       $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch)));
-       //$slap = 'xml=' . urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch));
+                       $message = array("guid" => $guid,
+                                       "parent_guid" => $parent_guid,
+                                       "parent_author_signature" => "",
+                                       "author_signature" => $signature['signature'],
+                                       "text" => implode(";", $signed_parts),
+                                       "diaspora_handle" => $handle);
+               }
+               return $message;
+       }
+
+       /**
+        * @brief Relays messages (like, comment, retraction) to other servers if we are the thread owner
+        *
+        * @param array $item The item that will be exported
+        * @param array $owner the array of the item owner
+        * @param array $contact Target of the communication
+        * @param bool $public_batch Is it a public post?
+        *
+        * @return int The result of the transmission
+        */
+       public static function send_relay($item, $owner, $contact, $public_batch = false) {
+
+               if ($item["deleted"])
+                       return self::send_retraction($item, $owner, $contact, $public_batch, true);
+               elseif ($item['verb'] === ACTIVITY_LIKE)
+                       $type = "like";
+               else
+                       $type = "comment";
 
-       return(diaspora_transmit($owner,$contact,$slap,$public_batch,false,$item['guid']));
+               logger("Got relayable data ".$type." for item ".$item["guid"]." (".$item["id"].")", LOGGER_DEBUG);
 
-}
+               // fetch the original signature
 
+               $r = q("SELECT `signed_text`, `signature`, `signer` FROM `sign` WHERE `iid` = %d LIMIT 1",
+                       intval($item["id"]));
 
+               if (!$r) {
+                       logger("Couldn't fetch signatur for item ".$item["guid"]." (".$item["id"].")", LOGGER_DEBUG);
+                       return false;
+               }
 
-function diaspora_send_retraction($item,$owner,$contact,$public_batch = false) {
+               $signature = $r[0];
+
+               // Old way - is used by the internal Friendica functions
+               /// @todo Change all signatur storing functions to the new format
+               if ($signature['signed_text'] AND $signature['signature'] AND $signature['signer'])
+                       $message = self::message_from_signature($item, $signature);
+               else {// New way
+                       $msg = json_decode($signature['signed_text'], true);
+
+                       $message = array();
+                       if (is_array($msg)) {
+                               foreach ($msg AS $field => $data) {
+                                       if (!$item["deleted"]) {
+                                               if ($field == "author")
+                                                       $field = "diaspora_handle";
+                                               if ($field == "parent_type")
+                                                       $field = "target_type";
+                                       }
 
-       $a = get_app();
-       $myaddr = $owner['nickname'] . '@' .  substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
+                                       $message[$field] = $data;
+                               }
+                       } else
+                               logger("Signature text for item ".$item["guid"]." (".$item["id"].") couldn't be extracted: ".$signature['signed_text'], LOGGER_DEBUG);
+               }
 
-       // Check whether the retraction is for a top-level post or whether it's a relayable
-       if( $item['uri'] !== $item['parent-uri'] ) {
+               $message["parent_author_signature"] = self::signature($owner, $message);
 
-               $tpl = get_markup_template('diaspora_relay_retraction.tpl');
-               $target_type = (($item['verb'] === ACTIVITY_LIKE) ? 'Like' : 'Comment');
-       }
-       else {
+               logger("Relayed data ".print_r($message, true), LOGGER_DEBUG);
 
-               $tpl = get_markup_template('diaspora_signed_retract.tpl');
-               $target_type = 'StatusMessage';
+               return self::build_and_transmit($owner, $contact, $type, $message, $public_batch, $item["guid"]);
        }
 
-       $signed_text = $item['guid'] . ';' . $target_type;
-
-       $msg = replace_macros($tpl, array(
-               '$guid'   => xmlify($item['guid']),
-               '$type'   => xmlify($target_type),
-               '$handle' => xmlify($myaddr),
-               '$signature' => xmlify(base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256')))
-       ));
+       /**
+        * @brief Sends a retraction (deletion) of a message, like or comment
+        *
+        * @param array $item The item that will be exported
+        * @param array $owner the array of the item owner
+        * @param array $contact Target of the communication
+        * @param bool $public_batch Is it a public post?
+        * @param bool $relay Is the retraction transmitted from a relay?
+        *
+        * @return int The result of the transmission
+        */
+       public static function send_retraction($item, $owner, $contact, $public_batch = false, $relay = false) {
 
-       logger('send guid '.$item['guid'], LOGGER_DEBUG);
+               $itemaddr = self::handle_from_contact($item["contact-id"], $item["gcontact-id"]);
 
-       $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch)));
-       //$slap = 'xml=' . urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch));
+               // Check whether the retraction is for a top-level post or whether it's a relayable
+               if ($item["uri"] !== $item["parent-uri"]) {
+                       $msg_type = "relayable_retraction";
+                       $target_type = (($item["verb"] === ACTIVITY_LIKE) ? "Like" : "Comment");
+               } else {
+                       $msg_type = "signed_retraction";
+                       $target_type = "StatusMessage";
+               }
 
-       return(diaspora_transmit($owner,$contact,$slap,$public_batch,false,$item['guid']));
-}
+               if ($relay AND ($item["uri"] !== $item["parent-uri"]))
+                       $signature = "parent_author_signature";
+               else
+                       $signature = "target_author_signature";
 
-function diaspora_send_mail($item,$owner,$contact) {
+               $signed_text = $item["guid"].";".$target_type;
 
-       $a = get_app();
-       $myaddr = $owner['nickname'] . '@' .  substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
+               $message = array("target_guid" => $item['guid'],
+                               "target_type" => $target_type,
+                               "sender_handle" => $itemaddr,
+                               $signature => base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256')));
 
-       $r = q("select * from conv where id = %d and uid = %d limit 1",
-               intval($item['convid']),
-               intval($item['uid'])
-       );
+               logger("Got message ".print_r($message, true), LOGGER_DEBUG);
 
-       if(! count($r)) {
-               logger('diaspora_send_mail: conversation not found.');
-               return;
+               return self::build_and_transmit($owner, $contact, $msg_type, $message, $public_batch, $item["guid"]);
        }
-       $cnv = $r[0];
-
-       $conv = array(
-               'guid' => xmlify($cnv['guid']),
-               'subject' => xmlify($cnv['subject']),
-               'created_at' => xmlify(datetime_convert('UTC','UTC',$cnv['created'],'Y-m-d H:i:s \U\T\C')),
-               'diaspora_handle' => xmlify($cnv['creator']),
-               'participant_handles' => xmlify($cnv['recips'])
-       );
-
-       $body = bb2diaspora($item['body']);
-       $created = datetime_convert('UTC','UTC',$item['created'],'Y-m-d H:i:s \U\T\C');
-
-       $signed_text =  $item['guid'] . ';' . $cnv['guid'] . ';' . $body .  ';'
-               . $created . ';' . $myaddr . ';' . $cnv['guid'];
-
-       $sig = base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256'));
-
-       $msg = array(
-               'guid' => xmlify($item['guid']),
-               'parent_guid' => xmlify($cnv['guid']),
-               'parent_author_signature' => xmlify($sig),
-               'author_signature' => xmlify($sig),
-               'text' => xmlify($body),
-               'created_at' => xmlify($created),
-               'diaspora_handle' => xmlify($myaddr),
-               'conversation_guid' => xmlify($cnv['guid'])
-       );
-
-       if($item['reply']) {
-               $tpl = get_markup_template('diaspora_message.tpl');
-               $xmsg = replace_macros($tpl, array('$msg' => $msg));
-       }
-       else {
-               $conv['messages'] = array($msg);
-               $tpl = get_markup_template('diaspora_conversation.tpl');
-               $xmsg = replace_macros($tpl, array('$conv' => $conv));
-       }
-
-       logger('diaspora_conversation: ' . print_r($xmsg,true), LOGGER_DATA);
-       logger('send guid '.$item['guid'], LOGGER_DEBUG);
-
-       $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($xmsg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],false)));
-       //$slap = 'xml=' . urlencode(diaspora_msg_build($xmsg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],false));
 
-       return(diaspora_transmit($owner,$contact,$slap,false,false,$item['guid']));
+       /**
+        * @brief Sends a mail
+        *
+        * @param array $item The item that will be exported
+        * @param array $owner The owner
+        * @param array $contact Target of the communication
+        *
+        * @return int The result of the transmission
+        */
+       public static function send_mail($item, $owner, $contact) {
 
+               $myaddr = self::my_handle($owner);
 
-}
-
-function diaspora_transmit($owner,$contact,$slap,$public_batch,$queue_run=false,$guid = "") {
-
-       $enabled = intval(get_config('system','diaspora_enabled'));
-       if(! $enabled) {
-               return 200;
-       }
+               $r = q("SELECT * FROM `conv` WHERE `id` = %d AND `uid` = %d LIMIT 1",
+                       intval($item["convid"]),
+                       intval($item["uid"])
+               );
 
-       $a = get_app();
-       $logid = random_string(4);
-       $dest_url = (($public_batch) ? $contact['batch'] : $contact['notify']);
-       if(! $dest_url) {
-               logger('diaspora_transmit: no url for contact: ' . $contact['id'] . ' batch mode =' . $public_batch);
-               return 0;
-       }
+               if (!$r) {
+                       logger("conversation not found.");
+                       return;
+               }
+               $cnv = $r[0];
+
+               $conv = array(
+                       "guid" => $cnv["guid"],
+                       "subject" => $cnv["subject"],
+                       "created_at" => datetime_convert("UTC", "UTC", $cnv['created'], 'Y-m-d H:i:s \U\T\C'),
+                       "diaspora_handle" => $cnv["creator"],
+                       "participant_handles" => $cnv["recips"]
+               );
 
-       logger('diaspora_transmit: '.$logid.'-'.$guid.' '.$dest_url);
+               $body = bb2diaspora($item["body"]);
+               $created = datetime_convert("UTC", "UTC", $item["created"], 'Y-m-d H:i:s \U\T\C');
+
+               $signed_text = $item["guid"].";".$cnv["guid"].";".$body.";".$created.";".$myaddr.";".$cnv['guid'];
+               $sig = base64_encode(rsa_sign($signed_text, $owner["uprvkey"], "sha256"));
+
+               $msg = array(
+                       "guid" => $item["guid"],
+                       "parent_guid" => $cnv["guid"],
+                       "parent_author_signature" => $sig,
+                       "author_signature" => $sig,
+                       "text" => $body,
+                       "created_at" => $created,
+                       "diaspora_handle" => $myaddr,
+                       "conversation_guid" => $cnv["guid"]
+               );
 
-       if( (! $queue_run) && (was_recently_delayed($contact['id'])) ) {
-               $return_code = 0;
-       }
-       else {
-               if (!intval(get_config('system','diaspora_test'))) {
-                       post_url($dest_url . '/', $slap);
-                       $return_code = $a->get_curl_code();
+               if ($item["reply"]) {
+                       $message = $msg;
+                       $type = "message";
                } else {
-                       logger('diaspora_transmit: test_mode');
-                       return 200;
+                       $message = array("guid" => $cnv["guid"],
+                                       "subject" => $cnv["subject"],
+                                       "created_at" => datetime_convert("UTC", "UTC", $cnv['created'], 'Y-m-d H:i:s \U\T\C'),
+                                       "message" => $msg,
+                                       "diaspora_handle" => $cnv["creator"],
+                                       "participant_handles" => $cnv["recips"]);
+
+                       $type = "conversation";
                }
+
+               return self::build_and_transmit($owner, $contact, $type, $message, false, $item["guid"]);
        }
 
-       logger('diaspora_transmit: '.$logid.'-'.$guid.' returns: '.$return_code);
+       /**
+        * @brief Sends profile data
+        *
+        * @param int $uid The user id
+        */
+       public static function send_profile($uid) {
 
-       if((! $return_code) || (($return_code == 503) && (stristr($a->get_curl_headers(),'retry-after')))) {
-               logger('diaspora_transmit: queue message');
+               if (!$uid)
+                       return;
 
-               $r = q("SELECT id from queue where cid = %d and network = '%s' and content = '%s' and batch = %d limit 1",
-                       intval($contact['id']),
+               $recips = q("SELECT `id`,`name`,`network`,`pubkey`,`notify` FROM `contact` WHERE `network` = '%s'
+                       AND `uid` = %d AND `rel` != %d",
                        dbesc(NETWORK_DIASPORA),
-                       dbesc($slap),
-                       intval($public_batch)
+                       intval($uid),
+                       intval(CONTACT_IS_SHARING)
+               );
+               if (!$recips)
+                       return;
+
+               $r = q("SELECT `profile`.`uid` AS `profile_uid`, `profile`.* , `user`.*, `user`.`prvkey` AS `uprvkey`, `contact`.`addr`
+                       FROM `profile`
+                       INNER JOIN `user` ON `profile`.`uid` = `user`.`uid`
+                       INNER JOIN `contact` ON `profile`.`uid` = `contact`.`uid`
+                       WHERE `user`.`uid` = %d AND `profile`.`is-default` AND `contact`.`self` LIMIT 1",
+                       intval($uid)
                );
-               if(count($r)) {
-                       logger('diaspora_transmit: add_to_queue ignored - identical item already in queue');
+
+               if (!$r)
+                       return;
+
+               $profile = $r[0];
+
+               $handle = $profile["addr"];
+               $first = ((strpos($profile['name'],' ')
+                       ? trim(substr($profile['name'],0,strpos($profile['name'],' '))) : $profile['name']));
+               $last = (($first === $profile['name']) ? '' : trim(substr($profile['name'], strlen($first))));
+               $large = App::get_baseurl().'/photo/custom/300/'.$profile['uid'].'.jpg';
+               $medium = App::get_baseurl().'/photo/custom/100/'.$profile['uid'].'.jpg';
+               $small = App::get_baseurl().'/photo/custom/50/'  .$profile['uid'].'.jpg';
+               $searchable = (($profile['publish'] && $profile['net-publish']) ? 'true' : 'false');
+
+               if ($searchable === 'true') {
+                       $dob = '1000-00-00';
+
+                       if (($profile['dob']) && ($profile['dob'] != '0000-00-00'))
+                               $dob = ((intval($profile['dob'])) ? intval($profile['dob']) : '1000') .'-'. datetime_convert('UTC','UTC',$profile['dob'],'m-d');
+
+                       $about = $profile['about'];
+                       $about = strip_tags(bbcode($about));
+
+                       $location = formatted_location($profile);
+                       $tags = '';
+                       if ($profile['pub_keywords']) {
+                               $kw = str_replace(',',' ',$profile['pub_keywords']);
+                               $kw = str_replace('  ',' ',$kw);
+                               $arr = explode(' ',$profile['pub_keywords']);
+                               if (count($arr)) {
+                                       for($x = 0; $x < 5; $x ++) {
+                                               if (trim($arr[$x]))
+                                                       $tags .= '#'. trim($arr[$x]) .' ';
+                                       }
+                               }
+                       }
+                       $tags = trim($tags);
                }
-               else {
-                       // queue message for redelivery
-                       add_to_queue($contact['id'],NETWORK_DIASPORA,$slap,$public_batch);
+
+               $message = array("diaspora_handle" => $handle,
+                               "first_name" => $first,
+                               "last_name" => $last,
+                               "image_url" => $large,
+                               "image_url_medium" => $medium,
+                               "image_url_small" => $small,
+                               "birthday" => $dob,
+                               "gender" => $profile['gender'],
+                               "bio" => $about,
+                               "location" => $location,
+                               "searchable" => $searchable,
+                               "tag_string" => $tags);
+
+               foreach($recips as $recip)
+                       self::build_and_transmit($profile, $recip, "profile", $message, false, "", true);
+       }
+
+       /**
+        * @brief Stores the signature for likes that are created on our system
+        *
+        * @param array $contact The contact array of the "like"
+        * @param int $post_id The post id of the "like"
+        *
+        * @return bool Success
+        */
+       public static function store_like_signature($contact, $post_id) {
+
+               $enabled = intval(get_config('system','diaspora_enabled'));
+               if (!$enabled) {
+                       logger('Diaspora support disabled, not storing like signature', LOGGER_DEBUG);
+                       return false;
                }
-       }
 
+               // Is the contact the owner? Then fetch the private key
+               if (!$contact['self'] OR ($contact['uid'] == 0)) {
+                       logger("No owner post, so not storing signature", LOGGER_DEBUG);
+                       return false;
+               }
 
-       return(($return_code) ? $return_code : (-1));
-}
+               $r = q("SELECT `prvkey` FROM `user` WHERE `uid` = %d LIMIT 1", intval($contact['uid']));
+               if(!$r)
+                       return false;
 
-function diaspora_fetch_relay() {
+               $contact["uprvkey"] = $r[0]['prvkey'];
 
-       $serverdata = get_config("system", "relay_server");
-       if ($serverdata == "")
-               return array();
+               $r = q("SELECT * FROM `item` WHERE `id` = %d LIMIT 1", intval($post_id));
+               if (!$r)
+                       return false;
 
-       $relay = array();
+               if (!in_array($r[0]["verb"], array(ACTIVITY_LIKE, ACTIVITY_DISLIKE)))
+                       return false;
 
-       $servers = explode(",", $serverdata);
+               $message = self::construct_like($r[0], $contact);
+               $message["author_signature"] = self::signature($contact, $message);
 
-       foreach($servers AS $server) {
-               $server = trim($server);
-               $batch = $server."/receive/public";
+               // In the future we will store the signature more flexible to support new fields.
+               // Right now we cannot change this since old Friendica versions (prior to 3.5) can only handle this format.
+               // (We are transmitting this data here via DFRN)
 
-               $relais = q("SELECT `batch`, `id`, `name`,`network` FROM `contact` WHERE `uid` = 0 AND `batch` = '%s' LIMIT 1", dbesc($batch));
+               $signed_text = $message["positive"].";".$message["guid"].";".$message["target_type"].";".
+                               $message["parent_guid"].";".$message["diaspora_handle"];
 
-               if (!$relais) {
-                       $addr = "relay@".str_replace("http://", "", normalise_link($server));
+               q("INSERT INTO `sign` (`iid`,`signed_text`,`signature`,`signer`) VALUES (%d,'%s','%s','%s')",
+                       intval($post_id),
+                       dbesc($signed_text),
+                       dbesc($message["author_signature"]),
+                       dbesc($message["diaspora_handle"])
+               );
 
-                       $r = q("INSERT INTO `contact` (`uid`, `created`, `name`, `nick`, `addr`, `url`, `nurl`, `batch`, `network`, `rel`, `blocked`, `pending`, `writable`, `name-date`, `uri-date`, `avatar-date`)
-                               VALUES (0, '%s', '%s', 'relay', '%s', '%s', '%s', '%s', '%s', %d, 0, 0, 1, '%s', '%s', '%s')",
-                               datetime_convert(),
-                               dbesc($addr),
-                               dbesc($addr),
-                               dbesc($server),
-                               dbesc(normalise_link($server)),
-                               dbesc($batch),
-                               dbesc(NETWORK_DIASPORA),
-                               intval(CONTACT_IS_FOLLOWER),
-                               dbesc(datetime_convert()),
-                               dbesc(datetime_convert()),
-                               dbesc(datetime_convert())
-                       );
+               // This here will replace the lines above, once Diaspora changed its protocol
+               //q("INSERT INTO `sign` (`iid`,`signed_text`) VALUES (%d,'%s')",
+               //      intval($message_id),
+               //      dbesc(json_encode($message))
+               //);
 
-                       $relais = q("SELECT `batch`, `id`, `name`,`network` FROM `contact` WHERE `uid` = 0 AND `batch` = '%s' LIMIT 1", dbesc($batch));
-                       if ($relais)
-                               $relay[] = $relais[0];
-               } else
-                       $relay[] = $relais[0];
+               logger('Stored diaspora like signature');
+               return true;
        }
 
-       return $relay;
+       /**
+        * @brief Stores the signature for comments that are created on our system
+        *
+        * @param array $item The item array of the comment
+        * @param array $contact The contact array of the item owner
+        * @param string $uprvkey The private key of the sender
+        * @param int $message_id The message id of the comment
+        *
+        * @return bool Success
+        */
+       public static function store_comment_signature($item, $contact, $uprvkey, $message_id) {
+
+               if ($uprvkey == "") {
+                       logger('No private key, so not storing comment signature', LOGGER_DEBUG);
+                       return false;
+               }
+
+               $enabled = intval(get_config('system','diaspora_enabled'));
+               if (!$enabled) {
+                       logger('Diaspora support disabled, not storing comment signature', LOGGER_DEBUG);
+                       return false;
+               }
+
+               $contact["uprvkey"] = $uprvkey;
+
+               $message = self::construct_comment($item, $contact);
+               $message["author_signature"] = self::signature($contact, $message);
+
+               // In the future we will store the signature more flexible to support new fields.
+               // Right now we cannot change this since old Friendica versions (prior to 3.5) can only handle this format.
+               // (We are transmitting this data here via DFRN)
+               $signed_text = $message["guid"].";".$message["parent_guid"].";".
+                               $message["text"].";".$message["diaspora_handle"];
+
+               q("INSERT INTO `sign` (`iid`,`signed_text`,`signature`,`signer`) VALUES (%d,'%s','%s','%s')",
+                       intval($message_id),
+                       dbesc($signed_text),
+                       dbesc($message["author_signature"]),
+                       dbesc($message["diaspora_handle"])
+               );
+
+               // This here will replace the lines above, once Diaspora changed its protocol
+               //q("INSERT INTO `sign` (`iid`,`signed_text`) VALUES (%d,'%s')",
+               //      intval($message_id),
+               //      dbesc(json_encode($message))
+               //);
+
+               logger('Stored diaspora comment signature');
+               return true;
+       }
 }
+?>
index a8f670334b5d66a00d8fc5022d584472dfa85b80..0b468faea189e522505ed5c686facd22be905406 100644 (file)
@@ -20,22 +20,14 @@ function discover_poco_run(&$argv, &$argc){
 
        require_once('include/session.php');
        require_once('include/datetime.php');
-       require_once('include/pidfile.php');
 
        load_config('config');
        load_config('system');
 
-       $maxsysload = intval(get_config('system','maxloadavg'));
-       if($maxsysload < 1)
-               $maxsysload = 50;
-
-       $load = current_load();
-       if($load) {
-               if(intval($load) > $maxsysload) {
-                       logger('system: load ' . $load . ' too high. discover_poco deferred to next scheduled run.');
+       // Don't check this stuff if the function is called by the poller
+       if (App::callstack() != "poller_run")
+               if (App::maxload_reached())
                        return;
-               }
-       }
 
        if(($argc > 2) && ($argv[1] == "dirsearch")) {
                $search = urldecode($argv[2]);
@@ -50,21 +42,10 @@ function discover_poco_run(&$argv, &$argc){
        } else
                die("Unknown or missing parameter ".$argv[1]."\n");
 
-       $lockpath = get_lockpath();
-       if ($lockpath != '') {
-               $pidfile = new pidfile($lockpath, 'discover_poco'.$mode.urlencode($search));
-               if($pidfile->is_already_running()) {
-                       logger("discover_poco: Already running");
-                       if ($pidfile->running_time() > 19*60) {
-                               $pidfile->kill();
-                               logger("discover_poco: killed stale process");
-                               // Calling a new instance
-                               if ($mode == 0)
-                                       proc_run('php','include/discover_poco.php');
-                       }
-                       exit;
-               }
-       }
+       // Don't check this stuff if the function is called by the poller
+       if (App::callstack() != "poller_run")
+               if (App::is_already_running('discover_poco'.$mode.urlencode($search), 'include/discover_poco.php', 1140))
+                       return;
 
        $a->set_baseurl(get_config('system','url'));
 
diff --git a/include/dsprphotoq.php b/include/dsprphotoq.php
deleted file mode 100644 (file)
index 0d8088d..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-require_once("boot.php");
-require_once('include/diaspora.php');
-
-function dsprphotoq_run($argv, $argc){
-       global $a, $db;
-
-       if(is_null($a)){
-               $a = new App;
-       }
-
-       if(is_null($db)){
-               @include(".htconfig.php");
-               require_once("include/dba.php");
-               $db = new dba($db_host, $db_user, $db_pass, $db_data);
-               unset($db_host, $db_user, $db_pass, $db_data);
-       };
-
-       logger("diaspora photo queue: running", LOGGER_DEBUG);
-
-       $r = q("SELECT * FROM dsprphotoq");
-       if(!$r)
-               return;
-
-       $dphotos = $r;
-
-       logger("diaspora photo queue: processing " . count($dphotos) . " photos");
-
-       foreach($dphotos as $dphoto) {
-
-               $r = array();
-
-               if ($dphoto['uid'] == 0)
-                       $r[0] = array("uid" => 0, "page-flags" => PAGE_FREELOVE);
-               else
-                       $r = q("SELECT * FROM user WHERE uid = %d",
-                               intval($dphoto['uid']));
-
-               if(!$r) {
-                       logger("diaspora photo queue: user " . $dphoto['uid'] . " not found");
-                       return;
-               }
-
-               $ret = diaspora_dispatch($r[0],unserialize($dphoto['msg']),$dphoto['attempt']);
-               q("DELETE FROM dsprphotoq WHERE id = %d",
-                  intval($dphoto['id'])
-               );
-       }
-}
-
-
-if (array_search(__file__,get_included_files())===0){
-  dsprphotoq_run($_SERVER["argv"],$_SERVER["argc"]);
-  killme();
-}
index 13c414c9e30f3907b875f429d5fe162efc7f629c..a9f054fc2ed2bf530e9c6c0d9fbadf7570313c6d 100644 (file)
@@ -76,7 +76,6 @@ function format_event_html($ev, $simple = false) {
 function parse_event($h) {
 
        require_once('include/Scrape.php');
-       require_once('library/HTMLPurifier.auto.php');
        require_once('include/html2bbcode');
 
        $h = '<html><body>' . $h . '</body></html>';
index eb91f7efd465b76dee003bdbe1fbb00f9b4587db..293de3cc965ed6b892599db7edd17c5aad6e12d4 100644 (file)
@@ -2,7 +2,18 @@
 require_once("include/html2bbcode.php");
 require_once("include/items.php");
 
-function feed_import($xml,$importer,&$contact, &$hub) {
+/**
+ * @brief Read a RSS/RDF/Atom feed and create an item entry for it
+ *
+ * @param string $xml The feed data
+ * @param array $importer The user record of the importer
+ * @param array $contact The contact record of the feed
+ * @param string $hub Unused dummy value for compatibility reasons
+ * @param bool $simulate If enabled, no data is imported
+ *
+ * @return array In simulation mode it returns the header and the first item
+ */
+function feed_import($xml,$importer,&$contact, &$hub, $simulate = false) {
 
        $a = get_app();
 
@@ -14,18 +25,19 @@ function feed_import($xml,$importer,&$contact, &$hub) {
        $doc = new DOMDocument();
        @$doc->loadXML($xml);
        $xpath = new DomXPath($doc);
-       $xpath->registerNamespace('atom', "http://www.w3.org/2005/Atom");
+       $xpath->registerNamespace('atom', NAMESPACE_ATOM1);
        $xpath->registerNamespace('dc', "http://purl.org/dc/elements/1.1/");
        $xpath->registerNamespace('content', "http://purl.org/rss/1.0/modules/content/");
        $xpath->registerNamespace('rdf', "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
        $xpath->registerNamespace('rss', "http://purl.org/rss/1.0/");
        $xpath->registerNamespace('media', "http://search.yahoo.com/mrss/");
+       $xpath->registerNamespace('poco', NAMESPACE_POCO);
 
        $author = array();
 
        // Is it RDF?
        if ($xpath->query('/rdf:RDF/rss:channel')->length > 0) {
-               //$author["author-link"] = $xpath->evaluate('/rdf:RDF/rss:channel/rss:link/text()')->item(0)->nodeValue;
+               $author["author-link"] = $xpath->evaluate('/rdf:RDF/rss:channel/rss:link/text()')->item(0)->nodeValue;
                $author["author-name"] = $xpath->evaluate('/rdf:RDF/rss:channel/rss:title/text()')->item(0)->nodeValue;
 
                if ($author["author-name"] == "")
@@ -36,19 +48,29 @@ function feed_import($xml,$importer,&$contact, &$hub) {
 
        // Is it Atom?
        if ($xpath->query('/atom:feed/atom:entry')->length > 0) {
-               //$self = $xpath->query("/atom:feed/atom:link[@rel='self']")->item(0)->attributes;
-               //if (is_object($self))
-               //      foreach($self AS $attributes)
-               //              if ($attributes->name == "href")
-               //                      $author["author-link"] = $attributes->textContent;
-
-               //if ($author["author-link"] == "") {
-               //      $alternate = $xpath->query("/atom:feed/atom:link[@rel='alternate']")->item(0)->attributes;
-               //      if (is_object($alternate))
-               //              foreach($alternate AS $attributes)
-               //                      if ($attributes->name == "href")
-               //                              $author["author-link"] = $attributes->textContent;
-               //}
+               $alternate = $xpath->query("atom:link[@rel='alternate']")->item(0)->attributes;
+               if (is_object($alternate))
+                       foreach($alternate AS $attributes)
+                               if ($attributes->name == "href")
+                                       $author["author-link"] = $attributes->textContent;
+
+               $author["author-id"] = $xpath->evaluate('/atom:feed/atom:author/atom:uri/text()')->item(0)->nodeValue;
+
+               if ($author["author-link"] == "")
+                       $author["author-link"] = $author["author-id"];
+
+               if ($author["author-link"] == "") {
+                       $self = $xpath->query("atom:link[@rel='self']")->item(0)->attributes;
+                       if (is_object($self))
+                               foreach($self AS $attributes)
+                                       if ($attributes->name == "href")
+                                               $author["author-link"] = $attributes->textContent;
+               }
+
+               if ($author["author-link"] == "")
+                       $author["author-link"] = $xpath->evaluate('/atom:feed/atom:id/text()')->item(0)->nodeValue;
+
+               $author["author-avatar"] = $xpath->evaluate('/atom:feed/atom:logo/text()')->item(0)->nodeValue;
 
                $author["author-name"] = $xpath->evaluate('/atom:feed/atom:title/text()')->item(0)->nodeValue;
 
@@ -58,7 +80,13 @@ function feed_import($xml,$importer,&$contact, &$hub) {
                if ($author["author-name"] == "")
                        $author["author-name"] = $xpath->evaluate('/atom:feed/atom:author/atom:name/text()')->item(0)->nodeValue;
 
-               //$author["author-avatar"] = $xpath->evaluate('/atom:feed/atom:logo/text()')->item(0)->nodeValue;
+               $value = $xpath->evaluate('atom:author/poco:displayName/text()')->item(0)->nodeValue;
+               if ($value != "")
+                       $author["author-name"] = $value;
+
+               $value = $xpath->evaluate('atom:author/poco:preferredUsername/text()')->item(0)->nodeValue;
+               if ($value != "")
+                       $author["author-nick"] = $value;
 
                $author["edited"] = $author["created"] = $xpath->query('/atom:feed/atom:updated/text()')->item(0)->nodeValue;
 
@@ -69,9 +97,10 @@ function feed_import($xml,$importer,&$contact, &$hub) {
 
        // Is it RSS?
        if ($xpath->query('/rss/channel')->length > 0) {
-               //$author["author-link"] = $xpath->evaluate('/rss/channel/link/text()')->item(0)->nodeValue;
+               $author["author-link"] = $xpath->evaluate('/rss/channel/link/text()')->item(0)->nodeValue;
+
                $author["author-name"] = $xpath->evaluate('/rss/channel/title/text()')->item(0)->nodeValue;
-               //$author["author-avatar"] = $xpath->evaluate('/rss/channel/image/url/text()')->item(0)->nodeValue;
+               $author["author-avatar"] = $xpath->evaluate('/rss/channel/image/url/text()')->item(0)->nodeValue;
 
                if ($author["author-name"] == "")
                        $author["author-name"] = $xpath->evaluate('/rss/channel/copyright/text()')->item(0)->nodeValue;
@@ -86,18 +115,22 @@ function feed_import($xml,$importer,&$contact, &$hub) {
                $entries = $xpath->query('/rss/channel/item');
        }
 
-       //if ($author["author-link"] == "")
+       if (!$simulate) {
                $author["author-link"] = $contact["url"];
 
-       if ($author["author-name"] == "")
-               $author["author-name"] = $contact["name"];
+               if ($author["author-name"] == "")
+                       $author["author-name"] = $contact["name"];
 
-       //if ($author["author-avatar"] == "")
                $author["author-avatar"] = $contact["thumb"];
 
-       $author["owner-link"] = $contact["url"];
-       $author["owner-name"] = $contact["name"];
-       $author["owner-avatar"] = $contact["thumb"];
+               $author["owner-link"] = $contact["url"];
+               $author["owner-name"] = $contact["name"];
+               $author["owner-avatar"] = $contact["thumb"];
+
+               // This is no field in the item table. So we have to unset it.
+               unset($author["author-nick"]);
+               unset($author["author-id"]);
+       }
 
        $header = array();
        $header["uid"] = $importer["uid"];
@@ -120,6 +153,8 @@ function feed_import($xml,$importer,&$contact, &$hub) {
        if (!is_object($entries))
                return;
 
+       $items = array();
+
        $entrylist = array();
 
        foreach ($entries AS $entry)
@@ -201,13 +236,13 @@ function feed_import($xml,$importer,&$contact, &$hub) {
                if ($creator != "")
                        $item["author-name"] = $creator;
 
-               //$item["object"] = $xml;
-
-               $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s', '%s')",
-                       intval($importer["uid"]), dbesc($item["uri"]), dbesc(NETWORK_FEED), dbesc(NETWORK_DFRN));
-               if ($r) {
-                       logger("Item with uri ".$item["uri"]." for user ".$importer["uid"]." already existed under id ".$r[0]["id"], LOGGER_DEBUG);
-                       continue;
+               if (!$simulate) {
+                       $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s', '%s')",
+                               intval($importer["uid"]), dbesc($item["uri"]), dbesc(NETWORK_FEED), dbesc(NETWORK_DFRN));
+                       if ($r) {
+                               logger("Item with uri ".$item["uri"]." for user ".$importer["uid"]." already existed under id ".$r[0]["id"], LOGGER_DEBUG);
+                               continue;
+                       }
                }
 
                /// @TODO ?
@@ -272,14 +307,21 @@ function feed_import($xml,$importer,&$contact, &$hub) {
                        $item["body"] = html2bbcode($body);
                }
 
-               logger("Stored feed: ".print_r($item, true), LOGGER_DEBUG);
+               if (!$simulate) {
+                       logger("Stored feed: ".print_r($item, true), LOGGER_DEBUG);
 
-               $notify = item_is_remote_self($contact, $item);
-               $id = item_store($item, false, $notify);
+                       $notify = item_is_remote_self($contact, $item);
+                       $id = item_store($item, false, $notify);
 
-               //print_r($item);
+                       logger("Feed for contact ".$contact["url"]." stored under id ".$id);
+               } else
+                       $items[] = $item;
 
-               logger("Feed for contact ".$contact["url"]." stored under id ".$id);
+               if ($simulate)
+                       break;
        }
+
+       if ($simulate)
+               return array("header" => $author, "items" => $items);
 }
 ?>
index 22ff079b633949b941201d1578dd47e770b76670..d0411a466af0411045ed9455f2c25019a67a3c00 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 require_once("include/Scrape.php");
+require_once("include/socgraph.php");
 
 function update_contact($id) {
        /*
@@ -43,6 +44,9 @@ function update_contact($id) {
                intval($id)
        );
 
+       // Update the corresponding gcontact entry
+       poco_last_updated($ret["url"]);
+
        return true;
 }
 
@@ -254,12 +258,10 @@ function new_contact($uid,$url,$interactive = false) {
        $contact_id  = $r[0]['id'];
        $result['cid'] = $contact_id;
 
-       $g = q("select def_gid from user where uid = %d limit 1",
-               intval($uid)
-       );
-       if($g && intval($g[0]['def_gid'])) {
+       $def_gid = get_default_group($uid, $contact["network"]);
+       if (intval($def_gid)) {
                require_once('include/group.php');
-               group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
+               group_add_member($uid, '', $contact_id, $def_gid);
        }
 
        require_once("include/Photo.php");
@@ -301,8 +303,8 @@ function new_contact($uid,$url,$interactive = false) {
                }
                if($contact['network'] == NETWORK_DIASPORA) {
                        require_once('include/diaspora.php');
-                       $ret = diaspora_share($a->user,$contact);
-                       logger('mod_follow: diaspora_share returns: ' . $ret);
+                       $ret = diaspora::send_share($a->user,$contact);
+                       logger('share returns: '.$ret);
                }
        }
 
index a1375e00dfa678f18368a407583d9d96e0625916..00b66ad58628f0db729447ed57987440b4a19e2f 100644 (file)
@@ -188,7 +188,7 @@ function group_public_members($gid) {
 }
 
 
-function mini_group_select($uid,$gid = 0) {
+function mini_group_select($uid,$gid = 0, $label = "") {
 
        $grps = array();
        $o = '';
@@ -205,8 +205,11 @@ function mini_group_select($uid,$gid = 0) {
        }
        logger('groups: ' . print_r($grps,true));
 
+       if ($label == "")
+               $label = t('Default privacy group for new contacts');
+
        $o = replace_macros(get_markup_template('group_selection.tpl'), array(
-               '$label' => t('Default privacy group for new contacts'),
+               '$label' => $label,
                '$groups' => $grps
        ));
        return $o;
@@ -215,7 +218,7 @@ function mini_group_select($uid,$gid = 0) {
 
 /**
  * @brief Create group sidebar widget
- * 
+ *
  * @param string $every
  * @param string $each
  * @param string $editmode
@@ -234,7 +237,7 @@ function group_side($every="contacts",$each="group",$editmode = "standard", $gro
                return '';
 
        $groups = array();
-       
+
        $groups[] = array(
                'text'  => t('Everybody'),
                'id' => 0,
@@ -255,7 +258,7 @@ function group_side($every="contacts",$each="group",$editmode = "standard", $gro
        if(count($r)) {
                foreach($r as $rr) {
                        $selected = (($group_id == $rr['id']) ? ' group-selected' : '');
-                       
+
                        if ($editmode == "full") {
                                $groupedit = array(
                                        'href' => "group/".$rr['id'],
@@ -264,7 +267,7 @@ function group_side($every="contacts",$each="group",$editmode = "standard", $gro
                        } else {
                                $groupedit = null;
                        }
-                       
+
                        $groups[] = array(
                                'id'            => $rr['id'],
                                'cid'           => $cid,
@@ -362,17 +365,41 @@ function groups_containing($uid,$c) {
  */
 function groups_count_unseen() {
 
-       $r = q("SELECT `group`.`id`, `group`.`name`, COUNT(`item`.`id`) AS `count` FROM `group`, `group_member`, `item`
-                       WHERE `group`.`uid` = %d
-                       AND `item`.`uid` = %d
-                       AND `item`.`unseen` AND `item`.`visible`
-                       AND NOT `item`.`deleted`
-                       AND `item`.`contact-id` = `group_member`.`contact-id`
-                       AND `group_member`.`gid` = `group`.`id`
-                       GROUP BY `group`.`id` ",
+       $r = q("SELECT `group`.`id`, `group`.`name`,
+                       (SELECT COUNT(*) FROM `item`
+                               WHERE `uid` = %d AND `unseen` AND
+                                       `contact-id` IN (SELECT `contact-id` FROM `group_member`
+                                                               WHERE `group_member`.`gid` = `group`.`id` AND `group_member`.`uid` = %d)) AS `count`
+                       FROM `group` WHERE `group`.`uid` = %d;",
+               intval(local_user()),
                intval(local_user()),
                intval(local_user())
        );
 
        return $r;
 }
+
+/**
+ * @brief Returns the default group for a given user and network
+ *
+ * @param int $uid User id
+ * @param string $network network name
+ *
+ * @return int group id
+ */
+function get_default_group($uid, $network = "") {
+
+       $default_group = 0;
+
+       if ($network == NETWORK_OSTATUS)
+               $default_group = get_pconfig($uid, "ostatus", "default_group");
+
+       if ($default_group != 0)
+               return $default_group;
+
+       $g = q("SELECT `def_gid` FROM `user` WHERE `uid` = %d LIMIT 1", intval($uid));
+       if($g && intval($g[0]["def_gid"]))
+               $default_group = $g[0]["def_gid"];
+
+       return $default_group;
+}
index ec66225d0ffd8261e82cbf87def67c723276186f..888a09ee6ff88c945384a278a1a0c2b4af3f6dc8 100644 (file)
@@ -237,6 +237,7 @@ function profile_sidebar($profile, $block = 0) {
        if ($connect AND ($profile['network'] != NETWORK_DFRN) AND !isset($profile['remoteconnect']))
                $connect = false;
 
+       $remoteconnect = NULL;
        if (isset($profile['remoteconnect']))
                $remoteconnect = $profile['remoteconnect'];
 
@@ -292,9 +293,9 @@ function profile_sidebar($profile, $block = 0) {
        // check if profile is a forum
        if((intval($profile['page-flags']) == PAGE_COMMUNITY)
                        || (intval($profile['page-flags']) == PAGE_PRVGROUP)
-                       || (intval($profile['forum']))
-                       || (intval($profile['prv']))
-                       || (intval($profile['community'])))
+                       || (isset($profile['forum']) && intval($profile['forum']))
+                       || (isset($profile['prv']) && intval($profile['prv']))
+                       || (isset($profile['community']) && intval($profile['community'])))
                $account_type = t('Forum');
        else
                $account_type = "";
@@ -332,9 +333,9 @@ function profile_sidebar($profile, $block = 0) {
                'fullname' => $profile['name'],
                'firstname' => $firstname,
                'lastname' => $lastname,
-               'photo300' => $a->get_cached_avatar_image($a->get_baseurl() . '/photo/custom/300/' . $profile['uid'] . '.jpg'),
-               'photo100' => $a->get_cached_avatar_image($a->get_baseurl() . '/photo/custom/100/' . $profile['uid'] . '.jpg'),
-               'photo50' => $a->get_cached_avatar_image($a->get_baseurl() . '/photo/custom/50/'  . $profile['uid'] . '.jpg'),
+               'photo300' => $a->get_baseurl() . '/photo/custom/300/' . $profile['uid'] . '.jpg',
+               'photo100' => $a->get_baseurl() . '/photo/custom/100/' . $profile['uid'] . '.jpg',
+               'photo50' => $a->get_baseurl() . '/photo/custom/50/'  . $profile['uid'] . '.jpg',
        );
 
        if (!$block){
index 798ee569589efa966f3a27688f6b4bcf6a2fb134..4627b10ca29491955e1db5bef9c22c47901c728d 100644 (file)
@@ -291,16 +291,6 @@ function add_page_info_to_body($body, $texturl = false, $no_photos = false) {
        return $body;
 }
 
-function add_guid($item) {
-       $r = q("SELECT `guid` FROM `guid` WHERE `guid` = '%s' LIMIT 1", dbesc($item["guid"]));
-       if ($r)
-               return;
-
-       q("INSERT INTO `guid` (`guid`,`plink`,`uri`,`network`) VALUES ('%s','%s','%s','%s')",
-               dbesc($item["guid"]), dbesc($item["plink"]),
-               dbesc($item["uri"]), dbesc($item["network"]));
-}
-
 /**
  * Adds a "lang" specification in a "postopts" element of given $arr,
  * if possible and not already present.
@@ -393,9 +383,9 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
        // Converting the plink
        if ($arr['network'] == NETWORK_OSTATUS) {
                if (isset($arr['plink']))
-                       $arr['plink'] = ostatus_convert_href($arr['plink']);
+                       $arr['plink'] = ostatus::convert_href($arr['plink']);
                elseif (isset($arr['uri']))
-                       $arr['plink'] = ostatus_convert_href($arr['uri']);
+                       $arr['plink'] = ostatus::convert_href($arr['uri']);
        }
 
        if(x($arr, 'gravity'))
@@ -509,6 +499,10 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
        $arr['inform']        = ((x($arr,'inform'))        ? trim($arr['inform'])                : '');
        $arr['file']          = ((x($arr,'file'))          ? trim($arr['file'])                  : '');
 
+
+       if (($arr['author-link'] == "") AND ($arr['owner-link'] == ""))
+               logger("Both author-link and owner-link are empty. Called by: ".App::callstack(), LOGGER_DEBUG);
+
        if ($arr['plink'] == "") {
                $a = get_app();
                $arr['plink'] = $a->get_baseurl().'/display/'.urlencode($arr['guid']);
@@ -713,9 +707,9 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
        if ($arr["uid"] == 0) {
                $arr["global"] = true;
 
-               q("UPDATE `item` SET `global` = 1 WHERE `guid` = '%s'", dbesc($arr["guid"]));
+               q("UPDATE `item` SET `global` = 1 WHERE `uri` = '%s'", dbesc($arr["uri"]));
        }  else {
-               $isglobal = q("SELECT `global` FROM `item` WHERE `uid` = 0 AND `guid` = '%s'", dbesc($arr["guid"]));
+               $isglobal = q("SELECT `global` FROM `item` WHERE `uid` = 0 AND `uri` = '%s'", dbesc($arr["uri"]));
 
                $arr["global"] = (count($isglobal) > 0);
        }
@@ -768,9 +762,6 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
                return 0;
        } elseif(count($r)) {
 
-               // Store the guid and other relevant data
-               add_guid($arr);
-
                $current_post = $r[0]['id'];
                logger('item_store: created item ' . $current_post);
 
@@ -891,9 +882,6 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
                        logger('item_store: new item not found in DB, id ' . $current_post);
        }
 
-       // Add every contact of the post to the global contact table
-       poco_store($arr);
-
        create_tags_from_item($current_post);
        create_files_from_item($current_post);
 
@@ -1255,7 +1243,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0)
                        //$tempfile = tempnam(get_temppath(), "ostatus2");
                        //file_put_contents($tempfile, $xml);
                        logger("Consume OStatus messages ", LOGGER_DEBUG);
-                       ostatus_import($xml,$importer,$contact, $hub);
+                       ostatus::import($xml,$importer,$contact, $hub);
                }
                return;
        }
@@ -1992,9 +1980,6 @@ function drop_item($id,$interactive = true) {
                                        intval($r[0]['id'])
                                );
                        }
-
-                       // Add a relayable_retraction signature for Diaspora.
-                       store_diaspora_retract_sig($item, $a->user, $a->get_baseurl());
                }
 
                $drop_id = intval($item['id']);
@@ -2127,51 +2112,3 @@ function posted_date_widget($url,$uid,$wall) {
        ));
        return $o;
 }
-
-function store_diaspora_retract_sig($item, $user, $baseurl) {
-       // Note that we can't add a target_author_signature
-       // if the comment was deleted by a remote user. That should be ok, because if a remote user is deleting
-       // the comment, that means we're the home of the post, and Diaspora will only
-       // check the parent_author_signature of retractions that it doesn't have to relay further
-       //
-       // I don't think this function gets called for an "unlike," but I'll check anyway
-
-       $enabled = intval(get_config('system','diaspora_enabled'));
-       if(! $enabled) {
-               logger('drop_item: diaspora support disabled, not storing retraction signature', LOGGER_DEBUG);
-               return;
-       }
-
-       logger('drop_item: storing diaspora retraction signature');
-
-       $signed_text = $item['guid'] . ';' . ( ($item['verb'] === ACTIVITY_LIKE) ? 'Like' : 'Comment');
-
-       if(local_user() == $item['uid']) {
-
-               $handle = $user['nickname'] . '@' . substr($baseurl, strpos($baseurl,'://') + 3);
-               $authorsig = base64_encode(rsa_sign($signed_text,$user['prvkey'],'sha256'));
-       }
-       else {
-               $r = q("SELECT `nick`, `url` FROM `contact` WHERE `id` = '%d' LIMIT 1",
-                       $item['contact-id'] // If this function gets called, drop_item() has already checked remote_user() == $item['contact-id']
-               );
-               if(count($r)) {
-                       // The below handle only works for NETWORK_DFRN. I think that's ok, because this function
-                       // only handles DFRN deletes
-                       $handle_baseurl_start = strpos($r['url'],'://') + 3;
-                       $handle_baseurl_length = strpos($r['url'],'/profile') - $handle_baseurl_start;
-                       $handle = $r['nick'] . '@' . substr($r['url'], $handle_baseurl_start, $handle_baseurl_length);
-                       $authorsig = '';
-               }
-       }
-
-       if(isset($handle))
-               q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
-                       intval($item['id']),
-                       dbesc($signed_text),
-                       dbesc($authorsig),
-                       dbesc($handle)
-               );
-
-       return;
-}
index 646e0727be4ec63f09773ed6fddea299ed635db0..15633fc7671de1112414d54086c4f327b45855bb 100644 (file)
@@ -1,4 +1,5 @@
 <?php
+require_once("include/diaspora.php");
 
 /**
  * @brief add/remove activity to an item
@@ -151,9 +152,6 @@ function do_like($item_id, $verb) {
                        intval($like_item['id'])
                );
 
-               // Save the author information for the unlike in case we need to relay to Diaspora
-               store_diaspora_like_retract_sig($activity, $item, $like_item, $contact);
-
                $like_item_id = $like_item['id'];
                proc_run('php',"include/notifier.php","like","$like_item_id");
 
@@ -196,6 +194,7 @@ EOT;
 
        $arr = array();
 
+       $arr['guid'] = get_guid(32);
        $arr['uri'] = $uri;
        $arr['uid'] = $owner_uid;
        $arr['contact-id'] = $contact['id'];
@@ -240,7 +239,7 @@ EOT;
 
 
        // Save the author information for the like in case we need to relay to Diaspora
-       store_diaspora_like_sig($activity, $post_type, $contact, $post_id);
+       diaspora::store_like_signature($contact, $post_id);
 
        $arr['id'] = $post_id;
 
@@ -250,149 +249,3 @@ EOT;
 
        return true;
 }
-
-
-
-function store_diaspora_like_retract_sig($activity, $item, $like_item, $contact) {
-       // Note that we can only create a signature for a user of the local server. We don't have
-       // a key for remote users. That is ok, because if a remote user is "unlike"ing a post, it
-       // means we are the relay, and for relayable_retractions, Diaspora
-       // only checks the parent_author_signature if it doesn't have to relay further
-       //
-       // If $item['resource-id'] exists, it means the item is a photo. Diaspora doesn't support
-       // likes on photos, so don't bother.
-
-       $enabled = intval(get_config('system','diaspora_enabled'));
-       if(! $enabled) {
-               logger('mod_like: diaspora support disabled, not storing like retraction signature', LOGGER_DEBUG);
-               return;
-       }
-
-       logger('mod_like: storing diaspora like retraction signature');
-
-       if(($activity === ACTIVITY_LIKE) && (! $item['resource-id'])) {
-               $signed_text = $like_item['guid'] . ';' . 'Like';
-
-               // Only works for NETWORK_DFRN
-               $contact_baseurl_start = strpos($contact['url'],'://') + 3;
-               $contact_baseurl_length = strpos($contact['url'],'/profile') - $contact_baseurl_start;
-               $contact_baseurl = substr($contact['url'], $contact_baseurl_start, $contact_baseurl_length);
-               $diaspora_handle = $contact['nick'] . '@' . $contact_baseurl;
-
-               // This code could never had worked (the return values form the queries were used in a wrong way.
-               // Additionally it is needlessly complicated. Either the contact is owner or not. And we have this data already.
-/*
-               // Get contact's private key if he's a user of the local Friendica server
-               $r = q("SELECT `contact`.`uid` FROM `contact` WHERE `url` = '%s' AND `self` = 1 LIMIT 1",
-                       dbesc($contact['url'])
-               );
-
-               if( $r) {
-                       $contact_uid = $r['uid'];
-                       $r = q("SELECT prvkey FROM user WHERE uid = %d LIMIT 1",
-                               intval($contact_uid)
-                       );
-*/
-               // Is the contact the owner? Then fetch the private key
-               if ($contact['self'] AND ($contact['uid'] > 0)) {
-                       $r = q("SELECT prvkey FROM user WHERE uid = %d LIMIT 1",
-                               intval($contact['uid'])
-                       );
-
-                       if($r)
-                               $authorsig = base64_encode(rsa_sign($signed_text,$r[0]['prvkey'],'sha256'));
-               }
-
-               if(! isset($authorsig))
-                       $authorsig = '';
-
-               q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
-                       intval($like_item['id']),
-                       dbesc($signed_text),
-                       dbesc($authorsig),
-                       dbesc($diaspora_handle)
-               );
-       }
-
-       return;
-}
-
-function store_diaspora_like_sig($activity, $post_type, $contact, $post_id) {
-       // Note that we can only create a signature for a user of the local server. We don't have
-       // a key for remote users. That is ok, because if a remote user is "unlike"ing a post, it
-       // means we are the relay, and for relayable_retractions, Diaspora
-       // only checks the parent_author_signature if it doesn't have to relay further
-
-       $enabled = intval(get_config('system','diaspora_enabled'));
-       if(! $enabled) {
-               logger('mod_like: diaspora support disabled, not storing like signature', LOGGER_DEBUG);
-               return;
-       }
-
-       logger('mod_like: storing diaspora like signature');
-
-       if(($activity === ACTIVITY_LIKE) && ($post_type === t('status'))) {
-               // Only works for NETWORK_DFRN
-               $contact_baseurl_start = strpos($contact['url'],'://') + 3;
-               $contact_baseurl_length = strpos($contact['url'],'/profile') - $contact_baseurl_start;
-               $contact_baseurl = substr($contact['url'], $contact_baseurl_start, $contact_baseurl_length);
-               $diaspora_handle = $contact['nick'] . '@' . $contact_baseurl;
-
-
-               // This code could never had worked (the return values form the queries were used in a wrong way.
-               // Additionally it is needlessly complicated. Either the contact is owner or not. And we have this data already.
-/*
-               // Get contact's private key if he's a user of the local Friendica server
-               $r = q("SELECT `contact`.`uid` FROM `contact` WHERE `url` = '%s' AND `self` = 1 LIMIT 1",
-                       dbesc($contact['url'])
-               );
-
-               if( $r) {
-                       $contact_uid = $r['uid'];
-                       $r = q("SELECT prvkey FROM user WHERE uid = %d LIMIT 1",
-                               intval($contact_uid)
-                       );
-
-                       if( $r)
-                               $contact_uprvkey = $r['prvkey'];
-               }
-*/
-
-               // Is the contact the owner? Then fetch the private key
-               if ($contact['self'] AND ($contact['uid'] > 0)) {
-                       $r = q("SELECT prvkey FROM user WHERE uid = %d LIMIT 1",
-                               intval($contact['uid'])
-                       );
-
-                       if($r)
-                               $contact_uprvkey = $r[0]['prvkey'];
-               }
-
-               $r = q("SELECT guid, parent FROM `item` WHERE id = %d LIMIT 1",
-                       intval($post_id)
-               );
-               if( $r) {
-                       $p = q("SELECT guid FROM `item` WHERE id = %d AND parent = %d LIMIT 1",
-                               intval($r[0]['parent']),
-                               intval($r[0]['parent'])
-                       );
-                       if( $p) {
-                               $signed_text = 'true;'.$r[0]['guid'].';Post;'.$p[0]['guid'].';'.$diaspora_handle;
-
-                               if(isset($contact_uprvkey))
-                                       $authorsig = base64_encode(rsa_sign($signed_text,$contact_uprvkey,'sha256'));
-                               else
-                                       $authorsig = '';
-
-                               q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
-                                       intval($post_id),
-                                       dbesc($signed_text),
-                                       dbesc($authorsig),
-                                       dbesc($diaspora_handle)
-                               );
-                       }
-               }
-       }
-
-       return;
-}
index 6512d35609f25e031674a1a673434b748211cb23..0fa671a27d5118a384aebbaf69945dd265f8772b 100644 (file)
@@ -82,7 +82,7 @@ function nav_info(&$a) {
                // user info
                $r = q("SELECT micro FROM contact WHERE uid=%d AND self=1", intval($a->user['uid']));
                $userinfo = array(
-                       'icon' => (count($r) ? $a->get_cached_avatar_image($r[0]['micro']) : $a->get_baseurl($ssl_state)."/images/person-48.jpg"),
+                       'icon' => (count($r) ? $a->remove_baseurl($r[0]['micro']) : "images/person-48.jpg"),
                        'name' => $a->user['username'],
                );
 
@@ -107,7 +107,7 @@ function nav_info(&$a) {
        if(($a->config['register_policy'] == REGISTER_OPEN) && (! local_user()) && (! remote_user()))
                $nav['register'] = array('register',t('Register'), "", t('Create an account'));
 
-       $help_url = $a->get_baseurl($ssl_state) . '/help';
+       $help_url = 'help';
 
        if(! get_config('system','hide_help'))
                $nav['help'] = array($help_url, t('Help'), "", t('Help and documentation'));
index c6379e407bcc79aae4a3ed852c3409d9225a1f09..27459112d6a9cf2476d7c51c4e7ba537bc9c7623 100644 (file)
@@ -862,64 +862,6 @@ function parse_xml_string($s,$strict = true) {
        return $x;
 }}
 
-function add_fcontact($arr,$update = false) {
-
-       if($update) {
-               $r = q("UPDATE `fcontact` SET
-                       `name` = '%s',
-                       `photo` = '%s',
-                       `request` = '%s',
-                       `nick` = '%s',
-                       `addr` = '%s',
-                       `batch` = '%s',
-                       `notify` = '%s',
-                       `poll` = '%s',
-                       `confirm` = '%s',
-                       `alias` = '%s',
-                       `pubkey` = '%s',
-                       `updated` = '%s'
-                       WHERE `url` = '%s' AND `network` = '%s'",
-                       dbesc($arr['name']),
-                       dbesc($arr['photo']),
-                       dbesc($arr['request']),
-                       dbesc($arr['nick']),
-                       dbesc($arr['addr']),
-                       dbesc($arr['batch']),
-                       dbesc($arr['notify']),
-                       dbesc($arr['poll']),
-                       dbesc($arr['confirm']),
-                       dbesc($arr['alias']),
-                       dbesc($arr['pubkey']),
-                       dbesc(datetime_convert()),
-                       dbesc($arr['url']),
-                       dbesc($arr['network'])
-               );
-       }
-       else {
-               $r = q("insert into fcontact ( `url`,`name`,`photo`,`request`,`nick`,`addr`,
-                       `batch`, `notify`,`poll`,`confirm`,`network`,`alias`,`pubkey`,`updated` )
-                       values('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')",
-                       dbesc($arr['url']),
-                       dbesc($arr['name']),
-                       dbesc($arr['photo']),
-                       dbesc($arr['request']),
-                       dbesc($arr['nick']),
-                       dbesc($arr['addr']),
-                       dbesc($arr['batch']),
-                       dbesc($arr['notify']),
-                       dbesc($arr['poll']),
-                       dbesc($arr['confirm']),
-                       dbesc($arr['network']),
-                       dbesc($arr['alias']),
-                       dbesc($arr['pubkey']),
-                       dbesc(datetime_convert())
-               );
-       }
-
-       return $r;
-}
-
-
 function scale_external_images($srctext, $include_link = true, $scale_replace = false) {
 
        // Suppress "view full size"
index 6c42f19c6aec66c22d0f2522b15f13a8382b7ea8..ffbb22e7bf10697a3baaf5d82c49011e9264a442 100644 (file)
@@ -223,13 +223,13 @@ function notifier_run(&$argv, &$argc){
 
        if(! ($mail || $fsuggest || $relocate)) {
 
-               $slap = ostatus_salmon($target_item,$owner);
+               $slap = ostatus::salmon($target_item,$owner);
 
                require_once('include/group.php');
 
                $parent = $items[0];
 
-               $thr_parent = q("SELECT `network` FROM `item` WHERE `uri` = '%s' AND `uid` = %d",
+               $thr_parent = q("SELECT `network`, `author-link`, `owner-link` FROM `item` WHERE `uri` = '%s' AND `uid` = %d",
                        dbesc($target_item["thr-parent"]), intval($target_item["uid"]));
 
                logger('Parent is '.$parent['network'].'. Thread parent is '.$thr_parent[0]['network'], LOGGER_DEBUG);
@@ -390,6 +390,20 @@ function notifier_run(&$argv, &$argc){
 
                        logger('Some parent is OStatus for '.$target_item["guid"], LOGGER_DEBUG);
 
+                       // Send a salmon to the parent author
+                       $probed_contact = probe_url($thr_parent[0]['author-link']);
+                       if ($probed_contact["notify"] != "") {
+                               logger('Notify parent author '.$probed_contact["url"].': '.$probed_contact["notify"]);
+                               $url_recipients[$probed_contact["notify"]] = $probed_contact["notify"];
+                       }
+
+                       // Send a salmon to the parent owner
+                       $probed_contact = probe_url($thr_parent[0]['owner-link']);
+                       if ($probed_contact["notify"] != "") {
+                               logger('Notify parent owner '.$probed_contact["url"].': '.$probed_contact["notify"]);
+                               $url_recipients[$probed_contact["notify"]] = $probed_contact["notify"];
+                       }
+
                        // Send a salmon notification to every person we mentioned in the post
                        $arr = explode(',',$target_item['tag']);
                        foreach($arr as $x) {
@@ -536,7 +550,7 @@ function notifier_run(&$argv, &$argc){
        if($public_message) {
 
                if (!$followup AND $top_level)
-                       $r0 = diaspora_fetch_relay();
+                       $r0 = diaspora::relay_list();
                else
                        $r0 = array();
 
@@ -628,13 +642,6 @@ function notifier_run(&$argv, &$argc){
                proc_run('php','include/pubsubpublish.php');
        }
 
-       // If the item was deleted, clean up the `sign` table
-       if($target_item['deleted']) {
-               $r = q("DELETE FROM sign where `retract_iid` = %d",
-                       intval($target_item['id'])
-               );
-       }
-
        logger('notifier: calling hooks', LOGGER_DEBUG);
 
        if($normal_mode)
index 6fb191f73da534dff3230ac9692848a9dd897d68..eb1045de142957dcab4e08d8b66ff8aa3ecb4750 100644 (file)
@@ -31,7 +31,6 @@ function onepoll_run(&$argv, &$argc){
        require_once('include/Contact.php');
        require_once('include/email.php');
        require_once('include/socgraph.php');
-       require_once('include/pidfile.php');
        require_once('include/queue_fn.php');
 
        load_config('config');
@@ -60,18 +59,10 @@ function onepoll_run(&$argv, &$argc){
                return;
        }
 
-       $lockpath = get_lockpath();
-       if ($lockpath != '') {
-               $pidfile = new pidfile($lockpath, 'onepoll'.$contact_id);
-               if ($pidfile->is_already_running()) {
-                       logger("onepoll: Already running for contact ".$contact_id);
-                       if ($pidfile->running_time() > 9*60) {
-                               $pidfile->kill();
-                               logger("killed stale process");
-                       }
-                       exit;
-               }
-       }
+       // Don't check this stuff if the function is called by the poller
+       if (App::callstack() != "poller_run")
+               if (App::is_already_running('onepoll'.$contact_id, '', 540))
+                       return;
 
        $d = datetime_convert();
 
index 00022f8c6cdd3ddfccf500d73e936008ab51814d..b798a605f91eb9a609dee34335949eafaff5847b 100644 (file)
@@ -1,4 +1,8 @@
 <?php
+/**
+ * @file include/ostatus.php
+ */
+
 require_once("include/Contact.php");
 require_once("include/threads.php");
 require_once("include/html2bbcode.php");
@@ -12,486 +16,486 @@ require_once("include/Scrape.php");
 require_once("include/follow.php");
 require_once("include/api.php");
 require_once("mod/proxy.php");
+require_once("include/xml.php");
+
+/**
+ * @brief This class contain functions for the OStatus protocol
+ *
+ */
+class ostatus {
+       const OSTATUS_DEFAULT_POLL_INTERVAL = 30; // given in minutes
+       const OSTATUS_DEFAULT_POLL_TIMEFRAME = 1440; // given in minutes
+       const OSTATUS_DEFAULT_POLL_TIMEFRAME_MENTIONS = 14400; // given in minutes
+
+       /**
+        * @brief Fetches author data
+        *
+        * @param object $xpath The xpath object
+        * @param object $context The xml context of the author detals
+        * @param array $importer user record of the importing user
+        * @param array $contact Called by reference, will contain the fetched contact
+        * @param bool $onlyfetch Only fetch the header without updating the contact entries
+        *
+        * @return array Array of author related entries for the item
+        */
+       private function fetchauthor($xpath, $context, $importer, &$contact, $onlyfetch) {
+
+               $author = array();
+               $author["author-link"] = $xpath->evaluate('atom:author/atom:uri/text()', $context)->item(0)->nodeValue;
+               $author["author-name"] = $xpath->evaluate('atom:author/atom:name/text()', $context)->item(0)->nodeValue;
+
+               $aliaslink = $author["author-link"];
+
+               $alternate = $xpath->query("atom:author/atom:link[@rel='alternate']", $context)->item(0)->attributes;
+               if (is_object($alternate))
+                       foreach($alternate AS $attributes)
+                               if ($attributes->name == "href")
+                                       $author["author-link"] = $attributes->textContent;
 
-define('OSTATUS_DEFAULT_POLL_INTERVAL', 30); // given in minutes
-define('OSTATUS_DEFAULT_POLL_TIMEFRAME', 1440); // given in minutes
-define('OSTATUS_DEFAULT_POLL_TIMEFRAME_MENTIONS', 14400); // given in minutes
-
-function ostatus_check_follow_friends() {
-       $r = q("SELECT `uid`,`v` FROM `pconfig` WHERE `cat`='system' AND `k`='ostatus_legacy_contact' AND `v` != ''");
-
-       if (!$r)
-               return;
-
-       foreach ($r AS $contact) {
-               ostatus_follow_friends($contact["uid"], $contact["v"]);
-               set_pconfig($contact["uid"], "system", "ostatus_legacy_contact", "");
-       }
-}
-
-// This function doesn't work reliable by now.
-function ostatus_follow_friends($uid, $url) {
-       $contact = probe_url($url);
-
-       if (!$contact)
-               return;
-
-       $api = $contact["baseurl"]."/api/";
+               $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `nurl` IN ('%s', '%s') AND `network` != '%s'",
+                       intval($importer["uid"]), dbesc(normalise_link($author["author-link"])),
+                       dbesc(normalise_link($aliaslink)), dbesc(NETWORK_STATUSNET));
+               if ($r) {
+                       $contact = $r[0];
+                       $author["contact-id"] = $r[0]["id"];
+               } else
+                       $author["contact-id"] = $contact["id"];
 
-       // Fetching friends
-       $data = z_fetch_url($api."statuses/friends.json?screen_name=".$contact["nick"]);
+               $avatarlist = array();
+               $avatars = $xpath->query("atom:author/atom:link[@rel='avatar']", $context);
+               foreach($avatars AS $avatar) {
+                       $href = "";
+                       $width = 0;
+                       foreach($avatar->attributes AS $attributes) {
+                               if ($attributes->name == "href")
+                                       $href = $attributes->textContent;
+                               if ($attributes->name == "width")
+                                       $width = $attributes->textContent;
+                       }
+                       if (($width > 0) AND ($href != ""))
+                               $avatarlist[$width] = $href;
+               }
+               if (count($avatarlist) > 0) {
+                       krsort($avatarlist);
+                       $author["author-avatar"] = current($avatarlist);
+               }
 
-       if (!$data["success"])
-               return;
+               $displayname = $xpath->evaluate('atom:author/poco:displayName/text()', $context)->item(0)->nodeValue;
+               if ($displayname != "")
+                       $author["author-name"] = $displayname;
 
-       $friends = json_decode($data["body"]);
+               $author["owner-name"] = $author["author-name"];
+               $author["owner-link"] = $author["author-link"];
+               $author["owner-avatar"] = $author["author-avatar"];
 
-       foreach ($friends AS $friend) {
-               $url = $friend->statusnet_profile_url;
-               $r = q("SELECT `url` FROM `contact` WHERE `uid` = %d AND
-                       (`nurl` = '%s' OR `alias` = '%s' OR `alias` = '%s') AND
-                       `network` != '%s' LIMIT 1",
-                       intval($uid), dbesc(normalise_link($url)),
-                       dbesc(normalise_link($url)), dbesc($url), dbesc(NETWORK_STATUSNET));
-               if (!$r) {
-                       $data = probe_url($friend->statusnet_profile_url);
-                       if ($data["network"] == NETWORK_OSTATUS) {
-                               $result = new_contact($uid,$friend->statusnet_profile_url);
-                               if ($result["success"])
-                                       logger($friend->name." ".$url." - success", LOGGER_DEBUG);
-                               else
-                                       logger($friend->name." ".$url." - failed", LOGGER_DEBUG);
-                       } else
-                               logger($friend->name." ".$url." - not OStatus", LOGGER_DEBUG);
-               }
-       }
-}
+               // Only update the contacts if it is an OStatus contact
+               if ($r AND !$onlyfetch AND ($contact["network"] == NETWORK_OSTATUS)) {
 
-function ostatus_fetchauthor($xpath, $context, $importer, &$contact, $onlyfetch) {
-
-       $author = array();
-       $author["author-link"] = $xpath->evaluate('atom:author/atom:uri/text()', $context)->item(0)->nodeValue;
-       $author["author-name"] = $xpath->evaluate('atom:author/atom:name/text()', $context)->item(0)->nodeValue;
-
-       // Preserve the value
-       $authorlink = $author["author-link"];
-
-       $alternate = $xpath->query("atom:author/atom:link[@rel='alternate']", $context)->item(0)->attributes;
-       if (is_object($alternate))
-               foreach($alternate AS $attributes)
-                       if ($attributes->name == "href")
-                               $author["author-link"] = $attributes->textContent;
-
-       $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `nurl` IN ('%s', '%s') AND `network` != '%s'",
-               intval($importer["uid"]), dbesc(normalise_link($author["author-link"])),
-               dbesc(normalise_link($authorlink)), dbesc(NETWORK_STATUSNET));
-       if ($r) {
-               $contact = $r[0];
-               $author["contact-id"] = $r[0]["id"];
-       } else
-               $author["contact-id"] = $contact["id"];
-
-       $avatarlist = array();
-       $avatars = $xpath->query("atom:author/atom:link[@rel='avatar']", $context);
-       foreach($avatars AS $avatar) {
-               $href = "";
-               $width = 0;
-               foreach($avatar->attributes AS $attributes) {
-                       if ($attributes->name == "href")
-                               $href = $attributes->textContent;
-                       if ($attributes->name == "width")
-                               $width = $attributes->textContent;
-               }
-               if (($width > 0) AND ($href != ""))
-                       $avatarlist[$width] = $href;
-       }
-       if (count($avatarlist) > 0) {
-               krsort($avatarlist);
-               $author["author-avatar"] = current($avatarlist);
-       }
+                       // Update contact data
 
-       $displayname = $xpath->evaluate('atom:author/poco:displayName/text()', $context)->item(0)->nodeValue;
-       if ($displayname != "")
-               $author["author-name"] = $displayname;
+                       // This query doesn't seem to work
+                       // $value = $xpath->query("atom:link[@rel='salmon']", $context)->item(0)->nodeValue;
+                       // if ($value != "")
+                       //      $contact["notify"] = $value;
 
-       $author["owner-name"] = $author["author-name"];
-       $author["owner-link"] = $author["author-link"];
-       $author["owner-avatar"] = $author["author-avatar"];
+                       // This query doesn't seem to work as well - I hate these queries
+                       // $value = $xpath->query("atom:link[@rel='self' and @type='application/atom+xml']", $context)->item(0)->nodeValue;
+                       // if ($value != "")
+                       //      $contact["poll"] = $value;
 
-       // Only update the contacts if it is an OStatus contact
-       if ($r AND !$onlyfetch AND ($contact["network"] == NETWORK_OSTATUS)) {
-               // Update contact data
+                       $value = $xpath->evaluate('atom:author/atom:uri/text()', $context)->item(0)->nodeValue;
+                       if ($value != "")
+                               $contact["alias"] = $value;
 
-               $value = $xpath->query("atom:link[@rel='salmon']", $context)->item(0)->nodeValue;
-               if ($value != "")
-                       $contact["notify"] = $value;
+                       $value = $xpath->evaluate('atom:author/poco:displayName/text()', $context)->item(0)->nodeValue;
+                       if ($value != "")
+                               $contact["name"] = $value;
 
-               $value = $xpath->evaluate('atom:author/uri/text()', $context)->item(0)->nodeValue;
-               if ($value != "")
-                       $contact["alias"] = $value;
+                       $value = $xpath->evaluate('atom:author/poco:preferredUsername/text()', $context)->item(0)->nodeValue;
+                       if ($value != "")
+                               $contact["nick"] = $value;
 
-               $value = $xpath->evaluate('atom:author/poco:displayName/text()', $context)->item(0)->nodeValue;
-               if ($value != "")
-                       $contact["name"] = $value;
+                       $value = $xpath->evaluate('atom:author/poco:note/text()', $context)->item(0)->nodeValue;
+                       if ($value != "")
+                               $contact["about"] = html2bbcode($value);
 
-               $value = $xpath->evaluate('atom:author/poco:preferredUsername/text()', $context)->item(0)->nodeValue;
-               if ($value != "")
-                       $contact["nick"] = $value;
+                       $value = $xpath->evaluate('atom:author/poco:address/poco:formatted/text()', $context)->item(0)->nodeValue;
+                       if ($value != "")
+                               $contact["location"] = $value;
 
-               $value = $xpath->evaluate('atom:author/poco:note/text()', $context)->item(0)->nodeValue;
-               if ($value != "")
-                       $contact["about"] = html2bbcode($value);
+                       if (($contact["name"] != $r[0]["name"]) OR ($contact["nick"] != $r[0]["nick"]) OR ($contact["about"] != $r[0]["about"]) OR
+                               ($contact["alias"] != $r[0]["alias"]) OR ($contact["location"] != $r[0]["location"])) {
 
-               $value = $xpath->evaluate('atom:author/poco:address/poco:formatted/text()', $context)->item(0)->nodeValue;
-               if ($value != "")
-                       $contact["location"] = $value;
+                               logger("Update contact data for contact ".$contact["id"], LOGGER_DEBUG);
 
-               if (($contact["name"] != $r[0]["name"]) OR ($contact["nick"] != $r[0]["nick"]) OR ($contact["about"] != $r[0]["about"]) OR ($contact["location"] != $r[0]["location"])) {
+                               q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `alias` = '%s', `about` = '%s', `location` = '%s', `name-date` = '%s' WHERE `id` = %d",
+                                       dbesc($contact["name"]), dbesc($contact["nick"]), dbesc($contact["alias"]),
+                                       dbesc($contact["about"]), dbesc($contact["location"]),
+                                       dbesc(datetime_convert()), intval($contact["id"]));
 
-                       logger("Update contact data for contact ".$contact["id"], LOGGER_DEBUG);
+                               poco_check($contact["url"], $contact["name"], $contact["network"], $author["author-avatar"], $contact["about"], $contact["location"],
+                                                       "", "", "", datetime_convert(), 2, $contact["id"], $contact["uid"]);
+                       }
 
-                       q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `about` = '%s', `location` = '%s', `name-date` = '%s' WHERE `id` = %d",
-                               dbesc($contact["name"]), dbesc($contact["nick"]), dbesc($contact["about"]), dbesc($contact["location"]),
-                               dbesc(datetime_convert()), intval($contact["id"]));
+                       if (isset($author["author-avatar"]) AND ($author["author-avatar"] != $r[0]['avatar'])) {
+                               logger("Update profile picture for contact ".$contact["id"], LOGGER_DEBUG);
 
-                       poco_check($contact["url"], $contact["name"], $contact["network"], $author["author-avatar"], $contact["about"], $contact["location"],
-                                               "", "", "", datetime_convert(), 2, $contact["id"], $contact["uid"]);
-               }
+                               update_contact_avatar($author["author-avatar"], $importer["uid"], $contact["id"]);
+                       }
 
-               if (isset($author["author-avatar"]) AND ($author["author-avatar"] != $r[0]['avatar'])) {
-                       logger("Update profile picture for contact ".$contact["id"], LOGGER_DEBUG);
+                       // Ensure that we are having this contact (with uid=0)
+                       $cid = get_contact($author["author-link"], 0);
+
+                       if ($cid) {
+                               // Update it with the current values
+                               q("UPDATE `contact` SET `url` = '%s', `name` = '%s', `nick` = '%s', `alias` = '%s',
+                                               `about` = '%s', `location` = '%s',
+                                               `success_update` = '%s', `last-update` = '%s'
+                                       WHERE `id` = %d",
+                                       dbesc($author["author-link"]), dbesc($contact["name"]), dbesc($contact["nick"]),
+                                       dbesc($contact["alias"]), dbesc($contact["about"]), dbesc($contact["location"]),
+                                       dbesc(datetime_convert()), dbesc(datetime_convert()), intval($cid));
+
+                               // Update the avatar
+                               update_contact_avatar($author["author-avatar"], 0, $cid);
+                       }
 
-                       update_contact_avatar($author["author-avatar"], $importer["uid"], $contact["id"]);
+                       $contact["generation"] = 2;
+                       $contact["photo"] = $author["author-avatar"];
+                       update_gcontact($contact);
                }
 
-
-               /// @todo Add the "addr" field
-               $contact["generation"] = 2;
-               $contact["photo"] = $author["author-avatar"];
-               update_gcontact($contact);
+               return($author);
        }
 
-       return($author);
-}
-
-function ostatus_salmon_author($xml, $importer) {
-       $a = get_app();
-
-       if ($xml == "")
-               return;
+       /**
+        * @brief Fetches author data from a given XML string
+        *
+        * @param string $xml The XML
+        * @param array $importer user record of the importing user
+        *
+        * @return array Array of author related entries for the item
+        */
+       public static function salmon_author($xml, $importer) {
+
+               if ($xml == "")
+                       return;
 
-       $doc = new DOMDocument();
-       @$doc->loadXML($xml);
+               $doc = new DOMDocument();
+               @$doc->loadXML($xml);
 
-       $xpath = new DomXPath($doc);
-       $xpath->registerNamespace('atom', NAMESPACE_ATOM1);
-       $xpath->registerNamespace('thr', NAMESPACE_THREAD);
-       $xpath->registerNamespace('georss', NAMESPACE_GEORSS);
-       $xpath->registerNamespace('activity', NAMESPACE_ACTIVITY);
-       $xpath->registerNamespace('media', NAMESPACE_MEDIA);
-       $xpath->registerNamespace('poco', NAMESPACE_POCO);
-       $xpath->registerNamespace('ostatus', NAMESPACE_OSTATUS);
-       $xpath->registerNamespace('statusnet', NAMESPACE_STATUSNET);
+               $xpath = new DomXPath($doc);
+               $xpath->registerNamespace('atom', NAMESPACE_ATOM1);
+               $xpath->registerNamespace('thr', NAMESPACE_THREAD);
+               $xpath->registerNamespace('georss', NAMESPACE_GEORSS);
+               $xpath->registerNamespace('activity', NAMESPACE_ACTIVITY);
+               $xpath->registerNamespace('media', NAMESPACE_MEDIA);
+               $xpath->registerNamespace('poco', NAMESPACE_POCO);
+               $xpath->registerNamespace('ostatus', NAMESPACE_OSTATUS);
+               $xpath->registerNamespace('statusnet', NAMESPACE_STATUSNET);
 
-       $entries = $xpath->query('/atom:entry');
+               $entries = $xpath->query('/atom:entry');
 
-       foreach ($entries AS $entry) {
-               // fetch the author
-               $author = ostatus_fetchauthor($xpath, $entry, $importer, $contact, true);
-               return $author;
+               foreach ($entries AS $entry) {
+                       // fetch the author
+                       $author = self::fetchauthor($xpath, $entry, $importer, $contact, true);
+                       return $author;
+               }
        }
-}
-
-function ostatus_import($xml,$importer,&$contact, &$hub) {
 
-       $a = get_app();
+       /**
+        * @brief Imports an XML string containing OStatus elements
+        *
+        * @param string $xml The XML
+        * @param array $importer user record of the importing user
+        * @param $contact
+        * @param array $hub Called by reference, returns the fetched hub data
+        */
+       public static function import($xml,$importer,&$contact, &$hub) {
+               /// @todo this function is too long. It has to be split in many parts
 
-       logger("Import OStatus message", LOGGER_DEBUG);
+               logger("Import OStatus message", LOGGER_DEBUG);
 
-       if ($xml == "")
-               return;
+               if ($xml == "")
+                       return;
 
-       $doc = new DOMDocument();
-       @$doc->loadXML($xml);
+               //$tempfile = tempnam(get_temppath(), "import");
+               //file_put_contents($tempfile, $xml);
+
+               $doc = new DOMDocument();
+               @$doc->loadXML($xml);
+
+               $xpath = new DomXPath($doc);
+               $xpath->registerNamespace('atom', NAMESPACE_ATOM1);
+               $xpath->registerNamespace('thr', NAMESPACE_THREAD);
+               $xpath->registerNamespace('georss', NAMESPACE_GEORSS);
+               $xpath->registerNamespace('activity', NAMESPACE_ACTIVITY);
+               $xpath->registerNamespace('media', NAMESPACE_MEDIA);
+               $xpath->registerNamespace('poco', NAMESPACE_POCO);
+               $xpath->registerNamespace('ostatus', NAMESPACE_OSTATUS);
+               $xpath->registerNamespace('statusnet', NAMESPACE_STATUSNET);
+
+               $gub = "";
+               $hub_attributes = $xpath->query("/atom:feed/atom:link[@rel='hub']")->item(0)->attributes;
+               if (is_object($hub_attributes))
+                       foreach($hub_attributes AS $hub_attribute)
+                               if ($hub_attribute->name == "href") {
+                                       $hub = $hub_attribute->textContent;
+                                       logger("Found hub ".$hub, LOGGER_DEBUG);
+                               }
 
-       $xpath = new DomXPath($doc);
-       $xpath->registerNamespace('atom', NAMESPACE_ATOM1);
-       $xpath->registerNamespace('thr', NAMESPACE_THREAD);
-       $xpath->registerNamespace('georss', NAMESPACE_GEORSS);
-       $xpath->registerNamespace('activity', NAMESPACE_ACTIVITY);
-       $xpath->registerNamespace('media', NAMESPACE_MEDIA);
-       $xpath->registerNamespace('poco', NAMESPACE_POCO);
-       $xpath->registerNamespace('ostatus', NAMESPACE_OSTATUS);
-       $xpath->registerNamespace('statusnet', NAMESPACE_STATUSNET);
+               $header = array();
+               $header["uid"] = $importer["uid"];
+               $header["network"] = NETWORK_OSTATUS;
+               $header["type"] = "remote";
+               $header["wall"] = 0;
+               $header["origin"] = 0;
+               $header["gravity"] = GRAVITY_PARENT;
 
-       $gub = "";
-       $hub_attributes = $xpath->query("/atom:feed/atom:link[@rel='hub']")->item(0)->attributes;
-       if (is_object($hub_attributes))
-               foreach($hub_attributes AS $hub_attribute)
-                       if ($hub_attribute->name == "href") {
-                               $hub = $hub_attribute->textContent;
-                               logger("Found hub ".$hub, LOGGER_DEBUG);
-                       }
+               // it could either be a received post or a post we fetched by ourselves
+               // depending on that, the first node is different
+               $first_child = $doc->firstChild->tagName;
 
-       $header = array();
-       $header["uid"] = $importer["uid"];
-       $header["network"] = NETWORK_OSTATUS;
-       $header["type"] = "remote";
-       $header["wall"] = 0;
-       $header["origin"] = 0;
-       $header["gravity"] = GRAVITY_PARENT;
-
-       // it could either be a received post or a post we fetched by ourselves
-       // depending on that, the first node is different
-       $first_child = $doc->firstChild->tagName;
-
-       if ($first_child == "feed")
-               $entries = $xpath->query('/atom:feed/atom:entry');
-       else
-               $entries = $xpath->query('/atom:entry');
+               if ($first_child == "feed")
+                       $entries = $xpath->query('/atom:feed/atom:entry');
+               else
+                       $entries = $xpath->query('/atom:entry');
 
-       $conversation = "";
-       $conversationlist = array();
-       $item_id = 0;
+               $conversation = "";
+               $conversationlist = array();
+               $item_id = 0;
 
-       // Reverse the order of the entries
-       $entrylist = array();
+               // Reverse the order of the entries
+               $entrylist = array();
 
-       foreach ($entries AS $entry)
-               $entrylist[] = $entry;
+               foreach ($entries AS $entry)
+                       $entrylist[] = $entry;
 
-       foreach (array_reverse($entrylist) AS $entry) {
+               foreach (array_reverse($entrylist) AS $entry) {
 
-               $mention = false;
+                       $mention = false;
 
-               // fetch the author
-               if ($first_child == "feed")
-                       $author = ostatus_fetchauthor($xpath, $doc->firstChild, $importer, $contact, false);
-               else
-                       $author = ostatus_fetchauthor($xpath, $entry, $importer, $contact, false);
+                       // fetch the author
+                       if ($first_child == "feed")
+                               $author = self::fetchauthor($xpath, $doc->firstChild, $importer, $contact, false);
+                       else
+                               $author = self::fetchauthor($xpath, $entry, $importer, $contact, false);
 
-               $value = $xpath->evaluate('atom:author/poco:preferredUsername/text()', $context)->item(0)->nodeValue;
-               if ($value != "")
-                       $nickname = $value;
-               else
-                       $nickname = $author["author-name"];
+                       $value = $xpath->evaluate('atom:author/poco:preferredUsername/text()', $context)->item(0)->nodeValue;
+                       if ($value != "")
+                               $nickname = $value;
+                       else
+                               $nickname = $author["author-name"];
 
-               $item = array_merge($header, $author);
+                       $item = array_merge($header, $author);
 
-               // Now get the item
-               $item["uri"] = $xpath->query('atom:id/text()', $entry)->item(0)->nodeValue;
+                       // Now get the item
+                       $item["uri"] = $xpath->query('atom:id/text()', $entry)->item(0)->nodeValue;
 
-               $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'",
-                       intval($importer["uid"]), dbesc($item["uri"]));
-               if ($r) {
-                       logger("Item with uri ".$item["uri"]." for user ".$importer["uid"]." already existed under id ".$r[0]["id"], LOGGER_DEBUG);
-                       continue;
-               }
+                       $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'",
+                               intval($importer["uid"]), dbesc($item["uri"]));
+                       if ($r) {
+                               logger("Item with uri ".$item["uri"]." for user ".$importer["uid"]." already existed under id ".$r[0]["id"], LOGGER_DEBUG);
+                               continue;
+                       }
 
-               $item["body"] = add_page_info_to_body(html2bbcode($xpath->query('atom:content/text()', $entry)->item(0)->nodeValue));
-               $item["object-type"] = $xpath->query('activity:object-type/text()', $entry)->item(0)->nodeValue;
+                       $item["body"] = add_page_info_to_body(html2bbcode($xpath->query('atom:content/text()', $entry)->item(0)->nodeValue));
+                       $item["object-type"] = $xpath->query('activity:object-type/text()', $entry)->item(0)->nodeValue;
 
-               if (($item["object-type"] == ACTIVITY_OBJ_BOOKMARK) OR ($item["object-type"] == ACTIVITY_OBJ_EVENT)) {
-                       $item["title"] = $xpath->query('atom:title/text()', $entry)->item(0)->nodeValue;
-                       $item["body"] = $xpath->query('atom:summary/text()', $entry)->item(0)->nodeValue;
-               } elseif ($item["object-type"] == ACTIVITY_OBJ_QUESTION)
-                       $item["title"] = $xpath->query('atom:title/text()', $entry)->item(0)->nodeValue;
+                       if (($item["object-type"] == ACTIVITY_OBJ_BOOKMARK) OR ($item["object-type"] == ACTIVITY_OBJ_EVENT)) {
+                               $item["title"] = $xpath->query('atom:title/text()', $entry)->item(0)->nodeValue;
+                               $item["body"] = $xpath->query('atom:summary/text()', $entry)->item(0)->nodeValue;
+                       } elseif ($item["object-type"] == ACTIVITY_OBJ_QUESTION)
+                               $item["title"] = $xpath->query('atom:title/text()', $entry)->item(0)->nodeValue;
 
-               $item["object"] = $xml;
-               $item["verb"] = $xpath->query('activity:verb/text()', $entry)->item(0)->nodeValue;
+                       $item["object"] = $xml;
+                       $item["verb"] = $xpath->query('activity:verb/text()', $entry)->item(0)->nodeValue;
 
-               /// @TODO
-               /// Delete a message
-               if ($item["verb"] == "qvitter-delete-notice") {
-                       // ignore "Delete" messages (by now)
-                       logger("Ignore delete message ".print_r($item, true));
-                       continue;
-               }
+                       /// @TODO
+                       /// Delete a message
+                       if ($item["verb"] == "qvitter-delete-notice") {
+                               // ignore "Delete" messages (by now)
+                               logger("Ignore delete message ".print_r($item, true));
+                               continue;
+                       }
 
-               if ($item["verb"] == ACTIVITY_JOIN) {
-                       // ignore "Join" messages
-                       logger("Ignore join message ".print_r($item, true));
-                       continue;
-               }
+                       if ($item["verb"] == ACTIVITY_JOIN) {
+                               // ignore "Join" messages
+                               logger("Ignore join message ".print_r($item, true));
+                               continue;
+                       }
 
-               if ($item["verb"] == ACTIVITY_FOLLOW) {
-                       new_follower($importer, $contact, $item, $nickname);
-                       continue;
-               }
+                       if ($item["verb"] == ACTIVITY_FOLLOW) {
+                               new_follower($importer, $contact, $item, $nickname);
+                               continue;
+                       }
 
-               if ($item["verb"] == NAMESPACE_OSTATUS."/unfollow") {
-                       lose_follower($importer, $contact, $item, $dummy);
-                       continue;
-               }
+                       if ($item["verb"] == NAMESPACE_OSTATUS."/unfollow") {
+                               lose_follower($importer, $contact, $item, $dummy);
+                               continue;
+                       }
 
-               if ($item["verb"] == ACTIVITY_FAVORITE) {
-                       $orig_uri = $xpath->query("activity:object/atom:id", $entry)->item(0)->nodeValue;
-                       logger("Favorite ".$orig_uri." ".print_r($item, true));
+                       if ($item["verb"] == ACTIVITY_FAVORITE) {
+                               $orig_uri = $xpath->query("activity:object/atom:id", $entry)->item(0)->nodeValue;
+                               logger("Favorite ".$orig_uri." ".print_r($item, true));
 
-                       $item["verb"] = ACTIVITY_LIKE;
-                       $item["parent-uri"] = $orig_uri;
-                       $item["gravity"] = GRAVITY_LIKE;
-               }
+                               $item["verb"] = ACTIVITY_LIKE;
+                               $item["parent-uri"] = $orig_uri;
+                               $item["gravity"] = GRAVITY_LIKE;
+                       }
 
-               if ($item["verb"] == NAMESPACE_OSTATUS."/unfavorite") {
-                       // Ignore "Unfavorite" message
-                       logger("Ignore unfavorite message ".print_r($item, true));
-                       continue;
-               }
+                       if ($item["verb"] == NAMESPACE_OSTATUS."/unfavorite") {
+                               // Ignore "Unfavorite" message
+                               logger("Ignore unfavorite message ".print_r($item, true));
+                               continue;
+                       }
 
-               // http://activitystrea.ms/schema/1.0/rsvp-yes
-               if (!in_array($item["verb"], array(ACTIVITY_POST, ACTIVITY_LIKE, ACTIVITY_SHARE)))
-                       logger("Unhandled verb ".$item["verb"]." ".print_r($item, true));
+                       // http://activitystrea.ms/schema/1.0/rsvp-yes
+                       if (!in_array($item["verb"], array(ACTIVITY_POST, ACTIVITY_LIKE, ACTIVITY_SHARE)))
+                               logger("Unhandled verb ".$item["verb"]." ".print_r($item, true));
 
-               $item["created"] = $xpath->query('atom:published/text()', $entry)->item(0)->nodeValue;
-               $item["edited"] = $xpath->query('atom:updated/text()', $entry)->item(0)->nodeValue;
-               $conversation = $xpath->query('ostatus:conversation/text()', $entry)->item(0)->nodeValue;
+                       $item["created"] = $xpath->query('atom:published/text()', $entry)->item(0)->nodeValue;
+                       $item["edited"] = $xpath->query('atom:updated/text()', $entry)->item(0)->nodeValue;
+                       $conversation = $xpath->query('ostatus:conversation/text()', $entry)->item(0)->nodeValue;
 
-               $related = "";
+                       $related = "";
 
-               $inreplyto = $xpath->query('thr:in-reply-to', $entry);
-               if (is_object($inreplyto->item(0))) {
-                       foreach($inreplyto->item(0)->attributes AS $attributes) {
-                               if ($attributes->name == "ref")
-                                       $item["parent-uri"] = $attributes->textContent;
-                               if ($attributes->name == "href")
-                                       $related = $attributes->textContent;
+                       $inreplyto = $xpath->query('thr:in-reply-to', $entry);
+                       if (is_object($inreplyto->item(0))) {
+                               foreach($inreplyto->item(0)->attributes AS $attributes) {
+                                       if ($attributes->name == "ref")
+                                               $item["parent-uri"] = $attributes->textContent;
+                                       if ($attributes->name == "href")
+                                               $related = $attributes->textContent;
+                               }
                        }
-               }
 
-               $georsspoint = $xpath->query('georss:point', $entry);
-               if ($georsspoint)
-                       $item["coord"] = $georsspoint->item(0)->nodeValue;
-
-               /// @TODO
-               /// $item["location"] =
-
-               $categories = $xpath->query('atom:category', $entry);
-               if ($categories) {
-                       foreach ($categories AS $category) {
-                               foreach($category->attributes AS $attributes)
-                                       if ($attributes->name == "term") {
-                                               $term = $attributes->textContent;
-                                               if(strlen($item["tag"]))
-                                                       $item["tag"] .= ',';
-                                               $item["tag"] .= "#[url=".$a->get_baseurl()."/search?tag=".$term."]".$term."[/url]";
-                                       }
+                       $georsspoint = $xpath->query('georss:point', $entry);
+                       if ($georsspoint)
+                               $item["coord"] = $georsspoint->item(0)->nodeValue;
+
+                       $categories = $xpath->query('atom:category', $entry);
+                       if ($categories) {
+                               foreach ($categories AS $category) {
+                                       foreach($category->attributes AS $attributes)
+                                               if ($attributes->name == "term") {
+                                                       $term = $attributes->textContent;
+                                                       if(strlen($item["tag"]))
+                                                               $item["tag"] .= ',';
+                                                       $item["tag"] .= "#[url=".App::get_baseurl()."/search?tag=".$term."]".$term."[/url]";
+                                               }
+                               }
                        }
-               }
-
-               $self = "";
-               $enclosure = "";
 
-               $links = $xpath->query('atom:link', $entry);
-               if ($links) {
-                       $rel = "";
-                       $href = "";
-                       $type = "";
-                       $length = "0";
-                       $title = "";
-                       foreach ($links AS $link) {
-                               foreach($link->attributes AS $attributes) {
-                                       if ($attributes->name == "href")
-                                               $href = $attributes->textContent;
-                                       if ($attributes->name == "rel")
-                                               $rel = $attributes->textContent;
-                                       if ($attributes->name == "type")
-                                               $type = $attributes->textContent;
-                                       if ($attributes->name == "length")
-                                               $length = $attributes->textContent;
-                                       if ($attributes->name == "title")
-                                               $title = $attributes->textContent;
-                               }
-                               if (($rel != "") AND ($href != ""))
-                                       switch($rel) {
-                                               case "alternate":
-                                                       $item["plink"] = $href;
-                                                       if (($item["object-type"] == ACTIVITY_OBJ_QUESTION) OR
-                                                               ($item["object-type"] == ACTIVITY_OBJ_EVENT))
-                                                               $item["body"] .= add_page_info($href);
-                                                       break;
-                                               case "ostatus:conversation":
-                                                       $conversation = $href;
-                                                       break;
-                                               case "enclosure":
-                                                       $enclosure = $href;
-                                                       if(strlen($item["attach"]))
-                                                               $item["attach"] .= ',';
-
-                                                       $item["attach"] .= '[attach]href="'.$href.'" length="'.$length.'" type="'.$type.'" title="'.$title.'"[/attach]';
-                                                       break;
-                                               case "related":
-                                                       if ($item["object-type"] != ACTIVITY_OBJ_BOOKMARK) {
-                                                               if (!isset($item["parent-uri"]))
-                                                                       $item["parent-uri"] = $href;
-
-                                                               if ($related == "")
-                                                                       $related = $href;
-                                                       } else
-                                                               $item["body"] .= add_page_info($href);
-                                                       break;
-                                               case "self":
-                                                       $self = $href;
-                                                       break;
-                                               case "mentioned":
-                                                       // Notification check
-                                                       if ($importer["nurl"] == normalise_link($href))
-                                                               $mention = true;
-                                                       break;
+                       $self = "";
+                       $enclosure = "";
+
+                       $links = $xpath->query('atom:link', $entry);
+                       if ($links) {
+                               $rel = "";
+                               $href = "";
+                               $type = "";
+                               $length = "0";
+                               $title = "";
+                               foreach ($links AS $link) {
+                                       foreach($link->attributes AS $attributes) {
+                                               if ($attributes->name == "href")
+                                                       $href = $attributes->textContent;
+                                               if ($attributes->name == "rel")
+                                                       $rel = $attributes->textContent;
+                                               if ($attributes->name == "type")
+                                                       $type = $attributes->textContent;
+                                               if ($attributes->name == "length")
+                                                       $length = $attributes->textContent;
+                                               if ($attributes->name == "title")
+                                                       $title = $attributes->textContent;
                                        }
+                                       if (($rel != "") AND ($href != ""))
+                                               switch($rel) {
+                                                       case "alternate":
+                                                               $item["plink"] = $href;
+                                                               if (($item["object-type"] == ACTIVITY_OBJ_QUESTION) OR
+                                                                       ($item["object-type"] == ACTIVITY_OBJ_EVENT))
+                                                                       $item["body"] .= add_page_info($href);
+                                                               break;
+                                                       case "ostatus:conversation":
+                                                               $conversation = $href;
+                                                               break;
+                                                       case "enclosure":
+                                                               $enclosure = $href;
+                                                               if(strlen($item["attach"]))
+                                                                       $item["attach"] .= ',';
+
+                                                               $item["attach"] .= '[attach]href="'.$href.'" length="'.$length.'" type="'.$type.'" title="'.$title.'"[/attach]';
+                                                               break;
+                                                       case "related":
+                                                               if ($item["object-type"] != ACTIVITY_OBJ_BOOKMARK) {
+                                                                       if (!isset($item["parent-uri"]))
+                                                                               $item["parent-uri"] = $href;
+
+                                                                       if ($related == "")
+                                                                               $related = $href;
+                                                               } else
+                                                                       $item["body"] .= add_page_info($href);
+                                                               break;
+                                                       case "self":
+                                                               $self = $href;
+                                                               break;
+                                                       case "mentioned":
+                                                               // Notification check
+                                                               if ($importer["nurl"] == normalise_link($href))
+                                                                       $mention = true;
+                                                               break;
+                                               }
+                               }
                        }
-               }
 
-               $local_id = "";
-               $repeat_of = "";
-
-               $notice_info = $xpath->query('statusnet:notice_info', $entry);
-               if ($notice_info AND ($notice_info->length > 0)) {
-                       foreach($notice_info->item(0)->attributes AS $attributes) {
-                               if ($attributes->name == "source")
-                                       $item["app"] = strip_tags($attributes->textContent);
-                               if ($attributes->name == "local_id")
-                                       $local_id = $attributes->textContent;
-                               if ($attributes->name == "repeat_of")
-                                       $repeat_of = $attributes->textContent;
+                       $local_id = "";
+                       $repeat_of = "";
+
+                       $notice_info = $xpath->query('statusnet:notice_info', $entry);
+                       if ($notice_info AND ($notice_info->length > 0)) {
+                               foreach($notice_info->item(0)->attributes AS $attributes) {
+                                       if ($attributes->name == "source")
+                                               $item["app"] = strip_tags($attributes->textContent);
+                                       if ($attributes->name == "local_id")
+                                               $local_id = $attributes->textContent;
+                                       if ($attributes->name == "repeat_of")
+                                               $repeat_of = $attributes->textContent;
+                               }
                        }
-               }
 
-               // Is it a repeated post?
-               if ($repeat_of != "") {
-                       $activityobjects = $xpath->query('activity:object', $entry)->item(0);
+                       // Is it a repeated post?
+                       if ($repeat_of != "") {
+                               $activityobjects = $xpath->query('activity:object', $entry)->item(0);
 
-                       if (is_object($activityobjects)) {
+                               if (is_object($activityobjects)) {
 
-                               $orig_uri = $xpath->query("activity:object/atom:id", $activityobjects)->item(0)->nodeValue;
-                               if (!isset($orig_uri))
-                                       $orig_uri = $xpath->query('atom:id/text()', $activityobjects)->item(0)->nodeValue;
+                                       $orig_uri = $xpath->query("activity:object/atom:id", $activityobjects)->item(0)->nodeValue;
+                                       if (!isset($orig_uri))
+                                               $orig_uri = $xpath->query('atom:id/text()', $activityobjects)->item(0)->nodeValue;
 
-                               $orig_links = $xpath->query("activity:object/atom:link[@rel='alternate']", $activityobjects);
-                               if ($orig_links AND ($orig_links->length > 0))
-                                       foreach($orig_links->item(0)->attributes AS $attributes)
-                                               if ($attributes->name == "href")
-                                                       $orig_link = $attributes->textContent;
+                                       $orig_links = $xpath->query("activity:object/atom:link[@rel='alternate']", $activityobjects);
+                                       if ($orig_links AND ($orig_links->length > 0))
+                                               foreach($orig_links->item(0)->attributes AS $attributes)
+                                                       if ($attributes->name == "href")
+                                                               $orig_link = $attributes->textContent;
 
-                               if (!isset($orig_link))
-                                       $orig_link = $xpath->query("atom:link[@rel='alternate']", $activityobjects)->item(0)->nodeValue;
+                                       if (!isset($orig_link))
+                                               $orig_link = $xpath->query("atom:link[@rel='alternate']", $activityobjects)->item(0)->nodeValue;
 
-                               if (!isset($orig_link))
-                                       $orig_link =  ostatus_convert_href($orig_uri);
+                                       if (!isset($orig_link))
+                                               $orig_link =  self::convert_href($orig_uri);
 
-                               $orig_body = $xpath->query('activity:object/atom:content/text()', $activityobjects)->item(0)->nodeValue;
-                               if (!isset($orig_body))
-                                       $orig_body = $xpath->query('atom:content/text()', $activityobjects)->item(0)->nodeValue;
+                                       $orig_body = $xpath->query('activity:object/atom:content/text()', $activityobjects)->item(0)->nodeValue;
+                                       if (!isset($orig_body))
+                                               $orig_body = $xpath->query('atom:content/text()', $activityobjects)->item(0)->nodeValue;
 
-                               $orig_created = $xpath->query('atom:published/text()', $activityobjects)->item(0)->nodeValue;
+                                       $orig_created = $xpath->query('atom:published/text()', $activityobjects)->item(0)->nodeValue;
 
-                               $orig_contact = $contact;
-                               $orig_author = ostatus_fetchauthor($xpath, $activityobjects, $importer, $orig_contact, false);
+                                       $orig_contact = $contact;
+                                       $orig_author = self::fetchauthor($xpath, $activityobjects, $importer, $orig_contact, false);
 
-                               //if (!intval(get_config('system','wall-to-wall_share'))) {
-                               //      $prefix = share_header($orig_author['author-name'], $orig_author['author-link'], $orig_author['author-avatar'], "", $orig_created, $orig_link);
-                               //      $item["body"] = $prefix.add_page_info_to_body(html2bbcode($orig_body))."[/share]";
-                               //} else {
                                        $item["author-name"] = $orig_author["author-name"];
                                        $item["author-link"] = $orig_author["author-link"];
                                        $item["author-avatar"] = $orig_author["author-avatar"];
@@ -500,1090 +504,1511 @@ function ostatus_import($xml,$importer,&$contact, &$hub) {
 
                                        $item["uri"] = $orig_uri;
                                        $item["plink"] = $orig_link;
-                               //}
 
-                               $item["verb"] = $xpath->query('activity:verb/text()', $activityobjects)->item(0)->nodeValue;
+                                       $item["verb"] = $xpath->query('activity:verb/text()', $activityobjects)->item(0)->nodeValue;
 
-                               $item["object-type"] = $xpath->query('activity:object/activity:object-type/text()', $activityobjects)->item(0)->nodeValue;
-                               if (!isset($item["object-type"]))
-                                       $item["object-type"] = $xpath->query('activity:object-type/text()', $activityobjects)->item(0)->nodeValue;
+                                       $item["object-type"] = $xpath->query('activity:object/activity:object-type/text()', $activityobjects)->item(0)->nodeValue;
+                                       if (!isset($item["object-type"]))
+                                               $item["object-type"] = $xpath->query('activity:object-type/text()', $activityobjects)->item(0)->nodeValue;
+                               }
                        }
-               }
 
-               //if ($enclosure != "")
-               //      $item["body"] .= add_page_info($enclosure);
+                       //if ($enclosure != "")
+                       //      $item["body"] .= add_page_info($enclosure);
 
-               if (isset($item["parent-uri"])) {
-                       $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'",
-                               intval($importer["uid"]), dbesc($item["parent-uri"]));
+                       if (isset($item["parent-uri"])) {
+                               $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'",
+                                       intval($importer["uid"]), dbesc($item["parent-uri"]));
 
-                       if (!$r AND ($related != "")) {
-                               $reply_path = str_replace("/notice/", "/api/statuses/show/", $related).".atom";
+                               if (!$r AND ($related != "")) {
+                                       $reply_path = str_replace("/notice/", "/api/statuses/show/", $related).".atom";
 
-                               if ($reply_path != $related) {
-                                       logger("Fetching related items for user ".$importer["uid"]." from ".$reply_path, LOGGER_DEBUG);
-                                       $reply_xml = fetch_url($reply_path);
+                                       if ($reply_path != $related) {
+                                               logger("Fetching related items for user ".$importer["uid"]." from ".$reply_path, LOGGER_DEBUG);
+                                               $reply_xml = fetch_url($reply_path);
 
-                                       $reply_contact = $contact;
-                                       ostatus_import($reply_xml,$importer,$reply_contact, $reply_hub);
+                                               $reply_contact = $contact;
+                                               self::import($reply_xml,$importer,$reply_contact, $reply_hub);
 
-                                       // After the import try to fetch the parent item again
-                                       $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'",
-                                               intval($importer["uid"]), dbesc($item["parent-uri"]));
+                                               // After the import try to fetch the parent item again
+                                               $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'",
+                                                       intval($importer["uid"]), dbesc($item["parent-uri"]));
+                                       }
                                }
-                       }
-                       if ($r) {
-                               $item["type"] = 'remote-comment';
-                               $item["gravity"] = GRAVITY_COMMENT;
-                       }
-               } else
-                       $item["parent-uri"] = $item["uri"];
+                               if ($r) {
+                                       $item["type"] = 'remote-comment';
+                                       $item["gravity"] = GRAVITY_COMMENT;
+                               }
+                       } else
+                               $item["parent-uri"] = $item["uri"];
 
-               $item_id = ostatus_completion($conversation, $importer["uid"], $item);
+                       $item_id = self::completion($conversation, $importer["uid"], $item, $self);
 
-               if (!$item_id) {
-                       logger("Error storing item", LOGGER_DEBUG);
-                       continue;
-               }
+                       if (!$item_id) {
+                               logger("Error storing item", LOGGER_DEBUG);
+                               continue;
+                       }
 
-               logger("Item was stored with id ".$item_id, LOGGER_DEBUG);
+                       logger("Item was stored with id ".$item_id, LOGGER_DEBUG);
+               }
        }
-}
 
-function ostatus_convert_href($href) {
-       $elements = explode(":",$href);
+       /**
+        * @brief Create an url out of an uri
+        *
+        * @param string $href URI in the format "parameter1:parameter1:..."
+        *
+        * @return string URL in the format http(s)://....
+        */
+       public static function convert_href($href) {
+               $elements = explode(":",$href);
 
-       if ((count($elements) <= 2) OR ($elements[0] != "tag"))
-               return $href;
+               if ((count($elements) <= 2) OR ($elements[0] != "tag"))
+                       return $href;
 
-       $server = explode(",", $elements[1]);
-       $conversation = explode("=", $elements[2]);
+               $server = explode(",", $elements[1]);
+               $conversation = explode("=", $elements[2]);
 
-       if ((count($elements) == 4) AND ($elements[2] == "post"))
-               return "http://".$server[0]."/notice/".$elements[3];
+               if ((count($elements) == 4) AND ($elements[2] == "post"))
+                       return "http://".$server[0]."/notice/".$elements[3];
+
+               if ((count($conversation) != 2) OR ($conversation[1] ==""))
+                       return $href;
+
+               if ($elements[3] == "objectType=thread")
+                       return "http://".$server[0]."/conversation/".$conversation[1];
+               else
+                       return "http://".$server[0]."/notice/".$conversation[1];
 
-       if ((count($conversation) != 2) OR ($conversation[1] ==""))
                return $href;
+       }
 
-       if ($elements[3] == "objectType=thread")
-               return "http://".$server[0]."/conversation/".$conversation[1];
-       else
-               return "http://".$server[0]."/notice/".$conversation[1];
+       /**
+        * @brief Checks if there are entries in conversations that aren't present on our side
+        *
+        * @param bool $mentions Fetch conversations where we are mentioned
+        * @param bool $override Override the interval setting
+        */
+       public static function check_conversations($mentions = false, $override = false) {
+               $last = get_config('system','ostatus_last_poll');
+
+               $poll_interval = intval(get_config('system','ostatus_poll_interval'));
+               if(! $poll_interval)
+                       $poll_interval = OSTATUS_DEFAULT_POLL_INTERVAL;
+
+               // Don't poll if the interval is set negative
+               if (($poll_interval < 0) AND !$override)
+                       return;
 
-       return $href;
-}
+               if (!$mentions) {
+                       $poll_timeframe = intval(get_config('system','ostatus_poll_timeframe'));
+                       if (!$poll_timeframe)
+                               $poll_timeframe = OSTATUS_DEFAULT_POLL_TIMEFRAME;
+               } else {
+                       $poll_timeframe = intval(get_config('system','ostatus_poll_timeframe'));
+                       if (!$poll_timeframe)
+                               $poll_timeframe = OSTATUS_DEFAULT_POLL_TIMEFRAME_MENTIONS;
+               }
 
-function check_conversations($mentions = false, $override = false) {
-       $last = get_config('system','ostatus_last_poll');
-
-       $poll_interval = intval(get_config('system','ostatus_poll_interval'));
-       if(! $poll_interval)
-               $poll_interval = OSTATUS_DEFAULT_POLL_INTERVAL;
-
-       // Don't poll if the interval is set negative
-       if (($poll_interval < 0) AND !$override)
-               return;
-
-       if (!$mentions) {
-               $poll_timeframe = intval(get_config('system','ostatus_poll_timeframe'));
-               if (!$poll_timeframe)
-                       $poll_timeframe = OSTATUS_DEFAULT_POLL_TIMEFRAME;
-       } else {
-               $poll_timeframe = intval(get_config('system','ostatus_poll_timeframe'));
-               if (!$poll_timeframe)
-                       $poll_timeframe = OSTATUS_DEFAULT_POLL_TIMEFRAME_MENTIONS;
-       }
 
+               if ($last AND !$override) {
+                       $next = $last + ($poll_interval * 60);
+                       if ($next > time()) {
+                               logger('poll interval not reached');
+                               return;
+                       }
+               }
+
+               logger('cron_start');
 
-       if ($last AND !$override) {
-               $next = $last + ($poll_interval * 60);
-               if ($next > time()) {
-                       logger('poll interval not reached');
-                       return;
+               $start = date("Y-m-d H:i:s", time() - ($poll_timeframe * 60));
+
+               if ($mentions)
+                       $conversations = q("SELECT `term`.`oid`, `term`.`url`, `term`.`uid` FROM `term`
+                                               STRAIGHT_JOIN `thread` ON `thread`.`iid` = `term`.`oid` AND `thread`.`uid` = `term`.`uid`
+                                               WHERE `term`.`type` = 7 AND `term`.`term` > '%s' AND `thread`.`mention`
+                                               GROUP BY `term`.`url`, `term`.`uid` ORDER BY `term`.`term` DESC", dbesc($start));
+               else
+                       $conversations = q("SELECT `oid`, `url`, `uid` FROM `term`
+                                               WHERE `type` = 7 AND `term` > '%s'
+                                               GROUP BY `url`, `uid` ORDER BY `term` DESC", dbesc($start));
+
+               foreach ($conversations AS $conversation) {
+                       self::completion($conversation['url'], $conversation['uid']);
                }
+
+               logger('cron_end');
+
+               set_config('system','ostatus_last_poll', time());
        }
 
-       logger('cron_start');
+       /**
+        * @brief Updates the gcontact table with actor data from the conversation
+        *
+        * @param object $actor The actor object that contains the contact data
+        */
+       private function conv_fetch_actor($actor) {
 
-       $start = date("Y-m-d H:i:s", time() - ($poll_timeframe * 60));
+               // We set the generation to "3" since the data here is not as reliable as the data we get on other occasions
+               $contact = array("network" => NETWORK_OSTATUS, "generation" => 3);
 
-       if ($mentions)
-               $conversations = q("SELECT `term`.`oid`, `term`.`url`, `term`.`uid` FROM `term`
-                                       STRAIGHT_JOIN `thread` ON `thread`.`iid` = `term`.`oid` AND `thread`.`uid` = `term`.`uid`
-                                       WHERE `term`.`type` = 7 AND `term`.`term` > '%s' AND `thread`.`mention`
-                                       GROUP BY `term`.`url`, `term`.`uid` ORDER BY `term`.`term` DESC", dbesc($start));
-       else
-               $conversations = q("SELECT `oid`, `url`, `uid` FROM `term`
-                                       WHERE `type` = 7 AND `term` > '%s'
-                                       GROUP BY `url`, `uid` ORDER BY `term` DESC", dbesc($start));
+               if (isset($actor->url))
+                       $contact["url"] = $actor->url;
 
-       foreach ($conversations AS $conversation) {
-               ostatus_completion($conversation['url'], $conversation['uid']);
-       }
+               if (isset($actor->displayName))
+                       $contact["name"] = $actor->displayName;
 
-       logger('cron_end');
+               if (isset($actor->portablecontacts_net->displayName))
+                       $contact["name"] = $actor->portablecontacts_net->displayName;
 
-       set_config('system','ostatus_last_poll', time());
-}
+               if (isset($actor->portablecontacts_net->preferredUsername))
+                       $contact["nick"] = $actor->portablecontacts_net->preferredUsername;
 
-function ostatus_completion($conversation_url, $uid, $item = array()) {
+               if (isset($actor->id))
+                       $contact["alias"] = $actor->id;
 
-       $a = get_app();
+               if (isset($actor->summary))
+                       $contact["about"] = $actor->summary;
 
-       $item_stored = -1;
+               if (isset($actor->portablecontacts_net->note))
+                       $contact["about"] = $actor->portablecontacts_net->note;
 
-       $conversation_url = ostatus_convert_href($conversation_url);
+               if (isset($actor->portablecontacts_net->addresses->formatted))
+                       $contact["location"] = $actor->portablecontacts_net->addresses->formatted;
 
-       // If the thread shouldn't be completed then store the item and go away
-       if ((intval(get_config('system','ostatus_poll_interval')) == -2) AND (count($item) > 0)) {
-               //$arr["app"] .= " (OStatus-NoCompletion)";
-               $item_stored = item_store($item, true);
-               return($item_stored);
+
+               if (isset($actor->image->url))
+                       $contact["photo"] = $actor->image->url;
+
+               if (isset($actor->image->width))
+                       $avatarwidth = $actor->image->width;
+
+               if (is_array($actor->status_net->avatarLinks))
+                       foreach ($actor->status_net->avatarLinks AS $avatar) {
+                               if ($avatarsize < $avatar->width) {
+                                       $contact["photo"] = $avatar->url;
+                                       $avatarsize = $avatar->width;
+                               }
+                       }
+
+               update_gcontact($contact);
        }
 
-       // Get the parent
-       $parents = q("SELECT `id`, `parent`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `id` IN
-                       (SELECT `parent` FROM `item` WHERE `id` IN
-                               (SELECT `oid` FROM `term` WHERE `uid` = %d AND `otype` = %d AND `type` = %d AND `url` = '%s'))",
-                       intval($uid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION), dbesc($conversation_url));
-
-       if ($parents)
-               $parent = $parents[0];
-       elseif (count($item) > 0) {
-               $parent = $item;
-               $parent["type"] = "remote";
-               $parent["verb"] = ACTIVITY_POST;
-               $parent["visible"] = 1;
-       } else {
-               // Preset the parent
-               $r = q("SELECT `id` FROM `contact` WHERE `self` AND `uid`=%d", $uid);
-               if (!$r)
-                       return(-2);
-
-               $parent = array();
-               $parent["id"] = 0;
-               $parent["parent"] = 0;
-               $parent["uri"] = "";
-               $parent["contact-id"] = $r[0]["id"];
-               $parent["type"] = "remote";
-               $parent["verb"] = ACTIVITY_POST;
-               $parent["visible"] = 1;
+       /**
+        * @brief Fetches the conversation url for a given item link or conversation id
+        *
+        * @param string $self The link to the posting
+        * @param string $conversation_id The conversation id
+        *
+        * @return string The conversation url
+        */
+       private function fetch_conversation($self, $conversation_id = "") {
+
+               if ($conversation_id != "") {
+                       $elements = explode(":", $conversation_id);
+
+                       if ((count($elements) <= 2) OR ($elements[0] != "tag"))
+                               return $conversation_id;
+               }
+
+               if ($self == "")
+                       return "";
+
+               $json = str_replace(".atom", ".json", $self);
+
+               $raw = fetch_url($json);
+               if ($raw == "")
+                       return "";
+
+               $data = json_decode($raw);
+               if (!is_object($data))
+                       return "";
+
+               $conversation_id = $data->statusnet_conversation_id;
+
+               $pos = strpos($self, "/api/statuses/show/");
+               $base_url = substr($self, 0, $pos);
+
+               return $base_url."/conversation/".$conversation_id;
        }
 
-       $conv = str_replace("/conversation/", "/api/statusnet/conversation/", $conversation_url).".as";
-       $pageno = 1;
-       $items = array();
+       /**
+        * @brief Fetches actor details of a given actor and user id
+        *
+        * @param string $actor The actor url
+        * @param int $uid The user id
+        * @param int $contact_id The default contact-id
+        *
+        * @return array Array with actor details
+        */
+       private function get_actor_details($actor, $uid, $contact_id) {
 
-       logger('fetching conversation url '.$conv.' for user '.$uid);
+               $details = array();
 
-       do {
-               $conv_arr = z_fetch_url($conv."?page=".$pageno);
+               $contact = q("SELECT `id`, `rel`, `network` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `network` != '%s'",
+                                       $uid, normalise_link($actor), NETWORK_STATUSNET);
 
-               // If it is a non-ssl site and there is an error, then try ssl or vice versa
-               if (!$conv_arr["success"] AND (substr($conv, 0, 7) == "http://")) {
-                       $conv = str_replace("http://", "https://", $conv);
-                       $conv_as = fetch_url($conv."?page=".$pageno);
-               } elseif (!$conv_arr["success"] AND (substr($conv, 0, 8) == "https://")) {
-                       $conv = str_replace("https://", "http://", $conv);
-                       $conv_as = fetch_url($conv."?page=".$pageno);
-               } else
-                       $conv_as = $conv_arr["body"];
+               if (!$contact)
+                       $contact = q("SELECT `id`, `rel`, `network` FROM `contact` WHERE `uid` = %d AND `alias` IN ('%s', '%s') AND `network` != '%s'",
+                                       $uid, $actor, normalise_link($actor), NETWORK_STATUSNET);
+
+               if ($contact) {
+                       logger("Found contact for url ".$actor, LOGGER_DEBUG);
+                       $details["contact_id"] = $contact[0]["id"];
+                       $details["network"] = $contact[0]["network"];
+
+                       $details["not_following"] = !in_array($contact[0]["rel"], array(CONTACT_IS_SHARING, CONTACT_IS_FRIEND));
+               } else {
+                       logger("No contact found for user ".$uid." and url ".$actor, LOGGER_DEBUG);
 
-               $conv_as = str_replace(',"statusnet:notice_info":', ',"statusnet_notice_info":', $conv_as);
-               $conv_as = json_decode($conv_as);
+                       // Adding a global contact
+                       /// @TODO Use this data for the post
+                       $details["global_contact_id"] = get_contact($actor, 0);
 
-               $no_of_items = sizeof($items);
+                       logger("Global contact ".$global_contact_id." found for url ".$actor, LOGGER_DEBUG);
 
-               if (@is_array($conv_as->items))
-                       foreach ($conv_as->items AS $single_item)
-                               $items[$single_item->id] = $single_item;
+                       $details["contact_id"] = $contact_id;
+                       $details["network"] = NETWORK_OSTATUS;
 
-               if ($no_of_items == sizeof($items))
-                       break;
+                       $details["not_following"] = true;
+               }
 
-               $pageno++;
+               return $details;
+       }
 
-       } while (true);
+       /**
+        * @brief Stores an item and completes the thread
+        *
+        * @param string $conversation_url The URI of the conversation
+        * @param integer $uid The user id
+        * @param array $item Data of the item that is to be posted
+        *
+        * @return integer The item id of the posted item array
+        */
+       private function completion($conversation_url, $uid, $item = array(), $self = "") {
 
-       logger('fetching conversation done. Found '.count($items).' items');
+               /// @todo This function is totally ugly and has to be rewritten totally
 
-       if (!sizeof($items)) {
-               if (count($item) > 0) {
-                       //$arr["app"] .= " (OStatus-NoConvFetched)";
-                       $item_stored = item_store($item, true);
+               $item_stored = -1;
 
-                       if ($item_stored) {
-                               logger("Conversation ".$conversation_url." couldn't be fetched. Item uri ".$item["uri"]." stored: ".$item_stored, LOGGER_DEBUG);
-                               ostatus_store_conversation($item_id, $conversation_url);
-                       }
+               $conversation_url = self::fetch_conversation($self, $conversation_url);
 
+               // If the thread shouldn't be completed then store the item and go away
+               // Don't do a completion on liked content
+               if (((intval(get_config('system','ostatus_poll_interval')) == -2) AND (count($item) > 0)) OR
+                       ($item["verb"] == ACTIVITY_LIKE) OR ($conversation_url == "")) {
+                       $item_stored = item_store($item, true);
                        return($item_stored);
-               } else
-                       return(-3);
-       }
+               }
+
+               // Get the parent
+               $parents = q("SELECT `id`, `parent`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `id` IN
+                               (SELECT `parent` FROM `item` WHERE `id` IN
+                                       (SELECT `oid` FROM `term` WHERE `uid` = %d AND `otype` = %d AND `type` = %d AND `url` = '%s'))",
+                               intval($uid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION), dbesc($conversation_url));
+
+               if ($parents)
+                       $parent = $parents[0];
+               elseif (count($item) > 0) {
+                       $parent = $item;
+                       $parent["type"] = "remote";
+                       $parent["verb"] = ACTIVITY_POST;
+                       $parent["visible"] = 1;
+               } else {
+                       // Preset the parent
+                       $r = q("SELECT `id` FROM `contact` WHERE `self` AND `uid`=%d", $uid);
+                       if (!$r)
+                               return(-2);
+
+                       $parent = array();
+                       $parent["id"] = 0;
+                       $parent["parent"] = 0;
+                       $parent["uri"] = "";
+                       $parent["contact-id"] = $r[0]["id"];
+                       $parent["type"] = "remote";
+                       $parent["verb"] = ACTIVITY_POST;
+                       $parent["visible"] = 1;
+               }
+
+               $conv = str_replace("/conversation/", "/api/statusnet/conversation/", $conversation_url).".as";
+               $pageno = 1;
+               $items = array();
+
+               logger('fetching conversation url '.$conv.' (Self: '.$self.') for user '.$uid);
+
+               do {
+                       $conv_arr = z_fetch_url($conv."?page=".$pageno);
+
+                       // If it is a non-ssl site and there is an error, then try ssl or vice versa
+                       if (!$conv_arr["success"] AND (substr($conv, 0, 7) == "http://")) {
+                               $conv = str_replace("http://", "https://", $conv);
+                               $conv_as = fetch_url($conv."?page=".$pageno);
+                       } elseif (!$conv_arr["success"] AND (substr($conv, 0, 8) == "https://")) {
+                               $conv = str_replace("https://", "http://", $conv);
+                               $conv_as = fetch_url($conv."?page=".$pageno);
+                       } else
+                               $conv_as = $conv_arr["body"];
+
+                       $conv_as = str_replace(',"statusnet:notice_info":', ',"statusnet_notice_info":', $conv_as);
+                       $conv_as = json_decode($conv_as);
+
+                       $no_of_items = sizeof($items);
+
+                       if (@is_array($conv_as->items))
+                               foreach ($conv_as->items AS $single_item)
+                                       $items[$single_item->id] = $single_item;
+
+                       if ($no_of_items == sizeof($items))
+                               break;
+
+                       $pageno++;
+
+               } while (true);
+
+               logger('fetching conversation done. Found '.count($items).' items');
+
+               if (!sizeof($items)) {
+                       if (count($item) > 0) {
+                               $item_stored = item_store($item, true);
+
+                               if ($item_stored) {
+                                       logger("Conversation ".$conversation_url." couldn't be fetched. Item uri ".$item["uri"]." stored: ".$item_stored, LOGGER_DEBUG);
+                                       self::store_conversation($item_id, $conversation_url);
+                               }
+
+                               return($item_stored);
+                       } else
+                               return(-3);
+               }
+
+               $items = array_reverse($items);
+
+               $r = q("SELECT `nurl` FROM `contact` WHERE `uid` = %d AND `self`", intval($uid));
+               $importer = $r[0];
+
+               $new_parent = true;
+
+               foreach ($items as $single_conv) {
 
-       $items = array_reverse($items);
+                       // Update the gcontact table
+                       self::conv_fetch_actor($single_conv->actor);
 
-       $r = q("SELECT `nurl` FROM `contact` WHERE `uid` = %d AND `self`", intval($uid));
-       $importer = $r[0];
+                       // Test - remove before flight
+                       //$tempfile = tempnam(get_temppath(), "conversation");
+                       //file_put_contents($tempfile, json_encode($single_conv));
 
-       foreach ($items as $single_conv) {
+                       $mention = false;
 
-               // Test - remove before flight
-               //$tempfile = tempnam(get_temppath(), "conversation");
-               //file_put_contents($tempfile, json_encode($single_conv));
+                       if (isset($single_conv->object->id))
+                               $single_conv->id = $single_conv->object->id;
 
-               $mention = false;
+                       $plink = self::convert_href($single_conv->id);
+                       if (isset($single_conv->object->url))
+                               $plink = self::convert_href($single_conv->object->url);
 
-               if (isset($single_conv->object->id))
-                       $single_conv->id = $single_conv->object->id;
+                       if (@!$single_conv->id)
+                               continue;
 
-               $plink = ostatus_convert_href($single_conv->id);
-               if (isset($single_conv->object->url))
-                       $plink = ostatus_convert_href($single_conv->object->url);
+                       logger("Got id ".$single_conv->id, LOGGER_DEBUG);
 
-               if (@!$single_conv->id)
-                       continue;
+                       if ($first_id == "") {
+                               $first_id = $single_conv->id;
 
-               logger("Got id ".$single_conv->id, LOGGER_DEBUG);
+                               // The first post of the conversation isn't our first post. There are three options:
+                               // 1. Our conversation hasn't the "real" thread starter
+                               // 2. This first post is a post inside our thread
+                               // 3. This first post is a post inside another thread
+                               if (($first_id != $parent["uri"]) AND ($parent["uri"] != "")) {
 
-               if ($first_id == "") {
-                       $first_id = $single_conv->id;
+                                       $new_parent = true;
 
-                       // The first post of the conversation isn't our first post. There are three options:
-                       // 1. Our conversation hasn't the "real" thread starter
-                       // 2. This first post is a post inside our thread
-                       // 3. This first post is a post inside another thread
-                       if (($first_id != $parent["uri"]) AND ($parent["uri"] != "")) {
-                               $new_parents = q("SELECT `id`, `parent`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `id` IN
-                                                       (SELECT `parent` FROM `item`
-                                                               WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s')) LIMIT 1",
-                                       intval($uid), dbesc($first_id), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN));
-                               if ($new_parents) {
-                                       if ($new_parents[0]["parent"] == $parent["parent"]) {
-                                               // Option 2: This post is already present inside our thread - but not as thread starter
-                                               logger("Option 2: uri present in our thread: ".$first_id, LOGGER_DEBUG);
-                                               $first_id = $parent["uri"];
+                                       $new_parents = q("SELECT `id`, `parent`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `id` IN
+                                                               (SELECT `parent` FROM `item`
+                                                                       WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s')) LIMIT 1",
+                                               intval($uid), dbesc($first_id), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN));
+                                       if ($new_parents) {
+                                               if ($new_parents[0]["parent"] == $parent["parent"]) {
+                                                       // Option 2: This post is already present inside our thread - but not as thread starter
+                                                       logger("Option 2: uri present in our thread: ".$first_id, LOGGER_DEBUG);
+                                                       $first_id = $parent["uri"];
+                                               } else {
+                                                       // Option 3: Not so good. We have mixed parents. We have to see how to clean this up.
+                                                       // For now just take the new parent.
+                                                       $parent = $new_parents[0];
+                                                       $first_id = $parent["uri"];
+                                                       logger("Option 3: mixed parents for uri ".$first_id, LOGGER_DEBUG);
+                                               }
                                        } else {
-                                               // Option 3: Not so good. We have mixed parents. We have to see how to clean this up.
-                                               // For now just take the new parent.
-                                               $parent = $new_parents[0];
-                                               $first_id = $parent["uri"];
-                                               logger("Option 3: mixed parents for uri ".$first_id, LOGGER_DEBUG);
+                                               // Option 1: We hadn't got the real thread starter
+                                               // We have to clean up our existing messages.
+                                               $parent["id"] = 0;
+                                               $parent["uri"] = $first_id;
+                                               logger("Option 1: we have a new parent: ".$first_id, LOGGER_DEBUG);
                                        }
-                               } else {
-                                       // Option 1: We hadn't got the real thread starter
-                                       // We have to clean up our existing messages.
+                               } elseif ($parent["uri"] == "") {
                                        $parent["id"] = 0;
                                        $parent["uri"] = $first_id;
-                                       logger("Option 1: we have a new parent: ".$first_id, LOGGER_DEBUG);
                                }
-                       } elseif ($parent["uri"] == "") {
-                               $parent["id"] = 0;
-                               $parent["uri"] = $first_id;
                        }
-               }
 
-               $parent_uri = $parent["uri"];
+                       $parent_uri = $parent["uri"];
 
-               // "context" only seems to exist on older servers
-               if (isset($single_conv->context->inReplyTo->id)) {
-                       $parent_exists = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1",
-                                               intval($uid), dbesc($single_conv->context->inReplyTo->id), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN));
-                       if ($parent_exists)
-                               $parent_uri = $single_conv->context->inReplyTo->id;
-               }
+                       // "context" only seems to exist on older servers
+                       if (isset($single_conv->context->inReplyTo->id)) {
+                               $parent_exists = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1",
+                                                       intval($uid), dbesc($single_conv->context->inReplyTo->id), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN));
+                               if ($parent_exists)
+                                       $parent_uri = $single_conv->context->inReplyTo->id;
+                       }
 
-               // This is the current way
-               if (isset($single_conv->object->inReplyTo->id)) {
-                       $parent_exists = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1",
-                                               intval($uid), dbesc($single_conv->object->inReplyTo->id), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN));
-                       if ($parent_exists)
-                               $parent_uri = $single_conv->object->inReplyTo->id;
-               }
+                       // This is the current way
+                       if (isset($single_conv->object->inReplyTo->id)) {
+                               $parent_exists = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1",
+                                                       intval($uid), dbesc($single_conv->object->inReplyTo->id), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN));
+                               if ($parent_exists)
+                                       $parent_uri = $single_conv->object->inReplyTo->id;
+                       }
 
-               $message_exists = q("SELECT `id`, `parent`, `uri` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1",
-                                               intval($uid), dbesc($single_conv->id),
-                                               dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN));
-               if ($message_exists) {
-                       logger("Message ".$single_conv->id." already existed on the system", LOGGER_DEBUG);
+                       $message_exists = q("SELECT `id`, `parent`, `uri` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1",
+                                                       intval($uid), dbesc($single_conv->id),
+                                                       dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN));
+                       if ($message_exists) {
+                               logger("Message ".$single_conv->id." already existed on the system", LOGGER_DEBUG);
 
-                       if ($parent["id"] != 0) {
-                               $existing_message = $message_exists[0];
+                               if ($parent["id"] != 0) {
+                                       $existing_message = $message_exists[0];
 
-                               // We improved the way we fetch OStatus messages, this shouldn't happen very often now
-                               /// @TODO We have to change the shadow copies as well. This way here is really ugly.
-                               if ($existing_message["parent"] != $parent["id"]) {
-                                       logger('updating id '.$existing_message["id"].' with parent '.$existing_message["parent"].' to parent '.$parent["id"].' uri '.$parent["uri"].' thread '.$parent_uri, LOGGER_DEBUG);
+                                       // We improved the way we fetch OStatus messages, this shouldn't happen very often now
+                                       /// @TODO We have to change the shadow copies as well. This way here is really ugly.
+                                       if ($existing_message["parent"] != $parent["id"]) {
+                                               logger('updating id '.$existing_message["id"].' with parent '.$existing_message["parent"].' to parent '.$parent["id"].' uri '.$parent["uri"].' thread '.$parent_uri, LOGGER_DEBUG);
 
-                                       // Update the parent id of the selected item
-                                       $r = q("UPDATE `item` SET `parent` = %d, `parent-uri` = '%s' WHERE `id` = %d",
-                                               intval($parent["id"]), dbesc($parent["uri"]), intval($existing_message["id"]));
+                                               // Update the parent id of the selected item
+                                               $r = q("UPDATE `item` SET `parent` = %d, `parent-uri` = '%s' WHERE `id` = %d",
+                                                       intval($parent["id"]), dbesc($parent["uri"]), intval($existing_message["id"]));
 
-                                       // Update the parent uri in the thread - but only if it points to itself
-                                       $r = q("UPDATE `item` SET `thr-parent` = '%s' WHERE `id` = %d AND `uri` = `thr-parent`",
-                                               dbesc($parent_uri), intval($existing_message["id"]));
+                                               // Update the parent uri in the thread - but only if it points to itself
+                                               $r = q("UPDATE `item` SET `thr-parent` = '%s' WHERE `id` = %d AND `uri` = `thr-parent`",
+                                                       dbesc($parent_uri), intval($existing_message["id"]));
 
-                                       // try to change all items of the same parent
-                                       $r = q("UPDATE `item` SET `parent` = %d, `parent-uri` = '%s' WHERE `parent` = %d",
-                                               intval($parent["id"]), dbesc($parent["uri"]), intval($existing_message["parent"]));
+                                               // try to change all items of the same parent
+                                               $r = q("UPDATE `item` SET `parent` = %d, `parent-uri` = '%s' WHERE `parent` = %d",
+                                                       intval($parent["id"]), dbesc($parent["uri"]), intval($existing_message["parent"]));
 
-                                       // Update the parent uri in the thread - but only if it points to itself
-                                       $r = q("UPDATE `item` SET `thr-parent` = '%s' WHERE (`parent` = %d) AND (`uri` = `thr-parent`)",
-                                               dbesc($parent["uri"]), intval($existing_message["parent"]));
+                                               // Update the parent uri in the thread - but only if it points to itself
+                                               $r = q("UPDATE `item` SET `thr-parent` = '%s' WHERE (`parent` = %d) AND (`uri` = `thr-parent`)",
+                                                       dbesc($parent["uri"]), intval($existing_message["parent"]));
 
-                                       // Now delete the thread
-                                       delete_thread($existing_message["parent"]);
+                                               // Now delete the thread
+                                               delete_thread($existing_message["parent"]);
+                                       }
                                }
-                       }
 
-                       // The item we are having on the system is the one that we wanted to store via the item array
-                       if (isset($item["uri"]) AND ($item["uri"] == $existing_message["uri"])) {
-                               $item = array();
-                               $item_stored = 0;
+                               // The item we are having on the system is the one that we wanted to store via the item array
+                               if (isset($item["uri"]) AND ($item["uri"] == $existing_message["uri"])) {
+                                       $item = array();
+                                       $item_stored = 0;
+                               }
+
+                               continue;
                        }
 
-                       continue;
-               }
+                       if (is_array($single_conv->to))
+                               foreach($single_conv->to AS $to)
+                                       if ($importer["nurl"] == normalise_link($to->id))
+                                               $mention = true;
 
-               if (is_array($single_conv->to))
-                       foreach($single_conv->to AS $to)
-                               if ($importer["nurl"] == normalise_link($to->id))
-                                       $mention = true;
+                       $actor = $single_conv->actor->id;
+                       if (isset($single_conv->actor->url))
+                               $actor = $single_conv->actor->url;
 
-               $actor = $single_conv->actor->id;
-               if (isset($single_conv->actor->url))
-                       $actor = $single_conv->actor->url;
+                       $details = self::get_actor_details($actor, $uid, $parent["contact-id"]);
 
-               $contact = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `network` != '%s'",
-                               $uid, normalise_link($actor), NETWORK_STATUSNET);
+                       // Do we only want to import threads that were started by our contacts?
+                       if ($details["not_following"] AND $new_parent AND get_config('system','ostatus_full_threads')) {
+                               logger("Don't import uri ".$first_id." because user ".$uid." doesn't follow the person ".$actor, LOGGER_DEBUG);
+                               continue;
+                       }
 
-               if (count($contact)) {
-                       logger("Found contact for url ".$actor, LOGGER_DEBUG);
-                       $contact_id = $contact[0]["id"];
-               } else {
-                       logger("No contact found for url ".$actor, LOGGER_DEBUG);
+                       $arr = array();
+                       $arr["network"] = $details["network"];
+                       $arr["uri"] = $single_conv->id;
+                       $arr["plink"] = $plink;
+                       $arr["uid"] = $uid;
+                       $arr["contact-id"] = $details["contact_id"];
+                       $arr["parent-uri"] = $parent_uri;
+                       $arr["created"] = $single_conv->published;
+                       $arr["edited"] = $single_conv->published;
+                       $arr["owner-name"] = $single_conv->actor->displayName;
+                       if ($arr["owner-name"] == '')
+                               $arr["owner-name"] = $single_conv->actor->contact->displayName;
+                       if ($arr["owner-name"] == '')
+                               $arr["owner-name"] = $single_conv->actor->portablecontacts_net->displayName;
+
+                       $arr["owner-link"] = $actor;
+                       $arr["owner-avatar"] = $single_conv->actor->image->url;
+                       $arr["author-name"] = $arr["owner-name"];
+                       $arr["author-link"] = $actor;
+                       $arr["author-avatar"] = $single_conv->actor->image->url;
+                       $arr["body"] = add_page_info_to_body(html2bbcode($single_conv->content));
+
+                       if (isset($single_conv->status_net->notice_info->source))
+                               $arr["app"] = strip_tags($single_conv->status_net->notice_info->source);
+                       elseif (isset($single_conv->statusnet->notice_info->source))
+                               $arr["app"] = strip_tags($single_conv->statusnet->notice_info->source);
+                       elseif (isset($single_conv->statusnet_notice_info->source))
+                               $arr["app"] = strip_tags($single_conv->statusnet_notice_info->source);
+                       elseif (isset($single_conv->provider->displayName))
+                               $arr["app"] = $single_conv->provider->displayName;
+                       else
+                               $arr["app"] = "OStatus";
 
-                       // Adding a global contact
-                       /// @TODO Use this data for the post
-                       $global_contact_id = get_contact($actor, 0);
 
-                       logger("Global contact ".$global_contact_id." found for url ".$actor, LOGGER_DEBUG);
+                       $arr["object"] = json_encode($single_conv);
+                       $arr["verb"] = $parent["verb"];
+                       $arr["visible"] = $parent["visible"];
+                       $arr["location"] = $single_conv->location->displayName;
+                       $arr["coord"] = trim($single_conv->location->lat." ".$single_conv->location->lon);
 
-                       $contact_id = $parent["contact-id"];
-               }
+                       // Is it a reshared item?
+                       if (isset($single_conv->verb) AND ($single_conv->verb == "share") AND isset($single_conv->object)) {
+                               if (is_array($single_conv->object))
+                                       $single_conv->object = $single_conv->object[0];
 
-               $arr = array();
-               $arr["network"] = NETWORK_OSTATUS;
-               $arr["uri"] = $single_conv->id;
-               $arr["plink"] = $plink;
-               $arr["uid"] = $uid;
-               $arr["contact-id"] = $contact_id;
-               $arr["parent-uri"] = $parent_uri;
-               $arr["created"] = $single_conv->published;
-               $arr["edited"] = $single_conv->published;
-               $arr["owner-name"] = $single_conv->actor->displayName;
-               if ($arr["owner-name"] == '')
-                       $arr["owner-name"] = $single_conv->actor->contact->displayName;
-               if ($arr["owner-name"] == '')
-                       $arr["owner-name"] = $single_conv->actor->portablecontacts_net->displayName;
-
-               $arr["owner-link"] = $actor;
-               $arr["owner-avatar"] = $single_conv->actor->image->url;
-               $arr["author-name"] = $arr["owner-name"];
-               $arr["author-link"] = $actor;
-               $arr["author-avatar"] = $single_conv->actor->image->url;
-               $arr["body"] = add_page_info_to_body(html2bbcode($single_conv->content));
-
-               if (isset($single_conv->status_net->notice_info->source))
-                       $arr["app"] = strip_tags($single_conv->status_net->notice_info->source);
-               elseif (isset($single_conv->statusnet->notice_info->source))
-                       $arr["app"] = strip_tags($single_conv->statusnet->notice_info->source);
-               elseif (isset($single_conv->statusnet_notice_info->source))
-                       $arr["app"] = strip_tags($single_conv->statusnet_notice_info->source);
-               elseif (isset($single_conv->provider->displayName))
-                       $arr["app"] = $single_conv->provider->displayName;
-               else
-                       $arr["app"] = "OStatus";
+                               logger("Found reshared item ".$single_conv->object->id);
 
-               //$arr["app"] .= " (Conversation)";
+                               // $single_conv->object->context->conversation;
 
-               $arr["object"] = json_encode($single_conv);
-               $arr["verb"] = $parent["verb"];
-               $arr["visible"] = $parent["visible"];
-               $arr["location"] = $single_conv->location->displayName;
-               $arr["coord"] = trim($single_conv->location->lat." ".$single_conv->location->lon);
+                               if (isset($single_conv->object->object->id))
+                                       $arr["uri"] = $single_conv->object->object->id;
+                               else
+                                       $arr["uri"] = $single_conv->object->id;
 
-               // Is it a reshared item?
-               if (isset($single_conv->verb) AND ($single_conv->verb == "share") AND isset($single_conv->object)) {
-                       if (is_array($single_conv->object))
-                               $single_conv->object = $single_conv->object[0];
+                               if (isset($single_conv->object->object->url))
+                                       $plink = self::convert_href($single_conv->object->object->url);
+                               else
+                                       $plink = self::convert_href($single_conv->object->url);
 
-                       logger("Found reshared item ".$single_conv->object->id);
+                               if (isset($single_conv->object->object->content))
+                                       $arr["body"] = add_page_info_to_body(html2bbcode($single_conv->object->object->content));
+                               else
+                                       $arr["body"] = add_page_info_to_body(html2bbcode($single_conv->object->content));
 
-                       // $single_conv->object->context->conversation;
+                               $arr["plink"] = $plink;
 
-                       if (isset($single_conv->object->object->id))
-                               $arr["uri"] = $single_conv->object->object->id;
-                       else
-                               $arr["uri"] = $single_conv->object->id;
+                               $arr["created"] = $single_conv->object->published;
+                               $arr["edited"] = $single_conv->object->published;
 
-                       if (isset($single_conv->object->object->url))
-                               $plink = ostatus_convert_href($single_conv->object->object->url);
-                       else
-                               $plink = ostatus_convert_href($single_conv->object->url);
+                               $arr["author-name"] = $single_conv->object->actor->displayName;
+                               if ($arr["owner-name"] == '')
+                                       $arr["author-name"] = $single_conv->object->actor->contact->displayName;
 
-                       if (isset($single_conv->object->object->content))
-                               $arr["body"] = add_page_info_to_body(html2bbcode($single_conv->object->object->content));
-                       else
-                               $arr["body"] = add_page_info_to_body(html2bbcode($single_conv->object->content));
+                               $arr["author-link"] = $single_conv->object->actor->url;
+                               $arr["author-avatar"] = $single_conv->object->actor->image->url;
 
-                       $arr["plink"] = $plink;
+                               $arr["app"] = $single_conv->object->provider->displayName."#";
+                               //$arr["verb"] = $single_conv->object->verb;
 
-                       $arr["created"] = $single_conv->object->published;
-                       $arr["edited"] = $single_conv->object->published;
+                               $arr["location"] = $single_conv->object->location->displayName;
+                               $arr["coord"] = trim($single_conv->object->location->lat." ".$single_conv->object->location->lon);
+                       }
 
-                       $arr["author-name"] = $single_conv->object->actor->displayName;
-                       if ($arr["owner-name"] == '')
-                               $arr["author-name"] = $single_conv->object->actor->contact->displayName;
+                       if ($arr["location"] == "")
+                               unset($arr["location"]);
 
-                       $arr["author-link"] = $single_conv->object->actor->url;
-                       $arr["author-avatar"] = $single_conv->object->actor->image->url;
+                       if ($arr["coord"] == "")
+                               unset($arr["coord"]);
 
-                       $arr["app"] = $single_conv->object->provider->displayName."#";
-                       //$arr["verb"] = $single_conv->object->verb;
+                       // Copy fields from given item array
+                       if (isset($item["uri"]) AND (($item["uri"] == $arr["uri"]) OR ($item["uri"] ==  $single_conv->id))) {
+                               $copy_fields = array("owner-name", "owner-link", "owner-avatar", "author-name", "author-link", "author-avatar",
+                                                       "gravity", "body", "object-type", "object", "verb", "created", "edited", "coord", "tag",
+                                                       "title", "attach", "app", "type", "location", "contact-id", "uri");
+                               foreach ($copy_fields AS $field)
+                                       if (isset($item[$field]))
+                                               $arr[$field] = $item[$field];
 
-                       $arr["location"] = $single_conv->object->location->displayName;
-                       $arr["coord"] = trim($single_conv->object->location->lat." ".$single_conv->object->location->lon);
-               }
+                       }
+
+                       $newitem = item_store($arr);
+                       if (!$newitem) {
+                               logger("Item wasn't stored ".print_r($arr, true), LOGGER_DEBUG);
+                               continue;
+                       }
 
-               if ($arr["location"] == "")
-                       unset($arr["location"]);
+                       if (isset($item["uri"]) AND ($item["uri"] == $arr["uri"])) {
+                               $item = array();
+                               $item_stored = $newitem;
+                       }
 
-               if ($arr["coord"] == "")
-                       unset($arr["coord"]);
+                       logger('Stored new item '.$plink.' for parent '.$arr["parent-uri"].' under id '.$newitem, LOGGER_DEBUG);
 
-               // Copy fields from given item array
-               if (isset($item["uri"]) AND (($item["uri"] == $arr["uri"]) OR ($item["uri"] ==  $single_conv->id))) {
-                       $copy_fields = array("owner-name", "owner-link", "owner-avatar", "author-name", "author-link", "author-avatar",
-                                               "gravity", "body", "object-type", "object", "verb", "created", "edited", "coord", "tag",
-                                               "title", "attach", "app", "type", "location", "contact-id", "uri");
-                       foreach ($copy_fields AS $field)
-                               if (isset($item[$field]))
-                                       $arr[$field] = $item[$field];
+                       // Add the conversation entry (but don't fetch the whole conversation)
+                       self::store_conversation($newitem, $conversation_url);
 
-                       //$arr["app"] .= " (OStatus)";
+                       // If the newly created item is the top item then change the parent settings of the thread
+                       // This shouldn't happen anymore. This is supposed to be absolote.
+                       if ($arr["uri"] == $first_id) {
+                               logger('setting new parent to id '.$newitem);
+                               $new_parents = q("SELECT `id`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `uid` = %d AND `id` = %d LIMIT 1",
+                                       intval($uid), intval($newitem));
+                               if ($new_parents)
+                                       $parent = $new_parents[0];
+                       }
                }
 
-               $newitem = item_store($arr);
-               if (!$newitem) {
-                       logger("Item wasn't stored ".print_r($arr, true), LOGGER_DEBUG);
-                       continue;
+               if (($item_stored < 0) AND (count($item) > 0)) {
+
+                       if (get_config('system','ostatus_full_threads')) {
+                               $details = self::get_actor_details($item["owner-link"], $uid, $item["contact-id"]);
+                               if ($details["not_following"]) {
+                                       logger("Don't import uri ".$item["uri"]." because user ".$uid." doesn't follow the person ".$item["owner-link"], LOGGER_DEBUG);
+                                       return false;
+                               }
+                       }
+
+                       $item_stored = item_store($item, true);
+                       if ($item_stored) {
+                               logger("Uri ".$item["uri"]." wasn't found in conversation ".$conversation_url, LOGGER_DEBUG);
+                               self::store_conversation($item_stored, $conversation_url);
+                       }
                }
 
-               if (isset($item["uri"]) AND ($item["uri"] == $arr["uri"])) {
-                       $item = array();
-                       $item_stored = $newitem;
+               return($item_stored);
+       }
+
+       /**
+        * @brief Stores conversation data into the database
+        *
+        * @param integer $itemid The id of the item
+        * @param string $conversation_url The uri of the conversation
+        */
+       private function store_conversation($itemid, $conversation_url) {
+
+               $conversation_url = self::convert_href($conversation_url);
+
+               $messages = q("SELECT `uid`, `parent`, `created`, `received`, `guid` FROM `item` WHERE `id` = %d LIMIT 1", intval($itemid));
+               if (!$messages)
+                       return;
+               $message = $messages[0];
+
+               // Store conversation url if not done before
+               $conversation = q("SELECT `url` FROM `term` WHERE `uid` = %d AND `oid` = %d AND `otype` = %d AND `type` = %d",
+                       intval($message["uid"]), intval($itemid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION));
+
+               if (!$conversation) {
+                       $r = q("INSERT INTO `term` (`uid`, `oid`, `otype`, `type`, `term`, `url`, `created`, `received`, `guid`) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s')",
+                               intval($message["uid"]), intval($itemid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION),
+                               dbesc($message["created"]), dbesc($conversation_url), dbesc($message["created"]), dbesc($message["received"]), dbesc($message["guid"]));
+                       logger('Storing conversation url '.$conversation_url.' for id '.$itemid);
                }
+       }
 
-               logger('Stored new item '.$plink.' for parent '.$arr["parent-uri"].' under id '.$newitem, LOGGER_DEBUG);
+       /**
+        * @brief Checks if the current post is a reshare
+        *
+        * @param array $item The item array of thw post
+        *
+        * @return string The guid if the post is a reshare
+        */
+       private function get_reshared_guid($item) {
+               $body = trim($item["body"]);
+
+               // Skip if it isn't a pure repeated messages
+               // Does it start with a share?
+               if (strpos($body, "[share") > 0)
+                       return("");
+
+               // Does it end with a share?
+               if (strlen($body) > (strrpos($body, "[/share]") + 8))
+                       return("");
+
+               $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
+               // Skip if there is no shared message in there
+               if ($body == $attributes)
+                       return(false);
+
+               $guid = "";
+               preg_match("/guid='(.*?)'/ism", $attributes, $matches);
+               if ($matches[1] != "")
+                       $guid = $matches[1];
+
+               preg_match('/guid="(.*?)"/ism', $attributes, $matches);
+               if ($matches[1] != "")
+                       $guid = $matches[1];
+
+               return $guid;
+       }
+
+       /**
+        * @brief Cleans the body of a post if it contains picture links
+        *
+        * @param string $body The body
+        *
+        * @return string The cleaned body
+        */
+       private function format_picture_post($body) {
+               $siteinfo = get_attached_data($body);
+
+               if (($siteinfo["type"] == "photo")) {
+                       if (isset($siteinfo["preview"]))
+                               $preview = $siteinfo["preview"];
+                       else
+                               $preview = $siteinfo["image"];
 
-               // Add the conversation entry (but don't fetch the whole conversation)
-               ostatus_store_conversation($newitem, $conversation_url);
+                       // Is it a remote picture? Then make a smaller preview here
+                       $preview = proxy_url($preview, false, PROXY_SIZE_SMALL);
 
-               // If the newly created item is the top item then change the parent settings of the thread
-               // This shouldn't happen anymore. This is supposed to be absolote.
-               if ($arr["uri"] == $first_id) {
-                       logger('setting new parent to id '.$newitem);
-                       $new_parents = q("SELECT `id`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `uid` = %d AND `id` = %d LIMIT 1",
-                               intval($uid), intval($newitem));
-                       if ($new_parents)
-                               $parent = $new_parents[0];
+                       // Is it a local picture? Then make it smaller here
+                       $preview = str_replace(array("-0.jpg", "-0.png"), array("-2.jpg", "-2.png"), $preview);
+                       $preview = str_replace(array("-1.jpg", "-1.png"), array("-2.jpg", "-2.png"), $preview);
+
+                       if (isset($siteinfo["url"]))
+                               $url = $siteinfo["url"];
+                       else
+                               $url = $siteinfo["image"];
+
+                       $body = trim($siteinfo["text"])." [url]".$url."[/url]\n[img]".$preview."[/img]";
                }
+
+               return $body;
+       }
+
+       /**
+        * @brief Adds the header elements to the XML document
+        *
+        * @param object $doc XML document
+        * @param array $owner Contact data of the poster
+        *
+        * @return object header root element
+        */
+       private function add_header($doc, $owner) {
+
+               $a = get_app();
+
+               $root = $doc->createElementNS(NAMESPACE_ATOM1, 'feed');
+               $doc->appendChild($root);
+
+               $root->setAttribute("xmlns:thr", NAMESPACE_THREAD);
+               $root->setAttribute("xmlns:georss", NAMESPACE_GEORSS);
+               $root->setAttribute("xmlns:activity", NAMESPACE_ACTIVITY);
+               $root->setAttribute("xmlns:media", NAMESPACE_MEDIA);
+               $root->setAttribute("xmlns:poco", NAMESPACE_POCO);
+               $root->setAttribute("xmlns:ostatus", NAMESPACE_OSTATUS);
+               $root->setAttribute("xmlns:statusnet", NAMESPACE_STATUSNET);
+
+               $attributes = array("uri" => "https://friendi.ca", "version" => FRIENDICA_VERSION."-".DB_UPDATE_VERSION);
+               xml::add_element($doc, $root, "generator", FRIENDICA_PLATFORM, $attributes);
+               xml::add_element($doc, $root, "id", App::get_baseurl()."/profile/".$owner["nick"]);
+               xml::add_element($doc, $root, "title", sprintf("%s timeline", $owner["name"]));
+               xml::add_element($doc, $root, "subtitle", sprintf("Updates from %s on %s", $owner["name"], $a->config["sitename"]));
+               xml::add_element($doc, $root, "logo", $owner["photo"]);
+               xml::add_element($doc, $root, "updated", datetime_convert("UTC", "UTC", "now", ATOM_TIME));
+
+               $author = self::add_author($doc, $owner);
+               $root->appendChild($author);
+
+               $attributes = array("href" => $owner["url"], "rel" => "alternate", "type" => "text/html");
+               xml::add_element($doc, $root, "link", "", $attributes);
+
+               /// @TODO We have to find out what this is
+               /// $attributes = array("href" => App::get_baseurl()."/sup",
+               ///             "rel" => "http://api.friendfeed.com/2008/03#sup",
+               ///             "type" => "application/json");
+               /// xml::add_element($doc, $root, "link", "", $attributes);
+
+               self::hublinks($doc, $root);
+
+               $attributes = array("href" => App::get_baseurl()."/salmon/".$owner["nick"], "rel" => "salmon");
+               xml::add_element($doc, $root, "link", "", $attributes);
+
+               $attributes = array("href" => App::get_baseurl()."/salmon/".$owner["nick"], "rel" => "http://salmon-protocol.org/ns/salmon-replies");
+               xml::add_element($doc, $root, "link", "", $attributes);
+
+               $attributes = array("href" => App::get_baseurl()."/salmon/".$owner["nick"], "rel" => "http://salmon-protocol.org/ns/salmon-mention");
+               xml::add_element($doc, $root, "link", "", $attributes);
+
+               $attributes = array("href" => App::get_baseurl()."/api/statuses/user_timeline/".$owner["nick"].".atom",
+                               "rel" => "self", "type" => "application/atom+xml");
+               xml::add_element($doc, $root, "link", "", $attributes);
+
+               return $root;
        }
 
-       if (($item_stored < 0) AND (count($item) > 0)) {
-               //$arr["app"] .= " (OStatus-NoConvFound)";
-               $item_stored = item_store($item, true);
-               if ($item_stored) {
-                       logger("Uri ".$item["uri"]." wasn't found in conversation ".$conversation_url, LOGGER_DEBUG);
-                       ostatus_store_conversation($item_stored, $conversation_url);
+       /**
+        * @brief Add the link to the push hubs to the XML document
+        *
+        * @param object $doc XML document
+        * @param object $root XML root element where the hub links are added
+        */
+       public static function hublinks($doc, $root) {
+               $hub = get_config('system','huburl');
+
+               $hubxml = '';
+               if(strlen($hub)) {
+                       $hubs = explode(',', $hub);
+                       if(count($hubs)) {
+                               foreach($hubs as $h) {
+                                       $h = trim($h);
+                                       if(! strlen($h))
+                                               continue;
+                                       if ($h === '[internal]')
+                                               $h = App::get_baseurl() . '/pubsubhubbub';
+                                       xml::add_element($doc, $root, "link", "", array("href" => $h, "rel" => "hub"));
+                               }
+                       }
                }
        }
 
-       return($item_stored);
-}
+       /**
+        * @brief Adds attachement data to the XML document
+        *
+        * @param object $doc XML document
+        * @param object $root XML root element where the hub links are added
+        * @param array $item Data of the item that is to be posted
+        */
+       private function get_attachment($doc, $root, $item) {
+               $o = "";
+               $siteinfo = get_attached_data($item["body"]);
+
+               switch($siteinfo["type"]) {
+                       case 'link':
+                               $attributes = array("rel" => "enclosure",
+                                               "href" => $siteinfo["url"],
+                                               "type" => "text/html; charset=UTF-8",
+                                               "length" => "",
+                                               "title" => $siteinfo["title"]);
+                               xml::add_element($doc, $root, "link", "", $attributes);
+                               break;
+                       case 'photo':
+                               $imgdata = get_photo_info($siteinfo["image"]);
+                               $attributes = array("rel" => "enclosure",
+                                               "href" => $siteinfo["image"],
+                                               "type" => $imgdata["mime"],
+                                               "length" => intval($imgdata["size"]));
+                               xml::add_element($doc, $root, "link", "", $attributes);
+                               break;
+                       case 'video':
+                               $attributes = array("rel" => "enclosure",
+                                               "href" => $siteinfo["url"],
+                                               "type" => "text/html; charset=UTF-8",
+                                               "length" => "",
+                                               "title" => $siteinfo["title"]);
+                               xml::add_element($doc, $root, "link", "", $attributes);
+                               break;
+                       default:
+                               break;
+               }
 
-function ostatus_store_conversation($itemid, $conversation_url) {
-       global $a;
+               if (($siteinfo["type"] != "photo") AND isset($siteinfo["image"])) {
+                       $photodata = get_photo_info($siteinfo["image"]);
 
-       $conversation_url = ostatus_convert_href($conversation_url);
+                       $attributes = array("rel" => "preview", "href" => $siteinfo["image"], "media:width" => $photodata[0], "media:height" => $photodata[1]);
+                       xml::add_element($doc, $root, "link", "", $attributes);
+               }
 
-       $messages = q("SELECT `uid`, `parent`, `created`, `received`, `guid` FROM `item` WHERE `id` = %d LIMIT 1", intval($itemid));
-       if (!$messages)
-               return;
-       $message = $messages[0];
 
-       // Store conversation url if not done before
-       $conversation = q("SELECT `url` FROM `term` WHERE `uid` = %d AND `oid` = %d AND `otype` = %d AND `type` = %d",
-               intval($message["uid"]), intval($itemid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION));
+               $arr = explode('[/attach],',$item['attach']);
+               if(count($arr)) {
+                       foreach($arr as $r) {
+                               $matches = false;
+                               $cnt = preg_match('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"|',$r,$matches);
+                               if($cnt) {
+                                       $attributes = array("rel" => "enclosure",
+                                                       "href" => $matches[1],
+                                                       "type" => $matches[3]);
 
-       if (!$conversation) {
-               $r = q("INSERT INTO `term` (`uid`, `oid`, `otype`, `type`, `term`, `url`, `created`, `received`, `guid`) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s')",
-                       intval($message["uid"]), intval($itemid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION),
-                       dbesc($message["created"]), dbesc($conversation_url), dbesc($message["created"]), dbesc($message["received"]), dbesc($message["guid"]));
-               logger('Storing conversation url '.$conversation_url.' for id '.$itemid);
-       }
-}
+                                       if(intval($matches[2]))
+                                               $attributes["length"] = intval($matches[2]);
 
-function get_reshared_guid($item) {
-       $body = trim($item["body"]);
+                                       if(trim($matches[4]) != "")
+                                               $attributes["title"] = trim($matches[4]);
 
-       // Skip if it isn't a pure repeated messages
-       // Does it start with a share?
-       if (strpos($body, "[share") > 0)
-               return("");
+                                       xml::add_element($doc, $root, "link", "", $attributes);
+                               }
+                       }
+               }
+       }
 
-       // Does it end with a share?
-       if (strlen($body) > (strrpos($body, "[/share]") + 8))
-               return("");
+       /**
+        * @brief Adds the author element to the XML document
+        *
+        * @param object $doc XML document
+        * @param array $owner Contact data of the poster
+        *
+        * @return object author element
+        */
+       private function add_author($doc, $owner) {
+
+               $r = q("SELECT `homepage` FROM `profile` WHERE `uid` = %d AND `is-default` LIMIT 1", intval($owner["uid"]));
+               if ($r)
+                       $profile = $r[0];
+
+               $author = $doc->createElement("author");
+               xml::add_element($doc, $author, "activity:object-type", ACTIVITY_OBJ_PERSON);
+               xml::add_element($doc, $author, "uri", $owner["url"]);
+               xml::add_element($doc, $author, "name", $owner["name"]);
+               xml::add_element($doc, $author, "summary", bbcode($owner["about"], false, false, 7));
+
+               $attributes = array("rel" => "alternate", "type" => "text/html", "href" => $owner["url"]);
+               xml::add_element($doc, $author, "link", "", $attributes);
 
-       $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
-       // Skip if there is no shared message in there
-       if ($body == $attributes)
-               return(false);
+               $attributes = array(
+                               "rel" => "avatar",
+                               "type" => "image/jpeg", // To-Do?
+                               "media:width" => 175,
+                               "media:height" => 175,
+                               "href" => $owner["photo"]);
+               xml::add_element($doc, $author, "link", "", $attributes);
+
+               if (isset($owner["thumb"])) {
+                       $attributes = array(
+                                       "rel" => "avatar",
+                                       "type" => "image/jpeg", // To-Do?
+                                       "media:width" => 80,
+                                       "media:height" => 80,
+                                       "href" => $owner["thumb"]);
+                       xml::add_element($doc, $author, "link", "", $attributes);
+               }
 
-       $guid = "";
-       preg_match("/guid='(.*?)'/ism", $attributes, $matches);
-       if ($matches[1] != "")
-               $guid = $matches[1];
+               xml::add_element($doc, $author, "poco:preferredUsername", $owner["nick"]);
+               xml::add_element($doc, $author, "poco:displayName", $owner["name"]);
+               xml::add_element($doc, $author, "poco:note", bbcode($owner["about"], false, false, 7));
 
-       preg_match('/guid="(.*?)"/ism', $attributes, $matches);
-       if ($matches[1] != "")
-               $guid = $matches[1];
+               if (trim($owner["location"]) != "") {
+                       $element = $doc->createElement("poco:address");
+                       xml::add_element($doc, $element, "poco:formatted", $owner["location"]);
+                       $author->appendChild($element);
+               }
 
-       return $guid;
-}
+               if (trim($profile["homepage"]) != "") {
+                       $urls = $doc->createElement("poco:urls");
+                       xml::add_element($doc, $urls, "poco:type", "homepage");
+                       xml::add_element($doc, $urls, "poco:value", $profile["homepage"]);
+                       xml::add_element($doc, $urls, "poco:primary", "true");
+                       $author->appendChild($urls);
+               }
 
-function xml_create_element($doc, $element, $value = "", $attributes = array()) {
-       $element = $doc->createElement($element, xmlify($value));
+               if (count($profile)) {
+                       xml::add_element($doc, $author, "followers", "", array("url" => App::get_baseurl()."/viewcontacts/".$owner["nick"]));
+                       xml::add_element($doc, $author, "statusnet:profile_info", "", array("local_id" => $owner["uid"]));
+               }
 
-       foreach ($attributes AS $key => $value) {
-               $attribute = $doc->createAttribute($key);
-               $attribute->value = xmlify($value);
-               $element->appendChild($attribute);
+               return $author;
        }
-       return $element;
-}
 
-function xml_add_element($doc, $parent, $element, $value = "", $attributes = array()) {
-       $element = xml_create_element($doc, $element, $value, $attributes);
-       $parent->appendChild($element);
-}
+       /**
+        * @TODO Picture attachments should look like this:
+        *      <a href="https://status.pirati.ca/attachment/572819" title="https://status.pirati.ca/file/heluecht-20151202T222602-rd3u49p.gif"
+        *      class="attachment thumbnail" id="attachment-572819" rel="nofollow external">https://status.pirati.ca/attachment/572819</a>
+        *
+       */
+
+       /**
+        * @brief Returns the given activity if present - otherwise returns the "post" activity
+        *
+        * @param array $item Data of the item that is to be posted
+        *
+        * @return string activity
+        */
+       function construct_verb($item) {
+               if ($item['verb'])
+                       return $item['verb'];
+               return ACTIVITY_POST;
+       }
 
-function ostatus_format_picture_post($body) {
-       $siteinfo = get_attached_data($body);
+       /**
+        * @brief Returns the given object type if present - otherwise returns the "note" object type
+        *
+        * @param array $item Data of the item that is to be posted
+        *
+        * @return string Object type
+        */
+       function construct_objecttype($item) {
+               if (in_array($item['object-type'], array(ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_COMMENT)))
+                       return $item['object-type'];
+               return ACTIVITY_OBJ_NOTE;
+       }
 
-       if (($siteinfo["type"] == "photo")) {
-               if (isset($siteinfo["preview"]))
-                       $preview = $siteinfo["preview"];
+       /**
+        * @brief Adds an entry element to the XML document
+        *
+        * @param object $doc XML document
+        * @param array $item Data of the item that is to be posted
+        * @param array $owner Contact data of the poster
+        * @param bool $toplevel
+        *
+        * @return object Entry element
+        */
+       private function entry($doc, $item, $owner, $toplevel = false) {
+               $repeated_guid = self::get_reshared_guid($item);
+               if ($repeated_guid != "")
+                       $xml = self::reshare_entry($doc, $item, $owner, $repeated_guid, $toplevel);
+
+               if ($xml)
+                       return $xml;
+
+               if ($item["verb"] == ACTIVITY_LIKE)
+                       return self::like_entry($doc, $item, $owner, $toplevel);
                else
-                       $preview = $siteinfo["image"];
+                       return self::note_entry($doc, $item, $owner, $toplevel);
+       }
 
-               // Is it a remote picture? Then make a smaller preview here
-               $preview = proxy_url($preview, false, PROXY_SIZE_SMALL);
+       /**
+        * @brief Adds a source entry to the XML document
+        *
+        * @param object $doc XML document
+        * @param array $contact Array of the contact that is added
+        *
+        * @return object Source element
+        */
+       private function source_entry($doc, $contact) {
+               $source = $doc->createElement("source");
+               xml::add_element($doc, $source, "id", $contact["poll"]);
+               xml::add_element($doc, $source, "title", $contact["name"]);
+               xml::add_element($doc, $source, "link", "", array("rel" => "alternate",
+                                                               "type" => "text/html",
+                                                               "href" => $contact["alias"]));
+               xml::add_element($doc, $source, "link", "", array("rel" => "self",
+                                                               "type" => "application/atom+xml",
+                                                               "href" => $contact["poll"]));
+               xml::add_element($doc, $source, "icon", $contact["photo"]);
+               xml::add_element($doc, $source, "updated", datetime_convert("UTC","UTC",$contact["success_update"]."+00:00",ATOM_TIME));
+
+               return $source;
+       }
 
-               // Is it a local picture? Then make it smaller here
-               $preview = str_replace(array("-0.jpg", "-0.png"), array("-2.jpg", "-2.png"), $preview);
-               $preview = str_replace(array("-1.jpg", "-1.png"), array("-2.jpg", "-2.png"), $preview);
+       /**
+        * @brief Fetches contact data from the contact or the gcontact table
+        *
+        * @param string $url URL of the contact
+        * @param array $owner Contact data of the poster
+        *
+        * @return array Contact array
+        */
+       private function contact_entry($url, $owner) {
+
+               $r = q("SELECT * FROM `contact` WHERE `nurl` = '%s' AND `uid` IN (0, %d) ORDER BY `uid` DESC LIMIT 1",
+                       dbesc(normalise_link($url)), intval($owner["uid"]));
+               if ($r) {
+                       $contact = $r[0];
+                       $contact["uid"] = -1;
+               }
 
-               if (isset($siteinfo["url"]))
-                       $url = $siteinfo["url"];
-               else
-                       $url = $siteinfo["image"];
+               if (!$r) {
+                       $r = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s' LIMIT 1",
+                               dbesc(normalise_link($url)));
+                       if ($r) {
+                               $contact = $r[0];
+                               $contact["uid"] = -1;
+                               $contact["success_update"] = $contact["updated"];
+                       }
+               }
+
+               if (!$r)
+                       $contact = owner;
+
+               if (!isset($contact["poll"])) {
+                       $data = probe_url($url);
+                       $contact["poll"] = $data["poll"];
 
-               $body = trim($siteinfo["text"])." [url]".$url."[/url]\n[img]".$preview."[/img]";
+                       if (!$contact["alias"])
+                               $contact["alias"] = $data["alias"];
+               }
+
+               if (!isset($contact["alias"]))
+                       $contact["alias"] = $contact["url"];
+
+               return $contact;
        }
 
-       return $body;
-}
+       /**
+        * @brief Adds an entry element with reshared content
+        *
+        * @param object $doc XML document
+        * @param array $item Data of the item that is to be posted
+        * @param array $owner Contact data of the poster
+        * @param $repeated_guid
+        * @param bool $toplevel Is it for en entry element (false) or a feed entry (true)?
+        *
+        * @return object Entry element
+        */
+       private function reshare_entry($doc, $item, $owner, $repeated_guid, $toplevel) {
+
+               if (($item["id"] != $item["parent"]) AND (normalise_link($item["author-link"]) != normalise_link($owner["url"]))) {
+                       logger("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", LOGGER_DEBUG);
+               }
 
-function ostatus_add_header($doc, $owner) {
-       $a = get_app();
+               $title = self::entry_header($doc, $entry, $owner, $toplevel);
 
-       $root = $doc->createElementNS(NAMESPACE_ATOM1, 'feed');
-       $doc->appendChild($root);
+               $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' AND NOT `private` AND `network` IN ('%s', '%s', '%s') LIMIT 1",
+                       intval($owner["uid"]), dbesc($repeated_guid),
+                       dbesc(NETWORK_DFRN), dbesc(NETWORK_DIASPORA), dbesc(NETWORK_OSTATUS));
+               if ($r)
+                       $repeated_item = $r[0];
+               else
+                       return false;
 
-       $root->setAttribute("xmlns:thr", NAMESPACE_THREAD);
-       $root->setAttribute("xmlns:georss", NAMESPACE_GEORSS);
-       $root->setAttribute("xmlns:activity", NAMESPACE_ACTIVITY);
-       $root->setAttribute("xmlns:media", NAMESPACE_MEDIA);
-       $root->setAttribute("xmlns:poco", NAMESPACE_POCO);
-       $root->setAttribute("xmlns:ostatus", NAMESPACE_OSTATUS);
-       $root->setAttribute("xmlns:statusnet", NAMESPACE_STATUSNET);
+               $contact = self::contact_entry($repeated_item['author-link'], $owner);
 
-       $attributes = array("uri" => "https://friendi.ca", "version" => FRIENDICA_VERSION."-".DB_UPDATE_VERSION);
-       xml_add_element($doc, $root, "generator", FRIENDICA_PLATFORM, $attributes);
-       xml_add_element($doc, $root, "id", $a->get_baseurl()."/profile/".$owner["nick"]);
-       xml_add_element($doc, $root, "title", sprintf("%s timeline", $owner["name"]));
-       xml_add_element($doc, $root, "subtitle", sprintf("Updates from %s on %s", $owner["name"], $a->config["sitename"]));
-       xml_add_element($doc, $root, "logo", $owner["photo"]);
-       xml_add_element($doc, $root, "updated", datetime_convert("UTC", "UTC", "now", ATOM_TIME));
+               $parent_item = (($item['thr-parent']) ? $item['thr-parent'] : $item['parent-uri']);
 
-       $author = ostatus_add_author($doc, $owner);
-       $root->appendChild($author);
+               $title = $owner["nick"]." repeated a notice by ".$contact["nick"];
 
-       $attributes = array("href" => $owner["url"], "rel" => "alternate", "type" => "text/html");
-       xml_add_element($doc, $root, "link", "", $attributes);
+               self::entry_content($doc, $entry, $item, $owner, $title, ACTIVITY_SHARE, false);
 
-       /// @TODO We have to find out what this is
-       /// $attributes = array("href" => $a->get_baseurl()."/sup",
-       ///             "rel" => "http://api.friendfeed.com/2008/03#sup",
-       ///             "type" => "application/json");
-       /// xml_add_element($doc, $root, "link", "", $attributes);
+               $as_object = $doc->createElement("activity:object");
 
-       ostatus_hublinks($doc, $root);
+               xml::add_element($doc, $as_object, "activity:object-type", NAMESPACE_ACTIVITY_SCHEMA."activity");
 
-       $attributes = array("href" => $a->get_baseurl()."/salmon/".$owner["nick"], "rel" => "salmon");
-       xml_add_element($doc, $root, "link", "", $attributes);
+               self::entry_content($doc, $as_object, $repeated_item, $owner, "", "", false);
 
-       $attributes = array("href" => $a->get_baseurl()."/salmon/".$owner["nick"], "rel" => "http://salmon-protocol.org/ns/salmon-replies");
-       xml_add_element($doc, $root, "link", "", $attributes);
+               $author = self::add_author($doc, $contact);
+                $as_object->appendChild($author);
 
-       $attributes = array("href" => $a->get_baseurl()."/salmon/".$owner["nick"], "rel" => "http://salmon-protocol.org/ns/salmon-mention");
-       xml_add_element($doc, $root, "link", "", $attributes);
+               $as_object2 = $doc->createElement("activity:object");
 
-       $attributes = array("href" => $a->get_baseurl()."/api/statuses/user_timeline/".$owner["nick"].".atom",
-                       "rel" => "self", "type" => "application/atom+xml");
-       xml_add_element($doc, $root, "link", "", $attributes);
+               xml::add_element($doc, $as_object2, "activity:object-type", self::construct_objecttype($repeated_item));
 
-       return $root;
-}
+               $title = sprintf("New comment by %s", $contact["nick"]);
 
-function ostatus_hublinks($doc, $root) {
-       $a = get_app();
-       $hub = get_config('system','huburl');
-
-       $hubxml = '';
-       if(strlen($hub)) {
-               $hubs = explode(',', $hub);
-               if(count($hubs)) {
-                       foreach($hubs as $h) {
-                               $h = trim($h);
-                               if(! strlen($h))
-                                       continue;
-                               if ($h === '[internal]')
-                                       $h = $a->get_baseurl() . '/pubsubhubbub';
-                               xml_add_element($doc, $root, "link", "", array("href" => $h, "rel" => "hub"));
-                       }
-               }
-       }
-}
+               self::entry_content($doc, $as_object2, $repeated_item, $owner, $title);
 
-function ostatus_get_attachment($doc, $root, $item) {
-       $o = "";
-       $siteinfo = get_attached_data($item["body"]);
-
-       switch($siteinfo["type"]) {
-               case 'link':
-                       $attributes = array("rel" => "enclosure",
-                                       "href" => $siteinfo["url"],
-                                       "type" => "text/html; charset=UTF-8",
-                                       "length" => "",
-                                       "title" => $siteinfo["title"]);
-                       xml_add_element($doc, $root, "link", "", $attributes);
-                       break;
-               case 'photo':
-                       $imgdata = get_photo_info($siteinfo["image"]);
-                       $attributes = array("rel" => "enclosure",
-                                       "href" => $siteinfo["image"],
-                                       "type" => $imgdata["mime"],
-                                       "length" => intval($imgdata["size"]));
-                       xml_add_element($doc, $root, "link", "", $attributes);
-                       break;
-               case 'video':
-                       $attributes = array("rel" => "enclosure",
-                                       "href" => $siteinfo["url"],
-                                       "type" => "text/html; charset=UTF-8",
-                                       "length" => "",
-                                       "title" => $siteinfo["title"]);
-                       xml_add_element($doc, $root, "link", "", $attributes);
-                       break;
-               default:
-                       break;
-       }
+               $as_object->appendChild($as_object2);
 
-       if (($siteinfo["type"] != "photo") AND isset($siteinfo["image"])) {
-               $photodata = get_photo_info($siteinfo["image"]);
+               self::entry_footer($doc, $as_object, $item, $owner, false);
 
-               $attributes = array("rel" => "preview", "href" => $siteinfo["image"], "media:width" => $photodata[0], "media:height" => $photodata[1]);
-               xml_add_element($doc, $root, "link", "", $attributes);
-       }
+               $source = self::source_entry($doc, $contact);
 
+               $as_object->appendChild($source);
 
-       $arr = explode('[/attach],',$item['attach']);
-       if(count($arr)) {
-               foreach($arr as $r) {
-                       $matches = false;
-                       $cnt = preg_match('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"|',$r,$matches);
-                       if($cnt) {
-                               $attributes = array("rel" => "enclosure",
-                                               "href" => $matches[1],
-                                               "type" => $matches[3]);
+               $entry->appendChild($as_object);
 
-                               if(intval($matches[2]))
-                                       $attributes["length"] = intval($matches[2]);
+               self::entry_footer($doc, $entry, $item, $owner);
 
-                               if(trim($matches[4]) != "")
-                                       $attributes["title"] = trim($matches[4]);
+               return $entry;
+       }
 
-                               xml_add_element($doc, $root, "link", "", $attributes);
-                       }
+       /**
+        * @brief Adds an entry element with a "like"
+        *
+        * @param object $doc XML document
+        * @param array $item Data of the item that is to be posted
+        * @param array $owner Contact data of the poster
+        * @param bool $toplevel Is it for en entry element (false) or a feed entry (true)?
+        *
+        * @return object Entry element with "like"
+        */
+       private function like_entry($doc, $item, $owner, $toplevel) {
+
+               if (($item["id"] != $item["parent"]) AND (normalise_link($item["author-link"]) != normalise_link($owner["url"]))) {
+                       logger("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", LOGGER_DEBUG);
                }
-       }
-}
 
-function ostatus_add_author($doc, $owner) {
-       $a = get_app();
+               $title = self::entry_header($doc, $entry, $owner, $toplevel);
 
-       $r = q("SELECT `homepage` FROM `profile` WHERE `uid` = %d AND `is-default` LIMIT 1", intval($owner["uid"]));
-       if ($r)
-               $profile = $r[0];
+               $verb = NAMESPACE_ACTIVITY_SCHEMA."favorite";
+               self::entry_content($doc, $entry, $item, $owner, "Favorite", $verb, false);
 
-       $author = $doc->createElement("author");
-       xml_add_element($doc, $author, "activity:object-type", ACTIVITY_OBJ_PERSON);
-       xml_add_element($doc, $author, "uri", $owner["url"]);
-       xml_add_element($doc, $author, "name", $owner["name"]);
+               $as_object = $doc->createElement("activity:object");
 
-       $attributes = array("rel" => "alternate", "type" => "text/html", "href" => $owner["url"]);
-       xml_add_element($doc, $author, "link", "", $attributes);
+               $parent = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d",
+                       dbesc($item["thr-parent"]), intval($item["uid"]));
+               $parent_item = (($item['thr-parent']) ? $item['thr-parent'] : $item['parent-uri']);
 
-       $attributes = array(
-                       "rel" => "avatar",
-                       "type" => "image/jpeg", // To-Do?
-                       "media:width" => 175,
-                       "media:height" => 175,
-                       "href" => $owner["photo"]);
-       xml_add_element($doc, $author, "link", "", $attributes);
+               xml::add_element($doc, $as_object, "activity:object-type", self::construct_objecttype($parent[0]));
 
-       if (isset($owner["thumb"])) {
-               $attributes = array(
-                               "rel" => "avatar",
-                               "type" => "image/jpeg", // To-Do?
-                               "media:width" => 80,
-                               "media:height" => 80,
-                               "href" => $owner["thumb"]);
-               xml_add_element($doc, $author, "link", "", $attributes);
-       }
+               self::entry_content($doc, $as_object, $parent[0], $owner, "New entry");
 
-       xml_add_element($doc, $author, "poco:preferredUsername", $owner["nick"]);
-       xml_add_element($doc, $author, "poco:displayName", $owner["name"]);
-       xml_add_element($doc, $author, "poco:note", $owner["about"]);
+               $entry->appendChild($as_object);
 
-       if (trim($owner["location"]) != "") {
-               $element = $doc->createElement("poco:address");
-               xml_add_element($doc, $element, "poco:formatted", $owner["location"]);
-               $author->appendChild($element);
-       }
+               self::entry_footer($doc, $entry, $item, $owner);
 
-       if (trim($profile["homepage"]) != "") {
-               $urls = $doc->createElement("poco:urls");
-               xml_add_element($doc, $urls, "poco:type", "homepage");
-               xml_add_element($doc, $urls, "poco:value", $profile["homepage"]);
-               xml_add_element($doc, $urls, "poco:primary", "true");
-               $author->appendChild($urls);
+               return $entry;
        }
 
-       if (count($profile)) {
-               xml_add_element($doc, $author, "followers", "", array("url" => $a->get_baseurl()."/viewcontacts/".$owner["nick"]));
-               xml_add_element($doc, $author, "statusnet:profile_info", "", array("local_id" => $owner["uid"]));
-       }
+       /**
+        * @brief Adds a regular entry element
+        *
+        * @param object $doc XML document
+        * @param array $item Data of the item that is to be posted
+        * @param array $owner Contact data of the poster
+        * @param bool $toplevel Is it for en entry element (false) or a feed entry (true)?
+        *
+        * @return object Entry element
+        */
+       private function note_entry($doc, $item, $owner, $toplevel) {
+
+               if (($item["id"] != $item["parent"]) AND (normalise_link($item["author-link"]) != normalise_link($owner["url"]))) {
+                       logger("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", LOGGER_DEBUG);
+               }
 
-       return $author;
-}
+               $title = self::entry_header($doc, $entry, $owner, $toplevel);
 
-/** 
- * @TODO Picture attachments should look like this:
- *     <a href="https://status.pirati.ca/attachment/572819" title="https://status.pirati.ca/file/heluecht-20151202T222602-rd3u49p.gif"
- *     class="attachment thumbnail" id="attachment-572819" rel="nofollow external">https://status.pirati.ca/attachment/572819</a>
- * 
-*/
+               xml::add_element($doc, $entry, "activity:object-type", ACTIVITY_OBJ_NOTE);
 
-function ostatus_entry($doc, $item, $owner, $toplevel = false, $repeat = false) {
-       $a = get_app();
+               self::entry_content($doc, $entry, $item, $owner, $title);
 
-       if (($item["id"] != $item["parent"]) AND (normalise_link($item["author-link"]) != normalise_link($owner["url"]))) {
-               logger("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", LOGGER_DEBUG);
+               self::entry_footer($doc, $entry, $item, $owner);
+
+               return $entry;
        }
 
-       $is_repeat = false;
+       /**
+        * @brief Adds a header element to the XML document
+        *
+        * @param object $doc XML document
+        * @param object $entry The entry element where the elements are added
+        * @param array $owner Contact data of the poster
+        * @param bool $toplevel Is it for en entry element (false) or a feed entry (true)?
+        *
+        * @return string The title for the element
+        */
+       private function entry_header($doc, &$entry, $owner, $toplevel) {
+               /// @todo Check if this title stuff is really needed (I guess not)
+               if (!$toplevel) {
+                       $entry = $doc->createElement("entry");
+                       $title = sprintf("New note by %s", $owner["nick"]);
+               } else {
+                       $entry = $doc->createElementNS(NAMESPACE_ATOM1, "entry");
 
-/*     if (!$repeat) {
-               $repeated_guid = get_reshared_guid($item);
+                       $entry->setAttribute("xmlns:thr", NAMESPACE_THREAD);
+                       $entry->setAttribute("xmlns:georss", NAMESPACE_GEORSS);
+                       $entry->setAttribute("xmlns:activity", NAMESPACE_ACTIVITY);
+                       $entry->setAttribute("xmlns:media", NAMESPACE_MEDIA);
+                       $entry->setAttribute("xmlns:poco", NAMESPACE_POCO);
+                       $entry->setAttribute("xmlns:ostatus", NAMESPACE_OSTATUS);
+                       $entry->setAttribute("xmlns:statusnet", NAMESPACE_STATUSNET);
 
-               if ($repeated_guid != "") {
-                       $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
-                               intval($owner["uid"]), dbesc($repeated_guid));
-                       if ($r) {
-                               $repeated_item = $r[0];
-                               $is_repeat = true;
-                       }
+                       $author = self::add_author($doc, $owner);
+                       $entry->appendChild($author);
+
+                       $title = sprintf("New comment by %s", $owner["nick"]);
                }
-       }
-*/
-       if (!$toplevel AND !$repeat) {
-               $entry = $doc->createElement("entry");
-               $title = sprintf("New note by %s", $owner["nick"]);
-       } elseif (!$toplevel AND $repeat) {
-               $entry = $doc->createElement("activity:object");
-               $title = sprintf("New note by %s", $owner["nick"]);
-       } else {
-               $entry = $doc->createElementNS(NAMESPACE_ATOM1, "entry");
-
-               $entry->setAttribute("xmlns:thr", NAMESPACE_THREAD);
-               $entry->setAttribute("xmlns:georss", NAMESPACE_GEORSS);
-               $entry->setAttribute("xmlns:activity", NAMESPACE_ACTIVITY);
-               $entry->setAttribute("xmlns:media", NAMESPACE_MEDIA);
-               $entry->setAttribute("xmlns:poco", NAMESPACE_POCO);
-               $entry->setAttribute("xmlns:ostatus", NAMESPACE_OSTATUS);
-               $entry->setAttribute("xmlns:statusnet", NAMESPACE_STATUSNET);
-
-               $author = ostatus_add_author($doc, $owner);
-               $entry->appendChild($author);
-
-               $title = sprintf("New comment by %s", $owner["nick"]);
+               return $title;
        }
 
-       // To use the object-type "bookmark" we have to implement these elements:
-       //
-       // <activity:object-type>http://activitystrea.ms/schema/1.0/bookmark</activity:object-type>
-       // <title>Historic Rocket Landing</title>
-       // <summary>Nur ein Testbeitrag.</summary>
-       // <link rel="related" href="https://www.youtube.com/watch?v=9pillaOxGCo"/>
-       // <link rel="preview" href="https://pirati.cc/file/thumb-4526-450x338-b48c8055f0c2fed0c3f67adc234c4b99484a90c42ed3cac73dc1081a4d0a7bc1.jpg.jpg" media:width="450" media:height="338"/>
-       //
-       // But: it seems as if it doesn't federate well between the GS servers
-       // So we just set it to "note" to be sure that it reaches their target systems
-
-       if (!$repeat)
-               xml_add_element($doc, $entry, "activity:object-type", ACTIVITY_OBJ_NOTE);
-       else
-               xml_add_element($doc, $entry, "activity:object-type", NAMESPACE_ACTIVITY_SCHEMA.'activity');
-
-       xml_add_element($doc, $entry, "id", $item["uri"]);
-       xml_add_element($doc, $entry, "title", $title);
-
-       if($item['allow_cid'] || $item['allow_gid'] || $item['deny_cid'] || $item['deny_gid'])
-               $body = fix_private_photos($item['body'],$owner['uid'],$item, 0);
-       else
-               $body = $item['body'];
-
-       $body = ostatus_format_picture_post($body);
-
-       if ($item['title'] != "")
-               $body = "[b]".$item['title']."[/b]\n\n".$body;
-
-       //$body = bb_remove_share_information($body);
-       $body = bbcode($body, false, false, 7);
-
-       xml_add_element($doc, $entry, "content", $body, array("type" => "html"));
-
-       xml_add_element($doc, $entry, "link", "", array("rel" => "alternate", "type" => "text/html",
-                                                       "href" => $a->get_baseurl()."/display/".$item["guid"]));
-
-       xml_add_element($doc, $entry, "status_net", "", array("notice_id" => $item["id"]));
-
-       if (!$is_repeat)
-               xml_add_element($doc, $entry, "activity:verb", construct_verb($item));
-       else
-               xml_add_element($doc, $entry, "activity:verb", ACTIVITY_SHARE);
-
-       xml_add_element($doc, $entry, "published", datetime_convert("UTC","UTC",$item["created"]."+00:00",ATOM_TIME));
-       xml_add_element($doc, $entry, "updated", datetime_convert("UTC","UTC",$item["edited"]."+00:00",ATOM_TIME));
-
-       if ($is_repeat) {
-               $repeated_owner = array();
-               $repeated_owner["name"] = $repeated_item["author-name"];
-               $repeated_owner["url"] = $repeated_item["author-link"];
-               $repeated_owner["photo"] = $repeated_item["author-avatar"];
-               $repeated_owner["nick"] = $repeated_owner["name"];
-               $repeated_owner["location"] = "";
-               $repeated_owner["about"] = "";
-               $repeated_owner["uid"] = 0;
-
-               // Fetch the missing data from the global contacts
-               $r =q("SELECT * FROM `gcontact` WHERE `nurl` = '%s'", normalise_link($repeated_item["author-link"]));
-               if ($r) {
-                       if ($r[0]["nick"] != "")
-                               $repeated_owner["nick"] = $r[0]["nick"];
+       /**
+        * @brief Adds elements to the XML document
+        *
+        * @param object $doc XML document
+        * @param object $entry Entry element where the content is added
+        * @param array $item Data of the item that is to be posted
+        * @param array $owner Contact data of the poster
+        * @param string $title Title for the post
+        * @param string $verb The activity verb
+        * @param bool $complete Add the "status_net" element?
+        */
+       private function entry_content($doc, $entry, $item, $owner, $title, $verb = "", $complete = true) {
 
-                       $repeated_owner["location"] = $r[0]["location"];
-                       $repeated_owner["about"] = $r[0]["about"];
-               }
+               if ($verb == "")
+                       $verb = self::construct_verb($item);
 
-               $entry_repeat = ostatus_entry($doc, $repeated_item, $repeated_owner, false, true);
-               $entry->appendChild($entry_repeat);
-       } elseif ($repeat) {
-               $author = ostatus_add_author($doc, $owner);
-               $entry->appendChild($author);
-       }
+               xml::add_element($doc, $entry, "id", $item["uri"]);
+               xml::add_element($doc, $entry, "title", $title);
 
-       $mentioned = array();
+               $body = self::format_picture_post($item['body']);
 
-       if (($item['parent'] != $item['id']) || ($item['parent-uri'] !== $item['uri']) || (($item['thr-parent'] !== '') && ($item['thr-parent'] !== $item['uri']))) {
-               $parent = q("SELECT `guid`, `author-link`, `owner-link` FROM `item` WHERE `id` = %d", intval($item["parent"]));
-               $parent_item = (($item['thr-parent']) ? $item['thr-parent'] : $item['parent-uri']);
+               if ($item['title'] != "")
+                       $body = "[b]".$item['title']."[/b]\n\n".$body;
 
-               $attributes = array(
-                               "ref" => $parent_item,
-                               "type" => "text/html",
-                               "href" => $a->get_baseurl()."/display/".$parent[0]["guid"]);
-               xml_add_element($doc, $entry, "thr:in-reply-to", "", $attributes);
+               $body = bbcode($body, false, false, 7);
 
-               $attributes = array(
-                               "rel" => "related",
-                               "href" => $a->get_baseurl()."/display/".$parent[0]["guid"]);
-               xml_add_element($doc, $entry, "link", "", $attributes);
+               xml::add_element($doc, $entry, "content", $body, array("type" => "html"));
 
-               $mentioned[$parent[0]["author-link"]] = $parent[0]["author-link"];
-               $mentioned[$parent[0]["owner-link"]] = $parent[0]["owner-link"];
+               xml::add_element($doc, $entry, "link", "", array("rel" => "alternate", "type" => "text/html",
+                                                               "href" => App::get_baseurl()."/display/".$item["guid"]));
 
-               $thrparent = q("SELECT `guid`, `author-link`, `owner-link` FROM `item` WHERE `uid` = %d AND `uri` = '%s'",
-                               intval($owner["uid"]),
-                               dbesc($parent_item));
-               if ($thrparent) {
-                       $mentioned[$thrparent[0]["author-link"]] = $thrparent[0]["author-link"];
-                       $mentioned[$thrparent[0]["owner-link"]] = $thrparent[0]["owner-link"];
-               }
-       }
+               if ($complete)
+                       xml::add_element($doc, $entry, "status_net", "", array("notice_id" => $item["id"]));
 
-       xml_add_element($doc, $entry, "link", "", array("rel" => "ostatus:conversation",
-                                                       "href" => $a->get_baseurl()."/display/".$owner["nick"]."/".$item["parent"]));
-       xml_add_element($doc, $entry, "ostatus:conversation", $a->get_baseurl()."/display/".$owner["nick"]."/".$item["parent"]);
+               xml::add_element($doc, $entry, "activity:verb", $verb);
 
-       $tags = item_getfeedtags($item);
+               xml::add_element($doc, $entry, "published", datetime_convert("UTC","UTC",$item["created"]."+00:00",ATOM_TIME));
+               xml::add_element($doc, $entry, "updated", datetime_convert("UTC","UTC",$item["edited"]."+00:00",ATOM_TIME));
+       }
 
-       if(count($tags))
-               foreach($tags as $t)
-                       if ($t[0] == "@")
-                               $mentioned[$t[1]] = $t[1];
+       /**
+        * @brief Adds the elements at the foot of an entry to the XML document
+        *
+        * @param object $doc XML document
+        * @param object $entry The entry element where the elements are added
+        * @param array $item Data of the item that is to be posted
+        * @param array $owner Contact data of the poster
+        * @param $complete
+        */
+       private function entry_footer($doc, $entry, $item, $owner, $complete = true) {
+
+               $mentioned = array();
+
+               if (($item['parent'] != $item['id']) || ($item['parent-uri'] !== $item['uri']) || (($item['thr-parent'] !== '') && ($item['thr-parent'] !== $item['uri']))) {
+                       $parent = q("SELECT `guid`, `author-link`, `owner-link` FROM `item` WHERE `id` = %d", intval($item["parent"]));
+                       $parent_item = (($item['thr-parent']) ? $item['thr-parent'] : $item['parent-uri']);
+
+                       $attributes = array(
+                                       "ref" => $parent_item,
+                                       "type" => "text/html",
+                                       "href" => App::get_baseurl()."/display/".$parent[0]["guid"]);
+                       xml::add_element($doc, $entry, "thr:in-reply-to", "", $attributes);
+
+                       $attributes = array(
+                                       "rel" => "related",
+                                       "href" => App::get_baseurl()."/display/".$parent[0]["guid"]);
+                       xml::add_element($doc, $entry, "link", "", $attributes);
+
+                       $mentioned[$parent[0]["author-link"]] = $parent[0]["author-link"];
+                       $mentioned[$parent[0]["owner-link"]] = $parent[0]["owner-link"];
+
+                       $thrparent = q("SELECT `guid`, `author-link`, `owner-link` FROM `item` WHERE `uid` = %d AND `uri` = '%s'",
+                                       intval($owner["uid"]),
+                                       dbesc($parent_item));
+                       if ($thrparent) {
+                               $mentioned[$thrparent[0]["author-link"]] = $thrparent[0]["author-link"];
+                               $mentioned[$thrparent[0]["owner-link"]] = $thrparent[0]["owner-link"];
+                       }
+               }
 
-       // Make sure that mentions are accepted (GNU Social has problems with mixing HTTP and HTTPS)
-       $newmentions = array();
-       foreach ($mentioned AS $mention) {
-               $newmentions[str_replace("http://", "https://", $mention)] = str_replace("http://", "https://", $mention);
-               $newmentions[str_replace("https://", "http://", $mention)] = str_replace("https://", "http://", $mention);
-       }
-       $mentioned = $newmentions;
-
-       foreach ($mentioned AS $mention) {
-               $r = q("SELECT `forum`, `prv` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s'",
-                       intval($owner["uid"]),
-                       dbesc(normalise_link($mention)));
-               if ($r[0]["forum"] OR $r[0]["prv"])
-                       xml_add_element($doc, $entry, "link", "", array("rel" => "mentioned",
-                                                                               "ostatus:object-type" => ACTIVITY_OBJ_GROUP,
-                                                                               "href" => $mention));
-               else
-                       xml_add_element($doc, $entry, "link", "", array("rel" => "mentioned",
-                                                                               "ostatus:object-type" => ACTIVITY_OBJ_PERSON,
-                                                                               "href" => $mention));
-       }
+               xml::add_element($doc, $entry, "link", "", array("rel" => "ostatus:conversation",
+                                                       "href" => App::get_baseurl()."/display/".$owner["nick"]."/".$item["parent"]));
+               xml::add_element($doc, $entry, "ostatus:conversation", App::get_baseurl()."/display/".$owner["nick"]."/".$item["parent"]);
 
-       if (!$item["private"])
-               xml_add_element($doc, $entry, "link", "", array("rel" => "mentioned",
-                                                               "ostatus:object-type" => "http://activitystrea.ms/schema/1.0/collection",
-                                                               "href" => "http://activityschema.org/collection/public"));
+               $tags = item_getfeedtags($item);
 
-       if(count($tags))
-               foreach($tags as $t)
-                       if ($t[0] != "@")
-                               xml_add_element($doc, $entry, "category", "", array("term" => $t[2]));
+               if(count($tags))
+                       foreach($tags as $t)
+                               if ($t[0] == "@")
+                                       $mentioned[$t[1]] = $t[1];
 
-       ostatus_get_attachment($doc, $entry, $item);
+               // Make sure that mentions are accepted (GNU Social has problems with mixing HTTP and HTTPS)
+               $newmentions = array();
+               foreach ($mentioned AS $mention) {
+                       $newmentions[str_replace("http://", "https://", $mention)] = str_replace("http://", "https://", $mention);
+                       $newmentions[str_replace("https://", "http://", $mention)] = str_replace("https://", "http://", $mention);
+               }
+               $mentioned = $newmentions;
 
-       /// @TODO
-       /// The API call has yet to be implemented
-       //$attributes = array("href" => $a->get_baseurl()."/api/statuses/show/".$item["id"].".atom",
-       //              "rel" => "self", "type" => "application/atom+xml");
-       //xml_add_element($doc, $entry, "link", "", $attributes);
+               foreach ($mentioned AS $mention) {
+                       $r = q("SELECT `forum`, `prv` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s'",
+                               intval($owner["uid"]),
+                               dbesc(normalise_link($mention)));
+                       if ($r[0]["forum"] OR $r[0]["prv"])
+                               xml::add_element($doc, $entry, "link", "", array("rel" => "mentioned",
+                                                                                       "ostatus:object-type" => ACTIVITY_OBJ_GROUP,
+                                                                                       "href" => $mention));
+                       else
+                               xml::add_element($doc, $entry, "link", "", array("rel" => "mentioned",
+                                                                                       "ostatus:object-type" => ACTIVITY_OBJ_PERSON,
+                                                                                       "href" => $mention));
+               }
 
-       //$attributes = array("href" => $a->get_baseurl()."/api/statuses/show/".$item["id"].".atom",
-       //              "rel" => "edit", "type" => "application/atom+xml");
-       //xml_add_element($doc, $entry, "link", "", $attributes);
+               if (!$item["private"]) {
+                       xml::add_element($doc, $entry, "link", "", array("rel" => "ostatus:attention",
+                                                                       "href" => "http://activityschema.org/collection/public"));
+                       xml::add_element($doc, $entry, "link", "", array("rel" => "mentioned",
+                                                                       "ostatus:object-type" => "http://activitystrea.ms/schema/1.0/collection",
+                                                                       "href" => "http://activityschema.org/collection/public"));
+               }
 
-       $app = $item["app"];
-       if ($app == "")
-               $app = "web";
+               if(count($tags))
+                       foreach($tags as $t)
+                               if ($t[0] != "@")
+                                       xml::add_element($doc, $entry, "category", "", array("term" => $t[2]));
 
+               self::get_attachment($doc, $entry, $item);
 
-       $attributes = array("local_id" => $item["id"], "source" => $app);
-       if ($is_repeat)
-               $attributes["repeat_of"] = $repeated_item["id"];
+               if ($complete) {
+                       $app = $item["app"];
+                       if ($app == "")
+                               $app = "web";
 
-       xml_add_element($doc, $entry, "statusnet:notice_info", "", $attributes);
+                       $attributes = array("local_id" => $item["id"], "source" => $app);
 
-       return $entry;
-}
+                       if (isset($parent["id"]))
+                               $attributes["repeat_of"] = $parent["id"];
 
-function ostatus_feed(&$a, $owner_nick, $last_update) {
-
-       $r = q("SELECT `contact`.*, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags`
-                       FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid`
-                       WHERE `contact`.`self` AND `user`.`nickname` = '%s' LIMIT 1",
-                       dbesc($owner_nick));
-       if (!$r)
-               return;
-
-       $owner = $r[0];
-
-       if(!strlen($last_update))
-               $last_update = 'now -30 days';
-
-       $check_date = datetime_convert('UTC','UTC',$last_update,'Y-m-d H:i:s');
-
-       $items = q("SELECT STRAIGHT_JOIN `item`.*, `item`.`id` AS `item_id` FROM `item`
-                       INNER JOIN `thread` ON `thread`.`iid` = `item`.`parent`
-                       LEFT JOIN `item` AS `thritem` ON `thritem`.`uri`=`item`.`thr-parent` AND `thritem`.`uid`=`item`.`uid`
-                       WHERE `item`.`uid` = %d AND `item`.`received` > '%s' AND NOT `item`.`private` AND NOT `item`.`deleted`
-                               AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid`  = '' AND `item`.`deny_gid`  = ''
-                               AND ((`item`.`wall` AND (`item`.`parent` = `item`.`id`))
-                                       OR (`item`.`network` = '%s' AND ((`thread`.`network` IN ('%s', '%s')) OR (`thritem`.`network` IN ('%s', '%s')))) AND `thread`.`mention`)
-                               AND ((`item`.`owner-link` IN ('%s', '%s') AND (`item`.`parent` = `item`.`id`))
-                                       OR (`item`.`author-link` IN ('%s', '%s')))
-                       ORDER BY `item`.`received` DESC
-                       LIMIT 0, 300",
-                       intval($owner["uid"]), dbesc($check_date), dbesc(NETWORK_DFRN),
-                       //dbesc(NETWORK_OSTATUS), dbesc(NETWORK_OSTATUS),
-                       //dbesc(NETWORK_OSTATUS), dbesc(NETWORK_OSTATUS),
-                       dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN),
-                       dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN),
-                       dbesc($owner["nurl"]), dbesc(str_replace("http://", "https://", $owner["nurl"])),
-                       dbesc($owner["nurl"]), dbesc(str_replace("http://", "https://", $owner["nurl"]))
-               );
-
-       $doc = new DOMDocument('1.0', 'utf-8');
-       $doc->formatOutput = true;
-
-       $root = ostatus_add_header($doc, $owner);
-
-       foreach ($items AS $item) {
-               $entry = ostatus_entry($doc, $item, $owner);
-               $root->appendChild($entry);
+                       if ($item["coord"] != "")
+                               xml::add_element($doc, $entry, "georss:point", $item["coord"]);
+
+                       xml::add_element($doc, $entry, "statusnet:notice_info", "", $attributes);
+               }
        }
 
-       return(trim($doc->saveXML()));
-}
+       /**
+        * @brief Creates the XML feed for a given nickname
+        *
+        * @param app $a The application class
+        * @param string $owner_nick Nickname of the feed owner
+        * @param string $last_update Date of the last update
+        *
+        * @return string XML feed
+        */
+       public static function feed(&$a, $owner_nick, $last_update) {
+
+               $r = q("SELECT `contact`.*, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags`
+                               FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid`
+                               WHERE `contact`.`self` AND `user`.`nickname` = '%s' LIMIT 1",
+                               dbesc($owner_nick));
+               if (!$r)
+                       return;
 
-function ostatus_salmon($item,$owner) {
+               $owner = $r[0];
+
+               if(!strlen($last_update))
+                       $last_update = 'now -30 days';
+
+               $check_date = datetime_convert('UTC','UTC',$last_update,'Y-m-d H:i:s');
+
+               $items = q("SELECT STRAIGHT_JOIN `item`.*, `item`.`id` AS `item_id` FROM `item`
+                               INNER JOIN `thread` ON `thread`.`iid` = `item`.`parent`
+                               LEFT JOIN `item` AS `thritem` ON `thritem`.`uri`=`item`.`thr-parent` AND `thritem`.`uid`=`item`.`uid`
+                               WHERE `item`.`uid` = %d AND `item`.`received` > '%s' AND NOT `item`.`private` AND NOT `item`.`deleted`
+                                       AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid`  = '' AND `item`.`deny_gid`  = ''
+                                       AND ((`item`.`wall` AND (`item`.`parent` = `item`.`id`))
+                                               OR (`item`.`network` = '%s' AND ((`thread`.`network` IN ('%s', '%s')) OR (`thritem`.`network` IN ('%s', '%s')))) AND `thread`.`mention`)
+                                       AND ((`item`.`owner-link` IN ('%s', '%s') AND (`item`.`parent` = `item`.`id`))
+                                               OR (`item`.`author-link` IN ('%s', '%s')))
+                               ORDER BY `item`.`received` DESC
+                               LIMIT 0, 300",
+                               intval($owner["uid"]), dbesc($check_date), dbesc(NETWORK_DFRN),
+                               //dbesc(NETWORK_OSTATUS), dbesc(NETWORK_OSTATUS),
+                               //dbesc(NETWORK_OSTATUS), dbesc(NETWORK_OSTATUS),
+                               dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN),
+                               dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN),
+                               dbesc($owner["nurl"]), dbesc(str_replace("http://", "https://", $owner["nurl"])),
+                               dbesc($owner["nurl"]), dbesc(str_replace("http://", "https://", $owner["nurl"]))
+                       );
+
+               $doc = new DOMDocument('1.0', 'utf-8');
+               $doc->formatOutput = true;
+
+               $root = self::add_header($doc, $owner);
+
+               foreach ($items AS $item) {
+                       $entry = self::entry($doc, $item, $owner);
+                       $root->appendChild($entry);
+               }
+
+               return(trim($doc->saveXML()));
+       }
 
-       $doc = new DOMDocument('1.0', 'utf-8');
-       $doc->formatOutput = true;
+       /**
+        * @brief Creates the XML for a salmon message
+        *
+        * @param array $item Data of the item that is to be posted
+        * @param array $owner Contact data of the poster
+        *
+        * @return string XML for the salmon
+        */
+       public static function salmon($item,$owner) {
 
-       $entry = ostatus_entry($doc, $item, $owner, true);
+               $doc = new DOMDocument('1.0', 'utf-8');
+               $doc->formatOutput = true;
 
-       $doc->appendChild($entry);
+               $entry = self::entry($doc, $item, $owner, true);
 
-       return(trim($doc->saveXML()));
+               $doc->appendChild($entry);
+
+               return(trim($doc->saveXML()));
+       }
 }
 ?>
index 05431bee2d27d2b6ca601d9f0a93d02e873c9590..a2b2c56522c6a49f8756421cc1724422a6e263f8 100644 (file)
@@ -132,7 +132,19 @@ function shortenmsg($msg, $limit, $twitter = false) {
        return($msg);
 }
 
-function plaintext($a, $b, $limit = 0, $includedlinks = false, $htmlmode = 2) {
+/**
+ * @brief Convert a message into plaintext for connectors to other networks
+ *
+ * @param App $a The application class
+ * @param array $b The message array that is about to be posted
+ * @param int $limit The maximum number of characters when posting to that network
+ * @param bool $includedlinks Has an attached link to be included into the message?
+ * @param int $htmlmode This triggers the behaviour of the bbcode conversion
+ * @param string $target_network Name of the network where the post should go to.
+ *
+ * @return string The converted message
+ */
+function plaintext($a, $b, $limit = 0, $includedlinks = false, $htmlmode = 2, $target_network = "") {
        require_once("include/bbcode.php");
        require_once("include/html2plain.php");
        require_once("include/network.php");
@@ -144,6 +156,9 @@ function plaintext($a, $b, $limit = 0, $includedlinks = false, $htmlmode = 2) {
        // Add an URL element if the text contains a raw link
        $body = preg_replace("/([^\]\='".'"'."]|^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", '$1[url]$2[/url]', $body);
 
+       // Remove the abstract
+       $body = remove_abstract($body);
+
        // At first look at data that is attached via "type-..." stuff
        // This will hopefully replaced with a dedicated bbcode later
        //$post = get_attached_data($b["body"]);
@@ -154,6 +169,44 @@ function plaintext($a, $b, $limit = 0, $includedlinks = false, $htmlmode = 2) {
        elseif ($b["title"] != "")
                $post["text"] = trim($b["title"]);
 
+       $abstract = "";
+
+       // Fetch the abstract from the given target network
+       if ($target_network != "") {
+               $default_abstract = fetch_abstract($b["body"]);
+               $abstract = fetch_abstract($b["body"], $target_network);
+
+               // If we post to a network with no limit we only fetch
+               // an abstract exactly for this network
+               if (($limit == 0) AND ($abstract == $default_abstract))
+                       $abstract = "";
+
+       } else // Try to guess the correct target network
+               switch ($htmlmode) {
+                       case 8:
+                               $abstract = fetch_abstract($b["body"], NETWORK_TWITTER);
+                               break;
+                       case 7:
+                               $abstract = fetch_abstract($b["body"], NETWORK_STATUSNET);
+                               break;
+                       case 6:
+                               $abstract = fetch_abstract($b["body"], NETWORK_APPNET);
+                               break;
+                       default: // We don't know the exact target.
+                                // We fetch an abstract since there is a posting limit.
+                               if ($limit > 0)
+                                       $abstract = fetch_abstract($b["body"]);
+               }
+
+       if ($abstract != "") {
+               $post["text"] = $abstract;
+
+               if ($post["type"] == "text") {
+                       $post["type"] = "link";
+                       $post["url"] = $b["plink"];
+               }
+       }
+
        $html = bbcode($post["text"], false, false, $htmlmode);
        $msg = html2plain($html, 0, true);
        $msg = trim(html_entity_decode($msg,ENT_QUOTES,'UTF-8'));
index 190f3fb1ad50f2985ed583cc8ef8588ada309814..7ffd47aa68e653635f5460dbaa6ff454dfbdeb9f 100644 (file)
@@ -26,17 +26,11 @@ function poller_run(&$argv, &$argc){
                unset($db_host, $db_user, $db_pass, $db_data);
        };
 
-       $load = current_load();
-       if($load) {
-               $maxsysload = intval(get_config('system','maxloadavg'));
-               if($maxsysload < 1)
-                       $maxsysload = 50;
+       if (poller_max_connections_reached())
+               return;
 
-               if(intval($load) > $maxsysload) {
-                       logger('system: load ' . $load . ' too high. poller deferred to next scheduled run.');
-                       return;
-               }
-       }
+       if (App::maxload_reached())
+               return;
 
        // Checking the number of workers
        if (poller_too_much_workers(1)) {
@@ -65,6 +59,10 @@ function poller_run(&$argv, &$argc){
 
        while ($r = q("SELECT * FROM `workerqueue` WHERE `executed` = '0000-00-00 00:00:00' ORDER BY `created` LIMIT 1")) {
 
+               // Constantly check the number of available database connections to let the frontend be accessible at any time
+               if (poller_max_connections_reached())
+                       return;
+
                // Count active workers and compare them with a maximum value that depends on the load
                if (poller_too_much_workers(3))
                        return;
@@ -117,12 +115,93 @@ function poller_run(&$argv, &$argc){
 
 }
 
+/**
+ * @brief Checks if the number of database connections has reached a critical limit.
+ *
+ * @return bool Are more than 3/4 of the maximum connections used?
+ */
+function poller_max_connections_reached() {
+
+       // Fetch the max value from the config. This is needed when the system cannot detect the correct value by itself.
+       $max = get_config("system", "max_connections");
+
+       if ($max == 0) {
+               // the maximum number of possible user connections can be a system variable
+               $r = q("SHOW VARIABLES WHERE `variable_name` = 'max_user_connections'");
+               if ($r)
+                       $max = $r[0]["Value"];
+
+               // Or it can be granted. This overrides the system variable
+               $r = q("SHOW GRANTS");
+               if ($r)
+                       foreach ($r AS $grants) {
+                               $grant = array_pop($grants);
+                               if (stristr($grant, "GRANT USAGE ON"))
+                                       if (preg_match("/WITH MAX_USER_CONNECTIONS (\d*)/", $grant, $match))
+                                               $max = $match[1];
+                       }
+       }
+
+       // If $max is set we will use the processlist to determine the current number of connections
+       // The processlist only shows entries of the current user
+       if ($max != 0) {
+               $r = q("SHOW PROCESSLIST");
+               if (!$r)
+                       return false;
+
+               $used = count($r);
+
+               logger("Connection usage (user values): ".$used."/".$max, LOGGER_DEBUG);
+
+               $level = $used / $max;
+
+               if ($level >= (3/4)) {
+                       logger("Maximum level (3/4) of user connections reached: ".$used."/".$max);
+                       return true;
+               }
+       }
+
+       // We will now check for the system values.
+       // This limit could be reached although the user limits are fine.
+       $r = q("SHOW VARIABLES WHERE `variable_name` = 'max_connections'");
+       if (!$r)
+               return false;
+
+       $max = intval($r[0]["Value"]);
+       if ($max == 0)
+               return false;
+
+       $r = q("SHOW STATUS WHERE `variable_name` = 'Threads_connected'");
+       if (!$r)
+               return false;
+
+       $used = intval($r[0]["Value"]);
+       if ($used == 0)
+               return false;
+
+       logger("Connection usage (system values): ".$used."/".$max, LOGGER_DEBUG);
+
+       $level = $used / $max;
+
+       if ($level < (3/4))
+               return false;
+
+       logger("Maximum level (3/4) of system connections reached: ".$used."/".$max);
+       return true;
+}
+
 /**
  * @brief fix the queue entry if the worker process died
  *
  */
 function poller_kill_stale_workers() {
        $r = q("SELECT `pid`, `executed` FROM `workerqueue` WHERE `executed` != '0000-00-00 00:00:00'");
+
+       if (!is_array($r) || count($r) == 0) {
+               // No processing here needed
+               return;
+       }
+
        foreach($r AS $pid)
                if (!posix_kill($pid["pid"], 0))
                        q("UPDATE `workerqueue` SET `executed` = '0000-00-00 00:00:00', `pid` = 0 WHERE `pid` = %d",
diff --git a/include/post_update.php b/include/post_update.php
new file mode 100644 (file)
index 0000000..2bdfe1f
--- /dev/null
@@ -0,0 +1,141 @@
+<?php
+/**
+ * @file include/post_update.php
+ */
+
+/**
+ * @brief Calls the post update functions
+ */
+function post_update() {
+
+       if (!post_update_1192())
+               return;
+
+       if (!post_update_1194())
+               return;
+}
+
+/**
+ * @brief set the gcontact-id in all item entries
+ *
+ * This job has to be started multiple times until all entries are set.
+ * It isn't started in the update function since it would consume too much time and can be done in the background.
+ *
+ * @return bool "true" when the job is done
+ */
+function post_update_1192() {
+
+       // Was the script completed?
+       if (get_config("system", "post_update_version") >= 1192)
+               return true;
+
+       // Check if the first step is done (Setting "gcontact-id" in the item table)
+       $r = q("SELECT `author-link`, `author-name`, `author-avatar`, `uid`, `network` FROM `item` WHERE `gcontact-id` = 0 LIMIT 1000");
+       if (!$r) {
+               // Are there unfinished entries in the thread table?
+               $r = q("SELECT COUNT(*) AS `total` FROM `thread`
+                       INNER JOIN `item` ON `item`.`id` =`thread`.`iid`
+                       WHERE `thread`.`gcontact-id` = 0 AND
+                               (`thread`.`uid` IN (SELECT `uid` from `user`) OR `thread`.`uid` = 0)");
+
+               if ($r AND ($r[0]["total"] == 0)) {
+                       set_config("system", "post_update_version", 1192);
+                       return true;
+               }
+
+               // Update the thread table from the item table
+               q("UPDATE `thread` INNER JOIN `item` ON `item`.`id`=`thread`.`iid`
+                               SET `thread`.`gcontact-id` = `item`.`gcontact-id`
+                       WHERE `thread`.`gcontact-id` = 0 AND
+                               (`thread`.`uid` IN (SELECT `uid` from `user`) OR `thread`.`uid` = 0)");
+
+               return false;
+       }
+
+       $item_arr = array();
+       foreach ($r AS $item) {
+               $index = $item["author-link"]."-".$item["uid"];
+               $item_arr[$index] = array("author-link" => $item["author-link"],
+                                               "uid" => $item["uid"],
+                                               "network" => $item["network"]);
+       }
+
+       // Set the "gcontact-id" in the item table and add a new gcontact entry if needed
+       foreach($item_arr AS $item) {
+               $gcontact_id = get_gcontact_id(array("url" => $item['author-link'], "network" => $item['network'],
+                                               "photo" => $item['author-avatar'], "name" => $item['author-name']));
+               q("UPDATE `item` SET `gcontact-id` = %d WHERE `uid` = %d AND `author-link` = '%s' AND `gcontact-id` = 0",
+                       intval($gcontact_id), intval($item["uid"]), dbesc($item["author-link"]));
+       }
+       return false;
+}
+
+/**
+ * @brief Updates the "global" field in the item table
+ *
+ * @return bool "true" when the job is done
+ */
+function post_update_1194() {
+
+       // Was the script completed?
+       if (get_config("system", "post_update_version") >= 1194)
+               return true;
+
+       logger("Start", LOGGER_DEBUG);
+
+       $end_id = get_config("system", "post_update_1194_end");
+       if (!$end_id) {
+               $r = q("SELECT `id` FROM `item` WHERE `uid` != 0 ORDER BY `id` DESC LIMIT 1");
+               if ($r) {
+                       set_config("system", "post_update_1194_end", $r[0]["id"]);
+                       $end_id = get_config("system", "post_update_1194_end");
+               }
+       }
+
+       logger("End ID: ".$end_id, LOGGER_DEBUG);
+
+       $start_id = get_config("system", "post_update_1194_start");
+
+       $query1 = "SELECT `item`.`id` FROM `item` ";
+
+       $query2 = "INNER JOIN `item` AS `shadow` ON `item`.`uri` = `shadow`.`uri` AND `shadow`.`uid` = 0 ";
+
+       $query3 = "WHERE `item`.`uid` != 0 AND `item`.`id` >= %d AND `item`.`id` <= %d
+                       AND `item`.`visible` AND NOT `item`.`private`
+                       AND NOT `item`.`deleted` AND NOT `item`.`moderated`
+                       AND `item`.`network` IN ('%s', '%s', '%s', '')
+                       AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = ''
+                       AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = ''
+                       AND NOT `item`.`global`";
+
+       $r = q($query1.$query2.$query3."  ORDER BY `item`.`id` LIMIT 1",
+               intval($start_id), intval($end_id),
+               dbesc(NETWORK_DFRN), dbesc(NETWORK_DIASPORA), dbesc(NETWORK_OSTATUS));
+       if (!$r) {
+               set_config("system", "post_update_version", 1194);
+               logger("Update is done", LOGGER_DEBUG);
+               return true;
+       } else {
+               set_config("system", "post_update_1194_start", $r[0]["id"]);
+               $start_id = get_config("system", "post_update_1194_start");
+       }
+
+       logger("Start ID: ".$start_id, LOGGER_DEBUG);
+
+       $r = q($query1.$query2.$query3."  ORDER BY `item`.`id` LIMIT 1000,1",
+               intval($start_id), intval($end_id),
+               dbesc(NETWORK_DFRN), dbesc(NETWORK_DIASPORA), dbesc(NETWORK_OSTATUS));
+       if ($r)
+               $pos_id = $r[0]["id"];
+       else
+               $pos_id = $end_id;
+
+       logger("Progress: Start: ".$start_id." position: ".$pos_id." end: ".$end_id, LOGGER_DEBUG);
+
+       $r = q("UPDATE `item` ".$query2." SET `item`.`global` = 1 ".$query3,
+               intval($start_id), intval($pos_id),
+               dbesc(NETWORK_DFRN), dbesc(NETWORK_DIASPORA), dbesc(NETWORK_OSTATUS));
+
+       logger("Done", LOGGER_DEBUG);
+}
+?>
index 7cc72cc86604d801d08bdbbc88532b1055393b2f..399150f21cb8f8d763840a9dfdd74a98d5892b61 100644 (file)
@@ -1,96 +1,6 @@
 <?php
-
-require_once('include/datetime.php');
 require_once('include/diaspora.php');
-require_once('include/queue_fn.php');
-require_once('include/Contact.php');
 
 function profile_change() {
-
-       $a = get_app();
-
-       if(! local_user())
-               return;
-
-//   $url = $a->get_baseurl() . '/profile/' . $a->user['nickname'];
-//   if($url && strlen(get_config('system','directory')))
-//      proc_run('php',"include/directory.php","$url");
-
-       $recips = q("SELECT `id`,`name`,`network`,`pubkey`,`notify` FROM `contact` WHERE `network` = '%s'
-               AND `uid` = %d AND `rel` != %d ",
-               dbesc(NETWORK_DIASPORA),
-               intval(local_user()),
-               intval(CONTACT_IS_SHARING)
-       );
-       if(! count($recips))
-               return;
-
-       $r = q("SELECT `profile`.`uid` AS `profile_uid`, `profile`.* , `user`.* FROM `profile`
-               INNER JOIN `user` ON `profile`.`uid` = `user`.`uid`
-               WHERE `user`.`uid` = %d AND `profile`.`is-default` = 1 LIMIT 1",
-               intval(local_user())
-       );
-
-       if(! count($r))
-               return;
-       $profile = $r[0];
-
-       $handle = xmlify($a->user['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3));
-       $first = xmlify(((strpos($profile['name'],' '))
-               ? trim(substr($profile['name'],0,strpos($profile['name'],' '))) : $profile['name']));
-       $last = xmlify((($first === $profile['name']) ? '' : trim(substr($profile['name'],strlen($first)))));
-       $large = xmlify($a->get_baseurl() . '/photo/custom/300/' . $profile['uid'] . '.jpg');
-       $medium = xmlify($a->get_baseurl() . '/photo/custom/100/' . $profile['uid'] . '.jpg');
-       $small = xmlify($a->get_baseurl() . '/photo/custom/50/'  . $profile['uid'] . '.jpg');
-       $searchable = xmlify((($profile['publish'] && $profile['net-publish']) ? 'true' : 'false' ));
-//     $searchable = 'true';
-
-       if($searchable === 'true') {
-               $dob = '1000-00-00';
-
-               if(($profile['dob']) && ($profile['dob'] != '0000-00-00'))
-                       $dob = ((intval($profile['dob'])) ? intval($profile['dob']) : '1000') . '-' . datetime_convert('UTC','UTC',$profile['dob'],'m-d');
-               $gender = xmlify($profile['gender']);
-               $about = xmlify($profile['about']);
-               require_once('include/bbcode.php');
-               $about = xmlify(strip_tags(bbcode($about)));
-               $location = formatted_location($profile);
-               $location = xmlify($location);
-               $tags = '';
-               if($profile['pub_keywords']) {
-                       $kw = str_replace(',',' ',$profile['pub_keywords']);
-                       $kw = str_replace('  ',' ',$kw);
-                       $arr = explode(' ',$profile['pub_keywords']);
-                       if(count($arr)) {
-                               for($x = 0; $x < 5; $x ++) {
-                                       if(trim($arr[$x]))
-                                               $tags .= '#' . trim($arr[$x]) . ' ';
-                               }
-                       }
-               }
-               $tags = xmlify(trim($tags));
-       }
-
-       $tpl = get_markup_template('diaspora_profile.tpl');
-
-       $msg = replace_macros($tpl,array(
-               '$handle' => $handle,
-               '$first' => $first,
-               '$last' => $last,
-               '$large' => $large,
-               '$medium' => $medium,
-               '$small' => $small,
-               '$dob' => $dob,
-               '$gender' => $gender,
-               '$about' => $about,
-               '$location' => $location,
-               '$searchable' => $searchable,
-               '$tags' => $tags
-       ));
-       logger('profile_change: ' . $msg, LOGGER_ALL);
-
-       foreach($recips as $recip) {
-               $msgtosend = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$a->user,$recip,$a->user['prvkey'],$recip['pubkey'],false)));
-               add_to_queue($recip['id'],NETWORK_DIASPORA,$msgtosend,false);
-       }
+       diaspora::send_profile(local_user());
 }
index 0ac50aaaa76a44e94a4839ce977abbb0d0fc0293..625eefc261b69af08b4011d44265ffd1ddb61a1f 100644 (file)
@@ -16,7 +16,7 @@ function handle_pubsubhubbub() {
 
                logger("Generate feed for user ".$rr['nickname']." - last updated ".$rr['last_update'], LOGGER_DEBUG);
 
-               $params = ostatus_feed($a, $rr['nickname'], $rr['last_update']);
+               $params = ostatus::feed($a, $rr['nickname'], $rr['last_update']);
                $hmac_sig = hash_hmac("sha1", $params, $rr['secret']);
 
                $headers = array("Content-type: application/atom+xml",
@@ -74,25 +74,14 @@ function pubsubpublish_run(&$argv, &$argc){
        };
 
        require_once('include/items.php');
-       require_once('include/pidfile.php');
 
        load_config('config');
        load_config('system');
 
-       $lockpath = get_lockpath();
-       if ($lockpath != '') {
-               $pidfile = new pidfile($lockpath, 'pubsubpublish');
-               if($pidfile->is_already_running()) {
-                       logger("Already running");
-                       if ($pidfile->running_time() > 9*60) {
-                               $pidfile->kill();
-                               logger("killed stale process");
-                               // Calling a new instance
-                               proc_run('php',"include/pubsubpublish.php");
-                       }
+       // Don't check this stuff if the function is called by the poller
+       if (App::callstack() != "poller_run")
+               if (App::is_already_running("pubsubpublish", "include/pubsubpublish.php", 540))
                        return;
-               }
-       }
 
        $a->set_baseurl(get_config('system','url'));
 
index 1525ca3abf561cd363c9cad5d9d3158b49f75b32..878c149731d7b1a8c443f052efda6bdbde4d216b 100644 (file)
@@ -22,26 +22,15 @@ function queue_run(&$argv, &$argc){
        require_once("include/datetime.php");
        require_once('include/items.php');
        require_once('include/bbcode.php');
-       require_once('include/pidfile.php');
        require_once('include/socgraph.php');
 
        load_config('config');
        load_config('system');
 
-       $lockpath = get_lockpath();
-       if ($lockpath != '') {
-               $pidfile = new pidfile($lockpath, 'queue');
-               if($pidfile->is_already_running()) {
-                       logger("queue: Already running");
-                       if ($pidfile->running_time() > 9*60) {
-                               $pidfile->kill();
-                               logger("queue: killed stale process");
-                               // Calling a new instance
-                               proc_run('php',"include/queue.php");
-                       }
+       // Don't check this stuff if the function is called by the poller
+       if (App::callstack() != "poller_run")
+               if (App::is_already_running('queue', 'include/queue.php', 540))
                        return;
-               }
-       }
 
        $a->set_baseurl(get_config('system','url'));
 
@@ -204,7 +193,7 @@ function queue_run(&$argv, &$argc){
                        case NETWORK_DIASPORA:
                                if($contact['notify']) {
                                        logger('queue: diaspora_delivery: item '.$q_item['id'].' for '.$contact['name'].' <'.$contact['url'].'>');
-                                       $deliver_status = diaspora_transmit($owner,$contact,$data,$public,true);
+                                       $deliver_status = diaspora::transmit($owner,$contact,$data,$public,true);
 
                                        if($deliver_status == (-1)) {
                                                update_queue_time($q_item['id']);
index 11641d6cea7d2fd056b45167be42bdae2b07cb2c..8f9d64606c00b3dda205e4e85badcca6316f8571 100644 (file)
@@ -69,7 +69,6 @@ function ref_session_destroy ($id) {
 if(! function_exists('ref_session_gc')) {
 function ref_session_gc($expire) {
        q("DELETE FROM `session` WHERE `expire` < %d", dbesc(time()));
-       q("OPTIMIZE TABLE `sess_data`");
        return true;
 }}
 
index c545343393d869265e50a016bc5390c764afd320..33d62dc5b9abba5030c28706ab53237c9b76ab7d 100644 (file)
@@ -10,7 +10,8 @@
 require_once('include/datetime.php');
 require_once("include/Scrape.php");
 require_once("include/html2bbcode.php");
-
+require_once("include/Contact.php");
+require_once("include/Photo.php");
 
 /*
  * poco_load
@@ -139,15 +140,16 @@ function poco_load($cid,$uid = 0,$zcid = 0,$url = null) {
                poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, $cid, $uid, $zcid);
 
                // Update the Friendica contacts. Diaspora is doing it via a message. (See include/diaspora.php)
-               if (($location != "") OR ($about != "") OR ($keywords != "") OR ($gender != ""))
-                       q("UPDATE `contact` SET `location` = '%s', `about` = '%s', `keywords` = '%s', `gender` = '%s'
-                               WHERE `nurl` = '%s' AND NOT `self` AND `network` = '%s'",
-                               dbesc($location),
-                               dbesc($about),
-                               dbesc($keywords),
-                               dbesc($gender),
-                               dbesc(normalise_link($profile_url)),
-                               dbesc(NETWORK_DFRN));
+               // Deactivated because we now update Friendica contacts in dfrn.php
+               //if (($location != "") OR ($about != "") OR ($keywords != "") OR ($gender != ""))
+               //      q("UPDATE `contact` SET `location` = '%s', `about` = '%s', `keywords` = '%s', `gender` = '%s'
+               //              WHERE `nurl` = '%s' AND NOT `self` AND `network` = '%s'",
+               //              dbesc($location),
+               //              dbesc($about),
+               //              dbesc($keywords),
+               //              dbesc($gender),
+               //              dbesc(normalise_link($profile_url)),
+               //              dbesc(NETWORK_DFRN));
        }
        logger("poco_load: loaded $total entries",LOGGER_DEBUG);
 
@@ -427,7 +429,7 @@ function poco_last_updated($profile, $force = false) {
        if (($gcontacts[0]["server_url"] != "") AND ($gcontacts[0]["nick"] != "")) {
 
                //  Use noscrape if possible
-               $server = q("SELECT `noscrape` FROM `gserver` WHERE `nurl` = '%s' AND `noscrape` != ''", dbesc(normalise_link($gcontacts[0]["server_url"])));
+               $server = q("SELECT `noscrape`, `network` FROM `gserver` WHERE `nurl` = '%s' AND `noscrape` != ''", dbesc(normalise_link($gcontacts[0]["server_url"])));
 
                if ($server) {
                        $noscraperet = z_fetch_url($server[0]["noscrape"]."/".$gcontacts[0]["nick"]);
@@ -436,69 +438,47 @@ function poco_last_updated($profile, $force = false) {
 
                                $noscrape = json_decode($noscraperet["body"], true);
 
-                               if (($noscrape["fn"] != "") AND ($noscrape["fn"] != $gcontacts[0]["name"]))
-                                       q("UPDATE `gcontact` SET `name` = '%s' WHERE `nurl` = '%s'",
-                                               dbesc($noscrape["fn"]), dbesc(normalise_link($profile)));
-
-                               if (($noscrape["photo"] != "") AND ($noscrape["photo"] != $gcontacts[0]["photo"]))
-                                       q("UPDATE `gcontact` SET `photo` = '%s' WHERE `nurl` = '%s'",
-                                               dbesc($noscrape["photo"]), dbesc(normalise_link($profile)));
-
-                               if (($noscrape["updated"] != "") AND ($noscrape["updated"] != $gcontacts[0]["updated"]))
-                                       q("UPDATE `gcontact` SET `updated` = '%s' WHERE `nurl` = '%s'",
-                                               dbesc($noscrape["updated"]), dbesc(normalise_link($profile)));
-
-                               if (($noscrape["gender"] != "") AND ($noscrape["gender"] != $gcontacts[0]["gender"]))
-                                       q("UPDATE `gcontact` SET `gender` = '%s' WHERE `nurl` = '%s'",
-                                               dbesc($noscrape["gender"]), dbesc(normalise_link($profile)));
+                               if (is_array($noscrape)) {
+                                       $contact = array("url" => $profile,
+                                                       "network" => $server[0]["network"],
+                                                       "generation" => $gcontacts[0]["generation"]);
 
-                               if (($noscrape["pdesc"] != "") AND ($noscrape["pdesc"] != $gcontacts[0]["about"]))
-                                       q("UPDATE `gcontact` SET `about` = '%s' WHERE `nurl` = '%s'",
-                                               dbesc($noscrape["pdesc"]), dbesc(normalise_link($profile)));
+                                       $contact["name"] = $noscrape["fn"];
+                                       $contact["community"] = $noscrape["comm"];
 
-                               if (($noscrape["about"] != "") AND ($noscrape["about"] != $gcontacts[0]["about"]))
-                                       q("UPDATE `gcontact` SET `about` = '%s' WHERE `nurl` = '%s'",
-                                               dbesc($noscrape["about"]), dbesc(normalise_link($profile)));
-
-                               if (isset($noscrape["comm"]) AND ($noscrape["comm"] != $gcontacts[0]["community"]))
-                                       q("UPDATE `gcontact` SET `community` = %d WHERE `nurl` = '%s'",
-                                               intval($noscrape["comm"]), dbesc(normalise_link($profile)));
-
-                               if (isset($noscrape["tags"]))
-                                       $keywords = implode(" ", $noscrape["tags"]);
-                               else
-                                       $keywords = "";
+                                       if (isset($noscrape["tags"])) {
+                                               $keywords = implode(" ", $noscrape["tags"]);
+                                               if ($keywords != "")
+                                                       $contact["keywords"] = $keywords;
+                                       }
 
-                               if (($keywords != "") AND ($keywords != $gcontacts[0]["keywords"]))
-                                       q("UPDATE `gcontact` SET `keywords` = '%s' WHERE `nurl` = '%s'",
-                                               dbesc($keywords), dbesc(normalise_link($profile)));
+                                       $location = formatted_location($noscrape);
+                                       if ($location)
+                                               $contact["location"] = $location;
 
-                               $location = $noscrape["locality"];
+                                       $contact["notify"] = $noscrape["dfrn-notify"];
 
-                               if ($noscrape["region"] != "") {
-                                       if ($location != "")
-                                               $location .= ", ";
+                                       // Remove all fields that are not present in the gcontact table
+                                       unset($noscrape["fn"]);
+                                       unset($noscrape["key"]);
+                                       unset($noscrape["homepage"]);
+                                       unset($noscrape["comm"]);
+                                       unset($noscrape["tags"]);
+                                       unset($noscrape["locality"]);
+                                       unset($noscrape["region"]);
+                                       unset($noscrape["country-name"]);
+                                       unset($noscrape["contacts"]);
+                                       unset($noscrape["dfrn-request"]);
+                                       unset($noscrape["dfrn-confirm"]);
+                                       unset($noscrape["dfrn-notify"]);
+                                       unset($noscrape["dfrn-poll"]);
 
-                                       $location .= $noscrape["region"];
-                               }
+                                       $contact = array_merge($contact, $noscrape);
 
-                               if ($noscrape["country-name"] != "") {
-                                       if ($location != "")
-                                               $location .= ", ";
+                                       update_gcontact($contact);
 
-                                       $location .= $noscrape["country-name"];
+                                       return $noscrape["updated"];
                                }
-
-                               if (($location != "") AND ($location != $gcontacts[0]["location"]))
-                                       q("UPDATE `gcontact` SET `location` = '%s' WHERE `nurl` = '%s'",
-                                               dbesc($location), dbesc(normalise_link($profile)));
-
-                               // If we got data from noscrape then mark the contact as reachable
-                               if (is_array($noscrape) AND count($noscrape))
-                                       q("UPDATE `gcontact` SET `last_contact` = '%s' WHERE `nurl` = '%s'",
-                                               dbesc(datetime_convert()), dbesc(normalise_link($profile)));
-
-                               return $noscrape["updated"];
                        }
                }
        }
@@ -533,25 +513,22 @@ function poco_last_updated($profile, $force = false) {
                return false;
        }
 
-       if (($data["name"] != "") AND ($data["name"] != $gcontacts[0]["name"]))
-               q("UPDATE `gcontact` SET `name` = '%s' WHERE `nurl` = '%s'",
-                       dbesc($data["name"]), dbesc(normalise_link($profile)));
+       $contact = array("generation" => $gcontacts[0]["generation"]);
 
-       if (($data["nick"] != "") AND ($data["nick"] != $gcontacts[0]["nick"]))
-               q("UPDATE `gcontact` SET `nick` = '%s' WHERE `nurl` = '%s'",
-                       dbesc($data["nick"]), dbesc(normalise_link($profile)));
+       $contact = array_merge($contact, $data);
 
-       if (($data["addr"] != "") AND ($data["addr"] != $gcontacts[0]["connect"]))
-               q("UPDATE `gcontact` SET `connect` = '%s' WHERE `nurl` = '%s'",
-                       dbesc($data["addr"]), dbesc(normalise_link($profile)));
+       $contact["server_url"] = $data["baseurl"];
 
-       if (($data["photo"] != "") AND ($data["photo"] != $gcontacts[0]["photo"]))
-               q("UPDATE `gcontact` SET `photo` = '%s' WHERE `nurl` = '%s'",
-                       dbesc($data["photo"]), dbesc(normalise_link($profile)));
+       unset($contact["batch"]);
+       unset($contact["poll"]);
+       unset($contact["request"]);
+       unset($contact["confirm"]);
+       unset($contact["poco"]);
+       unset($contact["priority"]);
+       unset($contact["pubkey"]);
+       unset($contact["baseurl"]);
 
-       if (($data["baseurl"] != "") AND ($data["baseurl"] != $gcontacts[0]["server_url"]))
-               q("UPDATE `gcontact` SET `server_url` = '%s' WHERE `nurl` = '%s'",
-                       dbesc($data["baseurl"]), dbesc(normalise_link($profile)));
+       update_gcontact($contact);
 
        $feedret = z_fetch_url($data["poll"]);
 
@@ -745,7 +722,8 @@ function poco_check_server($server_url, $network = "", $force = false) {
                // Will also return data for Friendica and GNU Social - but it will be overwritten later
                // The "not implemented" is a special treatment for really, really old Friendica versions
                $serverret = z_fetch_url($server_url."/api/statusnet/version.json");
-               if ($serverret["success"] AND ($serverret["body"] != '{"error":"not implemented"}') AND ($serverret["body"] != '') AND (strlen($serverret["body"]) < 250)) {
+               if ($serverret["success"] AND ($serverret["body"] != '{"error":"not implemented"}') AND
+                       ($serverret["body"] != '') AND (strlen($serverret["body"]) < 30)) {
                        $platform = "StatusNet";
                        $version = trim($serverret["body"], '"');
                        $network = NETWORK_OSTATUS;
@@ -753,7 +731,8 @@ function poco_check_server($server_url, $network = "", $force = false) {
 
                // Test for GNU Social
                $serverret = z_fetch_url($server_url."/api/gnusocial/version.json");
-               if ($serverret["success"] AND ($serverret["body"] != '{"error":"not implemented"}') AND ($serverret["body"] != '') AND (strlen($serverret["body"]) < 250)) {
+               if ($serverret["success"] AND ($serverret["body"] != '{"error":"not implemented"}') AND
+                       ($serverret["body"] != '') AND (strlen($serverret["body"]) < 30)) {
                        $platform = "GNU Social";
                        $version = trim($serverret["body"], '"');
                        $network = NETWORK_OSTATUS;
@@ -880,6 +859,11 @@ function poco_check_server($server_url, $network = "", $force = false) {
        // Check again if the server exists
        $servers = q("SELECT `nurl` FROM `gserver` WHERE `nurl` = '%s'", dbesc(normalise_link($server_url)));
 
+       $version = strip_tags($version);
+       $site_name = strip_tags($site_name);
+       $info = strip_tags($info);
+       $platform = strip_tags($platform);
+
        if ($servers)
                 q("UPDATE `gserver` SET `url` = '%s', `version` = '%s', `site_name` = '%s', `info` = '%s', `register_policy` = %d, `poco` = '%s', `noscrape` = '%s',
                        `network` = '%s', `platform` = '%s', `last_contact` = '%s', `last_failure` = '%s' WHERE `nurl` = '%s'",
@@ -920,88 +904,6 @@ function poco_check_server($server_url, $network = "", $force = false) {
        return !$failure;
 }
 
-function poco_contact_from_body($body, $created, $cid, $uid) {
-       preg_replace_callback("/\[share(.*?)\].*?\[\/share\]/ism",
-               function ($match) use ($created, $cid, $uid){
-                       return(sub_poco_from_share($match, $created, $cid, $uid));
-               }, $body);
-}
-
-function sub_poco_from_share($share, $created, $cid, $uid) {
-       $profile = "";
-       preg_match("/profile='(.*?)'/ism", $share[1], $matches);
-       if ($matches[1] != "")
-               $profile = $matches[1];
-
-       preg_match('/profile="(.*?)"/ism', $share[1], $matches);
-       if ($matches[1] != "")
-               $profile = $matches[1];
-
-       if ($profile == "")
-               return;
-
-       logger("prepare poco_check for profile ".$profile, LOGGER_DEBUG);
-       poco_check($profile, "", "", "", "", "", "", "", "", $created, 3, $cid, $uid);
-}
-
-function poco_store($item) {
-
-       // Isn't it public?
-       if ($item['private'])
-               return;
-
-       // Or is it from a network where we don't store the global contacts?
-       if (!in_array($item["network"], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS, NETWORK_STATUSNET, "")))
-               return;
-
-       // Is it a global copy?
-       $store_gcontact = ($item["uid"] == 0);
-
-       // Is it a comment on a global copy?
-       if (!$store_gcontact AND ($item["uri"] != $item["parent-uri"])) {
-               $q = q("SELECT `id` FROM `item` WHERE `uri`='%s' AND `uid` = 0", $item["parent-uri"]);
-               $store_gcontact = count($q);
-       }
-
-       if (!$store_gcontact)
-               return;
-
-       // "3" means: We don't know this contact directly (Maybe a reshared item)
-       $generation = 3;
-       $network = "";
-       $profile_url = $item["author-link"];
-
-       // Is it a user from our server?
-       $q = q("SELECT `id` FROM `contact` WHERE `self` AND `nurl` = '%s' LIMIT 1",
-               dbesc(normalise_link($item["author-link"])));
-       if (count($q)) {
-               logger("Our user (generation 1): ".$item["author-link"], LOGGER_DEBUG);
-               $generation = 1;
-               $network = NETWORK_DFRN;
-       } else { // Is it a contact from a user on our server?
-               $q = q("SELECT `network`, `url` FROM `contact` WHERE `uid` != 0 AND `network` != ''
-                       AND (`nurl` = '%s' OR `alias` IN ('%s', '%s')) AND `network` != '%s' LIMIT 1",
-                       dbesc(normalise_link($item["author-link"])),
-                       dbesc(normalise_link($item["author-link"])),
-                       dbesc($item["author-link"]),
-                       dbesc(NETWORK_STATUSNET));
-               if (count($q)) {
-                       $generation = 2;
-                       $network = $q[0]["network"];
-                       $profile_url = $q[0]["url"];
-                       logger("Known contact (generation 2): ".$profile_url, LOGGER_DEBUG);
-               }
-       }
-
-       if ($generation == 3)
-               logger("Unknown contact (generation 3): ".$item["author-link"], LOGGER_DEBUG);
-
-       poco_check($profile_url, $item["author-name"], $network, $item["author-avatar"], "", "", "", "", "", $item["received"], $generation, $item["contact-id"], $item["uid"]);
-
-       // Maybe its a body with a shared item? Then extract a global contact from it.
-       poco_contact_from_body($item["body"], $item["received"], $item["contact-id"], $item["uid"]);
-}
-
 function count_common_friends($uid,$cid) {
 
        $r = q("SELECT count(*) as `total`
@@ -1530,9 +1432,17 @@ function update_gcontact($contact) {
        unset($fields["url"]);
        unset($fields["updated"]);
 
+       // Bugfix: We had an error in the storing of keywords which lead to the "0"
+       // This value is still transmitted via poco.
+       if ($contact["keywords"] == "0")
+               unset($contact["keywords"]);
+
+       if ($r[0]["keywords"] == "0")
+               $r[0]["keywords"] = "";
+
        // assign all unassigned fields from the database entry
        foreach ($fields AS $field => $data)
-               if (!isset($contact[$field]))
+               if (!isset($contact[$field]) OR ($contact[$field] == ""))
                        $contact[$field] = $r[0][$field];
 
        if ($contact["network"] == NETWORK_STATUSNET)
@@ -1541,20 +1451,50 @@ function update_gcontact($contact) {
        if (!isset($contact["updated"]))
                $contact["updated"] = datetime_convert();
 
+       if ($contact["server_url"] == "") {
+               $server_url = $contact["url"];
+
+               $server_url = matching_url($server_url, $contact["alias"]);
+               if ($server_url != "")
+                       $contact["server_url"] = $server_url;
+
+               $server_url = matching_url($server_url, $contact["photo"]);
+               if ($server_url != "")
+                       $contact["server_url"] = $server_url;
+
+               $server_url = matching_url($server_url, $contact["notify"]);
+               if ($server_url != "")
+                       $contact["server_url"] = $server_url;
+       } else
+               $contact["server_url"] = normalise_link($contact["server_url"]);
+
+       if (($contact["addr"] == "") AND ($contact["server_url"] != "") AND ($contact["nick"] != "")) {
+               $hostname = str_replace("http://", "", $contact["server_url"]);
+               $contact["addr"] = $contact["nick"]."@".$hostname;
+       }
+
        // Check if any field changed
        $update = false;
        unset($fields["generation"]);
 
-       foreach ($fields AS $field => $data)
-               if ($contact[$field] != $r[0][$field])
-                       $update = true;
+       if ((($contact["generation"] > 0) AND ($contact["generation"] <= $r[0]["generation"])) OR ($r[0]["generation"] == 0)) {
+               foreach ($fields AS $field => $data)
+                       if ($contact[$field] != $r[0][$field]) {
+                               logger("Difference for contact ".$contact["url"]." in field '".$field."'. New value: '".$contact[$field]."', old value '".$r[0][$field]."'", LOGGER_DEBUG);
+                               $update = true;
+                       }
 
-       if ($contact["generation"] < $r[0]["generation"])
-               $update = true;
+               if ($contact["generation"] < $r[0]["generation"]) {
+                       logger("Difference for contact ".$contact["url"]." in field 'generation'. new value: '".$contact["generation"]."', old value '".$r[0]["generation"]."'", LOGGER_DEBUG);
+                       $update = true;
+               }
+       }
 
        if ($update) {
+               logger("Update gcontact for ".$contact["url"]." Callstack: ".App::callstack(), LOGGER_DEBUG);
+
                q("UPDATE `gcontact` SET `photo` = '%s', `name` = '%s', `nick` = '%s', `addr` = '%s', `network` = '%s',
-                                       `birthday` = '%s', `gender` = '%s', `keywords` = %d, `hide` = %d, `nsfw` = %d,
+                                       `birthday` = '%s', `gender` = '%s', `keywords` = '%s', `hide` = %d, `nsfw` = %d,
                                        `alias` = '%s', `notify` = '%s', `url` = '%s',
                                        `location` = '%s', `about` = '%s', `generation` = %d, `updated` = '%s',
                                        `server_url` = '%s', `connect` = '%s'
@@ -1567,6 +1507,28 @@ function update_gcontact($contact) {
                        intval($contact["generation"]), dbesc($contact["updated"]),
                        dbesc($contact["server_url"]), dbesc($contact["connect"]),
                        dbesc(normalise_link($contact["url"])), intval($contact["generation"]));
+
+
+               // Now update the contact entry with the user id "0" as well.
+               // This is used for the shadow copies of public items.
+               $r = q("SELECT `id` FROM `contact` WHERE `nurl` = '%s' AND `uid` = 0 ORDER BY `id` LIMIT 1",
+                       dbesc(normalise_link($contact["url"])));
+
+               if ($r) {
+                       logger("Update shadow contact ".$r[0]["id"], LOGGER_DEBUG);
+
+                       update_contact_avatar($contact["photo"], 0, $r[0]["id"]);
+
+                       q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `addr` = '%s',
+                                               `network` = '%s', `bd` = '%s', `gender` = '%s',
+                                               `keywords` = '%s', `alias` = '%s', `url` = '%s',
+                                               `location` = '%s', `about` = '%s'
+                                       WHERE `id` = %d",
+                               dbesc($contact["name"]), dbesc($contact["nick"]), dbesc($contact["addr"]),
+                               dbesc($contact["network"]), dbesc($contact["birthday"]), dbesc($contact["gender"]),
+                               dbesc($contact["keywords"]), dbesc($contact["alias"]), dbesc($contact["url"]),
+                               dbesc($contact["location"]), dbesc($contact["about"]), intval($r[0]["id"]));
+               }
        }
 
        return $gcontact_id;
@@ -1580,8 +1542,10 @@ function update_gcontact($contact) {
 function update_gcontact_from_probe($url) {
        $data = probe_url($url);
 
-       if ($data["network"] != NETWORK_PHANTOM)
-               update_gcontact($data);
+       if ($data["network"] == NETWORK_PHANTOM)
+               return;
+
+       update_gcontact($data);
 }
 
 /**
index c7681a4d58cb2485a8e34ac623d89440a4a36404..c868499cc69c28ded31a2293bccfb2c8a3068f93 100644 (file)
@@ -285,7 +285,7 @@ function paginate_data(&$a, $count=null) {
        if (($a->page_offset != "") AND !preg_match('/[?&].offset=/', $stripped))
                $stripped .= "&offset=".urlencode($a->page_offset);
 
-       $url = z_root() . '/' . $stripped;
+       $url = $stripped;
 
        $data = array();
        function _l(&$d, $name, $url, $text, $class="") {
@@ -923,7 +923,7 @@ function micropro($contact, $redirect = false, $class = '', $textmode = false) {
 
        if($redirect) {
                $a = get_app();
-               $redirect_url = z_root() . '/redir/' . $contact['id'];
+               $redirect_url = 'redir/' . $contact['id'];
                if(local_user() && ($contact['uid'] == local_user()) && ($contact['network'] === NETWORK_DFRN)) {
                        $redir = true;
                        $url = $redirect_url;
@@ -964,13 +964,13 @@ if(! function_exists('search')) {
  * @param string $url search url
  * @param boolean $savedsearch show save search button
  */
-function search($s,$id='search-box',$url='/search',$save = false, $aside = true) {
+function search($s,$id='search-box',$url='search',$save = false, $aside = true) {
        $a = get_app();
 
        $values = array(
                        '$s' => $s,
                        '$id' => $id,
-                       '$action_url' => $a->get_baseurl((stristr($url,'network')) ? true : false) . $url,
+                       '$action_url' => $url,
                        '$search_label' => t('Search'),
                        '$save_label' => t('Save'),
                        '$savedsearch' => feature_enabled(local_user(),'savedsearch'),
@@ -1148,41 +1148,41 @@ function smilies($s, $sample = false) {
        );
 
        $icons = array(
-               '<img class="smiley" src="' . z_root() . '/images/smiley-heart.gif" alt="&lt;3" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-brokenheart.gif" alt="&lt;/3" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-brokenheart.gif" alt="&lt;\\3" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-smile.gif" alt=":-)" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-wink.gif" alt=";-)" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-frown.gif" alt=":-(" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-tongue-out.gif" alt=":-P" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-tongue-out.gif" alt=":-p" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-\"" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-\"" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-x" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-X" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-laughing.gif" alt=":-D" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-surprised.gif" alt="8-|" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-surprised.gif" alt="8-O" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-surprised.gif" alt=":-O" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-thumbsup.gif" alt="\\o/" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="o.O" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="O.o" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="o_O" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="O_o" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-cry.gif" alt=":\'(" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-foot-in-mouth.gif" alt=":-!" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-undecided.gif" alt=":-/" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-embarassed.gif" alt=":-[" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-cool.gif" alt="8-)" />',
-               '<img class="smiley" src="' . z_root() . '/images/beer_mug.gif" alt=":beer" />',
-               '<img class="smiley" src="' . z_root() . '/images/beer_mug.gif" alt=":homebrew" />',
-               '<img class="smiley" src="' . z_root() . '/images/coffee.gif" alt=":coffee" />',
-               '<img class="smiley" src="' . z_root() . '/images/smiley-facepalm.gif" alt=":facepalm" />',
-               '<img class="smiley" src="' . z_root() . '/images/like.gif" alt=":like" />',
-               '<img class="smiley" src="' . z_root() . '/images/dislike.gif" alt=":dislike" />',
-               '<a href="http://friendica.com">~friendica <img class="smiley" src="' . z_root() . '/images/friendica-16.png" alt="~friendica" /></a>',
-               '<a href="http://redmatrix.me/">red<img class="smiley" src="' . z_root() . '/images/rm-16.png" alt="red" />matrix</a>',
-               '<a href="http://redmatrix.me/">red<img class="smiley" src="' . z_root() . '/images/rm-16.png" alt="red" />matrix</a>'
+               '<img class="smiley" src="' . z_root() . '/images/smiley-heart.gif" alt="&lt;3" title="&lt;3" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-brokenheart.gif" alt="&lt;/3" title="&lt;/3" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-brokenheart.gif" alt="&lt;\\3" title="&lt;\\3" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-smile.gif" alt=":-)" title=":-)" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-wink.gif" alt=";-)" title=";-)" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-frown.gif" alt=":-(" title=":-(" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-tongue-out.gif" alt=":-P" title=":-P" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-tongue-out.gif" alt=":-p" title=":-P" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-\" title=":-\" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-\" title=":-\" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-x" title=":-x" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-X" title=":-X" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-laughing.gif" alt=":-D" title=":-D"  />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-surprised.gif" alt="8-|" title="8-|" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-surprised.gif" alt="8-O" title="8-O" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-surprised.gif" alt=":-O" title="8-O" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-thumbsup.gif" alt="\\o/" title="\\o/" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="o.O" title="o.O" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="O.o" title="O.o" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="o_O" title="o_O" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="O_o" title="O_o" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-cry.gif" alt=":\'(" title=":\'("/>',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-foot-in-mouth.gif" alt=":-!" title=":-!" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-undecided.gif" alt=":-/" title=":-/" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-embarassed.gif" alt=":-[" title=":-[" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-cool.gif" alt="8-)" title="8-)" />',
+               '<img class="smiley" src="' . z_root() . '/images/beer_mug.gif" alt=":beer" title=":beer" />',
+               '<img class="smiley" src="' . z_root() . '/images/beer_mug.gif" alt=":homebrew" title=":homebrew" />',
+               '<img class="smiley" src="' . z_root() . '/images/coffee.gif" alt=":coffee" title=":coffee" />',
+               '<img class="smiley" src="' . z_root() . '/images/smiley-facepalm.gif" alt=":facepalm" title=":facepalm" />',
+               '<img class="smiley" src="' . z_root() . '/images/like.gif" alt=":like" title=":like" />',
+               '<img class="smiley" src="' . z_root() . '/images/dislike.gif" alt=":dislike" title=":dislike" />',
+               '<a href="http://friendica.com">~friendica <img class="smiley" src="' . z_root() . '/images/friendica-16.png" alt="~friendica" title="~friendica" /></a>',
+               '<a href="http://redmatrix.me/">red<img class="smiley" src="' . z_root() . '/images/rm-16.png" alt="red#" title="red#" />matrix</a>',
+               '<a href="http://redmatrix.me/">red<img class="smiley" src="' . z_root() . '/images/rm-16.png" alt="red#matrix" title="red#matrix" />matrix</a>'
        );
 
        $params = array('texts' => $texts, 'icons' => $icons, 'string' => $s);
@@ -1305,7 +1305,7 @@ function redir_private_images($a, &$item) {
 
                        if((local_user() == $item['uid']) && ($item['private'] != 0) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN)) {
                                //logger("redir_private_images: redir");
-                               $img_url = z_root() . '/redir?f=1&quiet=1&url=' . $mtch[1] . '&conurl=' . $item['author-link'];
+                               $img_url = 'redir?f=1&quiet=1&url=' . $mtch[1] . '&conurl=' . $item['author-link'];
                                $item['body'] = str_replace($mtch[0], "[img]".$img_url."[/img]", $item['body']);
                        }
                }
@@ -1421,7 +1421,7 @@ function prepare_body(&$item,$attach = false, $preview = false) {
                                        $mime = $mtch[3];
 
                                        if((local_user() == $item['uid']) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN))
-                                               $the_url = z_root() . '/redir/' . $item['contact-id'] . '?f=1&url=' . $mtch[1];
+                                               $the_url = 'redir/' . $item['contact-id'] . '?f=1&url=' . $mtch[1];
                                        else
                                                $the_url = $mtch[1];
 
@@ -1596,7 +1596,7 @@ function get_cats_and_terms($item) {
                        $categories[] = array(
                                'name' => xmlify(file_tag_decode($mtch[1])),
                                'url' =>  "#",
-                               'removeurl' => ((local_user() == $item['uid'])?z_root() . '/filerm/' . $item['id'] . '?f=&cat=' . xmlify(file_tag_decode($mtch[1])):""),
+                               'removeurl' => ((local_user() == $item['uid'])?'filerm/' . $item['id'] . '?f=&cat=' . xmlify(file_tag_decode($mtch[1])):""),
                                'first' => $first,
                                'last' => false
                        );
@@ -1614,7 +1614,7 @@ function get_cats_and_terms($item) {
                                $folders[] = array(
                                        'name' => xmlify(file_tag_decode($mtch[1])),
                                        'url' =>  "#",
-                                       'removeurl' => ((local_user() == $item['uid'])?z_root() . '/filerm/' . $item['id'] . '?f=&term=' . xmlify(file_tag_decode($mtch[1])):""),
+                                       'removeurl' => ((local_user() == $item['uid'])?'filerm/' . $item['id'] . '?f=&term=' . xmlify(file_tag_decode($mtch[1])):""),
                                        'first' => $first,
                                        'last' => false
                                );
@@ -1639,15 +1639,15 @@ function get_plink($item) {
 
        if ($a->user['nickname'] != "") {
                $ret = array(
-                               //'href' => z_root()."/display/".$a->user['nickname']."/".$item['id'],
-                               'href' => z_root()."/display/".$item['guid'],
-                               'orig' => z_root()."/display/".$item['guid'],
+                               //'href' => "display/".$a->user['nickname']."/".$item['id'],
+                               'href' => "display/".$item['guid'],
+                               'orig' => "display/".$item['guid'],
                                'title' => t('View on separate page'),
                                'orig_title' => t('view on separate page'),
                        );
 
                if (x($item,'plink')) {
-                       $ret["href"] = $item['plink'];
+                       $ret["href"] = $a->remove_baseurl($item['plink']);
                        $ret["title"] = t('link to source');
                }
 
index b5ea30a0a4d8803bedb8c0a80dc02ee3d977d283..88e1817f0b991ff6a0749c2d794b71510ab62b22 100644 (file)
@@ -16,7 +16,6 @@ function update_gcontact_run(&$argv, &$argc){
                unset($db_host, $db_user, $db_pass, $db_data);
        };
 
-       require_once('include/pidfile.php');
        require_once('include/Scrape.php');
        require_once("include/socgraph.php");
 
@@ -37,18 +36,10 @@ function update_gcontact_run(&$argv, &$argc){
                return;
        }
 
-       $lockpath = get_lockpath();
-       if ($lockpath != '') {
-               $pidfile = new pidfile($lockpath, 'update_gcontact'.$contact_id);
-               if ($pidfile->is_already_running()) {
-                       logger("update_gcontact: Already running for contact ".$contact_id);
-                       if ($pidfile->running_time() > 9*60) {
-                               $pidfile->kill();
-                               logger("killed stale process");
-                       }
-                       exit;
-               }
-       }
+       // Don't check this stuff if the function is called by the poller
+       if (App::callstack() != "poller_run")
+               if (App::is_already_running('update_gcontact'.$contact_id, '', 540))
+                       return;
 
        $r = q("SELECT * FROM `gcontact` WHERE `id` = %d", intval($contact_id));
 
diff --git a/include/xml.php b/include/xml.php
new file mode 100644 (file)
index 0000000..76ad88c
--- /dev/null
@@ -0,0 +1,131 @@
+<?php
+/**
+ * @file include/xml.php
+ */
+
+
+/**
+ * @brief This class contain functions to work with XML data
+ *
+ */
+class xml {
+       /**
+        * @brief Creates an XML structure out of a given array
+        *
+        * @param array $array The array of the XML structure that will be generated
+        * @param object $xml The createdXML will be returned by reference
+        * @param bool $remove_header Should the XML header be removed or not?
+        * @param array $namespaces List of namespaces
+        * @param bool $root - interally used parameter. Mustn't be used from outside.
+        *
+        * @return string The created XML
+        */
+       public static function from_array($array, &$xml, $remove_header = false, $namespaces = array(), $root = true) {
+
+               if ($root) {
+                       foreach($array as $key => $value) {
+                               foreach ($namespaces AS $nskey => $nsvalue)
+                                       $key .= " xmlns".($nskey == "" ? "":":").$nskey.'="'.$nsvalue.'"';
+
+                               $root = new SimpleXMLElement("<".$key."/>");
+                               self::from_array($value, $root, $remove_header, $namespaces, false);
+
+                               $dom = dom_import_simplexml($root)->ownerDocument;
+                               $dom->formatOutput = true;
+                               $xml = $dom;
+
+                               $xml_text = $dom->saveXML();
+
+                               if ($remove_header)
+                                       $xml_text = trim(substr($xml_text, 21));
+
+                               return $xml_text;
+                       }
+               }
+
+               foreach($array as $key => $value) {
+                       if ($key == "@attributes") {
+                               if (!isset($element) OR !is_array($value))
+                                       continue;
+
+                               foreach ($value as $attr_key => $attr_value) {
+                                       $element_parts = explode(":", $attr_key);
+                                       if ((count($element_parts) > 1) AND isset($namespaces[$element_parts[0]]))
+                                               $namespace = $namespaces[$element_parts[0]];
+                                       else
+                                               $namespace = NULL;
+
+                                       $element->addAttribute ($attr_key, $attr_value, $namespace);
+                               }
+
+                               continue;
+                       }
+
+                       $element_parts = explode(":", $key);
+                       if ((count($element_parts) > 1) AND isset($namespaces[$element_parts[0]]))
+                               $namespace = $namespaces[$element_parts[0]];
+                       else
+                               $namespace = NULL;
+
+                       if (!is_array($value))
+                               $element = $xml->addChild($key, xmlify($value), $namespace);
+                       elseif (is_array($value)) {
+                               $element = $xml->addChild($key, NULL, $namespace);
+                               self::from_array($value, $element, $remove_header, $namespaces, false);
+                       }
+               }
+       }
+
+       /**
+        * @brief Copies an XML object
+        *
+        * @param object $source The XML source
+        * @param object $target The XML target
+        * @param string $elementname Name of the XML element of the target
+        */
+       public static function copy(&$source, &$target, $elementname) {
+               if (count($source->children()) == 0)
+                       $target->addChild($elementname, xmlify($source));
+               else {
+                       $child = $target->addChild($elementname);
+                       foreach ($source->children() AS $childfield => $childentry)
+                               self::copy($childentry, $child, $childfield);
+               }
+       }
+
+       /**
+        * @brief Create an XML element
+        *
+        * @param object $doc XML root
+        * @param string $element XML element name
+        * @param string $value XML value
+        * @param array $attributes array containing the attributes
+        *
+        * @return object XML element object
+        */
+       public static function create_element($doc, $element, $value = "", $attributes = array()) {
+               $element = $doc->createElement($element, xmlify($value));
+
+               foreach ($attributes AS $key => $value) {
+                       $attribute = $doc->createAttribute($key);
+                       $attribute->value = xmlify($value);
+                       $element->appendChild($attribute);
+               }
+               return $element;
+       }
+
+       /**
+        * @brief Create an XML and append it to the parent object
+        *
+        * @param object $doc XML root
+        * @param object $parent parent object
+        * @param string $element XML element name
+        * @param string $value XML value
+        * @param array $attributes array containing the attributes
+        */
+       public static function add_element($doc, $parent, $element, $value = "", $attributes = array()) {
+               $element = self::create_element($doc, $element, $value, $attributes);
+               $parent->appendChild($element);
+       }
+}
+?>
index 2b1053cc1b7b976588d22a69168b5a87d293f010..625c2d82dc5806929ad7697e919e83eb319c403a 100644 (file)
--- a/index.php
+++ b/index.php
@@ -72,7 +72,8 @@ if(!$install) {
                (intval(get_config('system','ssl_policy')) == SSL_POLICY_FULL) AND
                (substr($a->get_baseurl(), 0, 8) == "https://")) {
                header("HTTP/1.1 302 Moved Temporarily");
-               header("location: ".$a->get_baseurl()."/".$a->query_string);
+               header("Location: ".$a->get_baseurl()."/".$a->query_string);
+               exit();
        }
 
        require_once("include/session.php");
@@ -233,16 +234,7 @@ if(strlen($a->module)) {
        }
 
        /**
-        * If not, next look for module overrides by the theme
-        */
-
-       if((! $a->module_loaded) && (file_exists("view/theme/" . current_theme() . "/mod/{$a->module}.php"))) {
-               include_once("view/theme/" . current_theme() . "/mod/{$a->module}.php");
-               // We will not set module_loaded to true to allow for partial overrides.
-       }
-
-       /**
-        * Finally, look for a 'standard' program module in the 'mod' directory
+        * If not, next look for a 'standard' program module in the 'mod' directory
         */
 
        if((! $a->module_loaded) && (file_exists("mod/{$a->module}.php"))) {
@@ -380,7 +372,7 @@ $a->init_page_end();
 if(x($_SESSION,'visitor_home'))
        $homebase = $_SESSION['visitor_home'];
 elseif(local_user())
-       $homebase = $a->get_baseurl() . '/profile/' . $a->user['nickname'];
+       $homebase = 'profile/' . $a->user['nickname'];
 
 if(isset($homebase))
        $a->page['content'] .= '<script>var homebase="' . $homebase . '" ; </script>';
@@ -416,15 +408,6 @@ if(x($_SESSION,'sysmsg_info')) {
 call_hooks('page_end', $a->page['content']);
 
 
-/**
- *
- * Add a place for the pause/resume Ajax indicator
- *
- */
-
-$a->page['content'] .=  '<div id="pause"></div>';
-
-
 /**
  *
  * Add the navigation (menu) template
@@ -441,10 +424,10 @@ if($a->module != 'install' && $a->module != 'maintenance') {
 
 if($a->is_mobile || $a->is_tablet) {
        if(isset($_SESSION['show-mobile']) && !$_SESSION['show-mobile']) {
-               $link = $a->get_baseurl() . '/toggle_mobile?address=' . curPageURL();
+               $link = 'toggle_mobile?address=' . curPageURL();
        }
        else {
-               $link = $a->get_baseurl() . '/toggle_mobile?off=1&address=' . curPageURL();
+               $link = 'toggle_mobile?off=1&address=' . curPageURL();
        }
        $a->page['footer'] = replace_macros(get_markup_template("toggle_mobile_footer.tpl"), array(
                                '$toggle_link' => $link,
diff --git a/library/HTMLPurifier.auto.php b/library/HTMLPurifier.auto.php
deleted file mode 100644 (file)
index 1960c39..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<?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
diff --git a/library/HTMLPurifier.autoload.php b/library/HTMLPurifier.autoload.php
deleted file mode 100644 (file)
index 8d40176..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-/**
- * @file
- * Convenience file that registers autoload handler for HTML Purifier.
- */
-
-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')) {
-    function __autoload($class) {
-        return HTMLPurifier_Bootstrap::autoload($class);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier.func.php b/library/HTMLPurifier.func.php
deleted file mode 100644 (file)
index 56a55b2..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-/**
- * @file
- * Defines a function wrapper for HTML Purifier for quick use.
- * @note ''HTMLPurifier()'' is NOT the same as ''new HTMLPurifier()''
- */
-
-/**
- * Purify HTML.
- * @param $html String HTML to purify
- * @param $config Configuration to use, can be any value accepted by
- *        HTMLPurifier_Config::create()
- */
-function HTMLPurifier($html, $config = null) {
-    static $purifier = false;
-    if (!$purifier) {
-        $purifier = new HTMLPurifier();
-    }
-    return $purifier->purify($html, $config);
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier.includes.php b/library/HTMLPurifier.includes.php
deleted file mode 100644 (file)
index 2ed0f0c..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-<?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.1.1
- *
- * @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/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/PercentEncoder.php';
-require 'HTMLPurifier/PropertyList.php';
-require 'HTMLPurifier/PropertyListIterator.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/AttrDef/CSS.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/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/SafeEmbed.php';
-require 'HTMLPurifier/AttrTransform/SafeObject.php';
-require 'HTMLPurifier/AttrTransform/SafeParam.php';
-require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
-require 'HTMLPurifier/AttrTransform/Textarea.php';
-require 'HTMLPurifier/ChildDef/Chameleon.php';
-require 'HTMLPurifier/ChildDef/Custom.php';
-require 'HTMLPurifier/ChildDef/Empty.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/Image.php';
-require 'HTMLPurifier/HTMLModule/Legacy.php';
-require 'HTMLPurifier/HTMLModule/List.php';
-require 'HTMLPurifier/HTMLModule/Name.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/Scripting.php';
-require 'HTMLPurifier/HTMLModule/StyleAttribute.php';
-require 'HTMLPurifier/HTMLModule/Tables.php';
-require 'HTMLPurifier/HTMLModule/Target.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/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/HostBlacklist.php';
-require 'HTMLPurifier/URIFilter/MakeAbsolute.php';
-require 'HTMLPurifier/URIFilter/Munge.php';
-require 'HTMLPurifier/URIScheme/data.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/VarParser/Flexible.php';
-require 'HTMLPurifier/VarParser/Native.php';
diff --git a/library/HTMLPurifier.kses.php b/library/HTMLPurifier.kses.php
deleted file mode 100644 (file)
index 3143feb..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-<?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);
-    $allowed_schemes = array();
-    if ($allowed_protocols !== null) {
-        $config->set('URI.AllowedSchemes', $allowed_protocols);
-    }
-    $purifier = new HTMLPurifier($config);
-    return $purifier->purify($string);
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier.path.php b/library/HTMLPurifier.path.php
deleted file mode 100644 (file)
index 39b1b65..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<?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
diff --git a/library/HTMLPurifier.php b/library/HTMLPurifier.php
deleted file mode 100644 (file)
index ba2c7b3..0000000
+++ /dev/null
@@ -1,237 +0,0 @@
-<?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.1.1 - 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 */
-    public $version = '4.1.1';
-
-    /** Constant with version of HTML Purifier */
-    const VERSION = '4.1.1';
-
-    /** Global configuration object */
-    public $config;
-
-    /** Array of extra HTMLPurifier_Filter objects to run on HTML, for backwards compatibility */
-    private $filters = array();
-
-    /** Single instance of HTML Purifier */
-    private static $instance;
-
-    protected $strategy, $generator;
-
-    /**
-     * Resultant HTMLPurifier_Context of last run purification. Is an array
-     * of contexts if the last called method was purifyArray().
-     */
-    public $context;
-
-    /**
-     * Initializes the purifier.
-     * @param $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 $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 $html String of HTML to purify
-     * @param $config HTMLPurifier_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 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 $config Optional HTMLPurifier_Config object for this operation.
-     *                See HTMLPurifier::purify() for more details.
-     * @return 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 $prototype Optional prototype HTMLPurifier instance to
-     *                   overload singleton with, or HTMLPurifier_Config
-     *                   instance to configure the generated version with.
-     */
-    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;
-    }
-
-    /**
-     * @note Backwards compatibility, see instance()
-     */
-    public static function getInstance($prototype = null) {
-        return HTMLPurifier::instance($prototype);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier.safe-includes.php b/library/HTMLPurifier.safe-includes.php
deleted file mode 100644 (file)
index 6402de0..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-<?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/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/PercentEncoder.php';
-require_once $__dir . '/HTMLPurifier/PropertyList.php';
-require_once $__dir . '/HTMLPurifier/PropertyListIterator.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/AttrDef/CSS.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/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/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/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/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/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/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/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/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/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/HostBlacklist.php';
-require_once $__dir . '/HTMLPurifier/URIFilter/MakeAbsolute.php';
-require_once $__dir . '/HTMLPurifier/URIFilter/Munge.php';
-require_once $__dir . '/HTMLPurifier/URIScheme/data.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/VarParser/Flexible.php';
-require_once $__dir . '/HTMLPurifier/VarParser/Native.php';
diff --git a/library/HTMLPurifier/AttrCollections.php b/library/HTMLPurifier/AttrCollections.php
deleted file mode 100644 (file)
index 555b86d..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-<?php
-
-/**
- * Defines common attribute collections that modules reference
- */
-
-class HTMLPurifier_AttrCollections
-{
-
-    /**
-     * Associative array of attribute collections, indexed by name
-     */
-    public $info = array();
-
-    /**
-     * Performs all expansions on internal data for use by other inclusions
-     * It also collects all attribute collection extensions from
-     * modules
-     * @param $attr_types HTMLPurifier_AttrTypes instance
-     * @param $modules Hash array of HTMLPurifier_HTMLModule members
-     */
-    public function __construct($attr_types, $modules) {
-        // load extensions from the modules
-        foreach ($modules as $module) {
-            foreach ($module->attr_collections as $coll_i => $coll) {
-                if (!isset($this->info[$coll_i])) {
-                    $this->info[$coll_i] = array();
-                }
-                foreach ($coll as $attr_i => $attr) {
-                    if ($attr_i === 0 && isset($this->info[$coll_i][$attr_i])) {
-                        // merge in includes
-                        $this->info[$coll_i][$attr_i] = array_merge(
-                            $this->info[$coll_i][$attr_i], $attr);
-                        continue;
-                    }
-                    $this->info[$coll_i][$attr_i] = $attr;
-                }
-            }
-        }
-        // perform internal expansions and inclusions
-        foreach ($this->info as $name => $attr) {
-            // merge attribute collections that include others
-            $this->performInclusions($this->info[$name]);
-            // replace string identifiers with actual attribute objects
-            $this->expandIdentifiers($this->info[$name], $attr_types);
-        }
-    }
-
-    /**
-     * Takes a reference to an attribute associative array and performs
-     * all inclusions specified by the zero index.
-     * @param &$attr Reference to attribute array
-     */
-    public function performInclusions(&$attr) {
-        if (!isset($attr[0])) return;
-        $merge = $attr[0];
-        $seen  = array(); // recursion guard
-        // loop through all the inclusions
-        for ($i = 0; isset($merge[$i]); $i++) {
-            if (isset($seen[$merge[$i]])) continue;
-            $seen[$merge[$i]] = true;
-            // foreach attribute of the inclusion, copy it over
-            if (!isset($this->info[$merge[$i]])) continue;
-            foreach ($this->info[$merge[$i]] as $key => $value) {
-                if (isset($attr[$key])) continue; // also catches more inclusions
-                $attr[$key] = $value;
-            }
-            if (isset($this->info[$merge[$i]][0])) {
-                // recursion
-                $merge = array_merge($merge, $this->info[$merge[$i]][0]);
-            }
-        }
-        unset($attr[0]);
-    }
-
-    /**
-     * Expands all string identifiers in an attribute array by replacing
-     * them with the appropriate values inside HTMLPurifier_AttrTypes
-     * @param &$attr Reference to attribute array
-     * @param $attr_types HTMLPurifier_AttrTypes instance
-     */
-    public function expandIdentifiers(&$attr, $attr_types) {
-
-        // because foreach will process new elements we add, make sure we
-        // skip duplicates
-        $processed = array();
-
-        foreach ($attr as $def_i => $def) {
-            // skip inclusions
-            if ($def_i === 0) continue;
-
-            if (isset($processed[$def_i])) continue;
-
-            // determine whether or not attribute is required
-            if ($required = (strpos($def_i, '*') !== false)) {
-                // rename the definition
-                unset($attr[$def_i]);
-                $def_i = trim($def_i, '*');
-                $attr[$def_i] = $def;
-            }
-
-            $processed[$def_i] = true;
-
-            // if we've already got a literal object, move on
-            if (is_object($def)) {
-                // preserve previous required
-                $attr[$def_i]->required = ($required || $attr[$def_i]->required);
-                continue;
-            }
-
-            if ($def === false) {
-                unset($attr[$def_i]);
-                continue;
-            }
-
-            if ($t = $attr_types->get($def)) {
-                $attr[$def_i] = $t;
-                $attr[$def_i]->required = $required;
-            } else {
-                unset($attr[$def_i]);
-            }
-        }
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef.php b/library/HTMLPurifier/AttrDef.php
deleted file mode 100644 (file)
index b2e4f36..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-<?php
-
-/**
- * Base class for all validating attribute definitions.
- *
- * This family of classes forms the core for not only HTML attribute validation,
- * but also any sort of string that needs to be validated or cleaned (which
- * means CSS properties and composite definitions are defined here too).
- * Besides defining (through code) what precisely makes the string valid,
- * subclasses are also responsible for cleaning the code if possible.
- */
-
-abstract class HTMLPurifier_AttrDef
-{
-
-    /**
-     * Tells us whether or not an HTML attribute is minimized. Has no
-     * meaning in other contexts.
-     */
-    public $minimized = false;
-
-    /**
-     * Tells us whether or not an HTML attribute is required. Has no
-     * meaning in other contexts
-     */
-    public $required = false;
-
-    /**
-     * Validates and cleans passed string according to a definition.
-     *
-     * @param $string String to be validated and cleaned.
-     * @param $config Mandatory HTMLPurifier_Config object.
-     * @param $context Mandatory HTMLPurifier_AttrContext object.
-     */
-    abstract public function validate($string, $config, $context);
-
-    /**
-     * Convenience method that parses a string as if it were CDATA.
-     *
-     * This method process a string in the manner specified at
-     * <http://www.w3.org/TR/html4/types.html#h-6.2> by removing
-     * leading and trailing whitespace, ignoring line feeds, and replacing
-     * carriage returns and tabs with spaces.  While most useful for HTML
-     * attributes specified as CDATA, it can also be applied to most CSS
-     * values.
-     *
-     * @note This method is not entirely standards compliant, as trim() removes
-     *       more types of whitespace than specified in the spec. In practice,
-     *       this is rarely a problem, as those extra characters usually have
-     *       already been removed by HTMLPurifier_Encoder.
-     *
-     * @warning This processing is inconsistent with XML's whitespace handling
-     *          as specified by section 3.3.3 and referenced XHTML 1.0 section
-     *          4.7.  However, note that we are NOT necessarily
-     *          parsing XML, thus, this behavior may still be correct. We
-     *          assume that newlines have been normalized.
-     */
-    public function parseCDATA($string) {
-        $string = trim($string);
-        $string = str_replace(array("\n", "\t", "\r"), ' ', $string);
-        return $string;
-    }
-
-    /**
-     * Factory method for creating this class from a string.
-     * @param $string String construction info
-     * @return Created AttrDef object corresponding to $string
-     */
-    public function make($string) {
-        // default implementation, return a flyweight of this object.
-        // If $string has an effect on the returned object (i.e. you
-        // need to overload this method), it is best
-        // to clone or instantiate new copies. (Instantiation is safer.)
-        return $this;
-    }
-
-    /**
-     * Removes spaces from rgb(0, 0, 0) so that shorthand CSS properties work
-     * properly. THIS IS A HACK!
-     */
-    protected function mungeRgb($string) {
-        return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string);
-    }
-
-    /**
-     * Parses a possibly escaped CSS string and returns the "pure" 
-     * version of it.
-     */
-    protected function expandCSSEscape($string) {
-        // flexibly parse it
-        $ret = '';
-        for ($i = 0, $c = strlen($string); $i < $c; $i++) {
-            if ($string[$i] === '\\') {
-                $i++;
-                if ($i >= $c) {
-                    $ret .= '\\';
-                    break;
-                }
-                if (ctype_xdigit($string[$i])) {
-                    $code = $string[$i];
-                    for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) {
-                        if (!ctype_xdigit($string[$i])) break;
-                        $code .= $string[$i];
-                    }
-                    // We have to be extremely careful when adding
-                    // new characters, to make sure we're not breaking
-                    // the encoding.
-                    $char = HTMLPurifier_Encoder::unichr(hexdec($code));
-                    if (HTMLPurifier_Encoder::cleanUTF8($char) === '') continue;
-                    $ret .= $char;
-                    if ($i < $c && trim($string[$i]) !== '') $i--;
-                    continue;
-                }
-                if ($string[$i] === "\n") continue;
-            }
-            $ret .= $string[$i];
-        }
-        return $ret;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS.php b/library/HTMLPurifier/AttrDef/CSS.php
deleted file mode 100644 (file)
index 953e706..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-
-/**
- * Validates the HTML attribute style, otherwise known as CSS.
- * @note We don't implement the whole CSS specification, so it might be
- *       difficult to reuse this component in the context of validating
- *       actual stylesheet declarations.
- * @note If we were really serious about validating the CSS, we would
- *       tokenize the styles and then parse the tokens. Obviously, we
- *       are not doing that. Doing that could seriously harm performance,
- *       but would make these components a lot more viable for a CSS
- *       filtering solution.
- */
-class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
-{
-
-    public function validate($css, $config, $context) {
-
-        $css = $this->parseCDATA($css);
-
-        $definition = $config->getCSSDefinition();
-
-        // we're going to break the spec and explode by semicolons.
-        // This is because semicolon rarely appears in escaped form
-        // Doing this is generally flaky but fast
-        // IT MIGHT APPEAR IN URIs, see HTMLPurifier_AttrDef_CSSURI
-        // for details
-
-        $declarations = explode(';', $css);
-        $propvalues = array();
-
-        /**
-         * Name of the current CSS property being validated.
-         */
-        $property = false;
-        $context->register('CurrentCSSProperty', $property);
-
-        foreach ($declarations as $declaration) {
-            if (!$declaration) continue;
-            if (!strpos($declaration, ':')) continue;
-            list($property, $value) = explode(':', $declaration, 2);
-            $property = trim($property);
-            $value    = trim($value);
-            $ok = false;
-            do {
-                if (isset($definition->info[$property])) {
-                    $ok = true;
-                    break;
-                }
-                if (ctype_lower($property)) break;
-                $property = strtolower($property);
-                if (isset($definition->info[$property])) {
-                    $ok = true;
-                    break;
-                }
-            } while(0);
-            if (!$ok) continue;
-            // inefficient call, since the validator will do this again
-            if (strtolower(trim($value)) !== 'inherit') {
-                // inherit works for everything (but only on the base property)
-                $result = $definition->info[$property]->validate(
-                    $value, $config, $context );
-            } else {
-                $result = 'inherit';
-            }
-            if ($result === false) continue;
-            $propvalues[$property] = $result;
-        }
-
-        $context->destroy('CurrentCSSProperty');
-
-        // procedure does not write the new CSS simultaneously, so it's
-        // slightly inefficient, but it's the only way of getting rid of
-        // duplicates. Perhaps config to optimize it, but not now.
-
-        $new_declarations = '';
-        foreach ($propvalues as $prop => $value) {
-            $new_declarations .= "$prop:$value;";
-        }
-
-        return $new_declarations ? $new_declarations : false;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php b/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php
deleted file mode 100644 (file)
index 292c040..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-class HTMLPurifier_AttrDef_CSS_AlphaValue extends HTMLPurifier_AttrDef_CSS_Number
-{
-
-    public function __construct() {
-        parent::__construct(false); // opacity is non-negative, but we will clamp it
-    }
-
-    public function validate($number, $config, $context) {
-        $result = parent::validate($number, $config, $context);
-        if ($result === false) return $result;
-        $float = (float) $result;
-        if ($float < 0.0) $result = '0';
-        if ($float > 1.0) $result = '1';
-        return $result;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/Background.php b/library/HTMLPurifier/AttrDef/CSS/Background.php
deleted file mode 100644 (file)
index 3a3d20c..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-
-/**
- * Validates shorthand CSS property background.
- * @warning Does not support url tokens that have internal spaces.
- */
-class HTMLPurifier_AttrDef_CSS_Background extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Local copy of component validators.
-     * @note See HTMLPurifier_AttrDef_Font::$info for a similar impl.
-     */
-    protected $info;
-
-    public function __construct($config) {
-        $def = $config->getCSSDefinition();
-        $this->info['background-color'] = $def->info['background-color'];
-        $this->info['background-image'] = $def->info['background-image'];
-        $this->info['background-repeat'] = $def->info['background-repeat'];
-        $this->info['background-attachment'] = $def->info['background-attachment'];
-        $this->info['background-position'] = $def->info['background-position'];
-    }
-
-    public function validate($string, $config, $context) {
-
-        // regular pre-processing
-        $string = $this->parseCDATA($string);
-        if ($string === '') return false;
-
-        // munge rgb() decl if necessary
-        $string = $this->mungeRgb($string);
-
-        // assumes URI doesn't have spaces in it
-        $bits = explode(' ', strtolower($string)); // bits to process
-
-        $caught = array();
-        $caught['color']    = false;
-        $caught['image']    = false;
-        $caught['repeat']   = false;
-        $caught['attachment'] = false;
-        $caught['position'] = false;
-
-        $i = 0; // number of catches
-        $none = false;
-
-        foreach ($bits as $bit) {
-            if ($bit === '') continue;
-            foreach ($caught as $key => $status) {
-                if ($key != 'position') {
-                    if ($status !== false) continue;
-                    $r = $this->info['background-' . $key]->validate($bit, $config, $context);
-                } else {
-                    $r = $bit;
-                }
-                if ($r === false) continue;
-                if ($key == 'position') {
-                    if ($caught[$key] === false) $caught[$key] = '';
-                    $caught[$key] .= $r . ' ';
-                } else {
-                    $caught[$key] = $r;
-                }
-                $i++;
-                break;
-            }
-        }
-
-        if (!$i) return false;
-        if ($caught['position'] !== false) {
-            $caught['position'] = $this->info['background-position']->
-                validate($caught['position'], $config, $context);
-        }
-
-        $ret = array();
-        foreach ($caught as $value) {
-            if ($value === false) continue;
-            $ret[] = $value;
-        }
-
-        if (empty($ret)) return false;
-        return implode(' ', $ret);
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php b/library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php
deleted file mode 100644 (file)
index fae82ea..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-<?php
-
-/* W3C says:
-    [ // adjective and number must be in correct order, even if
-      // you could switch them without introducing ambiguity.
-      // some browsers support that syntax
-        [
-            <percentage> | <length> | left | center | right
-        ]
-        [
-            <percentage> | <length> | top | center | bottom
-        ]?
-    ] |
-    [ // this signifies that the vertical and horizontal adjectives
-      // can be arbitrarily ordered, however, there can only be two,
-      // one of each, or none at all
-        [
-            left | center | right
-        ] ||
-        [
-            top | center | bottom
-        ]
-    ]
-    top, left = 0%
-    center, (none) = 50%
-    bottom, right = 100%
-*/
-
-/* QuirksMode says:
-    keyword + length/percentage must be ordered correctly, as per W3C
-
-    Internet Explorer and Opera, however, support arbitrary ordering. We
-    should fix it up.
-
-    Minor issue though, not strictly necessary.
-*/
-
-// control freaks may appreciate the ability to convert these to
-// percentages or something, but it's not necessary
-
-/**
- * Validates the value of background-position.
- */
-class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef
-{
-
-    protected $length;
-    protected $percentage;
-
-    public function __construct() {
-        $this->length     = new HTMLPurifier_AttrDef_CSS_Length();
-        $this->percentage = new HTMLPurifier_AttrDef_CSS_Percentage();
-    }
-
-    public function validate($string, $config, $context) {
-        $string = $this->parseCDATA($string);
-        $bits = explode(' ', $string);
-
-        $keywords = array();
-        $keywords['h'] = false; // left, right
-        $keywords['v'] = false; // top, bottom
-        $keywords['ch'] = false; // center (first word)
-        $keywords['cv'] = false; // center (second word)
-        $measures = array();
-
-        $i = 0;
-
-        $lookup = array(
-            'top' => 'v',
-            'bottom' => 'v',
-            'left' => 'h',
-            'right' => 'h',
-            'center' => 'c'
-        );
-
-        foreach ($bits as $bit) {
-            if ($bit === '') continue;
-
-            // test for keyword
-            $lbit = ctype_lower($bit) ? $bit : strtolower($bit);
-            if (isset($lookup[$lbit])) {
-                $status = $lookup[$lbit];
-                if ($status == 'c') {
-                    if ($i == 0) {
-                        $status = 'ch';
-                    } else {
-                        $status = 'cv';
-                    }
-                }
-                $keywords[$status] = $lbit;
-                $i++;
-            }
-
-            // test for length
-            $r = $this->length->validate($bit, $config, $context);
-            if ($r !== false) {
-                $measures[] = $r;
-                $i++;
-            }
-
-            // test for percentage
-            $r = $this->percentage->validate($bit, $config, $context);
-            if ($r !== false) {
-                $measures[] = $r;
-                $i++;
-            }
-
-        }
-
-        if (!$i) return false; // no valid values were caught
-
-        $ret = array();
-
-        // first keyword
-        if     ($keywords['h'])     $ret[] = $keywords['h'];
-        elseif ($keywords['ch']) {
-            $ret[] = $keywords['ch'];
-            $keywords['cv'] = false; // prevent re-use: center = center center
-        }
-        elseif (count($measures))   $ret[] = array_shift($measures);
-
-        if     ($keywords['v'])     $ret[] = $keywords['v'];
-        elseif ($keywords['cv'])    $ret[] = $keywords['cv'];
-        elseif (count($measures))   $ret[] = array_shift($measures);
-
-        if (empty($ret)) return false;
-        return implode(' ', $ret);
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/Border.php b/library/HTMLPurifier/AttrDef/CSS/Border.php
deleted file mode 100644 (file)
index 42a1d1b..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-/**
- * Validates the border property as defined by CSS.
- */
-class HTMLPurifier_AttrDef_CSS_Border extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Local copy of properties this property is shorthand for.
-     */
-    protected $info = array();
-
-    public function __construct($config) {
-        $def = $config->getCSSDefinition();
-        $this->info['border-width'] = $def->info['border-width'];
-        $this->info['border-style'] = $def->info['border-style'];
-        $this->info['border-top-color'] = $def->info['border-top-color'];
-    }
-
-    public function validate($string, $config, $context) {
-        $string = $this->parseCDATA($string);
-        $string = $this->mungeRgb($string);
-        $bits = explode(' ', $string);
-        $done = array(); // segments we've finished
-        $ret = ''; // return value
-        foreach ($bits as $bit) {
-            foreach ($this->info as $propname => $validator) {
-                if (isset($done[$propname])) continue;
-                $r = $validator->validate($bit, $config, $context);
-                if ($r !== false) {
-                    $ret .= $r . ' ';
-                    $done[$propname] = true;
-                    break;
-                }
-            }
-        }
-        return rtrim($ret);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/Color.php b/library/HTMLPurifier/AttrDef/CSS/Color.php
deleted file mode 100644 (file)
index 07f95a6..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-
-/**
- * Validates Color as defined by CSS.
- */
-class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
-{
-
-    public function validate($color, $config, $context) {
-
-        static $colors = null;
-        if ($colors === null) $colors = $config->get('Core.ColorKeywords');
-
-        $color = trim($color);
-        if ($color === '') return false;
-
-        $lower = strtolower($color);
-        if (isset($colors[$lower])) return $colors[$lower];
-
-        if (strpos($color, 'rgb(') !== false) {
-            // rgb literal handling
-            $length = strlen($color);
-            if (strpos($color, ')') !== $length - 1) return false;
-            $triad = substr($color, 4, $length - 4 - 1);
-            $parts = explode(',', $triad);
-            if (count($parts) !== 3) return false;
-            $type = false; // to ensure that they're all the same type
-            $new_parts = array();
-            foreach ($parts as $part) {
-                $part = trim($part);
-                if ($part === '') return false;
-                $length = strlen($part);
-                if ($part[$length - 1] === '%') {
-                    // handle percents
-                    if (!$type) {
-                        $type = 'percentage';
-                    } elseif ($type !== 'percentage') {
-                        return false;
-                    }
-                    $num = (float) substr($part, 0, $length - 1);
-                    if ($num < 0) $num = 0;
-                    if ($num > 100) $num = 100;
-                    $new_parts[] = "$num%";
-                } else {
-                    // handle integers
-                    if (!$type) {
-                        $type = 'integer';
-                    } elseif ($type !== 'integer') {
-                        return false;
-                    }
-                    $num = (int) $part;
-                    if ($num < 0) $num = 0;
-                    if ($num > 255) $num = 255;
-                    $new_parts[] = (string) $num;
-                }
-            }
-            $new_triad = implode(',', $new_parts);
-            $color = "rgb($new_triad)";
-        } else {
-            // hexadecimal handling
-            if ($color[0] === '#') {
-                $hex = substr($color, 1);
-            } else {
-                $hex = $color;
-                $color = '#' . $color;
-            }
-            $length = strlen($hex);
-            if ($length !== 3 && $length !== 6) return false;
-            if (!ctype_xdigit($hex)) return false;
-        }
-
-        return $color;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/Composite.php b/library/HTMLPurifier/AttrDef/CSS/Composite.php
deleted file mode 100644 (file)
index de1289c..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-/**
- * Allows multiple validators to attempt to validate attribute.
- *
- * Composite is just what it sounds like: a composite of many validators.
- * This means that multiple HTMLPurifier_AttrDef objects will have a whack
- * at the string.  If one of them passes, that's what is returned.  This is
- * especially useful for CSS values, which often are a choice between
- * an enumerated set of predefined values or a flexible data type.
- */
-class HTMLPurifier_AttrDef_CSS_Composite extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * List of HTMLPurifier_AttrDef objects that may process strings
-     * @todo Make protected
-     */
-    public $defs;
-
-    /**
-     * @param $defs List of HTMLPurifier_AttrDef objects
-     */
-    public function __construct($defs) {
-        $this->defs = $defs;
-    }
-
-    public function validate($string, $config, $context) {
-        foreach ($this->defs as $i => $def) {
-            $result = $this->defs[$i]->validate($string, $config, $context);
-            if ($result !== false) return $result;
-        }
-        return false;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php b/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php
deleted file mode 100644 (file)
index 6599c5b..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-/**
- * Decorator which enables CSS properties to be disabled for specific elements.
- */
-class HTMLPurifier_AttrDef_CSS_DenyElementDecorator extends HTMLPurifier_AttrDef
-{
-    public $def, $element;
-
-    /**
-     * @param $def Definition to wrap
-     * @param $element Element to deny
-     */
-    public function __construct($def, $element) {
-        $this->def = $def;
-        $this->element = $element;
-    }
-    /**
-     * Checks if CurrentToken is set and equal to $this->element
-     */
-    public function validate($string, $config, $context) {
-        $token = $context->get('CurrentToken', true);
-        if ($token && $token->name == $this->element) return false;
-        return $this->def->validate($string, $config, $context);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/Filter.php b/library/HTMLPurifier/AttrDef/CSS/Filter.php
deleted file mode 100644 (file)
index 147894b..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-
-/**
- * Microsoft's proprietary filter: CSS property
- * @note Currently supports the alpha filter. In the future, this will
- *       probably need an extensible framework
- */
-class HTMLPurifier_AttrDef_CSS_Filter extends HTMLPurifier_AttrDef
-{
-
-    protected $intValidator;
-
-    public function __construct() {
-        $this->intValidator = new HTMLPurifier_AttrDef_Integer();
-    }
-
-    public function validate($value, $config, $context) {
-        $value = $this->parseCDATA($value);
-        if ($value === 'none') return $value;
-        // if we looped this we could support multiple filters
-        $function_length = strcspn($value, '(');
-        $function = trim(substr($value, 0, $function_length));
-        if ($function !== 'alpha' &&
-            $function !== 'Alpha' &&
-            $function !== 'progid:DXImageTransform.Microsoft.Alpha'
-            ) return false;
-        $cursor = $function_length + 1;
-        $parameters_length = strcspn($value, ')', $cursor);
-        $parameters = substr($value, $cursor, $parameters_length);
-        $params = explode(',', $parameters);
-        $ret_params = array();
-        $lookup = array();
-        foreach ($params as $param) {
-            list($key, $value) = explode('=', $param);
-            $key   = trim($key);
-            $value = trim($value);
-            if (isset($lookup[$key])) continue;
-            if ($key !== 'opacity') continue;
-            $value = $this->intValidator->validate($value, $config, $context);
-            if ($value === false) continue;
-            $int = (int) $value;
-            if ($int > 100) $value = '100';
-            if ($int < 0) $value = '0';
-            $ret_params[] = "$key=$value";
-            $lookup[$key] = true;
-        }
-        $ret_parameters = implode(',', $ret_params);
-        $ret_function = "$function($ret_parameters)";
-        return $ret_function;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/Font.php b/library/HTMLPurifier/AttrDef/CSS/Font.php
deleted file mode 100644 (file)
index 699ee0b..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-<?php
-
-/**
- * Validates shorthand CSS property font.
- */
-class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Local copy of component validators.
-     *
-     * @note If we moved specific CSS property definitions to their own
-     *       classes instead of having them be assembled at run time by
-     *       CSSDefinition, this wouldn't be necessary.  We'd instantiate
-     *       our own copies.
-     */
-    protected $info = array();
-
-    public function __construct($config) {
-        $def = $config->getCSSDefinition();
-        $this->info['font-style']   = $def->info['font-style'];
-        $this->info['font-variant'] = $def->info['font-variant'];
-        $this->info['font-weight']  = $def->info['font-weight'];
-        $this->info['font-size']    = $def->info['font-size'];
-        $this->info['line-height']  = $def->info['line-height'];
-        $this->info['font-family']  = $def->info['font-family'];
-    }
-
-    public function validate($string, $config, $context) {
-
-        static $system_fonts = array(
-            'caption' => true,
-            'icon' => true,
-            'menu' => true,
-            'message-box' => true,
-            'small-caption' => true,
-            'status-bar' => true
-        );
-
-        // regular pre-processing
-        $string = $this->parseCDATA($string);
-        if ($string === '') return false;
-
-        // check if it's one of the keywords
-        $lowercase_string = strtolower($string);
-        if (isset($system_fonts[$lowercase_string])) {
-            return $lowercase_string;
-        }
-
-        $bits = explode(' ', $string); // bits to process
-        $stage = 0; // this indicates what we're looking for
-        $caught = array(); // which stage 0 properties have we caught?
-        $stage_1 = array('font-style', 'font-variant', 'font-weight');
-        $final = ''; // output
-
-        for ($i = 0, $size = count($bits); $i < $size; $i++) {
-            if ($bits[$i] === '') continue;
-            switch ($stage) {
-
-                // attempting to catch font-style, font-variant or font-weight
-                case 0:
-                    foreach ($stage_1 as $validator_name) {
-                        if (isset($caught[$validator_name])) continue;
-                        $r = $this->info[$validator_name]->validate(
-                                                $bits[$i], $config, $context);
-                        if ($r !== false) {
-                            $final .= $r . ' ';
-                            $caught[$validator_name] = true;
-                            break;
-                        }
-                    }
-                    // all three caught, continue on
-                    if (count($caught) >= 3) $stage = 1;
-                    if ($r !== false) break;
-
-                // attempting to catch font-size and perhaps line-height
-                case 1:
-                    $found_slash = false;
-                    if (strpos($bits[$i], '/') !== false) {
-                        list($font_size, $line_height) =
-                                                    explode('/', $bits[$i]);
-                        if ($line_height === '') {
-                            // ooh, there's a space after the slash!
-                            $line_height = false;
-                            $found_slash = true;
-                        }
-                    } else {
-                        $font_size = $bits[$i];
-                        $line_height = false;
-                    }
-                    $r = $this->info['font-size']->validate(
-                                              $font_size, $config, $context);
-                    if ($r !== false) {
-                        $final .= $r;
-                        // attempt to catch line-height
-                        if ($line_height === false) {
-                            // we need to scroll forward
-                            for ($j = $i + 1; $j < $size; $j++) {
-                                if ($bits[$j] === '') continue;
-                                if ($bits[$j] === '/') {
-                                    if ($found_slash) {
-                                        return false;
-                                    } else {
-                                        $found_slash = true;
-                                        continue;
-                                    }
-                                }
-                                $line_height = $bits[$j];
-                                break;
-                            }
-                        } else {
-                            // slash already found
-                            $found_slash = true;
-                            $j = $i;
-                        }
-                        if ($found_slash) {
-                            $i = $j;
-                            $r = $this->info['line-height']->validate(
-                                              $line_height, $config, $context);
-                            if ($r !== false) {
-                                $final .= '/' . $r;
-                            }
-                        }
-                        $final .= ' ';
-                        $stage = 2;
-                        break;
-                    }
-                    return false;
-
-                // attempting to catch font-family
-                case 2:
-                    $font_family =
-                        implode(' ', array_slice($bits, $i, $size - $i));
-                    $r = $this->info['font-family']->validate(
-                                              $font_family, $config, $context);
-                    if ($r !== false) {
-                        $final .= $r . ' ';
-                        // processing completed successfully
-                        return rtrim($final);
-                    }
-                    return false;
-            }
-        }
-        return false;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/FontFamily.php b/library/HTMLPurifier/AttrDef/CSS/FontFamily.php
deleted file mode 100644 (file)
index 42c2054..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?php
-
-/**
- * Validates a font family list according to CSS spec
- * @todo whitelisting allowed fonts would be nice
- */
-class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef
-{
-
-    public function validate($string, $config, $context) {
-        static $generic_names = array(
-            'serif' => true,
-            'sans-serif' => true,
-            'monospace' => true,
-            'fantasy' => true,
-            'cursive' => true
-        );
-
-        // assume that no font names contain commas in them
-        $fonts = explode(',', $string);
-        $final = '';
-        foreach($fonts as $font) {
-            $font = trim($font);
-            if ($font === '') continue;
-            // match a generic name
-            if (isset($generic_names[$font])) {
-                $final .= $font . ', ';
-                continue;
-            }
-            // match a quoted name
-            if ($font[0] === '"' || $font[0] === "'") {
-                $length = strlen($font);
-                if ($length <= 2) continue;
-                $quote = $font[0];
-                if ($font[$length - 1] !== $quote) continue;
-                $font = substr($font, 1, $length - 2);
-            }
-
-            $font = $this->expandCSSEscape($font);
-
-            // $font is a pure representation of the font name
-
-            if (ctype_alnum($font) && $font !== '') {
-                // very simple font, allow it in unharmed
-                $final .= $font . ', ';
-                continue;
-            }
-
-            // bugger out on whitespace.  form feed (0C) really
-            // shouldn't show up regardless
-            $font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font);
-
-            // These ugly transforms don't pose a security
-            // risk (as \\ and \" might).  We could try to be clever and
-            // use single-quote wrapping when there is a double quote
-            // present, but I have choosen not to implement that.
-            // (warning: this code relies on the selection of quotation
-            // mark below)
-            $font = str_replace('\\', '\\5C ', $font);
-            $font = str_replace('"',  '\\22 ', $font);
-
-            // complicated font, requires quoting
-            $final .= "\"$font\", "; // note that this will later get turned into &quot;
-        }
-        $final = rtrim($final, ', ');
-        if ($final === '') return false;
-        return $final;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php b/library/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php
deleted file mode 100644 (file)
index 4e6b35e..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * Decorator which enables !important to be used in CSS values.
- */
-class HTMLPurifier_AttrDef_CSS_ImportantDecorator extends HTMLPurifier_AttrDef
-{
-    public $def, $allow;
-
-    /**
-     * @param $def Definition to wrap
-     * @param $allow Whether or not to allow !important
-     */
-    public function __construct($def, $allow = false) {
-        $this->def = $def;
-        $this->allow = $allow;
-    }
-    /**
-     * Intercepts and removes !important if necessary
-     */
-    public function validate($string, $config, $context) {
-        // test for ! and important tokens
-        $string = trim($string);
-        $is_important = false;
-        // :TODO: optimization: test directly for !important and ! important
-        if (strlen($string) >= 9 && substr($string, -9) === 'important') {
-            $temp = rtrim(substr($string, 0, -9));
-            // use a temp, because we might want to restore important
-            if (strlen($temp) >= 1 && substr($temp, -1) === '!') {
-                $string = rtrim(substr($temp, 0, -1));
-                $is_important = true;
-            }
-        }
-        $string = $this->def->validate($string, $config, $context);
-        if ($this->allow && $is_important) $string .= ' !important';
-        return $string;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/Length.php b/library/HTMLPurifier/AttrDef/CSS/Length.php
deleted file mode 100644 (file)
index a07ec58..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-/**
- * Represents a Length as defined by CSS.
- */
-class HTMLPurifier_AttrDef_CSS_Length extends HTMLPurifier_AttrDef
-{
-
-    protected $min, $max;
-
-    /**
-     * @param HTMLPurifier_Length $max Minimum length, or null for no bound. String is also acceptable.
-     * @param HTMLPurifier_Length $max Maximum length, or null for no bound. String is also acceptable.
-     */
-    public function __construct($min = null, $max = null) {
-        $this->min = $min !== null ? HTMLPurifier_Length::make($min) : null;
-        $this->max = $max !== null ? HTMLPurifier_Length::make($max) : null;
-    }
-
-    public function validate($string, $config, $context) {
-        $string = $this->parseCDATA($string);
-
-        // Optimizations
-        if ($string === '') return false;
-        if ($string === '0') return '0';
-        if (strlen($string) === 1) return false;
-
-        $length = HTMLPurifier_Length::make($string);
-        if (!$length->isValid()) return false;
-
-        if ($this->min) {
-            $c = $length->compareTo($this->min);
-            if ($c === false) return false;
-            if ($c < 0) return false;
-        }
-        if ($this->max) {
-            $c = $length->compareTo($this->max);
-            if ($c === false) return false;
-            if ($c > 0) return false;
-        }
-
-        return $length->toString();
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/ListStyle.php b/library/HTMLPurifier/AttrDef/CSS/ListStyle.php
deleted file mode 100644 (file)
index 4406868..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-
-/**
- * Validates shorthand CSS property list-style.
- * @warning Does not support url tokens that have internal spaces.
- */
-class HTMLPurifier_AttrDef_CSS_ListStyle extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Local copy of component validators.
-     * @note See HTMLPurifier_AttrDef_CSS_Font::$info for a similar impl.
-     */
-    protected $info;
-
-    public function __construct($config) {
-        $def = $config->getCSSDefinition();
-        $this->info['list-style-type']     = $def->info['list-style-type'];
-        $this->info['list-style-position'] = $def->info['list-style-position'];
-        $this->info['list-style-image'] = $def->info['list-style-image'];
-    }
-
-    public function validate($string, $config, $context) {
-
-        // regular pre-processing
-        $string = $this->parseCDATA($string);
-        if ($string === '') return false;
-
-        // assumes URI doesn't have spaces in it
-        $bits = explode(' ', strtolower($string)); // bits to process
-
-        $caught = array();
-        $caught['type']     = false;
-        $caught['position'] = false;
-        $caught['image']    = false;
-
-        $i = 0; // number of catches
-        $none = false;
-
-        foreach ($bits as $bit) {
-            if ($i >= 3) return; // optimization bit
-            if ($bit === '') continue;
-            foreach ($caught as $key => $status) {
-                if ($status !== false) continue;
-                $r = $this->info['list-style-' . $key]->validate($bit, $config, $context);
-                if ($r === false) continue;
-                if ($r === 'none') {
-                    if ($none) continue;
-                    else $none = true;
-                    if ($key == 'image') continue;
-                }
-                $caught[$key] = $r;
-                $i++;
-                break;
-            }
-        }
-
-        if (!$i) return false;
-
-        $ret = array();
-
-        // construct type
-        if ($caught['type']) $ret[] = $caught['type'];
-
-        // construct image
-        if ($caught['image']) $ret[] = $caught['image'];
-
-        // construct position
-        if ($caught['position']) $ret[] = $caught['position'];
-
-        if (empty($ret)) return false;
-        return implode(' ', $ret);
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/Multiple.php b/library/HTMLPurifier/AttrDef/CSS/Multiple.php
deleted file mode 100644 (file)
index 4d62a40..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-
-/**
- * Framework class for strings that involve multiple values.
- *
- * Certain CSS properties such as border-width and margin allow multiple
- * lengths to be specified.  This class can take a vanilla border-width
- * definition and multiply it, usually into a max of four.
- *
- * @note Even though the CSS specification isn't clear about it, inherit
- *       can only be used alone: it will never manifest as part of a multi
- *       shorthand declaration.  Thus, this class does not allow inherit.
- */
-class HTMLPurifier_AttrDef_CSS_Multiple extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Instance of component definition to defer validation to.
-     * @todo Make protected
-     */
-    public $single;
-
-    /**
-     * Max number of values allowed.
-     * @todo Make protected
-     */
-    public $max;
-
-    /**
-     * @param $single HTMLPurifier_AttrDef to multiply
-     * @param $max Max number of values allowed (usually four)
-     */
-    public function __construct($single, $max = 4) {
-        $this->single = $single;
-        $this->max = $max;
-    }
-
-    public function validate($string, $config, $context) {
-        $string = $this->parseCDATA($string);
-        if ($string === '') return false;
-        $parts = explode(' ', $string); // parseCDATA replaced \r, \t and \n
-        $length = count($parts);
-        $final = '';
-        for ($i = 0, $num = 0; $i < $length && $num < $this->max; $i++) {
-            if (ctype_space($parts[$i])) continue;
-            $result = $this->single->validate($parts[$i], $config, $context);
-            if ($result !== false) {
-                $final .= $result . ' ';
-                $num++;
-            }
-        }
-        if ($final === '') return false;
-        return rtrim($final);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/Number.php b/library/HTMLPurifier/AttrDef/CSS/Number.php
deleted file mode 100644 (file)
index 3f99e12..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-
-/**
- * Validates a number as defined by the CSS spec.
- */
-class HTMLPurifier_AttrDef_CSS_Number extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Bool indicating whether or not only positive values allowed.
-     */
-    protected $non_negative = false;
-
-    /**
-     * @param $non_negative Bool indicating whether negatives are forbidden
-     */
-    public function __construct($non_negative = false) {
-        $this->non_negative = $non_negative;
-    }
-
-    /**
-     * @warning Some contexts do not pass $config, $context. These
-     *          variables should not be used without checking HTMLPurifier_Length
-     */
-    public function validate($number, $config, $context) {
-
-        $number = $this->parseCDATA($number);
-
-        if ($number === '') return false;
-        if ($number === '0') return '0';
-
-        $sign = '';
-        switch ($number[0]) {
-            case '-':
-                if ($this->non_negative) return false;
-                $sign = '-';
-            case '+':
-                $number = substr($number, 1);
-        }
-
-        if (ctype_digit($number)) {
-            $number = ltrim($number, '0');
-            return $number ? $sign . $number : '0';
-        }
-
-        // Period is the only non-numeric character allowed
-        if (strpos($number, '.') === false) return false;
-
-        list($left, $right) = explode('.', $number, 2);
-
-        if ($left === '' && $right === '') return false;
-        if ($left !== '' && !ctype_digit($left)) return false;
-
-        $left  = ltrim($left,  '0');
-        $right = rtrim($right, '0');
-
-        if ($right === '') {
-            return $left ? $sign . $left : '0';
-        } elseif (!ctype_digit($right)) {
-            return false;
-        }
-
-        return $sign . $left . '.' . $right;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/Percentage.php b/library/HTMLPurifier/AttrDef/CSS/Percentage.php
deleted file mode 100644 (file)
index c34b8fc..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * Validates a Percentage as defined by the CSS spec.
- */
-class HTMLPurifier_AttrDef_CSS_Percentage extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Instance of HTMLPurifier_AttrDef_CSS_Number to defer number validation
-     */
-    protected $number_def;
-
-    /**
-     * @param Bool indicating whether to forbid negative values
-     */
-    public function __construct($non_negative = false) {
-        $this->number_def = new HTMLPurifier_AttrDef_CSS_Number($non_negative);
-    }
-
-    public function validate($string, $config, $context) {
-
-        $string = $this->parseCDATA($string);
-
-        if ($string === '') return false;
-        $length = strlen($string);
-        if ($length === 1) return false;
-        if ($string[$length - 1] !== '%') return false;
-
-        $number = substr($string, 0, $length - 1);
-        $number = $this->number_def->validate($number, $config, $context);
-
-        if ($number === false) return false;
-        return "$number%";
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/TextDecoration.php b/library/HTMLPurifier/AttrDef/CSS/TextDecoration.php
deleted file mode 100644 (file)
index 772c922..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-/**
- * Validates the value for the CSS property text-decoration
- * @note This class could be generalized into a version that acts sort of
- *       like Enum except you can compound the allowed values.
- */
-class HTMLPurifier_AttrDef_CSS_TextDecoration extends HTMLPurifier_AttrDef
-{
-
-    public function validate($string, $config, $context) {
-
-        static $allowed_values = array(
-            'line-through' => true,
-            'overline' => true,
-            'underline' => true,
-        );
-
-        $string = strtolower($this->parseCDATA($string));
-
-        if ($string === 'none') return $string;
-
-        $parts = explode(' ', $string);
-        $final = '';
-        foreach ($parts as $part) {
-            if (isset($allowed_values[$part])) {
-                $final .= $part . ' ';
-            }
-        }
-        $final = rtrim($final);
-        if ($final === '') return false;
-        return $final;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/CSS/URI.php b/library/HTMLPurifier/AttrDef/CSS/URI.php
deleted file mode 100644 (file)
index 1df17dc..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-/**
- * Validates a URI in CSS syntax, which uses url('http://example.com')
- * @note While theoretically speaking a URI in a CSS document could
- *       be non-embedded, as of CSS2 there is no such usage so we're
- *       generalizing it. This may need to be changed in the future.
- * @warning Since HTMLPurifier_AttrDef_CSS blindly uses semicolons as
- *          the separator, you cannot put a literal semicolon in
- *          in the URI. Try percent encoding it, in that case.
- */
-class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI
-{
-
-    public function __construct() {
-        parent::__construct(true); // always embedded
-    }
-
-    public function validate($uri_string, $config, $context) {
-        // parse the URI out of the string and then pass it onto
-        // the parent object
-
-        $uri_string = $this->parseCDATA($uri_string);
-        if (strpos($uri_string, 'url(') !== 0) return false;
-        $uri_string = substr($uri_string, 4);
-        $new_length = strlen($uri_string) - 1;
-        if ($uri_string[$new_length] != ')') return false;
-        $uri = trim(substr($uri_string, 0, $new_length));
-
-        if (!empty($uri) && ($uri[0] == "'" || $uri[0] == '"')) {
-            $quote = $uri[0];
-            $new_length = strlen($uri) - 1;
-            if ($uri[$new_length] !== $quote) return false;
-            $uri = substr($uri, 1, $new_length - 1);
-        }
-
-        $uri = $this->expandCSSEscape($uri);
-
-        $result = parent::validate($uri, $config, $context);
-
-        if ($result === false) return false;
-
-        // extra sanity check; should have been done by URI
-        $result = str_replace(array('"', "\\", "\n", "\x0c", "\r"), "", $result);
-
-        return "url(\"$result\")";
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/Enum.php b/library/HTMLPurifier/AttrDef/Enum.php
deleted file mode 100644 (file)
index 5d603eb..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-<?php
-
-// Enum = Enumerated
-/**
- * Validates a keyword against a list of valid values.
- * @warning The case-insensitive compare of this function uses PHP's
- *          built-in strtolower and ctype_lower functions, which may
- *          cause problems with international comparisons
- */
-class HTMLPurifier_AttrDef_Enum extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Lookup table of valid values.
-     * @todo Make protected
-     */
-    public $valid_values   = array();
-
-    /**
-     * Bool indicating whether or not enumeration is case sensitive.
-     * @note In general this is always case insensitive.
-     */
-    protected $case_sensitive = false; // values according to W3C spec
-
-    /**
-     * @param $valid_values List of valid values
-     * @param $case_sensitive Bool indicating whether or not case sensitive
-     */
-    public function __construct(
-        $valid_values = array(), $case_sensitive = false
-    ) {
-        $this->valid_values = array_flip($valid_values);
-        $this->case_sensitive = $case_sensitive;
-    }
-
-    public function validate($string, $config, $context) {
-        $string = trim($string);
-        if (!$this->case_sensitive) {
-            // we may want to do full case-insensitive libraries
-            $string = ctype_lower($string) ? $string : strtolower($string);
-        }
-        $result = isset($this->valid_values[$string]);
-
-        return $result ? $string : false;
-    }
-
-    /**
-     * @param $string In form of comma-delimited list of case-insensitive
-     *      valid values. Example: "foo,bar,baz". Prepend "s:" to make
-     *      case sensitive
-     */
-    public function make($string) {
-        if (strlen($string) > 2 && $string[0] == 's' && $string[1] == ':') {
-            $string = substr($string, 2);
-            $sensitive = true;
-        } else {
-            $sensitive = false;
-        }
-        $values = explode(',', $string);
-        return new HTMLPurifier_AttrDef_Enum($values, $sensitive);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/HTML/Bool.php b/library/HTMLPurifier/AttrDef/HTML/Bool.php
deleted file mode 100644 (file)
index e06987e..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-/**
- * Validates a boolean attribute
- */
-class HTMLPurifier_AttrDef_HTML_Bool extends HTMLPurifier_AttrDef
-{
-
-    protected $name;
-    public $minimized = true;
-
-    public function __construct($name = false) {$this->name = $name;}
-
-    public function validate($string, $config, $context) {
-        if (empty($string)) return false;
-        return $this->name;
-    }
-
-    /**
-     * @param $string Name of attribute
-     */
-    public function make($string) {
-        return new HTMLPurifier_AttrDef_HTML_Bool($string);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/HTML/Class.php b/library/HTMLPurifier/AttrDef/HTML/Class.php
deleted file mode 100644 (file)
index 370068d..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-/**
- * Implements special behavior for class attribute (normally NMTOKENS)
- */
-class HTMLPurifier_AttrDef_HTML_Class extends HTMLPurifier_AttrDef_HTML_Nmtokens
-{
-    protected function split($string, $config, $context) {
-        // really, this twiddle should be lazy loaded
-        $name = $config->getDefinition('HTML')->doctype->name;
-        if ($name == "XHTML 1.1" || $name == "XHTML 2.0") {
-            return parent::split($string, $config, $context);
-        } else {
-            return preg_split('/\s+/', $string);
-        }
-    }
-    protected function filter($tokens, $config, $context) {
-        $allowed = $config->get('Attr.AllowedClasses');
-        $forbidden = $config->get('Attr.ForbiddenClasses');
-        $ret = array();
-        foreach ($tokens as $token) {
-            if (
-                ($allowed === null || isset($allowed[$token])) &&
-                !isset($forbidden[$token]) &&
-                // We need this O(n) check because of PHP's array
-                // implementation that casts -0 to 0.
-                !in_array($token, $ret, true)
-            ) {
-                $ret[] = $token;
-            }
-        }
-        return $ret;
-    }
-}
diff --git a/library/HTMLPurifier/AttrDef/HTML/Color.php b/library/HTMLPurifier/AttrDef/HTML/Color.php
deleted file mode 100644 (file)
index d01e204..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-/**
- * Validates a color according to the HTML spec.
- */
-class HTMLPurifier_AttrDef_HTML_Color extends HTMLPurifier_AttrDef
-{
-
-    public function validate($string, $config, $context) {
-
-        static $colors = null;
-        if ($colors === null) $colors = $config->get('Core.ColorKeywords');
-
-        $string = trim($string);
-
-        if (empty($string)) return false;
-        if (isset($colors[$string])) return $colors[$string];
-        if ($string[0] === '#') $hex = substr($string, 1);
-        else $hex = $string;
-
-        $length = strlen($hex);
-        if ($length !== 3 && $length !== 6) return false;
-        if (!ctype_xdigit($hex)) return false;
-        if ($length === 3) $hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2];
-
-        return "#$hex";
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php b/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php
deleted file mode 100644 (file)
index ae6ea7c..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-/**
- * Special-case enum attribute definition that lazy loads allowed frame targets
- */
-class HTMLPurifier_AttrDef_HTML_FrameTarget extends HTMLPurifier_AttrDef_Enum
-{
-
-    public $valid_values = false; // uninitialized value
-    protected $case_sensitive = false;
-
-    public function __construct() {}
-
-    public function validate($string, $config, $context) {
-        if ($this->valid_values === false) $this->valid_values = $config->get('Attr.AllowedFrameTargets');
-        return parent::validate($string, $config, $context);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/HTML/ID.php b/library/HTMLPurifier/AttrDef/HTML/ID.php
deleted file mode 100644 (file)
index 81d0376..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-/**
- * Validates the HTML attribute ID.
- * @warning Even though this is the id processor, it
- *          will ignore the directive Attr:IDBlacklist, since it will only
- *          go according to the ID accumulator. Since the accumulator is
- *          automatically generated, it will have already absorbed the
- *          blacklist. If you're hacking around, make sure you use load()!
- */
-
-class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
-{
-
-    // ref functionality disabled, since we also have to verify
-    // whether or not the ID it refers to exists
-
-    public function validate($id, $config, $context) {
-
-        if (!$config->get('Attr.EnableID')) return false;
-
-        $id = trim($id); // trim it first
-
-        if ($id === '') return false;
-
-        $prefix = $config->get('Attr.IDPrefix');
-        if ($prefix !== '') {
-            $prefix .= $config->get('Attr.IDPrefixLocal');
-            // prevent re-appending the prefix
-            if (strpos($id, $prefix) !== 0) $id = $prefix . $id;
-        } elseif ($config->get('Attr.IDPrefixLocal') !== '') {
-            trigger_error('%Attr.IDPrefixLocal cannot be used unless '.
-                '%Attr.IDPrefix is set', E_USER_WARNING);
-        }
-
-        //if (!$this->ref) {
-            $id_accumulator =& $context->get('IDAccumulator');
-            if (isset($id_accumulator->ids[$id])) return false;
-        //}
-
-        // we purposely avoid using regex, hopefully this is faster
-
-        if (ctype_alpha($id)) {
-            $result = true;
-        } else {
-            if (!ctype_alpha(@$id[0])) return false;
-            $trim = trim( // primitive style of regexps, I suppose
-                $id,
-                'A..Za..z0..9:-._'
-              );
-            $result = ($trim === '');
-        }
-
-        $regexp = $config->get('Attr.IDBlacklistRegexp');
-        if ($regexp && preg_match($regexp, $id)) {
-            return false;
-        }
-
-        if (/*!$this->ref && */$result) $id_accumulator->add($id);
-
-        // if no change was made to the ID, return the result
-        // else, return the new id if stripping whitespace made it
-        //     valid, or return false.
-        return $result ? $id : false;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/HTML/Length.php b/library/HTMLPurifier/AttrDef/HTML/Length.php
deleted file mode 100644 (file)
index a242f9c..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-/**
- * Validates the HTML type length (not to be confused with CSS's length).
- *
- * This accepts integer pixels or percentages as lengths for certain
- * HTML attributes.
- */
-
-class HTMLPurifier_AttrDef_HTML_Length extends HTMLPurifier_AttrDef_HTML_Pixels
-{
-
-    public function validate($string, $config, $context) {
-
-        $string = trim($string);
-        if ($string === '') return false;
-
-        $parent_result = parent::validate($string, $config, $context);
-        if ($parent_result !== false) return $parent_result;
-
-        $length = strlen($string);
-        $last_char = $string[$length - 1];
-
-        if ($last_char !== '%') return false;
-
-        $points = substr($string, 0, $length - 1);
-
-        if (!is_numeric($points)) return false;
-
-        $points = (int) $points;
-
-        if ($points < 0) return '0%';
-        if ($points > 100) return '100%';
-
-        return ((string) $points) . '%';
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/HTML/LinkTypes.php b/library/HTMLPurifier/AttrDef/HTML/LinkTypes.php
deleted file mode 100644 (file)
index 76d25ed..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-/**
- * Validates a rel/rev link attribute against a directive of allowed values
- * @note We cannot use Enum because link types allow multiple
- *       values.
- * @note Assumes link types are ASCII text
- */
-class HTMLPurifier_AttrDef_HTML_LinkTypes extends HTMLPurifier_AttrDef
-{
-
-    /** Name config attribute to pull. */
-    protected $name;
-
-    public function __construct($name) {
-        $configLookup = array(
-            'rel' => 'AllowedRel',
-            'rev' => 'AllowedRev'
-        );
-        if (!isset($configLookup[$name])) {
-            trigger_error('Unrecognized attribute name for link '.
-                'relationship.', E_USER_ERROR);
-            return;
-        }
-        $this->name = $configLookup[$name];
-    }
-
-    public function validate($string, $config, $context) {
-
-        $allowed = $config->get('Attr.' . $this->name);
-        if (empty($allowed)) return false;
-
-        $string = $this->parseCDATA($string);
-        $parts = explode(' ', $string);
-
-        // lookup to prevent duplicates
-        $ret_lookup = array();
-        foreach ($parts as $part) {
-            $part = strtolower(trim($part));
-            if (!isset($allowed[$part])) continue;
-            $ret_lookup[$part] = true;
-        }
-
-        if (empty($ret_lookup)) return false;
-        $string = implode(' ', array_keys($ret_lookup));
-
-        return $string;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/HTML/MultiLength.php b/library/HTMLPurifier/AttrDef/HTML/MultiLength.php
deleted file mode 100644 (file)
index c72fc76..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-/**
- * Validates a MultiLength as defined by the HTML spec.
- *
- * A multilength is either a integer (pixel count), a percentage, or
- * a relative number.
- */
-class HTMLPurifier_AttrDef_HTML_MultiLength extends HTMLPurifier_AttrDef_HTML_Length
-{
-
-    public function validate($string, $config, $context) {
-
-        $string = trim($string);
-        if ($string === '') return false;
-
-        $parent_result = parent::validate($string, $config, $context);
-        if ($parent_result !== false) return $parent_result;
-
-        $length = strlen($string);
-        $last_char = $string[$length - 1];
-
-        if ($last_char !== '*') return false;
-
-        $int = substr($string, 0, $length - 1);
-
-        if ($int == '') return '*';
-        if (!is_numeric($int)) return false;
-
-        $int = (int) $int;
-
-        if ($int < 0) return false;
-        if ($int == 0) return '0';
-        if ($int == 1) return '*';
-        return ((string) $int) . '*';
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/HTML/Nmtokens.php b/library/HTMLPurifier/AttrDef/HTML/Nmtokens.php
deleted file mode 100644 (file)
index aa34120..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-/**
- * Validates contents based on NMTOKENS attribute type.
- */
-class HTMLPurifier_AttrDef_HTML_Nmtokens extends HTMLPurifier_AttrDef
-{
-
-    public function validate($string, $config, $context) {
-
-        $string = trim($string);
-
-        // early abort: '' and '0' (strings that convert to false) are invalid
-        if (!$string) return false;
-
-        $tokens = $this->split($string, $config, $context);
-        $tokens = $this->filter($tokens, $config, $context);
-        if (empty($tokens)) return false;
-        return implode(' ', $tokens);
-
-    }
-
-    /**
-     * Splits a space separated list of tokens into its constituent parts.
-     */
-    protected function split($string, $config, $context) {
-        // OPTIMIZABLE!
-        // do the preg_match, capture all subpatterns for reformulation
-
-        // we don't support U+00A1 and up codepoints or
-        // escaping because I don't know how to do that with regexps
-        // and plus it would complicate optimization efforts (you never
-        // see that anyway).
-        $pattern = '/(?:(?<=\s)|\A)'. // look behind for space or string start
-                   '((?:--|-?[A-Za-z_])[A-Za-z_\-0-9]*)'.
-                   '(?:(?=\s)|\z)/'; // look ahead for space or string end
-        preg_match_all($pattern, $string, $matches);
-        return $matches[1];
-    }
-
-    /**
-     * Template method for removing certain tokens based on arbitrary criteria.
-     * @note If we wanted to be really functional, we'd do an array_filter
-     *       with a callback. But... we're not.
-     */
-    protected function filter($tokens, $config, $context) {
-        return $tokens;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/HTML/Pixels.php b/library/HTMLPurifier/AttrDef/HTML/Pixels.php
deleted file mode 100644 (file)
index 4cb2c1b..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-
-/**
- * Validates an integer representation of pixels according to the HTML spec.
- */
-class HTMLPurifier_AttrDef_HTML_Pixels extends HTMLPurifier_AttrDef
-{
-
-    protected $max;
-
-    public function __construct($max = null) {
-        $this->max = $max;
-    }
-
-    public function validate($string, $config, $context) {
-
-        $string = trim($string);
-        if ($string === '0') return $string;
-        if ($string === '')  return false;
-        $length = strlen($string);
-        if (substr($string, $length - 2) == 'px') {
-            $string = substr($string, 0, $length - 2);
-        }
-        if (!is_numeric($string)) return false;
-        $int = (int) $string;
-
-        if ($int < 0) return '0';
-
-        // upper-bound value, extremely high values can
-        // crash operating systems, see <http://ha.ckers.org/imagecrash.html>
-        // WARNING, above link WILL crash you if you're using Windows
-
-        if ($this->max !== null && $int > $this->max) return (string) $this->max;
-
-        return (string) $int;
-
-    }
-
-    public function make($string) {
-        if ($string === '') $max = null;
-        else $max = (int) $string;
-        $class = get_class($this);
-        return new $class($max);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/Integer.php b/library/HTMLPurifier/AttrDef/Integer.php
deleted file mode 100644 (file)
index d59738d..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-/**
- * Validates an integer.
- * @note While this class was modeled off the CSS definition, no currently
- *       allowed CSS uses this type.  The properties that do are: widows,
- *       orphans, z-index, counter-increment, counter-reset.  Some of the
- *       HTML attributes, however, find use for a non-negative version of this.
- */
-class HTMLPurifier_AttrDef_Integer extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Bool indicating whether or not negative values are allowed
-     */
-    protected $negative = true;
-
-    /**
-     * Bool indicating whether or not zero is allowed
-     */
-    protected $zero = true;
-
-    /**
-     * Bool indicating whether or not positive values are allowed
-     */
-    protected $positive = true;
-
-    /**
-     * @param $negative Bool indicating whether or not negative values are allowed
-     * @param $zero Bool indicating whether or not zero is allowed
-     * @param $positive Bool indicating whether or not positive values are allowed
-     */
-    public function __construct(
-        $negative = true, $zero = true, $positive = true
-    ) {
-        $this->negative = $negative;
-        $this->zero     = $zero;
-        $this->positive = $positive;
-    }
-
-    public function validate($integer, $config, $context) {
-
-        $integer = $this->parseCDATA($integer);
-        if ($integer === '') return false;
-
-        // we could possibly simply typecast it to integer, but there are
-        // certain fringe cases that must not return an integer.
-
-        // clip leading sign
-        if ( $this->negative && $integer[0] === '-' ) {
-            $digits = substr($integer, 1);
-            if ($digits === '0') $integer = '0'; // rm minus sign for zero
-        } elseif( $this->positive && $integer[0] === '+' ) {
-            $digits = $integer = substr($integer, 1); // rm unnecessary plus
-        } else {
-            $digits = $integer;
-        }
-
-        // test if it's numeric
-        if (!ctype_digit($digits)) return false;
-
-        // perform scope tests
-        if (!$this->zero     && $integer == 0) return false;
-        if (!$this->positive && $integer > 0) return false;
-        if (!$this->negative && $integer < 0) return false;
-
-        return $integer;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/Lang.php b/library/HTMLPurifier/AttrDef/Lang.php
deleted file mode 100644 (file)
index 10e6da5..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-/**
- * Validates the HTML attribute lang, effectively a language code.
- * @note Built according to RFC 3066, which obsoleted RFC 1766
- */
-class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef
-{
-
-    public function validate($string, $config, $context) {
-
-        $string = trim($string);
-        if (!$string) return false;
-
-        $subtags = explode('-', $string);
-        $num_subtags = count($subtags);
-
-        if ($num_subtags == 0) return false; // sanity check
-
-        // process primary subtag : $subtags[0]
-        $length = strlen($subtags[0]);
-        switch ($length) {
-            case 0:
-                return false;
-            case 1:
-                if (! ($subtags[0] == 'x' || $subtags[0] == 'i') ) {
-                    return false;
-                }
-                break;
-            case 2:
-            case 3:
-                if (! ctype_alpha($subtags[0]) ) {
-                    return false;
-                } elseif (! ctype_lower($subtags[0]) ) {
-                    $subtags[0] = strtolower($subtags[0]);
-                }
-                break;
-            default:
-                return false;
-        }
-
-        $new_string = $subtags[0];
-        if ($num_subtags == 1) return $new_string;
-
-        // process second subtag : $subtags[1]
-        $length = strlen($subtags[1]);
-        if ($length == 0 || ($length == 1 && $subtags[1] != 'x') || $length > 8 || !ctype_alnum($subtags[1])) {
-            return $new_string;
-        }
-        if (!ctype_lower($subtags[1])) $subtags[1] = strtolower($subtags[1]);
-
-        $new_string .= '-' . $subtags[1];
-        if ($num_subtags == 2) return $new_string;
-
-        // process all other subtags, index 2 and up
-        for ($i = 2; $i < $num_subtags; $i++) {
-            $length = strlen($subtags[$i]);
-            if ($length == 0 || $length > 8 || !ctype_alnum($subtags[$i])) {
-                return $new_string;
-            }
-            if (!ctype_lower($subtags[$i])) {
-                $subtags[$i] = strtolower($subtags[$i]);
-            }
-            $new_string .= '-' . $subtags[$i];
-        }
-
-        return $new_string;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/Switch.php b/library/HTMLPurifier/AttrDef/Switch.php
deleted file mode 100644 (file)
index c9e3ed1..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-/**
- * Decorator that, depending on a token, switches between two definitions.
- */
-class HTMLPurifier_AttrDef_Switch
-{
-
-    protected $tag;
-    protected $withTag, $withoutTag;
-
-    /**
-     * @param string $tag Tag name to switch upon
-     * @param HTMLPurifier_AttrDef $with_tag Call if token matches tag
-     * @param HTMLPurifier_AttrDef $without_tag Call if token doesn't match, or there is no token
-     */
-    public function __construct($tag, $with_tag, $without_tag) {
-        $this->tag = $tag;
-        $this->withTag = $with_tag;
-        $this->withoutTag = $without_tag;
-    }
-
-    public function validate($string, $config, $context) {
-        $token = $context->get('CurrentToken', true);
-        if (!$token || $token->name !== $this->tag) {
-            return $this->withoutTag->validate($string, $config, $context);
-        } else {
-            return $this->withTag->validate($string, $config, $context);
-        }
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/Text.php b/library/HTMLPurifier/AttrDef/Text.php
deleted file mode 100644 (file)
index c6216cc..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-/**
- * Validates arbitrary text according to the HTML spec.
- */
-class HTMLPurifier_AttrDef_Text extends HTMLPurifier_AttrDef
-{
-
-    public function validate($string, $config, $context) {
-        return $this->parseCDATA($string);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/URI.php b/library/HTMLPurifier/AttrDef/URI.php
deleted file mode 100644 (file)
index 01a6d83..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-/**
- * Validates a URI as defined by RFC 3986.
- * @note Scheme-specific mechanics deferred to HTMLPurifier_URIScheme
- */
-class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
-{
-
-    protected $parser;
-    protected $embedsResource;
-
-    /**
-     * @param $embeds_resource_resource Does the URI here result in an extra HTTP request?
-     */
-    public function __construct($embeds_resource = false) {
-        $this->parser = new HTMLPurifier_URIParser();
-        $this->embedsResource = (bool) $embeds_resource;
-    }
-
-    public function make($string) {
-        $embeds = (bool) $string;
-        return new HTMLPurifier_AttrDef_URI($embeds);
-    }
-
-    public function validate($uri, $config, $context) {
-
-        if ($config->get('URI.Disable')) return false;
-
-        $uri = $this->parseCDATA($uri);
-
-        // parse the URI
-        $uri = $this->parser->parse($uri);
-        if ($uri === false) return false;
-
-        // add embedded flag to context for validators
-        $context->register('EmbeddedURI', $this->embedsResource);
-
-        $ok = false;
-        do {
-
-            // generic validation
-            $result = $uri->validate($config, $context);
-            if (!$result) break;
-
-            // chained filtering
-            $uri_def = $config->getDefinition('URI');
-            $result = $uri_def->filter($uri, $config, $context);
-            if (!$result) break;
-
-            // scheme-specific validation
-            $scheme_obj = $uri->getSchemeObj($config, $context);
-            if (!$scheme_obj) break;
-            if ($this->embedsResource && !$scheme_obj->browsable) break;
-            $result = $scheme_obj->validate($uri, $config, $context);
-            if (!$result) break;
-
-            // Post chained filtering
-            $result = $uri_def->postFilter($uri, $config, $context);
-            if (!$result) break;
-
-            // survived gauntlet
-            $ok = true;
-
-        } while (false);
-
-        $context->destroy('EmbeddedURI');
-        if (!$ok) return false;
-
-        // back to string
-        return $uri->toString();
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/URI/Email.php b/library/HTMLPurifier/AttrDef/URI/Email.php
deleted file mode 100644 (file)
index bfee9d1..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-abstract class HTMLPurifier_AttrDef_URI_Email extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Unpacks a mailbox into its display-name and address
-     */
-    function unpack($string) {
-        // needs to be implemented
-    }
-
-}
-
-// sub-implementations
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php b/library/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php
deleted file mode 100644 (file)
index 94c715a..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-/**
- * Primitive email validation class based on the regexp found at
- * http://www.regular-expressions.info/email.html
- */
-class HTMLPurifier_AttrDef_URI_Email_SimpleCheck extends HTMLPurifier_AttrDef_URI_Email
-{
-
-    public function validate($string, $config, $context) {
-        // no support for named mailboxes i.e. "Bob <bob@example.com>"
-        // that needs more percent encoding to be done
-        if ($string == '') return false;
-        $string = trim($string);
-        $result = preg_match('/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i', $string);
-        return $result ? $string : false;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/URI/Host.php b/library/HTMLPurifier/AttrDef/URI/Host.php
deleted file mode 100644 (file)
index 2156c10..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-/**
- * Validates a host according to the IPv4, IPv6 and DNS (future) specifications.
- */
-class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Instance of HTMLPurifier_AttrDef_URI_IPv4 sub-validator
-     */
-    protected $ipv4;
-
-    /**
-     * Instance of HTMLPurifier_AttrDef_URI_IPv6 sub-validator
-     */
-    protected $ipv6;
-
-    public function __construct() {
-        $this->ipv4 = new HTMLPurifier_AttrDef_URI_IPv4();
-        $this->ipv6 = new HTMLPurifier_AttrDef_URI_IPv6();
-    }
-
-    public function validate($string, $config, $context) {
-        $length = strlen($string);
-        if ($string === '') return '';
-        if ($length > 1 && $string[0] === '[' && $string[$length-1] === ']') {
-            //IPv6
-            $ip = substr($string, 1, $length - 2);
-            $valid = $this->ipv6->validate($ip, $config, $context);
-            if ($valid === false) return false;
-            return '['. $valid . ']';
-        }
-
-        // need to do checks on unusual encodings too
-        $ipv4 = $this->ipv4->validate($string, $config, $context);
-        if ($ipv4 !== false) return $ipv4;
-
-        // A regular domain name.
-
-        // This breaks I18N domain names, but we don't have proper IRI support,
-        // so force users to insert Punycode. If there's complaining we'll
-        // try to fix things into an international friendly form.
-
-        // The productions describing this are:
-        $a   = '[a-z]';     // alpha
-        $an  = '[a-z0-9]';  // alphanum
-        $and = '[a-z0-9-]'; // alphanum | "-"
-        // domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
-        $domainlabel   = "$an($and*$an)?";
-        // toplabel    = alpha | alpha *( alphanum | "-" ) alphanum
-        $toplabel      = "$a($and*$an)?";
-        // hostname    = *( domainlabel "." ) toplabel [ "." ]
-        $match = preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string);
-        if (!$match) return false;
-
-        return $string;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/URI/IPv4.php b/library/HTMLPurifier/AttrDef/URI/IPv4.php
deleted file mode 100644 (file)
index ec4cf59..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
- * Validates an IPv4 address
- * @author Feyd @ forums.devnetwork.net (public domain)
- */
-class HTMLPurifier_AttrDef_URI_IPv4 extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * IPv4 regex, protected so that IPv6 can reuse it
-     */
-    protected $ip4;
-
-    public function validate($aIP, $config, $context) {
-
-        if (!$this->ip4) $this->_loadRegex();
-
-        if (preg_match('#^' . $this->ip4 . '$#s', $aIP))
-        {
-                return $aIP;
-        }
-
-        return false;
-
-    }
-
-    /**
-     * Lazy load function to prevent regex from being stuffed in
-     * cache.
-     */
-    protected function _loadRegex() {
-        $oct = '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])'; // 0-255
-        $this->ip4 = "(?:{$oct}\\.{$oct}\\.{$oct}\\.{$oct})";
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrDef/URI/IPv6.php b/library/HTMLPurifier/AttrDef/URI/IPv6.php
deleted file mode 100644 (file)
index 9454e9b..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-<?php
-
-/**
- * Validates an IPv6 address.
- * @author Feyd @ forums.devnetwork.net (public domain)
- * @note This function requires brackets to have been removed from address
- *       in URI.
- */
-class HTMLPurifier_AttrDef_URI_IPv6 extends HTMLPurifier_AttrDef_URI_IPv4
-{
-
-    public function validate($aIP, $config, $context) {
-
-        if (!$this->ip4) $this->_loadRegex();
-
-        $original = $aIP;
-
-        $hex = '[0-9a-fA-F]';
-        $blk = '(?:' . $hex . '{1,4})';
-        $pre = '(?:/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))';   // /0 - /128
-
-        //      prefix check
-        if (strpos($aIP, '/') !== false)
-        {
-                if (preg_match('#' . $pre . '$#s', $aIP, $find))
-                {
-                        $aIP = substr($aIP, 0, 0-strlen($find[0]));
-                        unset($find);
-                }
-                else
-                {
-                        return false;
-                }
-        }
-
-        //      IPv4-compatiblity check
-        if (preg_match('#(?<=:'.')' . $this->ip4 . '$#s', $aIP, $find))
-        {
-                $aIP = substr($aIP, 0, 0-strlen($find[0]));
-                $ip = explode('.', $find[0]);
-                $ip = array_map('dechex', $ip);
-                $aIP .= $ip[0] . $ip[1] . ':' . $ip[2] . $ip[3];
-                unset($find, $ip);
-        }
-
-        //      compression check
-        $aIP = explode('::', $aIP);
-        $c = count($aIP);
-        if ($c > 2)
-        {
-                return false;
-        }
-        elseif ($c == 2)
-        {
-                list($first, $second) = $aIP;
-                $first = explode(':', $first);
-                $second = explode(':', $second);
-
-                if (count($first) + count($second) > 8)
-                {
-                        return false;
-                }
-
-                while(count($first) < 8)
-                {
-                        array_push($first, '0');
-                }
-
-                array_splice($first, 8 - count($second), 8, $second);
-                $aIP = $first;
-                unset($first,$second);
-        }
-        else
-        {
-                $aIP = explode(':', $aIP[0]);
-        }
-        $c = count($aIP);
-
-        if ($c != 8)
-        {
-                return false;
-        }
-
-        //      All the pieces should be 16-bit hex strings. Are they?
-        foreach ($aIP as $piece)
-        {
-                if (!preg_match('#^[0-9a-fA-F]{4}$#s', sprintf('%04s', $piece)))
-                {
-                        return false;
-                }
-        }
-
-        return $original;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform.php b/library/HTMLPurifier/AttrTransform.php
deleted file mode 100644 (file)
index e61d3e0..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-/**
- * Processes an entire attribute array for corrections needing multiple values.
- *
- * Occasionally, a certain attribute will need to be removed and popped onto
- * another value.  Instead of creating a complex return syntax for
- * HTMLPurifier_AttrDef, we just pass the whole attribute array to a
- * specialized object and have that do the special work.  That is the
- * family of HTMLPurifier_AttrTransform.
- *
- * An attribute transformation can be assigned to run before or after
- * HTMLPurifier_AttrDef validation.  See HTMLPurifier_HTMLDefinition for
- * more details.
- */
-
-abstract class HTMLPurifier_AttrTransform
-{
-
-    /**
-     * Abstract: makes changes to the attributes dependent on multiple values.
-     *
-     * @param $attr Assoc array of attributes, usually from
-     *              HTMLPurifier_Token_Tag::$attr
-     * @param $config Mandatory HTMLPurifier_Config object.
-     * @param $context Mandatory HTMLPurifier_Context object
-     * @returns Processed attribute array.
-     */
-    abstract public function transform($attr, $config, $context);
-
-    /**
-     * Prepends CSS properties to the style attribute, creating the
-     * attribute if it doesn't exist.
-     * @param $attr Attribute array to process (passed by reference)
-     * @param $css CSS to prepend
-     */
-    public function prependCSS(&$attr, $css) {
-        $attr['style'] = isset($attr['style']) ? $attr['style'] : '';
-        $attr['style'] = $css . $attr['style'];
-    }
-
-    /**
-     * Retrieves and removes an attribute
-     * @param $attr Attribute array to process (passed by reference)
-     * @param $key Key of attribute to confiscate
-     */
-    public function confiscateAttr(&$attr, $key) {
-        if (!isset($attr[$key])) return null;
-        $value = $attr[$key];
-        unset($attr[$key]);
-        return $value;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/Background.php b/library/HTMLPurifier/AttrTransform/Background.php
deleted file mode 100644 (file)
index 0e1ff24..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-/**
- * Pre-transform that changes proprietary background attribute to CSS.
- */
-class HTMLPurifier_AttrTransform_Background extends HTMLPurifier_AttrTransform {
-
-    public function transform($attr, $config, $context) {
-
-        if (!isset($attr['background'])) return $attr;
-
-        $background = $this->confiscateAttr($attr, 'background');
-        // some validation should happen here
-
-        $this->prependCSS($attr, "background-image:url($background);");
-
-        return $attr;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/BdoDir.php b/library/HTMLPurifier/AttrTransform/BdoDir.php
deleted file mode 100644 (file)
index 4d1a056..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<?php
-
-// this MUST be placed in post, as it assumes that any value in dir is valid
-
-/**
- * Post-trasnform that ensures that bdo tags have the dir attribute set.
- */
-class HTMLPurifier_AttrTransform_BdoDir extends HTMLPurifier_AttrTransform
-{
-
-    public function transform($attr, $config, $context) {
-        if (isset($attr['dir'])) return $attr;
-        $attr['dir'] = $config->get('Attr.DefaultTextDir');
-        return $attr;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/BgColor.php b/library/HTMLPurifier/AttrTransform/BgColor.php
deleted file mode 100644 (file)
index ad3916b..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-/**
- * Pre-transform that changes deprecated bgcolor attribute to CSS.
- */
-class HTMLPurifier_AttrTransform_BgColor extends HTMLPurifier_AttrTransform {
-
-    public function transform($attr, $config, $context) {
-
-        if (!isset($attr['bgcolor'])) return $attr;
-
-        $bgcolor = $this->confiscateAttr($attr, 'bgcolor');
-        // some validation should happen here
-
-        $this->prependCSS($attr, "background-color:$bgcolor;");
-
-        return $attr;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/BoolToCSS.php b/library/HTMLPurifier/AttrTransform/BoolToCSS.php
deleted file mode 100644 (file)
index 51159b6..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-/**
- * Pre-transform that changes converts a boolean attribute to fixed CSS
- */
-class HTMLPurifier_AttrTransform_BoolToCSS extends HTMLPurifier_AttrTransform {
-
-    /**
-     * Name of boolean attribute that is trigger
-     */
-    protected $attr;
-
-    /**
-     * CSS declarations to add to style, needs trailing semicolon
-     */
-    protected $css;
-
-    /**
-     * @param $attr string attribute name to convert from
-     * @param $css string CSS declarations to add to style (needs semicolon)
-     */
-    public function __construct($attr, $css) {
-        $this->attr = $attr;
-        $this->css  = $css;
-    }
-
-    public function transform($attr, $config, $context) {
-        if (!isset($attr[$this->attr])) return $attr;
-        unset($attr[$this->attr]);
-        $this->prependCSS($attr, $this->css);
-        return $attr;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/Border.php b/library/HTMLPurifier/AttrTransform/Border.php
deleted file mode 100644 (file)
index 476b0b0..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-
-/**
- * Pre-transform that changes deprecated border attribute to CSS.
- */
-class HTMLPurifier_AttrTransform_Border extends HTMLPurifier_AttrTransform {
-
-    public function transform($attr, $config, $context) {
-        if (!isset($attr['border'])) return $attr;
-        $border_width = $this->confiscateAttr($attr, 'border');
-        // some validation should happen here
-        $this->prependCSS($attr, "border:{$border_width}px solid;");
-        return $attr;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/EnumToCSS.php b/library/HTMLPurifier/AttrTransform/EnumToCSS.php
deleted file mode 100644 (file)
index 2a5b451..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-
-/**
- * Generic pre-transform that converts an attribute with a fixed number of
- * values (enumerated) to CSS.
- */
-class HTMLPurifier_AttrTransform_EnumToCSS extends HTMLPurifier_AttrTransform {
-
-    /**
-     * Name of attribute to transform from
-     */
-    protected $attr;
-
-    /**
-     * Lookup array of attribute values to CSS
-     */
-    protected $enumToCSS = array();
-
-    /**
-     * Case sensitivity of the matching
-     * @warning Currently can only be guaranteed to work with ASCII
-     *          values.
-     */
-    protected $caseSensitive = false;
-
-    /**
-     * @param $attr String attribute name to transform from
-     * @param $enumToCSS Lookup array of attribute values to CSS
-     * @param $case_sensitive Boolean case sensitivity indicator, default false
-     */
-    public function __construct($attr, $enum_to_css, $case_sensitive = false) {
-        $this->attr = $attr;
-        $this->enumToCSS = $enum_to_css;
-        $this->caseSensitive = (bool) $case_sensitive;
-    }
-
-    public function transform($attr, $config, $context) {
-
-        if (!isset($attr[$this->attr])) return $attr;
-
-        $value = trim($attr[$this->attr]);
-        unset($attr[$this->attr]);
-
-        if (!$this->caseSensitive) $value = strtolower($value);
-
-        if (!isset($this->enumToCSS[$value])) {
-            return $attr;
-        }
-
-        $this->prependCSS($attr, $this->enumToCSS[$value]);
-
-        return $attr;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/ImgRequired.php b/library/HTMLPurifier/AttrTransform/ImgRequired.php
deleted file mode 100644 (file)
index 7f0e4b7..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-// must be called POST validation
-
-/**
- * Transform that supplies default values for the src and alt attributes
- * in img tags, as well as prevents the img tag from being removed
- * because of a missing alt tag. This needs to be registered as both
- * a pre and post attribute transform.
- */
-class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform
-{
-
-    public function transform($attr, $config, $context) {
-
-        $src = true;
-        if (!isset($attr['src'])) {
-            if ($config->get('Core.RemoveInvalidImg')) return $attr;
-            $attr['src'] = $config->get('Attr.DefaultInvalidImage');
-            $src = false;
-        }
-
-        if (!isset($attr['alt'])) {
-            if ($src) {
-                $alt = $config->get('Attr.DefaultImageAlt');
-                if ($alt === null) {
-                    // truncate if the alt is too long
-                    $attr['alt'] = substr(basename($attr['src']),0,40);
-                } else {
-                    $attr['alt'] = $alt;
-                }
-            } else {
-                $attr['alt'] = $config->get('Attr.DefaultInvalidImageAlt');
-            }
-        }
-
-        return $attr;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/ImgSpace.php b/library/HTMLPurifier/AttrTransform/ImgSpace.php
deleted file mode 100644 (file)
index fd84c10..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
- * Pre-transform that changes deprecated hspace and vspace attributes to CSS
- */
-class HTMLPurifier_AttrTransform_ImgSpace extends HTMLPurifier_AttrTransform {
-
-    protected $attr;
-    protected $css = array(
-        'hspace' => array('left', 'right'),
-        'vspace' => array('top', 'bottom')
-    );
-
-    public function __construct($attr) {
-        $this->attr = $attr;
-        if (!isset($this->css[$attr])) {
-            trigger_error(htmlspecialchars($attr) . ' is not valid space attribute');
-        }
-    }
-
-    public function transform($attr, $config, $context) {
-
-        if (!isset($attr[$this->attr])) return $attr;
-
-        $width = $this->confiscateAttr($attr, $this->attr);
-        // some validation could happen here
-
-        if (!isset($this->css[$this->attr])) return $attr;
-
-        $style = '';
-        foreach ($this->css[$this->attr] as $suffix) {
-            $property = "margin-$suffix";
-            $style .= "$property:{$width}px;";
-        }
-
-        $this->prependCSS($attr, $style);
-
-        return $attr;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/Input.php b/library/HTMLPurifier/AttrTransform/Input.php
deleted file mode 100644 (file)
index 1682955..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * Performs miscellaneous cross attribute validation and filtering for
- * input elements. This is meant to be a post-transform.
- */
-class HTMLPurifier_AttrTransform_Input extends HTMLPurifier_AttrTransform {
-
-    protected $pixels;
-
-    public function __construct() {
-        $this->pixels = new HTMLPurifier_AttrDef_HTML_Pixels();
-    }
-
-    public function transform($attr, $config, $context) {
-        if (!isset($attr['type'])) $t = 'text';
-        else $t = strtolower($attr['type']);
-        if (isset($attr['checked']) && $t !== 'radio' && $t !== 'checkbox') {
-            unset($attr['checked']);
-        }
-        if (isset($attr['maxlength']) && $t !== 'text' && $t !== 'password') {
-            unset($attr['maxlength']);
-        }
-        if (isset($attr['size']) && $t !== 'text' && $t !== 'password') {
-            $result = $this->pixels->validate($attr['size'], $config, $context);
-            if ($result === false) unset($attr['size']);
-            else $attr['size'] = $result;
-        }
-        if (isset($attr['src']) && $t !== 'image') {
-            unset($attr['src']);
-        }
-        if (!isset($attr['value']) && ($t === 'radio' || $t === 'checkbox')) {
-            $attr['value'] = '';
-        }
-        return $attr;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/Lang.php b/library/HTMLPurifier/AttrTransform/Lang.php
deleted file mode 100644 (file)
index 5869e7f..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-/**
- * Post-transform that copies lang's value to xml:lang (and vice-versa)
- * @note Theoretically speaking, this could be a pre-transform, but putting
- *       post is more efficient.
- */
-class HTMLPurifier_AttrTransform_Lang extends HTMLPurifier_AttrTransform
-{
-
-    public function transform($attr, $config, $context) {
-
-        $lang     = isset($attr['lang']) ? $attr['lang'] : false;
-        $xml_lang = isset($attr['xml:lang']) ? $attr['xml:lang'] : false;
-
-        if ($lang !== false && $xml_lang === false) {
-            $attr['xml:lang'] = $lang;
-        } elseif ($xml_lang !== false) {
-            $attr['lang'] = $xml_lang;
-        }
-
-        return $attr;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/Length.php b/library/HTMLPurifier/AttrTransform/Length.php
deleted file mode 100644 (file)
index ea2f304..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-/**
- * Class for handling width/height length attribute transformations to CSS
- */
-class HTMLPurifier_AttrTransform_Length extends HTMLPurifier_AttrTransform
-{
-
-    protected $name;
-    protected $cssName;
-
-    public function __construct($name, $css_name = null) {
-        $this->name = $name;
-        $this->cssName = $css_name ? $css_name : $name;
-    }
-
-    public function transform($attr, $config, $context) {
-        if (!isset($attr[$this->name])) return $attr;
-        $length = $this->confiscateAttr($attr, $this->name);
-        if(ctype_digit($length)) $length .= 'px';
-        $this->prependCSS($attr, $this->cssName . ":$length;");
-        return $attr;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/Name.php b/library/HTMLPurifier/AttrTransform/Name.php
deleted file mode 100644 (file)
index 15315bc..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-/**
- * Pre-transform that changes deprecated name attribute to ID if necessary
- */
-class HTMLPurifier_AttrTransform_Name extends HTMLPurifier_AttrTransform
-{
-
-    public function transform($attr, $config, $context) {
-        // Abort early if we're using relaxed definition of name
-        if ($config->get('HTML.Attr.Name.UseCDATA')) return $attr;
-        if (!isset($attr['name'])) return $attr;
-        $id = $this->confiscateAttr($attr, 'name');
-        if ( isset($attr['id']))   return $attr;
-        $attr['id'] = $id;
-        return $attr;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/NameSync.php b/library/HTMLPurifier/AttrTransform/NameSync.php
deleted file mode 100644 (file)
index a95638c..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-/**
- * Post-transform that performs validation to the name attribute; if
- * it is present with an equivalent id attribute, it is passed through;
- * otherwise validation is performed.
- */
-class HTMLPurifier_AttrTransform_NameSync extends HTMLPurifier_AttrTransform
-{
-
-    public function __construct() {
-        $this->idDef = new HTMLPurifier_AttrDef_HTML_ID();
-    }
-
-    public function transform($attr, $config, $context) {
-        if (!isset($attr['name'])) return $attr;
-        $name = $attr['name'];
-        if (isset($attr['id']) && $attr['id'] === $name) return $attr;
-        $result = $this->idDef->validate($name, $config, $context);
-        if ($result === false) unset($attr['name']);
-        else $attr['name'] = $result;
-        return $attr;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/SafeEmbed.php b/library/HTMLPurifier/AttrTransform/SafeEmbed.php
deleted file mode 100644 (file)
index 4da4499..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-class HTMLPurifier_AttrTransform_SafeEmbed extends HTMLPurifier_AttrTransform
-{
-    public $name = "SafeEmbed";
-
-    public function transform($attr, $config, $context) {
-        $attr['allowscriptaccess'] = 'never';
-        $attr['allownetworking'] = 'internal';
-        $attr['type'] = 'application/x-shockwave-flash';
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/SafeObject.php b/library/HTMLPurifier/AttrTransform/SafeObject.php
deleted file mode 100644 (file)
index 1ed7489..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-
-/**
- * Writes default type for all objects. Currently only supports flash.
- */
-class HTMLPurifier_AttrTransform_SafeObject extends HTMLPurifier_AttrTransform
-{
-    public $name = "SafeObject";
-
-    function transform($attr, $config, $context) {
-        if (!isset($attr['type'])) $attr['type'] = 'application/x-shockwave-flash';
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/SafeParam.php b/library/HTMLPurifier/AttrTransform/SafeParam.php
deleted file mode 100644 (file)
index 3f992ec..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-/**
- * Validates name/value pairs in param tags to be used in safe objects. This
- * will only allow name values it recognizes, and pre-fill certain attributes
- * with required values.
- *
- * @note
- *      This class only supports Flash. In the future, Quicktime support
- *      may be added.
- *
- * @warning
- *      This class expects an injector to add the necessary parameters tags.
- */
-class HTMLPurifier_AttrTransform_SafeParam extends HTMLPurifier_AttrTransform
-{
-    public $name = "SafeParam";
-    private $uri;
-
-    public function __construct() {
-        $this->uri = new HTMLPurifier_AttrDef_URI(true); // embedded
-    }
-
-    public function transform($attr, $config, $context) {
-        // If we add support for other objects, we'll need to alter the
-        // transforms.
-        switch ($attr['name']) {
-            // application/x-shockwave-flash
-            // Keep this synchronized with Injector/SafeObject.php
-            case 'allowScriptAccess':
-                $attr['value'] = 'never';
-                break;
-            case 'allowNetworking':
-                $attr['value'] = 'internal';
-                break;
-            case 'wmode':
-                $attr['value'] = 'window';
-                break;
-            case 'movie':
-            case 'src':
-                $attr['name'] = "movie";
-                $attr['value'] = $this->uri->validate($attr['value'], $config, $context);
-                break;
-            case 'flashvars':
-                // we're going to allow arbitrary inputs to the SWF, on
-                // the reasoning that it could only hack the SWF, not us.
-                break;
-            // add other cases to support other param name/value pairs
-            default:
-                $attr['name'] = $attr['value'] = null;
-        }
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/ScriptRequired.php b/library/HTMLPurifier/AttrTransform/ScriptRequired.php
deleted file mode 100644 (file)
index 4499050..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-
-/**
- * Implements required attribute stipulation for <script>
- */
-class HTMLPurifier_AttrTransform_ScriptRequired extends HTMLPurifier_AttrTransform
-{
-    public function transform($attr, $config, $context) {
-        if (!isset($attr['type'])) {
-            $attr['type'] = 'text/javascript';
-        }
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTransform/Textarea.php b/library/HTMLPurifier/AttrTransform/Textarea.php
deleted file mode 100644 (file)
index 81ac348..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-
-/**
- * Sets height/width defaults for <textarea>
- */
-class HTMLPurifier_AttrTransform_Textarea extends HTMLPurifier_AttrTransform
-{
-
-    public function transform($attr, $config, $context) {
-        // Calculated from Firefox
-        if (!isset($attr['cols'])) $attr['cols'] = '22';
-        if (!isset($attr['rows'])) $attr['rows'] = '3';
-        return $attr;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrTypes.php b/library/HTMLPurifier/AttrTypes.php
deleted file mode 100644 (file)
index fc2ea4e..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-/**
- * Provides lookup array of attribute types to HTMLPurifier_AttrDef objects
- */
-class HTMLPurifier_AttrTypes
-{
-    /**
-     * Lookup array of attribute string identifiers to concrete implementations
-     */
-    protected $info = array();
-
-    /**
-     * Constructs the info array, supplying default implementations for attribute
-     * types.
-     */
-    public function __construct() {
-        // pseudo-types, must be instantiated via shorthand
-        $this->info['Enum']    = new HTMLPurifier_AttrDef_Enum();
-        $this->info['Bool']    = new HTMLPurifier_AttrDef_HTML_Bool();
-
-        $this->info['CDATA']    = new HTMLPurifier_AttrDef_Text();
-        $this->info['ID']       = new HTMLPurifier_AttrDef_HTML_ID();
-        $this->info['Length']   = new HTMLPurifier_AttrDef_HTML_Length();
-        $this->info['MultiLength'] = new HTMLPurifier_AttrDef_HTML_MultiLength();
-        $this->info['NMTOKENS'] = new HTMLPurifier_AttrDef_HTML_Nmtokens();
-        $this->info['Pixels']   = new HTMLPurifier_AttrDef_HTML_Pixels();
-        $this->info['Text']     = new HTMLPurifier_AttrDef_Text();
-        $this->info['URI']      = new HTMLPurifier_AttrDef_URI();
-        $this->info['LanguageCode'] = new HTMLPurifier_AttrDef_Lang();
-        $this->info['Color']    = new HTMLPurifier_AttrDef_HTML_Color();
-
-        // unimplemented aliases
-        $this->info['ContentType'] = new HTMLPurifier_AttrDef_Text();
-        $this->info['ContentTypes'] = new HTMLPurifier_AttrDef_Text();
-        $this->info['Charsets'] = new HTMLPurifier_AttrDef_Text();
-        $this->info['Character'] = new HTMLPurifier_AttrDef_Text();
-
-        // "proprietary" types
-        $this->info['Class'] = new HTMLPurifier_AttrDef_HTML_Class();
-
-        // number is really a positive integer (one or more digits)
-        // FIXME: ^^ not always, see start and value of list items
-        $this->info['Number']   = new HTMLPurifier_AttrDef_Integer(false, false, true);
-    }
-
-    /**
-     * Retrieves a type
-     * @param $type String type name
-     * @return Object AttrDef for type
-     */
-    public function get($type) {
-
-        // determine if there is any extra info tacked on
-        if (strpos($type, '#') !== false) list($type, $string) = explode('#', $type, 2);
-        else $string = '';
-
-        if (!isset($this->info[$type])) {
-            trigger_error('Cannot retrieve undefined attribute type ' . $type, E_USER_ERROR);
-            return;
-        }
-
-        return $this->info[$type]->make($string);
-
-    }
-
-    /**
-     * Sets a new implementation for a type
-     * @param $type String type name
-     * @param $impl Object AttrDef for type
-     */
-    public function set($type, $impl) {
-        $this->info[$type] = $impl;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/AttrValidator.php b/library/HTMLPurifier/AttrValidator.php
deleted file mode 100644 (file)
index 829a0f8..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-<?php
-
-/**
- * Validates the attributes of a token. Doesn't manage required attributes
- * very well. The only reason we factored this out was because RemoveForeignElements
- * also needed it besides ValidateAttributes.
- */
-class HTMLPurifier_AttrValidator
-{
-
-    /**
-     * Validates the attributes of a token, returning a modified token
-     * that has valid tokens
-     * @param $token Reference to token to validate. We require a reference
-     *     because the operation this class performs on the token are
-     *     not atomic, so the context CurrentToken to be updated
-     *     throughout
-     * @param $config Instance of HTMLPurifier_Config
-     * @param $context Instance of HTMLPurifier_Context
-     */
-    public function validateToken(&$token, &$config, $context) {
-
-        $definition = $config->getHTMLDefinition();
-        $e =& $context->get('ErrorCollector', true);
-
-        // initialize IDAccumulator if necessary
-        $ok =& $context->get('IDAccumulator', true);
-        if (!$ok) {
-            $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
-            $context->register('IDAccumulator', $id_accumulator);
-        }
-
-        // initialize CurrentToken if necessary
-        $current_token =& $context->get('CurrentToken', true);
-        if (!$current_token) $context->register('CurrentToken', $token);
-
-        if (
-            !$token instanceof HTMLPurifier_Token_Start &&
-            !$token instanceof HTMLPurifier_Token_Empty
-        ) return $token;
-
-        // create alias to global definition array, see also $defs
-        // DEFINITION CALL
-        $d_defs = $definition->info_global_attr;
-
-        // don't update token until the very end, to ensure an atomic update
-        $attr = $token->attr;
-
-        // do global transformations (pre)
-        // nothing currently utilizes this
-        foreach ($definition->info_attr_transform_pre as $transform) {
-            $attr = $transform->transform($o = $attr, $config, $context);
-            if ($e) {
-                if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
-            }
-        }
-
-        // do local transformations only applicable to this element (pre)
-        // ex. <p align="right"> to <p style="text-align:right;">
-        foreach ($definition->info[$token->name]->attr_transform_pre as $transform) {
-            $attr = $transform->transform($o = $attr, $config, $context);
-            if ($e) {
-                if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
-            }
-        }
-
-        // create alias to this element's attribute definition array, see
-        // also $d_defs (global attribute definition array)
-        // DEFINITION CALL
-        $defs = $definition->info[$token->name]->attr;
-
-        $attr_key = false;
-        $context->register('CurrentAttr', $attr_key);
-
-        // iterate through all the attribute keypairs
-        // Watch out for name collisions: $key has previously been used
-        foreach ($attr as $attr_key => $value) {
-
-            // call the definition
-            if ( isset($defs[$attr_key]) ) {
-                // there is a local definition defined
-                if ($defs[$attr_key] === false) {
-                    // We've explicitly been told not to allow this element.
-                    // This is usually when there's a global definition
-                    // that must be overridden.
-                    // Theoretically speaking, we could have a
-                    // AttrDef_DenyAll, but this is faster!
-                    $result = false;
-                } else {
-                    // validate according to the element's definition
-                    $result = $defs[$attr_key]->validate(
-                                    $value, $config, $context
-                               );
-                }
-            } elseif ( isset($d_defs[$attr_key]) ) {
-                // there is a global definition defined, validate according
-                // to the global definition
-                $result = $d_defs[$attr_key]->validate(
-                                $value, $config, $context
-                           );
-            } else {
-                // system never heard of the attribute? DELETE!
-                $result = false;
-            }
-
-            // put the results into effect
-            if ($result === false || $result === null) {
-                // this is a generic error message that should replaced
-                // with more specific ones when possible
-                if ($e) $e->send(E_ERROR, 'AttrValidator: Attribute removed');
-
-                // remove the attribute
-                unset($attr[$attr_key]);
-            } elseif (is_string($result)) {
-                // generally, if a substitution is happening, there
-                // was some sort of implicit correction going on. We'll
-                // delegate it to the attribute classes to say exactly what.
-
-                // simple substitution
-                $attr[$attr_key] = $result;
-            } else {
-                // nothing happens
-            }
-
-            // we'd also want slightly more complicated substitution
-            // involving an array as the return value,
-            // although we're not sure how colliding attributes would
-            // resolve (certain ones would be completely overriden,
-            // others would prepend themselves).
-        }
-
-        $context->destroy('CurrentAttr');
-
-        // post transforms
-
-        // global (error reporting untested)
-        foreach ($definition->info_attr_transform_post as $transform) {
-            $attr = $transform->transform($o = $attr, $config, $context);
-            if ($e) {
-                if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
-            }
-        }
-
-        // local (error reporting untested)
-        foreach ($definition->info[$token->name]->attr_transform_post as $transform) {
-            $attr = $transform->transform($o = $attr, $config, $context);
-            if ($e) {
-                if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
-            }
-        }
-
-        $token->attr = $attr;
-
-        // destroy CurrentToken if we made it ourselves
-        if (!$current_token) $context->destroy('CurrentToken');
-
-    }
-
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Bootstrap.php b/library/HTMLPurifier/Bootstrap.php
deleted file mode 100644 (file)
index 559f61a..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-<?php
-
-// constants are slow, so we use as few as possible
-if (!defined('HTMLPURIFIER_PREFIX')) {
-    define('HTMLPURIFIER_PREFIX', realpath(dirname(__FILE__) . '/..'));
-}
-
-// accomodations for versions earlier than 5.0.2
-// borrowed from PHP_Compat, LGPL licensed, by Aidan Lister <aidan@php.net>
-if (!defined('PHP_EOL')) {
-    switch (strtoupper(substr(PHP_OS, 0, 3))) {
-        case 'WIN':
-            define('PHP_EOL', "\r\n");
-            break;
-        case 'DAR':
-            define('PHP_EOL', "\r");
-            break;
-        default:
-            define('PHP_EOL', "\n");
-    }
-}
-
-/**
- * Bootstrap class that contains meta-functionality for HTML Purifier such as
- * the autoload function.
- *
- * @note
- *      This class may be used without any other files from HTML Purifier.
- */
-class HTMLPurifier_Bootstrap
-{
-
-    /**
-     * Autoload function for HTML Purifier
-     * @param $class Class to load
-     */
-    public static function autoload($class) {
-        $file = HTMLPurifier_Bootstrap::getPath($class);
-        if (!$file) return false;
-        require HTMLPURIFIER_PREFIX . '/' . $file;
-        return true;
-    }
-
-    /**
-     * Returns the path for a specific class.
-     */
-    public static function getPath($class) {
-        if (strncmp('HTMLPurifier', $class, 12) !== 0) return false;
-        // Custom implementations
-        if (strncmp('HTMLPurifier_Language_', $class, 22) === 0) {
-            $code = str_replace('_', '-', substr($class, 22));
-            $file = 'HTMLPurifier/Language/classes/' . $code . '.php';
-        } else {
-            $file = str_replace('_', '/', $class) . '.php';
-        }
-        if (!file_exists(HTMLPURIFIER_PREFIX . '/' . $file)) return false;
-        return $file;
-    }
-
-    /**
-     * "Pre-registers" our autoloader on the SPL stack.
-     */
-    public static function registerAutoload() {
-        $autoload = array('HTMLPurifier_Bootstrap', 'autoload');
-        if ( ($funcs = spl_autoload_functions()) === false ) {
-            spl_autoload_register($autoload);
-        } elseif (function_exists('spl_autoload_unregister')) {
-            $compat = version_compare(PHP_VERSION, '5.1.2', '<=') &&
-                      version_compare(PHP_VERSION, '5.1.0', '>=');
-            foreach ($funcs as $func) {
-                if (is_array($func)) {
-                    // :TRICKY: There are some compatibility issues and some
-                    // places where we need to error out
-                    $reflector = new ReflectionMethod($func[0], $func[1]);
-                    if (!$reflector->isStatic()) {
-                        throw new Exception('
-                            HTML Purifier autoloader registrar is not compatible
-                            with non-static object methods due to PHP Bug #44144;
-                            Please do not use HTMLPurifier.autoload.php (or any
-                            file that includes this file); instead, place the code:
-                            spl_autoload_register(array(\'HTMLPurifier_Bootstrap\', \'autoload\'))
-                            after your own autoloaders.
-                        ');
-                    }
-                    // Suprisingly, spl_autoload_register supports the
-                    // Class::staticMethod callback format, although call_user_func doesn't
-                    if ($compat) $func = implode('::', $func);
-                }
-                spl_autoload_unregister($func);
-            }
-            spl_autoload_register($autoload);
-            foreach ($funcs as $func) spl_autoload_register($func);
-        }
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/CSSDefinition.php b/library/HTMLPurifier/CSSDefinition.php
deleted file mode 100644 (file)
index 6a2e6f5..0000000
+++ /dev/null
@@ -1,292 +0,0 @@
-<?php
-
-/**
- * Defines allowed CSS attributes and what their values are.
- * @see HTMLPurifier_HTMLDefinition
- */
-class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
-{
-
-    public $type = 'CSS';
-
-    /**
-     * Assoc array of attribute name to definition object.
-     */
-    public $info = array();
-
-    /**
-     * Constructs the info array.  The meat of this class.
-     */
-    protected function doSetup($config) {
-
-        $this->info['text-align'] = new HTMLPurifier_AttrDef_Enum(
-            array('left', 'right', 'center', 'justify'), false);
-
-        $border_style =
-        $this->info['border-bottom-style'] =
-        $this->info['border-right-style'] =
-        $this->info['border-left-style'] =
-        $this->info['border-top-style'] =  new HTMLPurifier_AttrDef_Enum(
-            array('none', 'hidden', 'dotted', 'dashed', 'solid', 'double',
-            'groove', 'ridge', 'inset', 'outset'), false);
-
-        $this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style);
-
-        $this->info['clear'] = new HTMLPurifier_AttrDef_Enum(
-            array('none', 'left', 'right', 'both'), false);
-        $this->info['float'] = new HTMLPurifier_AttrDef_Enum(
-            array('none', 'left', 'right'), false);
-        $this->info['font-style'] = new HTMLPurifier_AttrDef_Enum(
-            array('normal', 'italic', 'oblique'), false);
-        $this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum(
-            array('normal', 'small-caps'), false);
-
-        $uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite(
-            array(
-                new HTMLPurifier_AttrDef_Enum(array('none')),
-                new HTMLPurifier_AttrDef_CSS_URI()
-            )
-        );
-
-        $this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum(
-            array('inside', 'outside'), false);
-        $this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum(
-            array('disc', 'circle', 'square', 'decimal', 'lower-roman',
-            'upper-roman', 'lower-alpha', 'upper-alpha', 'none'), false);
-        $this->info['list-style-image'] = $uri_or_none;
-
-        $this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config);
-
-        $this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum(
-            array('capitalize', 'uppercase', 'lowercase', 'none'), false);
-        $this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color();
-
-        $this->info['background-image'] = $uri_or_none;
-        $this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum(
-            array('repeat', 'repeat-x', 'repeat-y', 'no-repeat')
-        );
-        $this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum(
-            array('scroll', 'fixed')
-        );
-        $this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition();
-
-        $border_color =
-        $this->info['border-top-color'] =
-        $this->info['border-bottom-color'] =
-        $this->info['border-left-color'] =
-        $this->info['border-right-color'] =
-        $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
-            new HTMLPurifier_AttrDef_Enum(array('transparent')),
-            new HTMLPurifier_AttrDef_CSS_Color()
-        ));
-
-        $this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config);
-
-        $this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color);
-
-        $border_width =
-        $this->info['border-top-width'] =
-        $this->info['border-bottom-width'] =
-        $this->info['border-left-width'] =
-        $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
-            new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')),
-            new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative
-        ));
-
-        $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width);
-
-        $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
-            new HTMLPurifier_AttrDef_Enum(array('normal')),
-            new HTMLPurifier_AttrDef_CSS_Length()
-        ));
-
-        $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
-            new HTMLPurifier_AttrDef_Enum(array('normal')),
-            new HTMLPurifier_AttrDef_CSS_Length()
-        ));
-
-        $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
-            new HTMLPurifier_AttrDef_Enum(array('xx-small', 'x-small',
-                'small', 'medium', 'large', 'x-large', 'xx-large',
-                'larger', 'smaller')),
-            new HTMLPurifier_AttrDef_CSS_Percentage(),
-            new HTMLPurifier_AttrDef_CSS_Length()
-        ));
-
-        $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
-            new HTMLPurifier_AttrDef_Enum(array('normal')),
-            new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives
-            new HTMLPurifier_AttrDef_CSS_Length('0'),
-            new HTMLPurifier_AttrDef_CSS_Percentage(true)
-        ));
-
-        $margin =
-        $this->info['margin-top'] =
-        $this->info['margin-bottom'] =
-        $this->info['margin-left'] =
-        $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
-            new HTMLPurifier_AttrDef_CSS_Length(),
-            new HTMLPurifier_AttrDef_CSS_Percentage(),
-            new HTMLPurifier_AttrDef_Enum(array('auto'))
-        ));
-
-        $this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin);
-
-        // non-negative
-        $padding =
-        $this->info['padding-top'] =
-        $this->info['padding-bottom'] =
-        $this->info['padding-left'] =
-        $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
-            new HTMLPurifier_AttrDef_CSS_Length('0'),
-            new HTMLPurifier_AttrDef_CSS_Percentage(true)
-        ));
-
-        $this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding);
-
-        $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
-            new HTMLPurifier_AttrDef_CSS_Length(),
-            new HTMLPurifier_AttrDef_CSS_Percentage()
-        ));
-
-        $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(array(
-            new HTMLPurifier_AttrDef_CSS_Length('0'),
-            new HTMLPurifier_AttrDef_CSS_Percentage(true),
-            new HTMLPurifier_AttrDef_Enum(array('auto'))
-        ));
-        $max = $config->get('CSS.MaxImgLength');
-
-        $this->info['width'] =
-        $this->info['height'] =
-            $max === null ?
-            $trusted_wh :
-            new HTMLPurifier_AttrDef_Switch('img',
-                // For img tags:
-                new HTMLPurifier_AttrDef_CSS_Composite(array(
-                    new HTMLPurifier_AttrDef_CSS_Length('0', $max),
-                    new HTMLPurifier_AttrDef_Enum(array('auto'))
-                )),
-                // For everyone else:
-                $trusted_wh
-            );
-
-        $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration();
-
-        $this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily();
-
-        // this could use specialized code
-        $this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum(
-            array('normal', 'bold', 'bolder', 'lighter', '100', '200', '300',
-            '400', '500', '600', '700', '800', '900'), false);
-
-        // MUST be called after other font properties, as it references
-        // a CSSDefinition object
-        $this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config);
-
-        // same here
-        $this->info['border'] =
-        $this->info['border-bottom'] =
-        $this->info['border-top'] =
-        $this->info['border-left'] =
-        $this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config);
-
-        $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(array(
-            'collapse', 'separate'));
-
-        $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(array(
-            'top', 'bottom'));
-
-        $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(array(
-            'auto', 'fixed'));
-
-        $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
-            new HTMLPurifier_AttrDef_Enum(array('baseline', 'sub', 'super',
-                'top', 'text-top', 'middle', 'bottom', 'text-bottom')),
-            new HTMLPurifier_AttrDef_CSS_Length(),
-            new HTMLPurifier_AttrDef_CSS_Percentage()
-        ));
-
-        $this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2);
-
-        // partial support
-        $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(array('nowrap'));
-
-        if ($config->get('CSS.Proprietary')) {
-            $this->doSetupProprietary($config);
-        }
-
-        if ($config->get('CSS.AllowTricky')) {
-            $this->doSetupTricky($config);
-        }
-
-        $allow_important = $config->get('CSS.AllowImportant');
-        // wrap all attr-defs with decorator that handles !important
-        foreach ($this->info as $k => $v) {
-            $this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important);
-        }
-
-        $this->setupConfigStuff($config);
-    }
-
-    protected function doSetupProprietary($config) {
-        // Internet Explorer only scrollbar colors
-        $this->info['scrollbar-arrow-color']        = new HTMLPurifier_AttrDef_CSS_Color();
-        $this->info['scrollbar-base-color']         = new HTMLPurifier_AttrDef_CSS_Color();
-        $this->info['scrollbar-darkshadow-color']   = new HTMLPurifier_AttrDef_CSS_Color();
-        $this->info['scrollbar-face-color']         = new HTMLPurifier_AttrDef_CSS_Color();
-        $this->info['scrollbar-highlight-color']    = new HTMLPurifier_AttrDef_CSS_Color();
-        $this->info['scrollbar-shadow-color']       = new HTMLPurifier_AttrDef_CSS_Color();
-
-        // technically not proprietary, but CSS3, and no one supports it
-        $this->info['opacity']          = new HTMLPurifier_AttrDef_CSS_AlphaValue();
-        $this->info['-moz-opacity']     = new HTMLPurifier_AttrDef_CSS_AlphaValue();
-        $this->info['-khtml-opacity']   = new HTMLPurifier_AttrDef_CSS_AlphaValue();
-
-        // only opacity, for now
-        $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter();
-
-    }
-
-    protected function doSetupTricky($config) {
-        $this->info['display'] = new HTMLPurifier_AttrDef_Enum(array(
-            'inline', 'block', 'list-item', 'run-in', 'compact',
-            'marker', 'table', 'inline-table', 'table-row-group',
-            'table-header-group', 'table-footer-group', 'table-row',
-            'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none'
-        ));
-        $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(array(
-            'visible', 'hidden', 'collapse'
-        ));
-        $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll'));
-    }
-
-
-    /**
-     * Performs extra config-based processing. Based off of
-     * HTMLPurifier_HTMLDefinition.
-     * @todo Refactor duplicate elements into common class (probably using
-     *       composition, not inheritance).
-     */
-    protected function setupConfigStuff($config) {
-
-        // setup allowed elements
-        $support = "(for information on implementing this, see the ".
-                   "support forums) ";
-        $allowed_attributes = $config->get('CSS.AllowedProperties');
-        if ($allowed_attributes !== null) {
-            foreach ($this->info as $name => $d) {
-                if(!isset($allowed_attributes[$name])) unset($this->info[$name]);
-                unset($allowed_attributes[$name]);
-            }
-            // emit errors
-            foreach ($allowed_attributes as $name => $d) {
-                // :TODO: Is this htmlspecialchars() call really necessary?
-                $name = htmlspecialchars($name);
-                trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING);
-            }
-        }
-
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ChildDef.php b/library/HTMLPurifier/ChildDef.php
deleted file mode 100644 (file)
index c5d5216..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-
-/**
- * Defines allowed child nodes and validates tokens against it.
- */
-abstract class HTMLPurifier_ChildDef
-{
-    /**
-     * Type of child definition, usually right-most part of class name lowercase.
-     * Used occasionally in terms of context.
-     */
-    public $type;
-
-    /**
-     * Bool that indicates whether or not an empty array of children is okay
-     *
-     * This is necessary for redundant checking when changes affecting
-     * a child node may cause a parent node to now be disallowed.
-     */
-    public $allow_empty;
-
-    /**
-     * Lookup array of all elements that this definition could possibly allow
-     */
-    public $elements = array();
-
-    /**
-     * Get lookup of tag names that should not close this element automatically.
-     * All other elements will do so.
-     */
-    public function getAllowedElements($config) {
-        return $this->elements;
-    }
-
-    /**
-     * Validates nodes according to definition and returns modification.
-     *
-     * @param $tokens_of_children Array of HTMLPurifier_Token
-     * @param $config HTMLPurifier_Config object
-     * @param $context HTMLPurifier_Context object
-     * @return bool true to leave nodes as is
-     * @return bool false to remove parent node
-     * @return array of replacement child tokens
-     */
-    abstract public function validateChildren($tokens_of_children, $config, $context);
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ChildDef/Chameleon.php b/library/HTMLPurifier/ChildDef/Chameleon.php
deleted file mode 100644 (file)
index 15c364e..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-
-/**
- * Definition that uses different definitions depending on context.
- *
- * The del and ins tags are notable because they allow different types of
- * elements depending on whether or not they're in a block or inline context.
- * Chameleon allows this behavior to happen by using two different
- * definitions depending on context.  While this somewhat generalized,
- * it is specifically intended for those two tags.
- */
-class HTMLPurifier_ChildDef_Chameleon extends HTMLPurifier_ChildDef
-{
-
-    /**
-     * Instance of the definition object to use when inline. Usually stricter.
-     */
-    public $inline;
-
-    /**
-     * Instance of the definition object to use when block.
-     */
-    public $block;
-
-    public $type = 'chameleon';
-
-    /**
-     * @param $inline List of elements to allow when inline.
-     * @param $block List of elements to allow when block.
-     */
-    public function __construct($inline, $block) {
-        $this->inline = new HTMLPurifier_ChildDef_Optional($inline);
-        $this->block  = new HTMLPurifier_ChildDef_Optional($block);
-        $this->elements = $this->block->elements;
-    }
-
-    public function validateChildren($tokens_of_children, $config, $context) {
-        if ($context->get('IsInline') === false) {
-            return $this->block->validateChildren(
-                $tokens_of_children, $config, $context);
-        } else {
-            return $this->inline->validateChildren(
-                $tokens_of_children, $config, $context);
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ChildDef/Custom.php b/library/HTMLPurifier/ChildDef/Custom.php
deleted file mode 100644 (file)
index b68047b..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-<?php
-
-/**
- * Custom validation class, accepts DTD child definitions
- *
- * @warning Currently this class is an all or nothing proposition, that is,
- *          it will only give a bool return value.
- */
-class HTMLPurifier_ChildDef_Custom extends HTMLPurifier_ChildDef
-{
-    public $type = 'custom';
-    public $allow_empty = false;
-    /**
-     * Allowed child pattern as defined by the DTD
-     */
-    public $dtd_regex;
-    /**
-     * PCRE regex derived from $dtd_regex
-     * @private
-     */
-    private $_pcre_regex;
-    /**
-     * @param $dtd_regex Allowed child pattern from the DTD
-     */
-    public function __construct($dtd_regex) {
-        $this->dtd_regex = $dtd_regex;
-        $this->_compileRegex();
-    }
-    /**
-     * Compiles the PCRE regex from a DTD regex ($dtd_regex to $_pcre_regex)
-     */
-    protected function _compileRegex() {
-        $raw = str_replace(' ', '', $this->dtd_regex);
-        if ($raw{0} != '(') {
-            $raw = "($raw)";
-        }
-        $el = '[#a-zA-Z0-9_.-]+';
-        $reg = $raw;
-
-        // COMPLICATED! AND MIGHT BE BUGGY! I HAVE NO CLUE WHAT I'M
-        // DOING! Seriously: if there's problems, please report them.
-
-        // collect all elements into the $elements array
-        preg_match_all("/$el/", $reg, $matches);
-        foreach ($matches[0] as $match) {
-            $this->elements[$match] = true;
-        }
-
-        // setup all elements as parentheticals with leading commas
-        $reg = preg_replace("/$el/", '(,\\0)', $reg);
-
-        // remove commas when they were not solicited
-        $reg = preg_replace("/([^,(|]\(+),/", '\\1', $reg);
-
-        // remove all non-paranthetical commas: they are handled by first regex
-        $reg = preg_replace("/,\(/", '(', $reg);
-
-        $this->_pcre_regex = $reg;
-    }
-    public function validateChildren($tokens_of_children, $config, $context) {
-        $list_of_children = '';
-        $nesting = 0; // depth into the nest
-        foreach ($tokens_of_children as $token) {
-            if (!empty($token->is_whitespace)) continue;
-
-            $is_child = ($nesting == 0); // direct
-
-            if ($token instanceof HTMLPurifier_Token_Start) {
-                $nesting++;
-            } elseif ($token instanceof HTMLPurifier_Token_End) {
-                $nesting--;
-            }
-
-            if ($is_child) {
-                $list_of_children .= $token->name . ',';
-            }
-        }
-        // add leading comma to deal with stray comma declarations
-        $list_of_children = ',' . rtrim($list_of_children, ',');
-        $okay =
-            preg_match(
-                '/^,?'.$this->_pcre_regex.'$/',
-                $list_of_children
-            );
-
-        return (bool) $okay;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ChildDef/Empty.php b/library/HTMLPurifier/ChildDef/Empty.php
deleted file mode 100644 (file)
index 13171f6..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-/**
- * Definition that disallows all elements.
- * @warning validateChildren() in this class is actually never called, because
- *          empty elements are corrected in HTMLPurifier_Strategy_MakeWellFormed
- *          before child definitions are parsed in earnest by
- *          HTMLPurifier_Strategy_FixNesting.
- */
-class HTMLPurifier_ChildDef_Empty extends HTMLPurifier_ChildDef
-{
-    public $allow_empty = true;
-    public $type = 'empty';
-    public function __construct() {}
-    public function validateChildren($tokens_of_children, $config, $context) {
-        return array();
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ChildDef/Optional.php b/library/HTMLPurifier/ChildDef/Optional.php
deleted file mode 100644 (file)
index 32bcb98..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-/**
- * Definition that allows a set of elements, and allows no children.
- * @note This is a hack to reuse code from HTMLPurifier_ChildDef_Required,
- *       really, one shouldn't inherit from the other.  Only altered behavior
- *       is to overload a returned false with an array.  Thus, it will never
- *       return false.
- */
-class HTMLPurifier_ChildDef_Optional extends HTMLPurifier_ChildDef_Required
-{
-    public $allow_empty = true;
-    public $type = 'optional';
-    public function validateChildren($tokens_of_children, $config, $context) {
-        $result = parent::validateChildren($tokens_of_children, $config, $context);
-        // we assume that $tokens_of_children is not modified
-        if ($result === false) {
-            if (empty($tokens_of_children)) return true;
-            elseif ($this->whitespace) return $tokens_of_children;
-            else return array();
-        }
-        return $result;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ChildDef/Required.php b/library/HTMLPurifier/ChildDef/Required.php
deleted file mode 100644 (file)
index 4889f24..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-<?php
-
-/**
- * Definition that allows a set of elements, but disallows empty children.
- */
-class HTMLPurifier_ChildDef_Required extends HTMLPurifier_ChildDef
-{
-    /**
-     * Lookup table of allowed elements.
-     * @public
-     */
-    public $elements = array();
-    /**
-     * Whether or not the last passed node was all whitespace.
-     */
-    protected $whitespace = false;
-    /**
-     * @param $elements List of allowed element names (lowercase).
-     */
-    public function __construct($elements) {
-        if (is_string($elements)) {
-            $elements = str_replace(' ', '', $elements);
-            $elements = explode('|', $elements);
-        }
-        $keys = array_keys($elements);
-        if ($keys == array_keys($keys)) {
-            $elements = array_flip($elements);
-            foreach ($elements as $i => $x) {
-                $elements[$i] = true;
-                if (empty($i)) unset($elements[$i]); // remove blank
-            }
-        }
-        $this->elements = $elements;
-    }
-    public $allow_empty = false;
-    public $type = 'required';
-    public function validateChildren($tokens_of_children, $config, $context) {
-        // Flag for subclasses
-        $this->whitespace = false;
-
-        // if there are no tokens, delete parent node
-        if (empty($tokens_of_children)) return false;
-
-        // the new set of children
-        $result = array();
-
-        // current depth into the nest
-        $nesting = 0;
-
-        // whether or not we're deleting a node
-        $is_deleting = false;
-
-        // whether or not parsed character data is allowed
-        // this controls whether or not we silently drop a tag
-        // or generate escaped HTML from it
-        $pcdata_allowed = isset($this->elements['#PCDATA']);
-
-        // a little sanity check to make sure it's not ALL whitespace
-        $all_whitespace = true;
-
-        // some configuration
-        $escape_invalid_children = $config->get('Core.EscapeInvalidChildren');
-
-        // generator
-        $gen = new HTMLPurifier_Generator($config, $context);
-
-        foreach ($tokens_of_children as $token) {
-            if (!empty($token->is_whitespace)) {
-                $result[] = $token;
-                continue;
-            }
-            $all_whitespace = false; // phew, we're not talking about whitespace
-
-            $is_child = ($nesting == 0);
-
-            if ($token instanceof HTMLPurifier_Token_Start) {
-                $nesting++;
-            } elseif ($token instanceof HTMLPurifier_Token_End) {
-                $nesting--;
-            }
-
-            if ($is_child) {
-                $is_deleting = false;
-                if (!isset($this->elements[$token->name])) {
-                    $is_deleting = true;
-                    if ($pcdata_allowed && $token instanceof HTMLPurifier_Token_Text) {
-                        $result[] = $token;
-                    } elseif ($pcdata_allowed && $escape_invalid_children) {
-                        $result[] = new HTMLPurifier_Token_Text(
-                            $gen->generateFromToken($token)
-                        );
-                    }
-                    continue;
-                }
-            }
-            if (!$is_deleting || ($pcdata_allowed && $token instanceof HTMLPurifier_Token_Text)) {
-                $result[] = $token;
-            } elseif ($pcdata_allowed && $escape_invalid_children) {
-                $result[] =
-                    new HTMLPurifier_Token_Text(
-                        $gen->generateFromToken($token)
-                    );
-            } else {
-                // drop silently
-            }
-        }
-        if (empty($result)) return false;
-        if ($all_whitespace) {
-            $this->whitespace = true;
-            return false;
-        }
-        if ($tokens_of_children == $result) return true;
-        return $result;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ChildDef/StrictBlockquote.php b/library/HTMLPurifier/ChildDef/StrictBlockquote.php
deleted file mode 100644 (file)
index dfae8a6..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-<?php
-
-/**
- * Takes the contents of blockquote when in strict and reformats for validation.
- */
-class HTMLPurifier_ChildDef_StrictBlockquote extends HTMLPurifier_ChildDef_Required
-{
-    protected $real_elements;
-    protected $fake_elements;
-    public $allow_empty = true;
-    public $type = 'strictblockquote';
-    protected $init = false;
-
-    /**
-     * @note We don't want MakeWellFormed to auto-close inline elements since
-     *       they might be allowed.
-     */
-    public function getAllowedElements($config) {
-        $this->init($config);
-        return $this->fake_elements;
-    }
-
-    public function validateChildren($tokens_of_children, $config, $context) {
-
-        $this->init($config);
-
-        // trick the parent class into thinking it allows more
-        $this->elements = $this->fake_elements;
-        $result = parent::validateChildren($tokens_of_children, $config, $context);
-        $this->elements = $this->real_elements;
-
-        if ($result === false) return array();
-        if ($result === true) $result = $tokens_of_children;
-
-        $def = $config->getHTMLDefinition();
-        $block_wrap_start = new HTMLPurifier_Token_Start($def->info_block_wrapper);
-        $block_wrap_end   = new HTMLPurifier_Token_End(  $def->info_block_wrapper);
-        $is_inline = false;
-        $depth = 0;
-        $ret = array();
-
-        // assuming that there are no comment tokens
-        foreach ($result as $i => $token) {
-            $token = $result[$i];
-            // ifs are nested for readability
-            if (!$is_inline) {
-                if (!$depth) {
-                     if (
-                        ($token instanceof HTMLPurifier_Token_Text && !$token->is_whitespace) ||
-                        (!$token instanceof HTMLPurifier_Token_Text && !isset($this->elements[$token->name]))
-                     ) {
-                        $is_inline = true;
-                        $ret[] = $block_wrap_start;
-                     }
-                }
-            } else {
-                if (!$depth) {
-                    // starting tokens have been inline text / empty
-                    if ($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) {
-                        if (isset($this->elements[$token->name])) {
-                            // ended
-                            $ret[] = $block_wrap_end;
-                            $is_inline = false;
-                        }
-                    }
-                }
-            }
-            $ret[] = $token;
-            if ($token instanceof HTMLPurifier_Token_Start) $depth++;
-            if ($token instanceof HTMLPurifier_Token_End)   $depth--;
-        }
-        if ($is_inline) $ret[] = $block_wrap_end;
-        return $ret;
-    }
-
-    private function init($config) {
-        if (!$this->init) {
-            $def = $config->getHTMLDefinition();
-            // allow all inline elements
-            $this->real_elements = $this->elements;
-            $this->fake_elements = $def->info_content_sets['Flow'];
-            $this->fake_elements['#PCDATA'] = true;
-            $this->init = true;
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ChildDef/Table.php b/library/HTMLPurifier/ChildDef/Table.php
deleted file mode 100644 (file)
index 34f0227..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php
-
-/**
- * Definition for tables
- */
-class HTMLPurifier_ChildDef_Table extends HTMLPurifier_ChildDef
-{
-    public $allow_empty = false;
-    public $type = 'table';
-    public $elements = array('tr' => true, 'tbody' => true, 'thead' => true,
-        'tfoot' => true, 'caption' => true, 'colgroup' => true, 'col' => true);
-    public function __construct() {}
-    public function validateChildren($tokens_of_children, $config, $context) {
-        if (empty($tokens_of_children)) return false;
-
-        // this ensures that the loop gets run one last time before closing
-        // up. It's a little bit of a hack, but it works! Just make sure you
-        // get rid of the token later.
-        $tokens_of_children[] = false;
-
-        // only one of these elements is allowed in a table
-        $caption = false;
-        $thead   = false;
-        $tfoot   = false;
-
-        // as many of these as you want
-        $cols    = array();
-        $content = array();
-
-        $nesting = 0; // current depth so we can determine nodes
-        $is_collecting = false; // are we globbing together tokens to package
-                                // into one of the collectors?
-        $collection = array(); // collected nodes
-        $tag_index = 0; // the first node might be whitespace,
-                            // so this tells us where the start tag is
-
-        foreach ($tokens_of_children as $token) {
-            $is_child = ($nesting == 0);
-
-            if ($token === false) {
-                // terminating sequence started
-            } elseif ($token instanceof HTMLPurifier_Token_Start) {
-                $nesting++;
-            } elseif ($token instanceof HTMLPurifier_Token_End) {
-                $nesting--;
-            }
-
-            // handle node collection
-            if ($is_collecting) {
-                if ($is_child) {
-                    // okay, let's stash the tokens away
-                    // first token tells us the type of the collection
-                    switch ($collection[$tag_index]->name) {
-                        case 'tr':
-                        case 'tbody':
-                            $content[] = $collection;
-                            break;
-                        case 'caption':
-                            if ($caption !== false) break;
-                            $caption = $collection;
-                            break;
-                        case 'thead':
-                        case 'tfoot':
-                            // access the appropriate variable, $thead or $tfoot
-                            $var = $collection[$tag_index]->name;
-                            if ($$var === false) {
-                                $$var = $collection;
-                            } else {
-                                // transmutate the first and less entries into
-                                // tbody tags, and then put into content
-                                $collection[$tag_index]->name = 'tbody';
-                                $collection[count($collection)-1]->name = 'tbody';
-                                $content[] = $collection;
-                            }
-                            break;
-                         case 'colgroup':
-                            $cols[] = $collection;
-                            break;
-                    }
-                    $collection = array();
-                    $is_collecting = false;
-                    $tag_index = 0;
-                } else {
-                    // add the node to the collection
-                    $collection[] = $token;
-                }
-            }
-
-            // terminate
-            if ($token === false) break;
-
-            if ($is_child) {
-                // determine what we're dealing with
-                if ($token->name == 'col') {
-                    // the only empty tag in the possie, we can handle it
-                    // immediately
-                    $cols[] = array_merge($collection, array($token));
-                    $collection = array();
-                    $tag_index = 0;
-                    continue;
-                }
-                switch($token->name) {
-                    case 'caption':
-                    case 'colgroup':
-                    case 'thead':
-                    case 'tfoot':
-                    case 'tbody':
-                    case 'tr':
-                        $is_collecting = true;
-                        $collection[] = $token;
-                        continue;
-                    default:
-                        if (!empty($token->is_whitespace)) {
-                            $collection[] = $token;
-                            $tag_index++;
-                        }
-                        continue;
-                }
-            }
-        }
-
-        if (empty($content)) return false;
-
-        $ret = array();
-        if ($caption !== false) $ret = array_merge($ret, $caption);
-        if ($cols !== false)    foreach ($cols as $token_array) $ret = array_merge($ret, $token_array);
-        if ($thead !== false)   $ret = array_merge($ret, $thead);
-        if ($tfoot !== false)   $ret = array_merge($ret, $tfoot);
-        foreach ($content as $token_array) $ret = array_merge($ret, $token_array);
-        if (!empty($collection) && $is_collecting == false){
-            // grab the trailing space
-            $ret = array_merge($ret, $collection);
-        }
-
-        array_pop($tokens_of_children); // remove phantom token
-
-        return ($ret === $tokens_of_children) ? true : $ret;
-
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Config.php b/library/HTMLPurifier/Config.php
deleted file mode 100644 (file)
index 2a334b0..0000000
+++ /dev/null
@@ -1,580 +0,0 @@
-<?php
-
-/**
- * Configuration object that triggers customizable behavior.
- *
- * @warning This class is strongly defined: that means that the class
- *          will fail if an undefined directive is retrieved or set.
- *
- * @note Many classes that could (although many times don't) use the
- *       configuration object make it a mandatory parameter.  This is
- *       because a configuration object should always be forwarded,
- *       otherwise, you run the risk of missing a parameter and then
- *       being stumped when a configuration directive doesn't work.
- *
- * @todo Reconsider some of the public member variables
- */
-class HTMLPurifier_Config
-{
-
-    /**
-     * HTML Purifier's version
-     */
-    public $version = '4.1.1';
-
-    /**
-     * Bool indicator whether or not to automatically finalize
-     * the object if a read operation is done
-     */
-    public $autoFinalize = true;
-
-    // protected member variables
-
-    /**
-     * Namespace indexed array of serials for specific namespaces (see
-     * getSerial() for more info).
-     */
-    protected $serials = array();
-
-    /**
-     * Serial for entire configuration object
-     */
-    protected $serial;
-
-    /**
-     * Parser for variables
-     */
-    protected $parser;
-
-    /**
-     * Reference HTMLPurifier_ConfigSchema for value checking
-     * @note This is public for introspective purposes. Please don't
-     *       abuse!
-     */
-    public $def;
-
-    /**
-     * Indexed array of definitions
-     */
-    protected $definitions;
-
-    /**
-     * Bool indicator whether or not config is finalized
-     */
-    protected $finalized = false;
-
-    /**
-     * Property list containing configuration directives.
-     */
-    protected $plist;
-
-    /**
-     * Whether or not a set is taking place due to an
-     * alias lookup.
-     */
-    private $aliasMode;
-
-    /**
-     * Set to false if you do not want line and file numbers in errors
-     * (useful when unit testing)
-     */
-    public $chatty = true;
-
-    /**
-     * Current lock; only gets to this namespace are allowed.
-     */
-    private $lock;
-
-    /**
-     * @param $definition HTMLPurifier_ConfigSchema that defines what directives
-     *                    are allowed.
-     */
-    public function __construct($definition, $parent = null) {
-        $parent = $parent ? $parent : $definition->defaultPlist;
-        $this->plist = new HTMLPurifier_PropertyList($parent);
-        $this->def = $definition; // keep a copy around for checking
-        $this->parser = new HTMLPurifier_VarParser_Flexible();
-    }
-
-    /**
-     * Convenience constructor that creates a config object based on a mixed var
-     * @param mixed $config Variable that defines the state of the config
-     *                      object. Can be: a HTMLPurifier_Config() object,
-     *                      an array of directives based on loadArray(),
-     *                      or a string filename of an ini file.
-     * @param HTMLPurifier_ConfigSchema Schema object
-     * @return Configured HTMLPurifier_Config object
-     */
-    public static function create($config, $schema = null) {
-        if ($config instanceof HTMLPurifier_Config) {
-            // pass-through
-            return $config;
-        }
-        if (!$schema) {
-            $ret = HTMLPurifier_Config::createDefault();
-        } else {
-            $ret = new HTMLPurifier_Config($schema);
-        }
-        if (is_string($config)) $ret->loadIni($config);
-        elseif (is_array($config)) $ret->loadArray($config);
-        return $ret;
-    }
-
-    /**
-     * Creates a new config object that inherits from a previous one.
-     * @param HTMLPurifier_Config $config Configuration object to inherit
-     *        from.
-     * @return HTMLPurifier_Config object with $config as its parent.
-     */
-    public static function inherit(HTMLPurifier_Config $config) {
-        return new HTMLPurifier_Config($config->def, $config->plist);
-    }
-
-    /**
-     * Convenience constructor that creates a default configuration object.
-     * @return Default HTMLPurifier_Config object.
-     */
-    public static function createDefault() {
-        $definition = HTMLPurifier_ConfigSchema::instance();
-        $config = new HTMLPurifier_Config($definition);
-        return $config;
-    }
-
-    /**
-     * Retreives a value from the configuration.
-     * @param $key String key
-     */
-    public function get($key, $a = null) {
-        if ($a !== null) {
-            $this->triggerError("Using deprecated API: use \$config->get('$key.$a') instead", E_USER_WARNING);
-            $key = "$key.$a";
-        }
-        if (!$this->finalized) $this->autoFinalize();
-        if (!isset($this->def->info[$key])) {
-            // can't add % due to SimpleTest bug
-            $this->triggerError('Cannot retrieve value of undefined directive ' . htmlspecialchars($key),
-                E_USER_WARNING);
-            return;
-        }
-        if (isset($this->def->info[$key]->isAlias)) {
-            $d = $this->def->info[$key];
-            $this->triggerError('Cannot get value from aliased directive, use real name ' . $d->key,
-                E_USER_ERROR);
-            return;
-        }
-        if ($this->lock) {
-            list($ns) = explode('.', $key);
-            if ($ns !== $this->lock) {
-                $this->triggerError('Cannot get value of namespace ' . $ns . ' when lock for ' . $this->lock . ' is active, this probably indicates a Definition setup method is accessing directives that are not within its namespace', E_USER_ERROR);
-                return;
-            }
-        }
-        return $this->plist->get($key);
-    }
-
-    /**
-     * Retreives an array of directives to values from a given namespace
-     * @param $namespace String namespace
-     */
-    public function getBatch($namespace) {
-        if (!$this->finalized) $this->autoFinalize();
-        $full = $this->getAll();
-        if (!isset($full[$namespace])) {
-            $this->triggerError('Cannot retrieve undefined namespace ' . htmlspecialchars($namespace),
-                E_USER_WARNING);
-            return;
-        }
-        return $full[$namespace];
-    }
-
-    /**
-     * Returns a md5 signature of a segment of the configuration object
-     * that uniquely identifies that particular configuration
-     * @note Revision is handled specially and is removed from the batch
-     *       before processing!
-     * @param $namespace Namespace to get serial for
-     */
-    public function getBatchSerial($namespace) {
-        if (empty($this->serials[$namespace])) {
-            $batch = $this->getBatch($namespace);
-            unset($batch['DefinitionRev']);
-            $this->serials[$namespace] = md5(serialize($batch));
-        }
-        return $this->serials[$namespace];
-    }
-
-    /**
-     * Returns a md5 signature for the entire configuration object
-     * that uniquely identifies that particular configuration
-     */
-    public function getSerial() {
-        if (empty($this->serial)) {
-            $this->serial = md5(serialize($this->getAll()));
-        }
-        return $this->serial;
-    }
-
-    /**
-     * Retrieves all directives, organized by namespace
-     * @warning This is a pretty inefficient function, avoid if you can
-     */
-    public function getAll() {
-        if (!$this->finalized) $this->autoFinalize();
-        $ret = array();
-        foreach ($this->plist->squash() as $name => $value) {
-            list($ns, $key) = explode('.', $name, 2);
-            $ret[$ns][$key] = $value;
-        }
-        return $ret;
-    }
-
-    /**
-     * Sets a value to configuration.
-     * @param $key String key
-     * @param $value Mixed value
-     */
-    public function set($key, $value, $a = null) {
-        if (strpos($key, '.') === false) {
-            $namespace = $key;
-            $directive = $value;
-            $value = $a;
-            $key = "$key.$directive";
-            $this->triggerError("Using deprecated API: use \$config->set('$key', ...) instead", E_USER_NOTICE);
-        } else {
-            list($namespace) = explode('.', $key);
-        }
-        if ($this->isFinalized('Cannot set directive after finalization')) return;
-        if (!isset($this->def->info[$key])) {
-            $this->triggerError('Cannot set undefined directive ' . htmlspecialchars($key) . ' to value',
-                E_USER_WARNING);
-            return;
-        }
-        $def = $this->def->info[$key];
-
-        if (isset($def->isAlias)) {
-            if ($this->aliasMode) {
-                $this->triggerError('Double-aliases not allowed, please fix '.
-                    'ConfigSchema bug with' . $key, E_USER_ERROR);
-                return;
-            }
-            $this->aliasMode = true;
-            $this->set($def->key, $value);
-            $this->aliasMode = false;
-            $this->triggerError("$key is an alias, preferred directive name is {$def->key}", E_USER_NOTICE);
-            return;
-        }
-
-        // Raw type might be negative when using the fully optimized form
-        // of stdclass, which indicates allow_null == true
-        $rtype = is_int($def) ? $def : $def->type;
-        if ($rtype < 0) {
-            $type = -$rtype;
-            $allow_null = true;
-        } else {
-            $type = $rtype;
-            $allow_null = isset($def->allow_null);
-        }
-
-        try {
-            $value = $this->parser->parse($value, $type, $allow_null);
-        } catch (HTMLPurifier_VarParserException $e) {
-            $this->triggerError('Value for ' . $key . ' is of invalid type, should be ' . HTMLPurifier_VarParser::getTypeName($type), E_USER_WARNING);
-            return;
-        }
-        if (is_string($value) && is_object($def)) {
-            // resolve value alias if defined
-            if (isset($def->aliases[$value])) {
-                $value = $def->aliases[$value];
-            }
-            // check to see if the value is allowed
-            if (isset($def->allowed) && !isset($def->allowed[$value])) {
-                $this->triggerError('Value not supported, valid values are: ' .
-                    $this->_listify($def->allowed), E_USER_WARNING);
-                return;
-            }
-        }
-        $this->plist->set($key, $value);
-
-        // reset definitions if the directives they depend on changed
-        // this is a very costly process, so it's discouraged
-        // with finalization
-        if ($namespace == 'HTML' || $namespace == 'CSS' || $namespace == 'URI') {
-            $this->definitions[$namespace] = null;
-        }
-
-        $this->serials[$namespace] = false;
-    }
-
-    /**
-     * Convenience function for error reporting
-     */
-    private function _listify($lookup) {
-        $list = array();
-        foreach ($lookup as $name => $b) $list[] = $name;
-        return implode(', ', $list);
-    }
-
-    /**
-     * Retrieves object reference to the HTML definition.
-     * @param $raw Return a copy that has not been setup yet. Must be
-     *             called before it's been setup, otherwise won't work.
-     */
-    public function getHTMLDefinition($raw = false) {
-        return $this->getDefinition('HTML', $raw);
-    }
-
-    /**
-     * Retrieves object reference to the CSS definition
-     * @param $raw Return a copy that has not been setup yet. Must be
-     *             called before it's been setup, otherwise won't work.
-     */
-    public function getCSSDefinition($raw = false) {
-        return $this->getDefinition('CSS', $raw);
-    }
-
-    /**
-     * Retrieves a definition
-     * @param $type Type of definition: HTML, CSS, etc
-     * @param $raw  Whether or not definition should be returned raw
-     */
-    public function getDefinition($type, $raw = false) {
-        if (!$this->finalized) $this->autoFinalize();
-        // temporarily suspend locks, so we can handle recursive definition calls
-        $lock = $this->lock;
-        $this->lock = null;
-        $factory = HTMLPurifier_DefinitionCacheFactory::instance();
-        $cache = $factory->create($type, $this);
-        $this->lock = $lock;
-        if (!$raw) {
-            // see if we can quickly supply a definition
-            if (!empty($this->definitions[$type])) {
-                if (!$this->definitions[$type]->setup) {
-                    $this->definitions[$type]->setup($this);
-                    $cache->set($this->definitions[$type], $this);
-                }
-                return $this->definitions[$type];
-            }
-            // memory check missed, try cache
-            $this->definitions[$type] = $cache->get($this);
-            if ($this->definitions[$type]) {
-                // definition in cache, return it
-                return $this->definitions[$type];
-            }
-        } elseif (
-            !empty($this->definitions[$type]) &&
-            !$this->definitions[$type]->setup
-        ) {
-            // raw requested, raw in memory, quick return
-            return $this->definitions[$type];
-        }
-        // quick checks failed, let's create the object
-        if ($type == 'HTML') {
-            $this->definitions[$type] = new HTMLPurifier_HTMLDefinition();
-        } elseif ($type == 'CSS') {
-            $this->definitions[$type] = new HTMLPurifier_CSSDefinition();
-        } elseif ($type == 'URI') {
-            $this->definitions[$type] = new HTMLPurifier_URIDefinition();
-        } else {
-            throw new HTMLPurifier_Exception("Definition of $type type not supported");
-        }
-        // quick abort if raw
-        if ($raw) {
-            if (is_null($this->get($type . '.DefinitionID'))) {
-                // fatally error out if definition ID not set
-                throw new HTMLPurifier_Exception("Cannot retrieve raw version without specifying %$type.DefinitionID");
-            }
-            return $this->definitions[$type];
-        }
-        // set it up
-        $this->lock = $type;
-        $this->definitions[$type]->setup($this);
-        $this->lock = null;
-        // save in cache
-        $cache->set($this->definitions[$type], $this);
-        return $this->definitions[$type];
-    }
-
-    /**
-     * Loads configuration values from an array with the following structure:
-     * Namespace.Directive => Value
-     * @param $config_array Configuration associative array
-     */
-    public function loadArray($config_array) {
-        if ($this->isFinalized('Cannot load directives after finalization')) return;
-        foreach ($config_array as $key => $value) {
-            $key = str_replace('_', '.', $key);
-            if (strpos($key, '.') !== false) {
-                $this->set($key, $value);
-            } else {
-                $namespace = $key;
-                $namespace_values = $value;
-                foreach ($namespace_values as $directive => $value) {
-                    $this->set($namespace .'.'. $directive, $value);
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns a list of array(namespace, directive) for all directives
-     * that are allowed in a web-form context as per an allowed
-     * namespaces/directives list.
-     * @param $allowed List of allowed namespaces/directives
-     */
-    public static function getAllowedDirectivesForForm($allowed, $schema = null) {
-        if (!$schema) {
-            $schema = HTMLPurifier_ConfigSchema::instance();
-        }
-        if ($allowed !== true) {
-             if (is_string($allowed)) $allowed = array($allowed);
-             $allowed_ns = array();
-             $allowed_directives = array();
-             $blacklisted_directives = array();
-             foreach ($allowed as $ns_or_directive) {
-                 if (strpos($ns_or_directive, '.') !== false) {
-                     // directive
-                     if ($ns_or_directive[0] == '-') {
-                         $blacklisted_directives[substr($ns_or_directive, 1)] = true;
-                     } else {
-                         $allowed_directives[$ns_or_directive] = true;
-                     }
-                 } else {
-                     // namespace
-                     $allowed_ns[$ns_or_directive] = true;
-                 }
-             }
-        }
-        $ret = array();
-        foreach ($schema->info as $key => $def) {
-            list($ns, $directive) = explode('.', $key, 2);
-            if ($allowed !== true) {
-                if (isset($blacklisted_directives["$ns.$directive"])) continue;
-                if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) continue;
-            }
-            if (isset($def->isAlias)) continue;
-            if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') continue;
-            $ret[] = array($ns, $directive);
-        }
-        return $ret;
-    }
-
-    /**
-     * Loads configuration values from $_GET/$_POST that were posted
-     * via ConfigForm
-     * @param $array $_GET or $_POST array to import
-     * @param $index Index/name that the config variables are in
-     * @param $allowed List of allowed namespaces/directives
-     * @param $mq_fix Boolean whether or not to enable magic quotes fix
-     * @param $schema Instance of HTMLPurifier_ConfigSchema to use, if not global copy
-     */
-    public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) {
-        $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $schema);
-        $config = HTMLPurifier_Config::create($ret, $schema);
-        return $config;
-    }
-
-    /**
-     * Merges in configuration values from $_GET/$_POST to object. NOT STATIC.
-     * @note Same parameters as loadArrayFromForm
-     */
-    public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true) {
-         $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $this->def);
-         $this->loadArray($ret);
-    }
-
-    /**
-     * Prepares an array from a form into something usable for the more
-     * strict parts of HTMLPurifier_Config
-     */
-    public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) {
-        if ($index !== false) $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array();
-        $mq = $mq_fix && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc();
-
-        $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $schema);
-        $ret = array();
-        foreach ($allowed as $key) {
-            list($ns, $directive) = $key;
-            $skey = "$ns.$directive";
-            if (!empty($array["Null_$skey"])) {
-                $ret[$ns][$directive] = null;
-                continue;
-            }
-            if (!isset($array[$skey])) continue;
-            $value = $mq ? stripslashes($array[$skey]) : $array[$skey];
-            $ret[$ns][$directive] = $value;
-        }
-        return $ret;
-    }
-
-    /**
-     * Loads configuration values from an ini file
-     * @param $filename Name of ini file
-     */
-    public function loadIni($filename) {
-        if ($this->isFinalized('Cannot load directives after finalization')) return;
-        $array = parse_ini_file($filename, true);
-        $this->loadArray($array);
-    }
-
-    /**
-     * Checks whether or not the configuration object is finalized.
-     * @param $error String error message, or false for no error
-     */
-    public function isFinalized($error = false) {
-        if ($this->finalized && $error) {
-            $this->triggerError($error, E_USER_ERROR);
-        }
-        return $this->finalized;
-    }
-
-    /**
-     * Finalizes configuration only if auto finalize is on and not
-     * already finalized
-     */
-    public function autoFinalize() {
-        if ($this->autoFinalize) {
-            $this->finalize();
-        } else {
-            $this->plist->squash(true);
-        }
-    }
-
-    /**
-     * Finalizes a configuration object, prohibiting further change
-     */
-    public function finalize() {
-        $this->finalized = true;
-        unset($this->parser);
-    }
-
-    /**
-     * Produces a nicely formatted error message by supplying the
-     * stack frame information from two levels up and OUTSIDE of
-     * HTMLPurifier_Config.
-     */
-    protected function triggerError($msg, $no) {
-        // determine previous stack frame
-        $backtrace = debug_backtrace();
-        if ($this->chatty && isset($backtrace[1])) {
-            $frame = $backtrace[1];
-            $extra = " on line {$frame['line']} in file {$frame['file']}";
-        } else {
-            $extra = '';
-        }
-        trigger_error($msg . $extra, $no);
-    }
-
-    /**
-     * Returns a serialized form of the configuration object that can
-     * be reconstituted.
-     */
-    public function serialize() {
-        $this->getDefinition('HTML');
-        $this->getDefinition('CSS');
-        $this->getDefinition('URI');
-        return serialize($this);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema.php b/library/HTMLPurifier/ConfigSchema.php
deleted file mode 100644 (file)
index 67be5c7..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-<?php
-
-/**
- * Configuration definition, defines directives and their defaults.
- */
-class HTMLPurifier_ConfigSchema {
-
-    /**
-     * Defaults of the directives and namespaces.
-     * @note This shares the exact same structure as HTMLPurifier_Config::$conf
-     */
-    public $defaults = array();
-
-    /**
-     * The default property list. Do not edit this property list.
-     */
-    public $defaultPlist;
-
-    /**
-     * Definition of the directives. The structure of this is:
-     *
-     *  array(
-     *      'Namespace' => array(
-     *          'Directive' => new stdclass(),
-     *      )
-     *  )
-     *
-     * The stdclass may have the following properties:
-     *
-     *  - If isAlias isn't set:
-     *      - type: Integer type of directive, see HTMLPurifier_VarParser for definitions
-     *      - allow_null: If set, this directive allows null values
-     *      - aliases: If set, an associative array of value aliases to real values
-     *      - allowed: If set, a lookup array of allowed (string) values
-     *  - If isAlias is set:
-     *      - namespace: Namespace this directive aliases to
-     *      - name: Directive name this directive aliases to
-     *
-     * In certain degenerate cases, stdclass will actually be an integer. In
-     * that case, the value is equivalent to an stdclass with the type
-     * property set to the integer. If the integer is negative, type is
-     * equal to the absolute value of integer, and allow_null is true.
-     *
-     * This class is friendly with HTMLPurifier_Config. If you need introspection
-     * about the schema, you're better of using the ConfigSchema_Interchange,
-     * which uses more memory but has much richer information.
-     */
-    public $info = array();
-
-    /**
-     * Application-wide singleton
-     */
-    static protected $singleton;
-
-    public function __construct() {
-        $this->defaultPlist = new HTMLPurifier_PropertyList();
-    }
-
-    /**
-     * Unserializes the default ConfigSchema.
-     */
-    public static function makeFromSerial() {
-        return unserialize(file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema.ser'));
-    }
-
-    /**
-     * Retrieves an instance of the application-wide configuration definition.
-     */
-    public static function instance($prototype = null) {
-        if ($prototype !== null) {
-            HTMLPurifier_ConfigSchema::$singleton = $prototype;
-        } elseif (HTMLPurifier_ConfigSchema::$singleton === null || $prototype === true) {
-            HTMLPurifier_ConfigSchema::$singleton = HTMLPurifier_ConfigSchema::makeFromSerial();
-        }
-        return HTMLPurifier_ConfigSchema::$singleton;
-    }
-
-    /**
-     * Defines a directive for configuration
-     * @warning Will fail of directive's namespace is defined.
-     * @warning This method's signature is slightly different from the legacy
-     *          define() static method! Beware!
-     * @param $namespace Namespace the directive is in
-     * @param $name Key of directive
-     * @param $default Default value of directive
-     * @param $type Allowed type of the directive. See
-     *      HTMLPurifier_DirectiveDef::$type for allowed values
-     * @param $allow_null Whether or not to allow null values
-     */
-    public function add($key, $default, $type, $allow_null) {
-        $obj = new stdclass();
-        $obj->type = is_int($type) ? $type : HTMLPurifier_VarParser::$types[$type];
-        if ($allow_null) $obj->allow_null = true;
-        $this->info[$key] = $obj;
-        $this->defaults[$key] = $default;
-        $this->defaultPlist->set($key, $default);
-    }
-
-    /**
-     * Defines a directive value alias.
-     *
-     * Directive value aliases are convenient for developers because it lets
-     * them set a directive to several values and get the same result.
-     * @param $namespace Directive's namespace
-     * @param $name Name of Directive
-     * @param $aliases Hash of aliased values to the real alias
-     */
-    public function addValueAliases($key, $aliases) {
-        if (!isset($this->info[$key]->aliases)) {
-            $this->info[$key]->aliases = array();
-        }
-        foreach ($aliases as $alias => $real) {
-            $this->info[$key]->aliases[$alias] = $real;
-        }
-    }
-
-    /**
-     * Defines a set of allowed values for a directive.
-     * @warning This is slightly different from the corresponding static
-     *          method definition.
-     * @param $namespace Namespace of directive
-     * @param $name Name of directive
-     * @param $allowed Lookup array of allowed values
-     */
-    public function addAllowedValues($key, $allowed) {
-        $this->info[$key]->allowed = $allowed;
-    }
-
-    /**
-     * Defines a directive alias for backwards compatibility
-     * @param $namespace
-     * @param $name Directive that will be aliased
-     * @param $new_namespace
-     * @param $new_name Directive that the alias will be to
-     */
-    public function addAlias($key, $new_key) {
-        $obj = new stdclass;
-        $obj->key = $new_key;
-        $obj->isAlias = true;
-        $this->info[$key] = $obj;
-    }
-
-    /**
-     * Replaces any stdclass that only has the type property with type integer.
-     */
-    public function postProcess() {
-        foreach ($this->info as $key => $v) {
-            if (count((array) $v) == 1) {
-                $this->info[$key] = $v->type;
-            } elseif (count((array) $v) == 2 && isset($v->allow_null)) {
-                $this->info[$key] = -$v->type;
-            }
-        }
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php b/library/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php
deleted file mode 100644 (file)
index c05668a..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
- * Converts HTMLPurifier_ConfigSchema_Interchange to our runtime
- * representation used to perform checks on user configuration.
- */
-class HTMLPurifier_ConfigSchema_Builder_ConfigSchema
-{
-
-    public function build($interchange) {
-        $schema = new HTMLPurifier_ConfigSchema();
-        foreach ($interchange->directives as $d) {
-            $schema->add(
-                $d->id->key,
-                $d->default,
-                $d->type,
-                $d->typeAllowsNull
-            );
-            if ($d->allowed !== null) {
-                $schema->addAllowedValues(
-                    $d->id->key,
-                    $d->allowed
-                );
-            }
-            foreach ($d->aliases as $alias) {
-                $schema->addAlias(
-                    $alias->key,
-                    $d->id->key
-                );
-            }
-            if ($d->valueAliases !== null) {
-                $schema->addValueAliases(
-                    $d->id->key,
-                    $d->valueAliases
-                );
-            }
-        }
-        $schema->postProcess();
-        return $schema;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/Builder/Xml.php b/library/HTMLPurifier/ConfigSchema/Builder/Xml.php
deleted file mode 100644 (file)
index 244561a..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-<?php
-
-/**
- * Converts HTMLPurifier_ConfigSchema_Interchange to an XML format,
- * which can be further processed to generate documentation.
- */
-class HTMLPurifier_ConfigSchema_Builder_Xml extends XMLWriter
-{
-
-    protected $interchange;
-    private $namespace;
-
-    protected function writeHTMLDiv($html) {
-        $this->startElement('div');
-
-        $purifier = HTMLPurifier::getInstance();
-        $html = $purifier->purify($html);
-        $this->writeAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
-        $this->writeRaw($html);
-
-        $this->endElement(); // div
-    }
-
-    protected function export($var) {
-        if ($var === array()) return 'array()';
-        return var_export($var, true);
-    }
-
-    public function build($interchange) {
-        // global access, only use as last resort
-        $this->interchange = $interchange;
-
-        $this->setIndent(true);
-        $this->startDocument('1.0', 'UTF-8');
-        $this->startElement('configdoc');
-        $this->writeElement('title', $interchange->name);
-
-        foreach ($interchange->directives as $directive) {
-            $this->buildDirective($directive);
-        }
-
-        if ($this->namespace) $this->endElement(); // namespace
-
-        $this->endElement(); // configdoc
-        $this->flush();
-    }
-
-    public function buildDirective($directive) {
-
-        // Kludge, although I suppose having a notion of a "root namespace"
-        // certainly makes things look nicer when documentation is built.
-        // Depends on things being sorted.
-        if (!$this->namespace || $this->namespace !== $directive->id->getRootNamespace()) {
-            if ($this->namespace) $this->endElement(); // namespace
-            $this->namespace = $directive->id->getRootNamespace();
-            $this->startElement('namespace');
-            $this->writeAttribute('id', $this->namespace);
-            $this->writeElement('name', $this->namespace);
-        }
-
-        $this->startElement('directive');
-        $this->writeAttribute('id', $directive->id->toString());
-
-        $this->writeElement('name', $directive->id->getDirective());
-
-        $this->startElement('aliases');
-            foreach ($directive->aliases as $alias) $this->writeElement('alias', $alias->toString());
-        $this->endElement(); // aliases
-
-        $this->startElement('constraints');
-            if ($directive->version) $this->writeElement('version', $directive->version);
-            $this->startElement('type');
-                if ($directive->typeAllowsNull) $this->writeAttribute('allow-null', 'yes');
-                $this->text($directive->type);
-            $this->endElement(); // type
-            if ($directive->allowed) {
-                $this->startElement('allowed');
-                    foreach ($directive->allowed as $value => $x) $this->writeElement('value', $value);
-                $this->endElement(); // allowed
-            }
-            $this->writeElement('default', $this->export($directive->default));
-            $this->writeAttribute('xml:space', 'preserve');
-            if ($directive->external) {
-                $this->startElement('external');
-                    foreach ($directive->external as $project) $this->writeElement('project', $project);
-                $this->endElement();
-            }
-        $this->endElement(); // constraints
-
-        if ($directive->deprecatedVersion) {
-            $this->startElement('deprecated');
-                $this->writeElement('version', $directive->deprecatedVersion);
-                $this->writeElement('use', $directive->deprecatedUse->toString());
-            $this->endElement(); // deprecated
-        }
-
-        $this->startElement('description');
-            $this->writeHTMLDiv($directive->description);
-        $this->endElement(); // description
-
-        $this->endElement(); // directive
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/Exception.php b/library/HTMLPurifier/ConfigSchema/Exception.php
deleted file mode 100644 (file)
index 2671516..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-
-/**
- * Exceptions related to configuration schema
- */
-class HTMLPurifier_ConfigSchema_Exception extends HTMLPurifier_Exception
-{
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/Interchange.php b/library/HTMLPurifier/ConfigSchema/Interchange.php
deleted file mode 100644 (file)
index 91a5aa7..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-
-/**
- * Generic schema interchange format that can be converted to a runtime
- * representation (HTMLPurifier_ConfigSchema) or HTML documentation. Members
- * are completely validated.
- */
-class HTMLPurifier_ConfigSchema_Interchange
-{
-
-    /**
-     * Name of the application this schema is describing.
-     */
-    public $name;
-
-    /**
-     * Array of Directive ID => array(directive info)
-     */
-    public $directives = array();
-
-    /**
-     * Adds a directive array to $directives
-     */
-    public function addDirective($directive) {
-        if (isset($this->directives[$i = $directive->id->toString()])) {
-            throw new HTMLPurifier_ConfigSchema_Exception("Cannot redefine directive '$i'");
-        }
-        $this->directives[$i] = $directive;
-    }
-
-    /**
-     * Convenience function to perform standard validation. Throws exception
-     * on failed validation.
-     */
-    public function validate() {
-        $validator = new HTMLPurifier_ConfigSchema_Validator();
-        return $validator->validate($this);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/Interchange/Directive.php b/library/HTMLPurifier/ConfigSchema/Interchange/Directive.php
deleted file mode 100644 (file)
index ac8be0d..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-/**
- * Interchange component class describing configuration directives.
- */
-class HTMLPurifier_ConfigSchema_Interchange_Directive
-{
-
-    /**
-     * ID of directive, instance of HTMLPurifier_ConfigSchema_Interchange_Id.
-     */
-    public $id;
-
-    /**
-     * String type, e.g. 'integer' or 'istring'.
-     */
-    public $type;
-
-    /**
-     * Default value, e.g. 3 or 'DefaultVal'.
-     */
-    public $default;
-
-    /**
-     * HTML description.
-     */
-    public $description;
-
-    /**
-     * Boolean whether or not null is allowed as a value.
-     */
-    public $typeAllowsNull = false;
-
-    /**
-     * Lookup table of allowed scalar values, e.g. array('allowed' => true).
-     * Null if all values are allowed.
-     */
-    public $allowed;
-
-    /**
-     * List of aliases for the directive,
-     * e.g. array(new HTMLPurifier_ConfigSchema_Interchange_Id('Ns', 'Dir'))).
-     */
-    public $aliases = array();
-
-    /**
-     * Hash of value aliases, e.g. array('alt' => 'real'). Null if value
-     * aliasing is disabled (necessary for non-scalar types).
-     */
-    public $valueAliases;
-
-    /**
-     * Version of HTML Purifier the directive was introduced, e.g. '1.3.1'.
-     * Null if the directive has always existed.
-     */
-    public $version;
-
-    /**
-     * ID of directive that supercedes this old directive, is an instance
-     * of HTMLPurifier_ConfigSchema_Interchange_Id. Null if not deprecated.
-     */
-    public $deprecatedUse;
-
-    /**
-     * Version of HTML Purifier this directive was deprecated. Null if not
-     * deprecated.
-     */
-    public $deprecatedVersion;
-
-    /**
-     * List of external projects this directive depends on, e.g. array('CSSTidy').
-     */
-    public $external = array();
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/Interchange/Id.php b/library/HTMLPurifier/ConfigSchema/Interchange/Id.php
deleted file mode 100644 (file)
index b9b3c6f..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-
-/**
- * Represents a directive ID in the interchange format.
- */
-class HTMLPurifier_ConfigSchema_Interchange_Id
-{
-
-    public $key;
-
-    public function __construct($key) {
-        $this->key = $key;
-    }
-
-    /**
-     * @warning This is NOT magic, to ensure that people don't abuse SPL and
-     *          cause problems for PHP 5.0 support.
-     */
-    public function toString() {
-        return $this->key;
-    }
-
-    public function getRootNamespace() {
-        return substr($this->key, 0, strpos($this->key, "."));
-    }
-
-    public function getDirective() {
-        return substr($this->key, strpos($this->key, ".") + 1);
-    }
-
-    public static function make($id) {
-        return new HTMLPurifier_ConfigSchema_Interchange_Id($id);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php b/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php
deleted file mode 100644 (file)
index 785b72c..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-<?php
-
-class HTMLPurifier_ConfigSchema_InterchangeBuilder
-{
-
-    /**
-     * Used for processing DEFAULT, nothing else.
-     */
-    protected $varParser;
-
-    public function __construct($varParser = null) {
-        $this->varParser = $varParser ? $varParser : new HTMLPurifier_VarParser_Native();
-    }
-
-    public static function buildFromDirectory($dir = null) {
-        $builder     = new HTMLPurifier_ConfigSchema_InterchangeBuilder();
-        $interchange = new HTMLPurifier_ConfigSchema_Interchange();
-        return $builder->buildDir($interchange, $dir);
-    }
-
-    public function buildDir($interchange, $dir = null) {
-        if (!$dir) $dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema';
-        if (file_exists($dir . '/info.ini')) {
-            $info = parse_ini_file($dir . '/info.ini');
-            $interchange->name = $info['name'];
-        }
-
-        $files = array();
-        $dh = opendir($dir);
-        while (false !== ($file = readdir($dh))) {
-            if (!$file || $file[0] == '.' || strrchr($file, '.') !== '.txt') {
-                continue;
-            }
-            $files[] = $file;
-        }
-        closedir($dh);
-
-        sort($files);
-        foreach ($files as $file) {
-            $this->buildFile($interchange, $dir . '/' . $file);
-        }
-
-        return $interchange;
-    }
-
-    public function buildFile($interchange, $file) {
-        $parser = new HTMLPurifier_StringHashParser();
-        $this->build(
-            $interchange,
-            new HTMLPurifier_StringHash( $parser->parseFile($file) )
-        );
-    }
-
-    /**
-     * Builds an interchange object based on a hash.
-     * @param $interchange HTMLPurifier_ConfigSchema_Interchange object to build
-     * @param $hash HTMLPurifier_ConfigSchema_StringHash source data
-     */
-    public function build($interchange, $hash) {
-        if (!$hash instanceof HTMLPurifier_StringHash) {
-            $hash = new HTMLPurifier_StringHash($hash);
-        }
-        if (!isset($hash['ID'])) {
-            throw new HTMLPurifier_ConfigSchema_Exception('Hash does not have any ID');
-        }
-        if (strpos($hash['ID'], '.') === false) {
-            if (count($hash) == 2 && isset($hash['DESCRIPTION'])) {
-                $hash->offsetGet('DESCRIPTION'); // prevent complaining
-            } else {
-                throw new HTMLPurifier_ConfigSchema_Exception('All directives must have a namespace');
-            }
-        } else {
-            $this->buildDirective($interchange, $hash);
-        }
-        $this->_findUnused($hash);
-    }
-
-    public function buildDirective($interchange, $hash) {
-        $directive = new HTMLPurifier_ConfigSchema_Interchange_Directive();
-
-        // These are required elements:
-        $directive->id = $this->id($hash->offsetGet('ID'));
-        $id = $directive->id->toString(); // convenience
-
-        if (isset($hash['TYPE'])) {
-            $type = explode('/', $hash->offsetGet('TYPE'));
-            if (isset($type[1])) $directive->typeAllowsNull = true;
-            $directive->type = $type[0];
-        } else {
-            throw new HTMLPurifier_ConfigSchema_Exception("TYPE in directive hash '$id' not defined");
-        }
-
-        if (isset($hash['DEFAULT'])) {
-            try {
-                $directive->default = $this->varParser->parse($hash->offsetGet('DEFAULT'), $directive->type, $directive->typeAllowsNull);
-            } catch (HTMLPurifier_VarParserException $e) {
-                throw new HTMLPurifier_ConfigSchema_Exception($e->getMessage() . " in DEFAULT in directive hash '$id'");
-            }
-        }
-
-        if (isset($hash['DESCRIPTION'])) {
-            $directive->description = $hash->offsetGet('DESCRIPTION');
-        }
-
-        if (isset($hash['ALLOWED'])) {
-            $directive->allowed = $this->lookup($this->evalArray($hash->offsetGet('ALLOWED')));
-        }
-
-        if (isset($hash['VALUE-ALIASES'])) {
-            $directive->valueAliases = $this->evalArray($hash->offsetGet('VALUE-ALIASES'));
-        }
-
-        if (isset($hash['ALIASES'])) {
-            $raw_aliases = trim($hash->offsetGet('ALIASES'));
-            $aliases = preg_split('/\s*,\s*/', $raw_aliases);
-            foreach ($aliases as $alias) {
-                $directive->aliases[] = $this->id($alias);
-            }
-        }
-
-        if (isset($hash['VERSION'])) {
-            $directive->version = $hash->offsetGet('VERSION');
-        }
-
-        if (isset($hash['DEPRECATED-USE'])) {
-            $directive->deprecatedUse = $this->id($hash->offsetGet('DEPRECATED-USE'));
-        }
-
-        if (isset($hash['DEPRECATED-VERSION'])) {
-            $directive->deprecatedVersion = $hash->offsetGet('DEPRECATED-VERSION');
-        }
-
-        if (isset($hash['EXTERNAL'])) {
-            $directive->external = preg_split('/\s*,\s*/', trim($hash->offsetGet('EXTERNAL')));
-        }
-
-        $interchange->addDirective($directive);
-    }
-
-    /**
-     * Evaluates an array PHP code string without array() wrapper
-     */
-    protected function evalArray($contents) {
-        return eval('return array('. $contents .');');
-    }
-
-    /**
-     * Converts an array list into a lookup array.
-     */
-    protected function lookup($array) {
-        $ret = array();
-        foreach ($array as $val) $ret[$val] = true;
-        return $ret;
-    }
-
-    /**
-     * Convenience function that creates an HTMLPurifier_ConfigSchema_Interchange_Id
-     * object based on a string Id.
-     */
-    protected function id($id) {
-        return HTMLPurifier_ConfigSchema_Interchange_Id::make($id);
-    }
-
-    /**
-     * Triggers errors for any unused keys passed in the hash; such keys
-     * may indicate typos, missing values, etc.
-     * @param $hash Instance of ConfigSchema_StringHash to check.
-     */
-    protected function _findUnused($hash) {
-        $accessed = $hash->getAccessed();
-        foreach ($hash as $k => $v) {
-            if (!isset($accessed[$k])) {
-                trigger_error("String hash key '$k' not used by builder", E_USER_NOTICE);
-            }
-        }
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/Validator.php b/library/HTMLPurifier/ConfigSchema/Validator.php
deleted file mode 100644 (file)
index f374f6a..0000000
+++ /dev/null
@@ -1,206 +0,0 @@
-<?php
-
-/**
- * Performs validations on HTMLPurifier_ConfigSchema_Interchange
- *
- * @note If you see '// handled by InterchangeBuilder', that means a
- *       design decision in that class would prevent this validation from
- *       ever being necessary. We have them anyway, however, for
- *       redundancy.
- */
-class HTMLPurifier_ConfigSchema_Validator
-{
-
-    /**
-     * Easy to access global objects.
-     */
-    protected $interchange, $aliases;
-
-    /**
-     * Context-stack to provide easy to read error messages.
-     */
-    protected $context = array();
-
-    /**
-     * HTMLPurifier_VarParser to test default's type.
-     */
-    protected $parser;
-
-    public function __construct() {
-        $this->parser = new HTMLPurifier_VarParser();
-    }
-
-    /**
-     * Validates a fully-formed interchange object. Throws an
-     * HTMLPurifier_ConfigSchema_Exception if there's a problem.
-     */
-    public function validate($interchange) {
-        $this->interchange = $interchange;
-        $this->aliases = array();
-        // PHP is a bit lax with integer <=> string conversions in
-        // arrays, so we don't use the identical !== comparison
-        foreach ($interchange->directives as $i => $directive) {
-            $id = $directive->id->toString();
-            if ($i != $id) $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'");
-            $this->validateDirective($directive);
-        }
-        return true;
-    }
-
-    /**
-     * Validates a HTMLPurifier_ConfigSchema_Interchange_Id object.
-     */
-    public function validateId($id) {
-        $id_string = $id->toString();
-        $this->context[] = "id '$id_string'";
-        if (!$id instanceof HTMLPurifier_ConfigSchema_Interchange_Id) {
-            // handled by InterchangeBuilder
-            $this->error(false, 'is not an instance of HTMLPurifier_ConfigSchema_Interchange_Id');
-        }
-        // keys are now unconstrained (we might want to narrow down to A-Za-z0-9.)
-        // we probably should check that it has at least one namespace
-        $this->with($id, 'key')
-            ->assertNotEmpty()
-            ->assertIsString(); // implicit assertIsString handled by InterchangeBuilder
-        array_pop($this->context);
-    }
-
-    /**
-     * Validates a HTMLPurifier_ConfigSchema_Interchange_Directive object.
-     */
-    public function validateDirective($d) {
-        $id = $d->id->toString();
-        $this->context[] = "directive '$id'";
-        $this->validateId($d->id);
-
-        $this->with($d, 'description')
-            ->assertNotEmpty();
-
-        // BEGIN - handled by InterchangeBuilder
-        $this->with($d, 'type')
-            ->assertNotEmpty();
-        $this->with($d, 'typeAllowsNull')
-            ->assertIsBool();
-        try {
-            // This also tests validity of $d->type
-            $this->parser->parse($d->default, $d->type, $d->typeAllowsNull);
-        } catch (HTMLPurifier_VarParserException $e) {
-            $this->error('default', 'had error: ' . $e->getMessage());
-        }
-        // END - handled by InterchangeBuilder
-
-        if (!is_null($d->allowed) || !empty($d->valueAliases)) {
-            // allowed and valueAliases require that we be dealing with
-            // strings, so check for that early.
-            $d_int = HTMLPurifier_VarParser::$types[$d->type];
-            if (!isset(HTMLPurifier_VarParser::$stringTypes[$d_int])) {
-                $this->error('type', 'must be a string type when used with allowed or value aliases');
-            }
-        }
-
-        $this->validateDirectiveAllowed($d);
-        $this->validateDirectiveValueAliases($d);
-        $this->validateDirectiveAliases($d);
-
-        array_pop($this->context);
-    }
-
-    /**
-     * Extra validation if $allowed member variable of
-     * HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
-     */
-    public function validateDirectiveAllowed($d) {
-        if (is_null($d->allowed)) return;
-        $this->with($d, 'allowed')
-            ->assertNotEmpty()
-            ->assertIsLookup(); // handled by InterchangeBuilder
-        if (is_string($d->default) && !isset($d->allowed[$d->default])) {
-            $this->error('default', 'must be an allowed value');
-        }
-        $this->context[] = 'allowed';
-        foreach ($d->allowed as $val => $x) {
-            if (!is_string($val)) $this->error("value $val", 'must be a string');
-        }
-        array_pop($this->context);
-    }
-
-    /**
-     * Extra validation if $valueAliases member variable of
-     * HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
-     */
-    public function validateDirectiveValueAliases($d) {
-        if (is_null($d->valueAliases)) return;
-        $this->with($d, 'valueAliases')
-            ->assertIsArray(); // handled by InterchangeBuilder
-        $this->context[] = 'valueAliases';
-        foreach ($d->valueAliases as $alias => $real) {
-            if (!is_string($alias)) $this->error("alias $alias", 'must be a string');
-            if (!is_string($real))  $this->error("alias target $real from alias '$alias'",  'must be a string');
-            if ($alias === $real) {
-                $this->error("alias '$alias'", "must not be an alias to itself");
-            }
-        }
-        if (!is_null($d->allowed)) {
-            foreach ($d->valueAliases as $alias => $real) {
-                if (isset($d->allowed[$alias])) {
-                    $this->error("alias '$alias'", 'must not be an allowed value');
-                } elseif (!isset($d->allowed[$real])) {
-                    $this->error("alias '$alias'", 'must be an alias to an allowed value');
-                }
-            }
-        }
-        array_pop($this->context);
-    }
-
-    /**
-     * Extra validation if $aliases member variable of
-     * HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
-     */
-    public function validateDirectiveAliases($d) {
-        $this->with($d, 'aliases')
-            ->assertIsArray(); // handled by InterchangeBuilder
-        $this->context[] = 'aliases';
-        foreach ($d->aliases as $alias) {
-            $this->validateId($alias);
-            $s = $alias->toString();
-            if (isset($this->interchange->directives[$s])) {
-                $this->error("alias '$s'", 'collides with another directive');
-            }
-            if (isset($this->aliases[$s])) {
-                $other_directive = $this->aliases[$s];
-                $this->error("alias '$s'", "collides with alias for directive '$other_directive'");
-            }
-            $this->aliases[$s] = $d->id->toString();
-        }
-        array_pop($this->context);
-    }
-
-    // protected helper functions
-
-    /**
-     * Convenience function for generating HTMLPurifier_ConfigSchema_ValidatorAtom
-     * for validating simple member variables of objects.
-     */
-    protected function with($obj, $member) {
-        return new HTMLPurifier_ConfigSchema_ValidatorAtom($this->getFormattedContext(), $obj, $member);
-    }
-
-    /**
-     * Emits an error, providing helpful context.
-     */
-    protected function error($target, $msg) {
-        if ($target !== false) $prefix = ucfirst($target) . ' in ' .  $this->getFormattedContext();
-        else $prefix = ucfirst($this->getFormattedContext());
-        throw new HTMLPurifier_ConfigSchema_Exception(trim($prefix . ' ' . $msg));
-    }
-
-    /**
-     * Returns a formatted context string.
-     */
-    protected function getFormattedContext() {
-        return implode(' in ', array_reverse($this->context));
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php b/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php
deleted file mode 100644 (file)
index b95aea1..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-
-/**
- * Fluent interface for validating the contents of member variables.
- * This should be immutable. See HTMLPurifier_ConfigSchema_Validator for
- * use-cases. We name this an 'atom' because it's ONLY for validations that
- * are independent and usually scalar.
- */
-class HTMLPurifier_ConfigSchema_ValidatorAtom
-{
-
-    protected $context, $obj, $member, $contents;
-
-    public function __construct($context, $obj, $member) {
-        $this->context     = $context;
-        $this->obj         = $obj;
-        $this->member      = $member;
-        $this->contents    =& $obj->$member;
-    }
-
-    public function assertIsString() {
-        if (!is_string($this->contents)) $this->error('must be a string');
-        return $this;
-    }
-
-    public function assertIsBool() {
-        if (!is_bool($this->contents)) $this->error('must be a boolean');
-        return $this;
-    }
-
-    public function assertIsArray() {
-        if (!is_array($this->contents)) $this->error('must be an array');
-        return $this;
-    }
-
-    public function assertNotNull() {
-        if ($this->contents === null) $this->error('must not be null');
-        return $this;
-    }
-
-    public function assertAlnum() {
-        $this->assertIsString();
-        if (!ctype_alnum($this->contents)) $this->error('must be alphanumeric');
-        return $this;
-    }
-
-    public function assertNotEmpty() {
-        if (empty($this->contents)) $this->error('must not be empty');
-        return $this;
-    }
-
-    public function assertIsLookup() {
-        $this->assertIsArray();
-        foreach ($this->contents as $v) {
-            if ($v !== true) $this->error('must be a lookup array');
-        }
-        return $this;
-    }
-
-    protected function error($msg) {
-        throw new HTMLPurifier_ConfigSchema_Exception(ucfirst($this->member) . ' in ' . $this->context . ' ' . $msg);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema.ser b/library/HTMLPurifier/ConfigSchema/schema.ser
deleted file mode 100644 (file)
index 22b8d54..0000000
Binary files a/library/HTMLPurifier/ConfigSchema/schema.ser and /dev/null differ
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt b/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt
deleted file mode 100644 (file)
index 0517fed..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-Attr.AllowedClasses
-TYPE: lookup/null
-VERSION: 4.0.0
-DEFAULT: null
---DESCRIPTION--
-List of allowed class values in the class attribute. By default, this is null,
-which means all classes are allowed.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt b/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt
deleted file mode 100644 (file)
index 249edd6..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-Attr.AllowedFrameTargets
-TYPE: lookup
-DEFAULT: array()
---DESCRIPTION--
-Lookup table of all allowed link frame targets.  Some commonly used link
-targets include _blank, _self, _parent and _top. Values should be
-lowercase, as validation will be done in a case-sensitive manner despite
-W3C's recommendation. XHTML 1.0 Strict does not permit the target attribute
-so this directive will have no effect in that doctype. XHTML 1.1 does not
-enable the Target module by default, you will have to manually enable it
-(see the module documentation for more details.)
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt b/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt
deleted file mode 100644 (file)
index 9a8fa6a..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-Attr.AllowedRel
-TYPE: lookup
-VERSION: 1.6.0
-DEFAULT: array()
---DESCRIPTION--
-List of allowed forward document relationships in the rel attribute. Common
-values may be nofollow or print. By default, this is empty, meaning that no
-document relationships are allowed.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt b/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt
deleted file mode 100644 (file)
index b017883..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-Attr.AllowedRev
-TYPE: lookup
-VERSION: 1.6.0
-DEFAULT: array()
---DESCRIPTION--
-List of allowed reverse document relationships in the rev attribute. This
-attribute is a bit of an edge-case; if you don't know what it is for, stay
-away.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt b/library/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt
deleted file mode 100644 (file)
index e774b82..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-Attr.ClassUseCDATA
-TYPE: bool/null
-DEFAULT: null
-VERSION: 4.0.0
---DESCRIPTION--
-If null, class will auto-detect the doctype and, if matching XHTML 1.1 or
-XHTML 2.0, will use the restrictive NMTOKENS specification of class. Otherwise,
-it will use a relaxed CDATA definition.  If true, the relaxed CDATA definition
-is forced; if false, the NMTOKENS definition is forced.  To get behavior
-of HTML Purifier prior to 4.0.0, set this directive to false.
-
-Some rational behind the auto-detection:
-in previous versions of HTML Purifier, it was assumed that the form of
-class was NMTOKENS, as specified by the XHTML Modularization (representing
-XHTML 1.1 and XHTML 2.0).  The DTDs for HTML 4.01 and XHTML 1.0, however
-specify class as CDATA.  HTML 5 effectively defines it as CDATA, but
-with the additional constraint that each name should be unique (this is not
-explicitly outlined in previous specifications).
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt b/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt
deleted file mode 100644 (file)
index 533165e..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-Attr.DefaultImageAlt
-TYPE: string/null
-DEFAULT: null
-VERSION: 3.2.0
---DESCRIPTION--
-This is the content of the alt tag of an image if the user had not
-previously specified an alt attribute.  This applies to all images without
-a valid alt attribute, as opposed to %Attr.DefaultInvalidImageAlt, which
-only applies to invalid images, and overrides in the case of an invalid image.
-Default behavior with null is to use the basename of the src tag for the alt.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt b/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt
deleted file mode 100644 (file)
index 9eb7e38..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-Attr.DefaultInvalidImage
-TYPE: string
-DEFAULT: ''
---DESCRIPTION--
-This is the default image an img tag will be pointed to if it does not have
-a valid src attribute.  In future versions, we may allow the image tag to
-be removed completely, but due to design issues, this is not possible right
-now.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt b/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt
deleted file mode 100644 (file)
index 2f17bf4..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-Attr.DefaultInvalidImageAlt
-TYPE: string
-DEFAULT: 'Invalid image'
---DESCRIPTION--
-This is the content of the alt tag of an invalid image if the user had not
-previously specified an alt attribute.  It has no effect when the image is
-valid but there was no alt attribute present.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt b/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt
deleted file mode 100644 (file)
index 52654b5..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-Attr.DefaultTextDir
-TYPE: string
-DEFAULT: 'ltr'
---DESCRIPTION--
-Defines the default text direction (ltr or rtl) of the document being
-parsed.  This generally is the same as the value of the dir attribute in
-HTML, or ltr if that is not specified.
---ALLOWED--
-'ltr', 'rtl'
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt b/library/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt
deleted file mode 100644 (file)
index 6440d21..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-Attr.EnableID
-TYPE: bool
-DEFAULT: false
-VERSION: 1.2.0
---DESCRIPTION--
-Allows the ID attribute in HTML.  This is disabled by default due to the
-fact that without proper configuration user input can easily break the
-validation of a webpage by specifying an ID that is already on the
-surrounding HTML.  If you don't mind throwing caution to the wind, enable
-this directive, but I strongly recommend you also consider blacklisting IDs
-you use (%Attr.IDBlacklist) or prefixing all user supplied IDs
-(%Attr.IDPrefix).  When set to true HTML Purifier reverts to the behavior of
-pre-1.2.0 versions.
---ALIASES--
-HTML.EnableAttrID
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt b/library/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt
deleted file mode 100644 (file)
index f31d226..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-Attr.ForbiddenClasses
-TYPE: lookup
-VERSION: 4.0.0
-DEFAULT: array()
---DESCRIPTION--
-List of forbidden class values in the class attribute. By default, this is
-empty, which means that no classes are forbidden. See also %Attr.AllowedClasses.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt b/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt
deleted file mode 100644 (file)
index 5f2b5e3..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-Attr.IDBlacklist
-TYPE: list
-DEFAULT: array()
-DESCRIPTION: Array of IDs not allowed in the document.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt b/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt
deleted file mode 100644 (file)
index 6f58245..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-Attr.IDBlacklistRegexp
-TYPE: string/null
-VERSION: 1.6.0
-DEFAULT: NULL
---DESCRIPTION--
-PCRE regular expression to be matched against all IDs. If the expression is
-matches, the ID is rejected. Use this with care: may cause significant
-degradation. ID matching is done after all other validation.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt b/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt
deleted file mode 100644 (file)
index cc49d43..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-Attr.IDPrefix
-TYPE: string
-VERSION: 1.2.0
-DEFAULT: ''
---DESCRIPTION--
-String to prefix to IDs.  If you have no idea what IDs your pages may use,
-you may opt to simply add a prefix to all user-submitted ID attributes so
-that they are still usable, but will not conflict with core page IDs.
-Example: setting the directive to 'user_' will result in a user submitted
-'foo' to become 'user_foo'  Be sure to set %HTML.EnableAttrID to true
-before using this.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt b/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt
deleted file mode 100644 (file)
index 2c5924a..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-Attr.IDPrefixLocal
-TYPE: string
-VERSION: 1.2.0
-DEFAULT: ''
---DESCRIPTION--
-Temporary prefix for IDs used in conjunction with %Attr.IDPrefix.  If you
-need to allow multiple sets of user content on web page, you may need to
-have a seperate prefix that changes with each iteration.  This way,
-seperately submitted user content displayed on the same page doesn't
-clobber each other. Ideal values are unique identifiers for the content it
-represents (i.e. the id of the row in the database). Be sure to add a
-seperator (like an underscore) at the end.  Warning: this directive will
-not work unless %Attr.IDPrefix is set to a non-empty value!
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt b/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt
deleted file mode 100644 (file)
index d5caa1b..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-AutoFormat.AutoParagraph
-TYPE: bool
-VERSION: 2.0.1
-DEFAULT: false
---DESCRIPTION--
-
-<p>
-  This directive turns on auto-paragraphing, where double newlines are
-  converted in to paragraphs whenever possible. Auto-paragraphing:
-</p>
-<ul>
-  <li>Always applies to inline elements or text in the root node,</li>
-  <li>Applies to inline elements or text with double newlines in nodes
-      that allow paragraph tags,</li>
-  <li>Applies to double newlines in paragraph tags</li>
-</ul>
-<p>
-  <code>p</code> tags must be allowed for this directive to take effect.
-  We do not use <code>br</code> tags for paragraphing, as that is
-  semantically incorrect.
-</p>
-<p>
-  To prevent auto-paragraphing as a content-producer, refrain from using
-  double-newlines except to specify a new paragraph or in contexts where
-  it has special meaning (whitespace usually has no meaning except in
-  tags like <code>pre</code>, so this should not be difficult.) To prevent
-  the paragraphing of inline text adjacent to block elements, wrap them
-  in <code>div</code> tags (the behavior is slightly different outside of
-  the root node.)
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt b/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt
deleted file mode 100644 (file)
index 2a47648..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-AutoFormat.Custom
-TYPE: list
-VERSION: 2.0.1
-DEFAULT: array()
---DESCRIPTION--
-
-<p>
-  This directive can be used to add custom auto-format injectors.
-  Specify an array of injector names (class name minus the prefix)
-  or concrete implementations. Injector class must exist.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt b/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt
deleted file mode 100644 (file)
index 663064a..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-AutoFormat.DisplayLinkURI
-TYPE: bool
-VERSION: 3.2.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-  This directive turns on the in-text display of URIs in &lt;a&gt; tags, and disables
-  those links. For example, <a href="http://example.com">example</a> becomes
-  example (<a>http://example.com</a>).
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt b/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt
deleted file mode 100644 (file)
index 3a48ba9..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-AutoFormat.Linkify
-TYPE: bool
-VERSION: 2.0.1
-DEFAULT: false
---DESCRIPTION--
-
-<p>
-  This directive turns on linkification, auto-linking http, ftp and
-  https URLs. <code>a</code> tags with the <code>href</code> attribute
-  must be allowed.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt b/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt
deleted file mode 100644 (file)
index db58b13..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-AutoFormat.PurifierLinkify.DocURL
-TYPE: string
-VERSION: 2.0.1
-DEFAULT: '#%s'
-ALIASES: AutoFormatParam.PurifierLinkifyDocURL
---DESCRIPTION--
-<p>
-  Location of configuration documentation to link to, let %s substitute
-  into the configuration's namespace and directive names sans the percent
-  sign.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt b/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt
deleted file mode 100644 (file)
index 7996488..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-AutoFormat.PurifierLinkify
-TYPE: bool
-VERSION: 2.0.1
-DEFAULT: false
---DESCRIPTION--
-
-<p>
-  Internal auto-formatter that converts configuration directives in
-  syntax <a>%Namespace.Directive</a> to links. <code>a</code> tags
-  with the <code>href</code> attribute must be allowed.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt b/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt
deleted file mode 100644 (file)
index 35c393b..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions
-TYPE: lookup
-VERSION: 4.0.0
-DEFAULT: array('td' => true, 'th' => true)
---DESCRIPTION--
-<p>
-  When %AutoFormat.RemoveEmpty and %AutoFormat.RemoveEmpty.RemoveNbsp
-  are enabled, this directive defines what HTML elements should not be
-  removede if they have only a non-breaking space in them.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt b/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt
deleted file mode 100644 (file)
index ca17eb1..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-AutoFormat.RemoveEmpty.RemoveNbsp
-TYPE: bool
-VERSION: 4.0.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-  When enabled, HTML Purifier will treat any elements that contain only
-  non-breaking spaces as well as regular whitespace as empty, and remove
-  them when %AutoForamt.RemoveEmpty is enabled.
-</p>
-<p>
-  See %AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions for a list of elements
-  that don't have this behavior applied to them.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt b/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt
deleted file mode 100644 (file)
index 34657ba..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-AutoFormat.RemoveEmpty
-TYPE: bool
-VERSION: 3.2.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-  When enabled, HTML Purifier will attempt to remove empty elements that
-  contribute no semantic information to the document. The following types
-  of nodes will be removed:
-</p>
-<ul><li>
-    Tags with no attributes and no content, and that are not empty
-    elements (remove <code>&lt;a&gt;&lt;/a&gt;</code> but not
-    <code>&lt;br /&gt;</code>), and
-  </li>
-  <li>
-    Tags with no content, except for:<ul>
-      <li>The <code>colgroup</code> element, or</li>
-      <li>
-        Elements with the <code>id</code> or <code>name</code> attribute,
-        when those attributes are permitted on those elements.
-      </li>
-    </ul></li>
-</ul>
-<p>
-  Please be very careful when using this functionality; while it may not
-  seem that empty elements contain useful information, they can alter the
-  layout of a document given appropriate styling. This directive is most
-  useful when you are processing machine-generated HTML, please avoid using
-  it on regular user HTML.
-</p>
-<p>
-  Elements that contain only whitespace will be treated as empty. Non-breaking
-  spaces, however, do not count as whitespace. See
-  %AutoFormat.RemoveEmpty.RemoveNbsp for alternate behavior.
-</p>
-<p>
-  This algorithm is not perfect; you may still notice some empty tags,
-  particularly if a node had elements, but those elements were later removed
-  because they were not permitted in that context, or tags that, after
-  being auto-closed by another tag, where empty. This is for safety reasons
-  to prevent clever code from breaking validation. The general rule of thumb:
-  if a tag looked empty on the way in, it will get removed; if HTML Purifier
-  made it empty, it will stay.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt b/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt
deleted file mode 100644 (file)
index dde990a..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-AutoFormat.RemoveSpansWithoutAttributes
-TYPE: bool
-VERSION: 4.0.1
-DEFAULT: false
---DESCRIPTION--
-<p>
-  This directive causes <code>span</code> tags without any attributes
-  to be removed. It will also remove spans that had all attributes
-  removed during processing.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt b/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt
deleted file mode 100644 (file)
index b324608..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-CSS.AllowImportant
-TYPE: bool
-DEFAULT: false
-VERSION: 3.1.0
---DESCRIPTION--
-This parameter determines whether or not !important cascade modifiers should
-be allowed in user CSS. If false, !important will stripped.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt b/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt
deleted file mode 100644 (file)
index 748be0e..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-CSS.AllowTricky
-TYPE: bool
-DEFAULT: false
-VERSION: 3.1.0
---DESCRIPTION--
-This parameter determines whether or not to allow "tricky" CSS properties and
-values. Tricky CSS properties/values can drastically modify page layout or
-be used for deceptive practices but do not directly constitute a security risk.
-For example, <code>display:none;</code> is considered a tricky property that
-will only be allowed if this directive is set to true.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt b/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt
deleted file mode 100644 (file)
index 460112e..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-CSS.AllowedProperties
-TYPE: lookup/null
-VERSION: 3.1.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    If HTML Purifier's style attributes set is unsatisfactory for your needs,
-    you can overload it with your own list of tags to allow.  Note that this
-    method is subtractive: it does its job by taking away from HTML Purifier
-    usual feature set, so you cannot add an attribute that HTML Purifier never
-    supported in the first place.
-</p>
-<p>
-    <strong>Warning:</strong> If another directive conflicts with the
-    elements here, <em>that</em> directive will win and override.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt b/library/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt
deleted file mode 100644 (file)
index 5cb7dda..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-CSS.DefinitionRev
-TYPE: int
-VERSION: 2.0.0
-DEFAULT: 1
---DESCRIPTION--
-
-<p>
-    Revision identifier for your custom definition. See
-    %HTML.DefinitionRev for details.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt b/library/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt
deleted file mode 100644 (file)
index 7a32914..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-CSS.MaxImgLength
-TYPE: string/null
-DEFAULT: '1200px'
-VERSION: 3.1.1
---DESCRIPTION--
-<p>
- This parameter sets the maximum allowed length on <code>img</code> tags,
- effectively the <code>width</code> and <code>height</code> properties.
- Only absolute units of measurement (in, pt, pc, mm, cm) and pixels (px) are allowed. This is
- in place to prevent imagecrash attacks, disable with null at your own risk.
- This directive is similar to %HTML.MaxImgLength, and both should be
- concurrently edited, although there are
- subtle differences in the input format (the CSS max is a number with
- a unit).
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt b/library/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt
deleted file mode 100644 (file)
index 148eedb..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-CSS.Proprietary
-TYPE: bool
-VERSION: 3.0.0
-DEFAULT: false
---DESCRIPTION--
-
-<p>
-    Whether or not to allow safe, proprietary CSS values.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt b/library/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt
deleted file mode 100644 (file)
index c486724..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-Cache.DefinitionImpl
-TYPE: string/null
-VERSION: 2.0.0
-DEFAULT: 'Serializer'
---DESCRIPTION--
-
-This directive defines which method to use when caching definitions,
-the complex data-type that makes HTML Purifier tick. Set to null
-to disable caching (not recommended, as you will see a definite
-performance degradation).
-
---ALIASES--
-Core.DefinitionCache
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt b/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt
deleted file mode 100644 (file)
index 5403650..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-Cache.SerializerPath
-TYPE: string/null
-VERSION: 2.0.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    Absolute path with no trailing slash to store serialized definitions in.
-    Default is within the
-    HTML Purifier library inside DefinitionCache/Serializer. This
-    path must be writable by the webserver.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt b/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt
deleted file mode 100644 (file)
index 568cbf3..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-Core.AggressivelyFixLt
-TYPE: bool
-VERSION: 2.1.0
-DEFAULT: true
---DESCRIPTION--
-<p>
-    This directive enables aggressive pre-filter fixes HTML Purifier can
-    perform in order to ensure that open angled-brackets do not get killed
-    during parsing stage. Enabling this will result in two preg_replace_callback
-    calls and at least two preg_replace calls for every HTML document parsed;
-    if your users make very well-formed HTML, you can set this directive false.
-    This has no effect when DirectLex is used.
-</p>
-<p>
-    <strong>Notice:</strong> This directive's default turned from false to true
-    in HTML Purifier 3.2.0.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt b/library/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt
deleted file mode 100644 (file)
index d731791..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-Core.CollectErrors
-TYPE: bool
-VERSION: 2.0.0
-DEFAULT: false
---DESCRIPTION--
-
-Whether or not to collect errors found while filtering the document. This
-is a useful way to give feedback to your users. <strong>Warning:</strong>
-Currently this feature is very patchy and experimental, with lots of
-possible error messages not yet implemented. It will not cause any
-problems, but it may not help your users either.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt b/library/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt
deleted file mode 100644 (file)
index 08b381d..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-Core.ColorKeywords
-TYPE: hash
-VERSION: 2.0.0
---DEFAULT--
-array (
-  'maroon' => '#800000',
-  'red' => '#FF0000',
-  'orange' => '#FFA500',
-  'yellow' => '#FFFF00',
-  'olive' => '#808000',
-  'purple' => '#800080',
-  'fuchsia' => '#FF00FF',
-  'white' => '#FFFFFF',
-  'lime' => '#00FF00',
-  'green' => '#008000',
-  'navy' => '#000080',
-  'blue' => '#0000FF',
-  'aqua' => '#00FFFF',
-  'teal' => '#008080',
-  'black' => '#000000',
-  'silver' => '#C0C0C0',
-  'gray' => '#808080',
-)
---DESCRIPTION--
-
-Lookup array of color names to six digit hexadecimal number corresponding
-to color, with preceding hash mark. Used when parsing colors.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt b/library/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt
deleted file mode 100644 (file)
index 64b114f..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-Core.ConvertDocumentToFragment
-TYPE: bool
-DEFAULT: true
---DESCRIPTION--
-
-This parameter determines whether or not the filter should convert
-input that is a full document with html and body tags to a fragment
-of just the contents of a body tag. This parameter is simply something
-HTML Purifier can do during an edge-case: for most inputs, this
-processing is not necessary.
-
---ALIASES--
-Core.AcceptFullDocuments
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt b/library/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt
deleted file mode 100644 (file)
index 36f16e0..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-Core.DirectLexLineNumberSyncInterval
-TYPE: int
-VERSION: 2.0.0
-DEFAULT: 0
---DESCRIPTION--
-
-<p>
-  Specifies the number of tokens the DirectLex line number tracking
-  implementations should process before attempting to resyncronize the
-  current line count by manually counting all previous new-lines. When
-  at 0, this functionality is disabled. Lower values will decrease
-  performance, and this is only strictly necessary if the counting
-  algorithm is buggy (in which case you should report it as a bug).
-  This has no effect when %Core.MaintainLineNumbers is disabled or DirectLex is
-  not being used.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt b/library/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt
deleted file mode 100644 (file)
index 8bfb47c..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-Core.Encoding
-TYPE: istring
-DEFAULT: 'utf-8'
---DESCRIPTION--
-If for some reason you are unable to convert all webpages to UTF-8, you can
-use this directive as a stop-gap compatibility change to let HTML Purifier
-deal with non UTF-8 input.  This technique has notable deficiencies:
-absolutely no characters outside of the selected character encoding will be
-preserved, not even the ones that have been ampersand escaped (this is due
-to a UTF-8 specific <em>feature</em> that automatically resolves all
-entities), making it pretty useless for anything except the most I18N-blind
-applications, although %Core.EscapeNonASCIICharacters offers fixes this
-trouble with another tradeoff. This directive only accepts ISO-8859-1 if
-iconv is not enabled.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt b/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt
deleted file mode 100644 (file)
index 4d5b505..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-Core.EscapeInvalidChildren
-TYPE: bool
-DEFAULT: false
---DESCRIPTION--
-When true, a child is found that is not allowed in the context of the
-parent element will be transformed into text as if it were ASCII. When
-false, that element and all internal tags will be dropped, though text will
-be preserved.  There is no option for dropping the element but preserving
-child nodes.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt b/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt
deleted file mode 100644 (file)
index a7a5b24..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-Core.EscapeInvalidTags
-TYPE: bool
-DEFAULT: false
---DESCRIPTION--
-When true, invalid tags will be written back to the document as plain text.
-Otherwise, they are silently dropped.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt b/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt
deleted file mode 100644 (file)
index abb4999..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-Core.EscapeNonASCIICharacters
-TYPE: bool
-VERSION: 1.4.0
-DEFAULT: false
---DESCRIPTION--
-This directive overcomes a deficiency in %Core.Encoding by blindly
-converting all non-ASCII characters into decimal numeric entities before
-converting it to its native encoding. This means that even characters that
-can be expressed in the non-UTF-8 encoding will be entity-ized, which can
-be a real downer for encodings like Big5. It also assumes that the ASCII
-repetoire is available, although this is the case for almost all encodings.
-Anyway, use UTF-8!
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt b/library/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt
deleted file mode 100644 (file)
index 915391e..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-Core.HiddenElements
-TYPE: lookup
---DEFAULT--
-array (
-  'script' => true,
-  'style' => true,
-)
---DESCRIPTION--
-
-<p>
-  This directive is a lookup array of elements which should have their
-  contents removed when they are not allowed by the HTML definition.
-  For example, the contents of a <code>script</code> tag are not
-  normally shown in a document, so if script tags are to be removed,
-  their contents should be removed to. This is opposed to a <code>b</code>
-  tag, which defines some presentational changes but does not hide its
-  contents.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Core.Language.txt b/library/HTMLPurifier/ConfigSchema/schema/Core.Language.txt
deleted file mode 100644 (file)
index 233fca1..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-Core.Language
-TYPE: string
-VERSION: 2.0.0
-DEFAULT: 'en'
---DESCRIPTION--
-
-ISO 639 language code for localizable things in HTML Purifier to use,
-which is mainly error reporting. There is currently only an English (en)
-translation, so this directive is currently useless.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt b/library/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt
deleted file mode 100644 (file)
index 8983e2c..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-Core.LexerImpl
-TYPE: mixed/null
-VERSION: 2.0.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-  This parameter determines what lexer implementation can be used. The
-  valid values are:
-</p>
-<dl>
-  <dt><em>null</em></dt>
-  <dd>
-    Recommended, the lexer implementation will be auto-detected based on
-    your PHP-version and configuration.
-  </dd>
-  <dt><em>string</em> lexer identifier</dt>
-  <dd>
-    This is a slim way of manually overridding the implementation.
-    Currently recognized values are: DOMLex (the default PHP5
-implementation)
-    and DirectLex (the default PHP4 implementation). Only use this if
-    you know what you are doing: usually, the auto-detection will
-    manage things for cases you aren't even aware of.
-  </dd>
-  <dt><em>object</em> lexer instance</dt>
-  <dd>
-    Super-advanced: you can specify your own, custom, implementation that
-    implements the interface defined by <code>HTMLPurifier_Lexer</code>.
-    I may remove this option simply because I don't expect anyone
-    to use it.
-  </dd>
-</dl>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt b/library/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt
deleted file mode 100644 (file)
index eb841a7..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-Core.MaintainLineNumbers
-TYPE: bool/null
-VERSION: 2.0.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-  If true, HTML Purifier will add line number information to all tokens.
-  This is useful when error reporting is turned on, but can result in
-  significant performance degradation and should not be used when
-  unnecessary. This directive must be used with the DirectLex lexer,
-  as the DOMLex lexer does not (yet) support this functionality.
-  If the value is null, an appropriate value will be selected based
-  on other configuration.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt b/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt
deleted file mode 100644 (file)
index 4070c2a..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-Core.RemoveInvalidImg
-TYPE: bool
-DEFAULT: true
-VERSION: 1.3.0
---DESCRIPTION--
-
-<p>
-  This directive enables pre-emptive URI checking in <code>img</code>
-  tags, as the attribute validation strategy is not authorized to
-  remove elements from the document. Revert to pre-1.3.0 behavior by setting to false.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt b/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt
deleted file mode 100644 (file)
index a4cd966..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-Core.RemoveScriptContents
-TYPE: bool/null
-DEFAULT: NULL
-VERSION: 2.0.0
-DEPRECATED-VERSION: 2.1.0
-DEPRECATED-USE: Core.HiddenElements
---DESCRIPTION--
-<p>
-  This directive enables HTML Purifier to remove not only script tags
-  but all of their contents.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt b/library/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt
deleted file mode 100644 (file)
index 3db50ef..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-Filter.Custom
-TYPE: list
-VERSION: 3.1.0
-DEFAULT: array()
---DESCRIPTION--
-<p>
-  This directive can be used to add custom filters; it is nearly the
-  equivalent of the now deprecated <code>HTMLPurifier-&gt;addFilter()</code>
-  method. Specify an array of concrete implementations.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt b/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt
deleted file mode 100644 (file)
index 16829bc..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-Filter.ExtractStyleBlocks.Escaping
-TYPE: bool
-VERSION: 3.0.0
-DEFAULT: true
-ALIASES: Filter.ExtractStyleBlocksEscaping, FilterParam.ExtractStyleBlocksEscaping
---DESCRIPTION--
-
-<p>
-  Whether or not to escape the dangerous characters &lt;, &gt; and &amp;
-  as \3C, \3E and \26, respectively. This is can be safely set to false
-  if the contents of StyleBlocks will be placed in an external stylesheet,
-  where there is no risk of it being interpreted as HTML.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt b/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt
deleted file mode 100644 (file)
index 7f95f54..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-Filter.ExtractStyleBlocks.Scope
-TYPE: string/null
-VERSION: 3.0.0
-DEFAULT: NULL
-ALIASES: Filter.ExtractStyleBlocksScope, FilterParam.ExtractStyleBlocksScope
---DESCRIPTION--
-
-<p>
-  If you would like users to be able to define external stylesheets, but
-  only allow them to specify CSS declarations for a specific node and
-  prevent them from fiddling with other elements, use this directive.
-  It accepts any valid CSS selector, and will prepend this to any
-  CSS declaration extracted from the document. For example, if this
-  directive is set to <code>#user-content</code> and a user uses the
-  selector <code>a:hover</code>, the final selector will be
-  <code>#user-content a:hover</code>.
-</p>
-<p>
-  The comma shorthand may be used; consider the above example, with
-  <code>#user-content, #user-content2</code>, the final selector will
-  be <code>#user-content a:hover, #user-content2 a:hover</code>.
-</p>
-<p>
-  <strong>Warning:</strong> It is possible for users to bypass this measure
-  using a naughty + selector. This is a bug in CSS Tidy 1.3, not HTML
-  Purifier, and I am working to get it fixed. Until then, HTML Purifier
-  performs a basic check to prevent this.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt b/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt
deleted file mode 100644 (file)
index 6c231b2..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-Filter.ExtractStyleBlocks.TidyImpl
-TYPE: mixed/null
-VERSION: 3.1.0
-DEFAULT: NULL
-ALIASES: FilterParam.ExtractStyleBlocksTidyImpl
---DESCRIPTION--
-<p>
-  If left NULL, HTML Purifier will attempt to instantiate a <code>csstidy</code>
-  class to use for internal cleaning. This will usually be good enough.
-</p>
-<p>
-  However, for trusted user input, you can set this to <code>false</code> to
-  disable cleaning. In addition, you can supply your own concrete implementation
-  of Tidy's interface to use, although I don't know why you'd want to do that.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt b/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt
deleted file mode 100644 (file)
index 078d087..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-Filter.ExtractStyleBlocks
-TYPE: bool
-VERSION: 3.1.0
-DEFAULT: false
-EXTERNAL: CSSTidy
---DESCRIPTION--
-<p>
-  This directive turns on the style block extraction filter, which removes
-  <code>style</code> blocks from input HTML, cleans them up with CSSTidy,
-  and places them in the <code>StyleBlocks</code> context variable, for further
-  use by you, usually to be placed in an external stylesheet, or a
-  <code>style</code> block in the <code>head</code> of your document.
-</p>
-<p>
-  Sample usage:
-</p>
-<pre><![CDATA[
-<?php
-    header('Content-type: text/html; charset=utf-8');
-    echo '<?xml version="1.0" encoding="UTF-8"?>';
-?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
-<head>
-  <title>Filter.ExtractStyleBlocks</title>
-<?php
-    require_once '/path/to/library/HTMLPurifier.auto.php';
-    require_once '/path/to/csstidy.class.php';
-
-    $dirty = '<style>body {color:#F00;}</style> Some text';
-
-    $config = HTMLPurifier_Config::createDefault();
-    $config->set('Filter', 'ExtractStyleBlocks', true);
-    $purifier = new HTMLPurifier($config);
-
-    $html = $purifier->purify($dirty);
-
-    // This implementation writes the stylesheets to the styles/ directory.
-    // You can also echo the styles inside the document, but it's a bit
-    // more difficult to make sure they get interpreted properly by
-    // browsers; try the usual CSS armoring techniques.
-    $styles = $purifier->context->get('StyleBlocks');
-    $dir = 'styles/';
-    if (!is_dir($dir)) mkdir($dir);
-    $hash = sha1($_GET['html']);
-    foreach ($styles as $i => $style) {
-        file_put_contents($name = $dir . $hash . "_$i");
-        echo '<link rel="stylesheet" type="text/css" href="'.$name.'" />';
-    }
-?>
-</head>
-<body>
-  <div>
-    <?php echo $html; ?>
-  </div>
-</b]]><![CDATA[ody>
-</html>
-]]></pre>
-<p>
-  <strong>Warning:</strong> It is possible for a user to mount an
-  imagecrash attack using this CSS. Counter-measures are difficult;
-  it is not simply enough to limit the range of CSS lengths (using
-  relative lengths with many nesting levels allows for large values
-  to be attained without actually specifying them in the stylesheet),
-  and the flexible nature of selectors makes it difficult to selectively
-  disable lengths on image tags (HTML Purifier, however, does disable
-  CSS width and height in inline styling). There are probably two effective
-  counter measures: an explicit width and height set to auto in all
-  images in your document (unlikely) or the disabling of width and
-  height (somewhat reasonable). Whether or not these measures should be
-  used is left to the reader.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt b/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt
deleted file mode 100644 (file)
index 7fa6536..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-Filter.YouTube
-TYPE: bool
-VERSION: 3.1.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-  This directive enables YouTube video embedding in HTML Purifier. Check
-  <a href="http://htmlpurifier.org/docs/enduser-youtube.html">this document
-  on embedding videos</a> for more information on what this filter does.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt
deleted file mode 100644 (file)
index 3e231d2..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-HTML.Allowed
-TYPE: itext/null
-VERSION: 2.0.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    This is a convenience directive that rolls the functionality of
-    %HTML.AllowedElements and %HTML.AllowedAttributes into one directive.
-    Specify elements and attributes that are allowed using:
-    <code>element1[attr1|attr2],element2...</code>. You can also use
-    newlines instead of commas to separate elements.
-</p>
-<p>
-    <strong>Warning</strong>:
-    All of the constraints on the component directives are still enforced.
-    The syntax is a <em>subset</em> of TinyMCE's <code>valid_elements</code>
-    whitelist: directly copy-pasting it here will probably result in
-    broken whitelists. If %HTML.AllowedElements or %HTML.AllowedAttributes
-    are set, this directive has no effect.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt
deleted file mode 100644 (file)
index fcf093f..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-HTML.AllowedAttributes
-TYPE: lookup/null
-VERSION: 1.3.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    If HTML Purifier's attribute set is unsatisfactory, overload it!
-    The syntax is "tag.attr" or "*.attr" for the global attributes
-    (style, id, class, dir, lang, xml:lang).
-</p>
-<p>
-    <strong>Warning:</strong> If another directive conflicts with the
-    elements here, <em>that</em> directive will win and override. For
-    example, %HTML.EnableAttrID will take precedence over *.id in this
-    directive.  You must set that directive to true before you can use
-    IDs at all.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt
deleted file mode 100644 (file)
index 888d558..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-HTML.AllowedElements
-TYPE: lookup/null
-VERSION: 1.3.0
-DEFAULT: NULL
---DESCRIPTION--
-<p>
-    If HTML Purifier's tag set is unsatisfactory for your needs, you
-    can overload it with your own list of tags to allow.  Note that this
-    method is subtractive: it does its job by taking away from HTML Purifier
-    usual feature set, so you cannot add a tag that HTML Purifier never
-    supported in the first place (like embed, form or head).  If you
-    change this, you probably also want to change %HTML.AllowedAttributes.
-</p>
-<p>
-    <strong>Warning:</strong> If another directive conflicts with the
-    elements here, <em>that</em> directive will win and override.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt
deleted file mode 100644 (file)
index 5a59a55..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-HTML.AllowedModules
-TYPE: lookup/null
-VERSION: 2.0.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    A doctype comes with a set of usual modules to use. Without having
-    to mucking about with the doctypes, you can quickly activate or
-    disable these modules by specifying which modules you wish to allow
-    with this directive. This is most useful for unit testing specific
-    modules, although end users may find it useful for their own ends.
-</p>
-<p>
-    If you specify a module that does not exist, the manager will silently
-    fail to use it, so be careful! User-defined modules are not affected
-    by this directive. Modules defined in %HTML.CoreModules are not
-    affected by this directive.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt
deleted file mode 100644 (file)
index 151fb7b..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-HTML.Attr.Name.UseCDATA
-TYPE: bool
-DEFAULT: false
-VERSION: 4.0.0
---DESCRIPTION--
-The W3C specification DTD defines the name attribute to be CDATA, not ID, due
-to limitations of DTD.  In certain documents, this relaxed behavior is desired,
-whether it is to specify duplicate names, or to specify names that would be
-illegal IDs (for example, names that begin with a digit.) Set this configuration
-directive to true to use the relaxed parsing rules.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt
deleted file mode 100644 (file)
index 45ae469..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-HTML.BlockWrapper
-TYPE: string
-VERSION: 1.3.0
-DEFAULT: 'p'
---DESCRIPTION--
-
-<p>
-    String name of element to wrap inline elements that are inside a block
-    context.  This only occurs in the children of blockquote in strict mode.
-</p>
-<p>
-    Example: by default value,
-    <code>&lt;blockquote&gt;Foo&lt;/blockquote&gt;</code> would become
-    <code>&lt;blockquote&gt;&lt;p&gt;Foo&lt;/p&gt;&lt;/blockquote&gt;</code>.
-    The <code>&lt;p&gt;</code> tags can be replaced with whatever you desire,
-    as long as it is a block level element.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt
deleted file mode 100644 (file)
index 5246188..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-HTML.CoreModules
-TYPE: lookup
-VERSION: 2.0.0
---DEFAULT--
-array (
-  'Structure' => true,
-  'Text' => true,
-  'Hypertext' => true,
-  'List' => true,
-  'NonXMLCommonAttributes' => true,
-  'XMLCommonAttributes' => true,
-  'CommonAttributes' => true,
-)
---DESCRIPTION--
-
-<p>
-    Certain modularized doctypes (XHTML, namely), have certain modules
-    that must be included for the doctype to be an conforming document
-    type: put those modules here. By default, XHTML's core modules
-    are used. You can set this to a blank array to disable core module
-    protection, but this is not recommended.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt
deleted file mode 100644 (file)
index a64e3d7..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-HTML.CustomDoctype
-TYPE: string/null
-VERSION: 2.0.1
-DEFAULT: NULL
---DESCRIPTION--
-
-A custom doctype for power-users who defined there own document
-type. This directive only applies when %HTML.Doctype is blank.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt
deleted file mode 100644 (file)
index 103db75..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-HTML.DefinitionID
-TYPE: string/null
-DEFAULT: NULL
-VERSION: 2.0.0
---DESCRIPTION--
-
-<p>
-    Unique identifier for a custom-built HTML definition. If you edit
-    the raw version of the HTMLDefinition, introducing changes that the
-    configuration object does not reflect, you must specify this variable.
-    If you change your custom edits, you should change this directive, or
-    clear your cache. Example:
-</p>
-<pre>
-$config = HTMLPurifier_Config::createDefault();
-$config->set('HTML', 'DefinitionID', '1');
-$def = $config->getHTMLDefinition();
-$def->addAttribute('a', 'tabindex', 'Number');
-</pre>
-<p>
-    In the above example, the configuration is still at the defaults, but
-    using the advanced API, an extra attribute has been added. The
-    configuration object normally has no way of knowing that this change
-    has taken place, so it needs an extra directive: %HTML.DefinitionID.
-    If someone else attempts to use the default configuration, these two
-    pieces of code will not clobber each other in the cache, since one has
-    an extra directive attached to it.
-</p>
-<p>
-    You <em>must</em> specify a value to this directive to use the
-    advanced API features.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt
deleted file mode 100644 (file)
index 229ae02..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-HTML.DefinitionRev
-TYPE: int
-VERSION: 2.0.0
-DEFAULT: 1
---DESCRIPTION--
-
-<p>
-    Revision identifier for your custom definition specified in
-    %HTML.DefinitionID.  This serves the same purpose: uniquely identifying
-    your custom definition, but this one does so in a chronological
-    context: revision 3 is more up-to-date then revision 2.  Thus, when
-    this gets incremented, the cache handling is smart enough to clean
-    up any older revisions of your definition as well as flush the
-    cache.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt
deleted file mode 100644 (file)
index 9dab497..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-HTML.Doctype
-TYPE: string/null
-DEFAULT: NULL
---DESCRIPTION--
-Doctype to use during filtering. Technically speaking this is not actually
-a doctype (as it does not identify a corresponding DTD), but we are using
-this name for sake of simplicity. When non-blank, this will override any
-older directives like %HTML.XHTML or %HTML.Strict.
---ALLOWED--
-'HTML 4.01 Transitional', 'HTML 4.01 Strict', 'XHTML 1.0 Transitional', 'XHTML 1.0 Strict', 'XHTML 1.1'
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt
deleted file mode 100644 (file)
index 57358f9..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-HTML.ForbiddenAttributes
-TYPE: lookup
-VERSION: 3.1.0
-DEFAULT: array()
---DESCRIPTION--
-<p>
-    While this directive is similar to %HTML.AllowedAttributes, for
-    forwards-compatibility with XML, this attribute has a different syntax. Instead of
-    <code>tag.attr</code>, use <code>tag@attr</code>. To disallow <code>href</code>
-    attributes in <code>a</code> tags, set this directive to
-    <code>a@href</code>. You can also disallow an attribute globally with
-    <code>attr</code> or <code>*@attr</code> (either syntax is fine; the latter
-    is provided for consistency with %HTML.AllowedAttributes).
-</p>
-<p>
-    <strong>Warning:</strong> This directive complements %HTML.ForbiddenElements,
-    accordingly, check
-    out that directive for a discussion of why you
-    should think twice before using this directive.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt
deleted file mode 100644 (file)
index 93a53e1..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-HTML.ForbiddenElements
-TYPE: lookup
-VERSION: 3.1.0
-DEFAULT: array()
---DESCRIPTION--
-<p>
-    This was, perhaps, the most requested feature ever in HTML
-    Purifier. Please don't abuse it! This is the logical inverse of
-    %HTML.AllowedElements, and it will override that directive, or any
-    other directive.
-</p>
-<p>
-    If possible, %HTML.Allowed is recommended over this directive, because it
-    can sometimes be difficult to tell whether or not you've forbidden all of
-    the behavior you would like to disallow. If you forbid <code>img</code>
-    with the expectation of preventing images on your site, you'll be in for
-    a nasty surprise when people start using the <code>background-image</code>
-    CSS property.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt
deleted file mode 100644 (file)
index e424c38..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-HTML.MaxImgLength
-TYPE: int/null
-DEFAULT: 1200
-VERSION: 3.1.1
---DESCRIPTION--
-<p>
- This directive controls the maximum number of pixels in the width and
- height attributes in <code>img</code> tags. This is
- in place to prevent imagecrash attacks, disable with null at your own risk.
- This directive is similar to %CSS.MaxImgLength, and both should be
- concurrently edited, although there are
- subtle differences in the input format (the HTML max is an integer).
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt
deleted file mode 100644 (file)
index 62e8e16..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-HTML.Parent
-TYPE: string
-VERSION: 1.3.0
-DEFAULT: 'div'
---DESCRIPTION--
-
-<p>
-    String name of element that HTML fragment passed to library will be
-    inserted in.  An interesting variation would be using span as the
-    parent element, meaning that only inline tags would be allowed.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt
deleted file mode 100644 (file)
index dfb7204..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-HTML.Proprietary
-TYPE: bool
-VERSION: 3.1.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-    Whether or not to allow proprietary elements and attributes in your
-    documents, as per <code>HTMLPurifier_HTMLModule_Proprietary</code>.
-    <strong>Warning:</strong> This can cause your documents to stop
-    validating!
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt
deleted file mode 100644 (file)
index cdda09a..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-HTML.SafeEmbed
-TYPE: bool
-VERSION: 3.1.1
-DEFAULT: false
---DESCRIPTION--
-<p>
-    Whether or not to permit embed tags in documents, with a number of extra
-    security features added to prevent script execution. This is similar to
-    what websites like MySpace do to embed tags. Embed is a proprietary
-    element and will cause your website to stop validating; you should
-    see if you can use %Output.FlashCompat with %HTML.SafeObject instead
-    first.</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt
deleted file mode 100644 (file)
index ceb342e..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-HTML.SafeObject
-TYPE: bool
-VERSION: 3.1.1
-DEFAULT: false
---DESCRIPTION--
-<p>
-    Whether or not to permit object tags in documents, with a number of extra
-    security features added to prevent script execution. This is similar to
-    what websites like MySpace do to object tags.  You should also enable
-    %Output.FlashCompat in order to generate Internet Explorer
-    compatibility code for your object tags.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt
deleted file mode 100644 (file)
index a8b1de5..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-HTML.Strict
-TYPE: bool
-VERSION: 1.3.0
-DEFAULT: false
-DEPRECATED-VERSION: 1.7.0
-DEPRECATED-USE: HTML.Doctype
---DESCRIPTION--
-Determines whether or not to use Transitional (loose) or Strict rulesets.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt
deleted file mode 100644 (file)
index b4c271b..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-HTML.TidyAdd
-TYPE: lookup
-VERSION: 2.0.0
-DEFAULT: array()
---DESCRIPTION--
-
-Fixes to add to the default set of Tidy fixes as per your level.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt
deleted file mode 100644 (file)
index 4186ccd..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-HTML.TidyLevel
-TYPE: string
-VERSION: 2.0.0
-DEFAULT: 'medium'
---DESCRIPTION--
-
-<p>General level of cleanliness the Tidy module should enforce.
-There are four allowed values:</p>
-<dl>
-    <dt>none</dt>
-    <dd>No extra tidying should be done</dd>
-    <dt>light</dt>
-    <dd>Only fix elements that would be discarded otherwise due to
-    lack of support in doctype</dd>
-    <dt>medium</dt>
-    <dd>Enforce best practices</dd>
-    <dt>heavy</dt>
-    <dd>Transform all deprecated elements and attributes to standards
-    compliant equivalents</dd>
-</dl>
-
---ALLOWED--
-'none', 'light', 'medium', 'heavy'
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt
deleted file mode 100644 (file)
index 996762b..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-HTML.TidyRemove
-TYPE: lookup
-VERSION: 2.0.0
-DEFAULT: array()
---DESCRIPTION--
-
-Fixes to remove from the default set of Tidy fixes as per your level.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt
deleted file mode 100644 (file)
index 89133b1..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-HTML.Trusted
-TYPE: bool
-VERSION: 2.0.0
-DEFAULT: false
---DESCRIPTION--
-Indicates whether or not the user input is trusted or not. If the input is
-trusted, a more expansive set of allowed tags and attributes will be used.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt
deleted file mode 100644 (file)
index 2a47e38..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-HTML.XHTML
-TYPE: bool
-DEFAULT: true
-VERSION: 1.1.0
-DEPRECATED-VERSION: 1.7.0
-DEPRECATED-USE: HTML.Doctype
---DESCRIPTION--
-Determines whether or not output is XHTML 1.0 or HTML 4.01 flavor.
---ALIASES--
-Core.XHTML
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt b/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt
deleted file mode 100644 (file)
index 08921fd..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-Output.CommentScriptContents
-TYPE: bool
-VERSION: 2.0.0
-DEFAULT: true
---DESCRIPTION--
-Determines whether or not HTML Purifier should attempt to fix up the
-contents of script tags for legacy browsers with comments.
---ALIASES--
-Core.CommentScriptContents
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt b/library/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt
deleted file mode 100644 (file)
index 93398e8..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-Output.FlashCompat
-TYPE: bool
-VERSION: 4.1.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-  If true, HTML Purifier will generate Internet Explorer compatibility
-  code for all object code.  This is highly recommended if you enable
-  %HTML.SafeObject.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt b/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt
deleted file mode 100644 (file)
index 79f8ad8..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-Output.Newline
-TYPE: string/null
-VERSION: 2.0.1
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    Newline string to format final output with. If left null, HTML Purifier
-    will auto-detect the default newline type of the system and use that;
-    you can manually override it here. Remember, \r\n is Windows, \r
-    is Mac, and \n is Unix.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt b/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt
deleted file mode 100644 (file)
index 232b023..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-Output.SortAttr
-TYPE: bool
-VERSION: 3.2.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-  If true, HTML Purifier will sort attributes by name before writing them back
-  to the document, converting a tag like: <code>&lt;el b="" a="" c="" /&gt;</code>
-  to <code>&lt;el a="" b="" c="" /&gt;</code>. This is a workaround for
-  a bug in FCKeditor which causes it to swap attributes order, adding noise
-  to text diffs. If you're not seeing this bug, chances are, you don't need
-  this directive.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt b/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt
deleted file mode 100644 (file)
index 06bab00..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-Output.TidyFormat
-TYPE: bool
-VERSION: 1.1.1
-DEFAULT: false
---DESCRIPTION--
-<p>
-    Determines whether or not to run Tidy on the final output for pretty
-    formatting reasons, such as indentation and wrap.
-</p>
-<p>
-    This can greatly improve readability for editors who are hand-editing
-    the HTML, but is by no means necessary as HTML Purifier has already
-    fixed all major errors the HTML may have had. Tidy is a non-default
-    extension, and this directive will silently fail if Tidy is not
-    available.
-</p>
-<p>
-    If you are looking to make the overall look of your page's source
-    better, I recommend running Tidy on the entire page rather than just
-    user-content (after all, the indentation relative to the containing
-    blocks will be incorrect).
-</p>
---ALIASES--
-Core.TidyFormat
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt b/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt
deleted file mode 100644 (file)
index 071bc02..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-Test.ForceNoIconv
-TYPE: bool
-DEFAULT: false
---DESCRIPTION--
-When set to true, HTMLPurifier_Encoder will act as if iconv does not exist
-and use only pure PHP implementations.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt
deleted file mode 100644 (file)
index ae3a913..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-URI.AllowedSchemes
-TYPE: lookup
---DEFAULT--
-array (
-  'http' => true,
-  'https' => true,
-  'mailto' => true,
-  'ftp' => true,
-  'nntp' => true,
-  'news' => true,
-)
---DESCRIPTION--
-Whitelist that defines the schemes that a URI is allowed to have.  This
-prevents XSS attacks from using pseudo-schemes like javascript or mocha.
-There is also support for the <code>data</code> URI scheme, but it is not
-enabled by default.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt
deleted file mode 100644 (file)
index 876f068..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-URI.Base
-TYPE: string/null
-VERSION: 2.1.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    The base URI is the URI of the document this purified HTML will be
-    inserted into.  This information is important if HTML Purifier needs
-    to calculate absolute URIs from relative URIs, such as when %URI.MakeAbsolute
-    is on.  You may use a non-absolute URI for this value, but behavior
-    may vary (%URI.MakeAbsolute deals nicely with both absolute and
-    relative paths, but forwards-compatibility is not guaranteed).
-    <strong>Warning:</strong> If set, the scheme on this URI
-    overrides the one specified by %URI.DefaultScheme.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt
deleted file mode 100644 (file)
index 728e378..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-URI.DefaultScheme
-TYPE: string
-DEFAULT: 'http'
---DESCRIPTION--
-
-<p>
-    Defines through what scheme the output will be served, in order to
-    select the proper object validator when no scheme information is present.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt
deleted file mode 100644 (file)
index f05312b..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-URI.DefinitionID
-TYPE: string/null
-VERSION: 2.1.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    Unique identifier for a custom-built URI definition. If you  want
-    to add custom URIFilters, you must specify this value.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt
deleted file mode 100644 (file)
index 80cfea9..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-URI.DefinitionRev
-TYPE: int
-VERSION: 2.1.0
-DEFAULT: 1
---DESCRIPTION--
-
-<p>
-    Revision identifier for your custom definition. See
-    %HTML.DefinitionRev for details.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt
deleted file mode 100644 (file)
index 71ce025..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-URI.Disable
-TYPE: bool
-VERSION: 1.3.0
-DEFAULT: false
---DESCRIPTION--
-
-<p>
-    Disables all URIs in all forms. Not sure why you'd want to do that
-    (after all, the Internet's founded on the notion of a hyperlink).
-</p>
-
---ALIASES--
-Attr.DisableURI
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt
deleted file mode 100644 (file)
index 13c122c..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-URI.DisableExternal
-TYPE: bool
-VERSION: 1.2.0
-DEFAULT: false
---DESCRIPTION--
-Disables links to external websites.  This is a highly effective anti-spam
-and anti-pagerank-leech measure, but comes at a hefty price: nolinks or
-images outside of your domain will be allowed.  Non-linkified URIs will
-still be preserved.  If you want to be able to link to subdomains or use
-absolute URIs, specify %URI.Host for your website.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt
deleted file mode 100644 (file)
index abcc1ef..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-URI.DisableExternalResources
-TYPE: bool
-VERSION: 1.3.0
-DEFAULT: false
---DESCRIPTION--
-Disables the embedding of external resources, preventing users from
-embedding things like images from other hosts. This prevents access
-tracking (good for email viewers), bandwidth leeching, cross-site request
-forging, goatse.cx posting, and other nasties, but also results in a loss
-of end-user functionality (they can't directly post a pic they posted from
-Flickr anymore). Use it if you don't have a robust user-content moderation
-team.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt
deleted file mode 100644 (file)
index 51e6ea9..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-URI.DisableResources
-TYPE: bool
-VERSION: 1.3.0
-DEFAULT: false
---DESCRIPTION--
-
-<p>
-    Disables embedding resources, essentially meaning no pictures. You can
-    still link to them though. See %URI.DisableExternalResources for why
-    this might be a good idea.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt
deleted file mode 100644 (file)
index ee83b12..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-URI.Host
-TYPE: string/null
-VERSION: 1.2.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    Defines the domain name of the server, so we can determine whether or
-    an absolute URI is from your website or not.  Not strictly necessary,
-    as users should be using relative URIs to reference resources on your
-    website.  It will, however, let you use absolute URIs to link to
-    subdomains of the domain you post here: i.e. example.com will allow
-    sub.example.com.  However, higher up domains will still be excluded:
-    if you set %URI.Host to sub.example.com, example.com will be blocked.
-    <strong>Note:</strong> This directive overrides %URI.Base because
-    a given page may be on a sub-domain, but you wish HTML Purifier to be
-    more relaxed and allow some of the parent domains too.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt
deleted file mode 100644 (file)
index 0b6df76..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-URI.HostBlacklist
-TYPE: list
-VERSION: 1.3.0
-DEFAULT: array()
---DESCRIPTION--
-List of strings that are forbidden in the host of any URI. Use it to kill
-domain names of spam, etc. Note that it will catch anything in the domain,
-so <tt>moo.com</tt> will catch <tt>moo.com.example.com</tt>.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt
deleted file mode 100644 (file)
index 4214900..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-URI.MakeAbsolute
-TYPE: bool
-VERSION: 2.1.0
-DEFAULT: false
---DESCRIPTION--
-
-<p>
-    Converts all URIs into absolute forms. This is useful when the HTML
-    being filtered assumes a specific base path, but will actually be
-    viewed in a different context (and setting an alternate base URI is
-    not possible). %URI.Base must be set for this directive to work.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt
deleted file mode 100644 (file)
index 58c81dc..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-URI.Munge
-TYPE: string/null
-VERSION: 1.3.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    Munges all browsable (usually http, https and ftp)
-    absolute URIs into another URI, usually a URI redirection service.
-    This directive accepts a URI, formatted with a <code>%s</code> where
-    the url-encoded original URI should be inserted (sample:
-    <code>http://www.google.com/url?q=%s</code>).
-</p>
-<p>
-    Uses for this directive:
-</p>
-<ul>
-    <li>
-        Prevent PageRank leaks, while being fairly transparent
-        to users (you may also want to add some client side JavaScript to
-        override the text in the statusbar). <strong>Notice</strong>:
-        Many security experts believe that this form of protection does not deter spam-bots.
-    </li>
-    <li>
-        Redirect users to a splash page telling them they are leaving your
-        website. While this is poor usability practice, it is often mandated
-        in corporate environments.
-    </li>
-</ul>
-<p>
-    Prior to HTML Purifier 3.1.1, this directive also enabled the munging
-    of browsable external resources, which could break things if your redirection
-    script was a splash page or used <code>meta</code> tags. To revert to
-    previous behavior, please use %URI.MungeResources.
-</p>
-<p>
-    You may want to also use %URI.MungeSecretKey along with this directive
-    in order to enforce what URIs your redirector script allows. Open
-    redirector scripts can be a security risk and negatively affect the
-    reputation of your domain name.
-</p>
-<p>
-    Starting with HTML Purifier 3.1.1, there is also these substitutions:
-</p>
-<table>
-    <thead>
-        <tr>
-            <th>Key</th>
-            <th>Description</th>
-            <th>Example <code>&lt;a href=""&gt;</code></th>
-        </tr>
-    </thead>
-    <tbody>
-        <tr>
-            <td>%r</td>
-            <td>1 - The URI embeds a resource<br />(blank) - The URI is merely a link</td>
-            <td></td>
-        </tr>
-        <tr>
-            <td>%n</td>
-            <td>The name of the tag this URI came from</td>
-            <td>a</td>
-        </tr>
-        <tr>
-            <td>%m</td>
-            <td>The name of the attribute this URI came from</td>
-            <td>href</td>
-        </tr>
-        <tr>
-            <td>%p</td>
-            <td>The name of the CSS property this URI came from, or blank if irrelevant</td>
-            <td></td>
-        </tr>
-    </tbody>
-</table>
-<p>
-    Admittedly, these letters are somewhat arbitrary; the only stipulation
-    was that they couldn't be a through f. r is for resource (I would have preferred
-    e, but you take what you can get), n is for name, m
-    was picked because it came after n (and I couldn't use a), p is for
-    property.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt
deleted file mode 100644 (file)
index 6fce0fd..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-URI.MungeResources
-TYPE: bool
-VERSION: 3.1.1
-DEFAULT: false
---DESCRIPTION--
-<p>
-    If true, any URI munging directives like %URI.Munge
-    will also apply to embedded resources, such as <code>&lt;img src=""&gt;</code>.
-    Be careful enabling this directive if you have a redirector script
-    that does not use the <code>Location</code> HTTP header; all of your images
-    and other embedded resources will break.
-</p>
-<p>
-    <strong>Warning:</strong> It is strongly advised you use this in conjunction
-    %URI.MungeSecretKey to mitigate the security risk of an open redirector.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt
deleted file mode 100644 (file)
index 0d00f62..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-URI.MungeSecretKey
-TYPE: string/null
-VERSION: 3.1.1
-DEFAULT: NULL
---DESCRIPTION--
-<p>
-    This directive enables secure checksum generation along with %URI.Munge.
-    It should be set to a secure key that is not shared with anyone else.
-    The checksum can be placed in the URI using %t. Use of this checksum
-    affords an additional level of protection by allowing a redirector
-    to check if a URI has passed through HTML Purifier with this line:
-</p>
-
-<pre>$checksum === sha1($secret_key . ':' . $url)</pre>
-
-<p>
-    If the output is TRUE, the redirector script should accept the URI.
-</p>
-
-<p>
-    Please note that it would still be possible for an attacker to procure
-    secure hashes en-mass by abusing your website's Preview feature or the
-    like, but this service affords an additional level of protection
-    that should be combined with website blacklisting.
-</p>
-
-<p>
-    Remember this has no effect if %URI.Munge is not on.
-</p>
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt
deleted file mode 100644 (file)
index 23331a4..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-URI.OverrideAllowedSchemes
-TYPE: bool
-DEFAULT: true
---DESCRIPTION--
-If this is set to true (which it is by default), you can override
-%URI.AllowedSchemes by simply registering a HTMLPurifier_URIScheme to the
-registry.  If false, you will also have to update that directive in order
-to add more schemes.
---# vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ConfigSchema/schema/info.ini b/library/HTMLPurifier/ConfigSchema/schema/info.ini
deleted file mode 100644 (file)
index 5de4505..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-name = "HTML Purifier"
-
-; vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ContentSets.php b/library/HTMLPurifier/ContentSets.php
deleted file mode 100644 (file)
index 3b6e96f..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-<?php
-
-/**
- * @todo Unit test
- */
-class HTMLPurifier_ContentSets
-{
-
-    /**
-     * List of content set strings (pipe seperators) indexed by name.
-     */
-    public $info = array();
-
-    /**
-     * List of content set lookups (element => true) indexed by name.
-     * @note This is in HTMLPurifier_HTMLDefinition->info_content_sets
-     */
-    public $lookup = array();
-
-    /**
-     * Synchronized list of defined content sets (keys of info)
-     */
-    protected $keys = array();
-    /**
-     * Synchronized list of defined content values (values of info)
-     */
-    protected $values = array();
-
-    /**
-     * Merges in module's content sets, expands identifiers in the content
-     * sets and populates the keys, values and lookup member variables.
-     * @param $modules List of HTMLPurifier_HTMLModule
-     */
-    public function __construct($modules) {
-        if (!is_array($modules)) $modules = array($modules);
-        // populate content_sets based on module hints
-        // sorry, no way of overloading
-        foreach ($modules as $module_i => $module) {
-            foreach ($module->content_sets as $key => $value) {
-                $temp = $this->convertToLookup($value);
-                if (isset($this->lookup[$key])) {
-                    // add it into the existing content set
-                    $this->lookup[$key] = array_merge($this->lookup[$key], $temp);
-                } else {
-                    $this->lookup[$key] = $temp;
-                }
-            }
-        }
-        $old_lookup = false;
-        while ($old_lookup !== $this->lookup) {
-            $old_lookup = $this->lookup;
-            foreach ($this->lookup as $i => $set) {
-                $add = array();
-                foreach ($set as $element => $x) {
-                    if (isset($this->lookup[$element])) {
-                        $add += $this->lookup[$element];
-                        unset($this->lookup[$i][$element]);
-                    }
-                }
-                $this->lookup[$i] += $add;
-            }
-        }
-
-        foreach ($this->lookup as $key => $lookup) {
-            $this->info[$key] = implode(' | ', array_keys($lookup));
-        }
-        $this->keys   = array_keys($this->info);
-        $this->values = array_values($this->info);
-    }
-
-    /**
-     * Accepts a definition; generates and assigns a ChildDef for it
-     * @param $def HTMLPurifier_ElementDef reference
-     * @param $module Module that defined the ElementDef
-     */
-    public function generateChildDef(&$def, $module) {
-        if (!empty($def->child)) return; // already done!
-        $content_model = $def->content_model;
-        if (is_string($content_model)) {
-            // Assume that $this->keys is alphanumeric
-            $def->content_model = preg_replace_callback(
-                '/\b(' . implode('|', $this->keys) . ')\b/',
-                array($this, 'generateChildDefCallback'),
-                $content_model
-            );
-            //$def->content_model = str_replace(
-            //    $this->keys, $this->values, $content_model);
-        }
-        $def->child = $this->getChildDef($def, $module);
-    }
-
-    public function generateChildDefCallback($matches) {
-        return $this->info[$matches[0]];
-    }
-
-    /**
-     * Instantiates a ChildDef based on content_model and content_model_type
-     * member variables in HTMLPurifier_ElementDef
-     * @note This will also defer to modules for custom HTMLPurifier_ChildDef
-     *       subclasses that need content set expansion
-     * @param $def HTMLPurifier_ElementDef to have ChildDef extracted
-     * @return HTMLPurifier_ChildDef corresponding to ElementDef
-     */
-    public function getChildDef($def, $module) {
-        $value = $def->content_model;
-        if (is_object($value)) {
-            trigger_error(
-                'Literal object child definitions should be stored in '.
-                'ElementDef->child not ElementDef->content_model',
-                E_USER_NOTICE
-            );
-            return $value;
-        }
-        switch ($def->content_model_type) {
-            case 'required':
-                return new HTMLPurifier_ChildDef_Required($value);
-            case 'optional':
-                return new HTMLPurifier_ChildDef_Optional($value);
-            case 'empty':
-                return new HTMLPurifier_ChildDef_Empty();
-            case 'custom':
-                return new HTMLPurifier_ChildDef_Custom($value);
-        }
-        // defer to its module
-        $return = false;
-        if ($module->defines_child_def) { // save a func call
-            $return = $module->getChildDef($def);
-        }
-        if ($return !== false) return $return;
-        // error-out
-        trigger_error(
-            'Could not determine which ChildDef class to instantiate',
-            E_USER_ERROR
-        );
-        return false;
-    }
-
-    /**
-     * Converts a string list of elements separated by pipes into
-     * a lookup array.
-     * @param $string List of elements
-     * @return Lookup array of elements
-     */
-    protected function convertToLookup($string) {
-        $array = explode('|', str_replace(' ', '', $string));
-        $ret = array();
-        foreach ($array as $i => $k) {
-            $ret[$k] = true;
-        }
-        return $ret;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Context.php b/library/HTMLPurifier/Context.php
deleted file mode 100644 (file)
index 9ddf0c5..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-
-/**
- * Registry object that contains information about the current context.
- * @warning Is a bit buggy when variables are set to null: it thinks
- *          they don't exist! So use false instead, please.
- * @note Since the variables Context deals with may not be objects,
- *       references are very important here! Do not remove!
- */
-class HTMLPurifier_Context
-{
-
-    /**
-     * Private array that stores the references.
-     */
-    private $_storage = array();
-
-    /**
-     * Registers a variable into the context.
-     * @param $name String name
-     * @param $ref Reference to variable to be registered
-     */
-    public function register($name, &$ref) {
-        if (isset($this->_storage[$name])) {
-            trigger_error("Name $name produces collision, cannot re-register",
-                          E_USER_ERROR);
-            return;
-        }
-        $this->_storage[$name] =& $ref;
-    }
-
-    /**
-     * Retrieves a variable reference from the context.
-     * @param $name String name
-     * @param $ignore_error Boolean whether or not to ignore error
-     */
-    public function &get($name, $ignore_error = false) {
-        if (!isset($this->_storage[$name])) {
-            if (!$ignore_error) {
-                trigger_error("Attempted to retrieve non-existent variable $name",
-                              E_USER_ERROR);
-            }
-            $var = null; // so we can return by reference
-            return $var;
-        }
-        return $this->_storage[$name];
-    }
-
-    /**
-     * Destorys a variable in the context.
-     * @param $name String name
-     */
-    public function destroy($name) {
-        if (!isset($this->_storage[$name])) {
-            trigger_error("Attempted to destroy non-existent variable $name",
-                          E_USER_ERROR);
-            return;
-        }
-        unset($this->_storage[$name]);
-    }
-
-    /**
-     * Checks whether or not the variable exists.
-     * @param $name String name
-     */
-    public function exists($name) {
-        return isset($this->_storage[$name]);
-    }
-
-    /**
-     * Loads a series of variables from an associative array
-     * @param $context_array Assoc array of variables to load
-     */
-    public function loadArray($context_array) {
-        foreach ($context_array as $key => $discard) {
-            $this->register($key, $context_array[$key]);
-        }
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Definition.php b/library/HTMLPurifier/Definition.php
deleted file mode 100644 (file)
index a7408c9..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
- * Super-class for definition datatype objects, implements serialization
- * functions for the class.
- */
-abstract class HTMLPurifier_Definition
-{
-
-    /**
-     * Has setup() been called yet?
-     */
-    public $setup = false;
-
-    /**
-     * What type of definition is it?
-     */
-    public $type;
-
-    /**
-     * Sets up the definition object into the final form, something
-     * not done by the constructor
-     * @param $config HTMLPurifier_Config instance
-     */
-    abstract protected function doSetup($config);
-
-    /**
-     * Setup function that aborts if already setup
-     * @param $config HTMLPurifier_Config instance
-     */
-    public function setup($config) {
-        if ($this->setup) return;
-        $this->setup = true;
-        $this->doSetup($config);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/DefinitionCache.php b/library/HTMLPurifier/DefinitionCache.php
deleted file mode 100644 (file)
index c6e1e38..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php
-
-/**
- * Abstract class representing Definition cache managers that implements
- * useful common methods and is a factory.
- * @todo Create a separate maintenance file advanced users can use to
- *       cache their custom HTMLDefinition, which can be loaded
- *       via a configuration directive
- * @todo Implement memcached
- */
-abstract class HTMLPurifier_DefinitionCache
-{
-
-    public $type;
-
-    /**
-     * @param $name Type of definition objects this instance of the
-     *      cache will handle.
-     */
-    public function __construct($type) {
-        $this->type = $type;
-    }
-
-    /**
-     * Generates a unique identifier for a particular configuration
-     * @param Instance of HTMLPurifier_Config
-     */
-    public function generateKey($config) {
-        return $config->version . ',' . // possibly replace with function calls
-               $config->getBatchSerial($this->type) . ',' .
-               $config->get($this->type . '.DefinitionRev');
-    }
-
-    /**
-     * Tests whether or not a key is old with respect to the configuration's
-     * version and revision number.
-     * @param $key Key to test
-     * @param $config Instance of HTMLPurifier_Config to test against
-     */
-    public function isOld($key, $config) {
-        if (substr_count($key, ',') < 2) return true;
-        list($version, $hash, $revision) = explode(',', $key, 3);
-        $compare = version_compare($version, $config->version);
-        // version mismatch, is always old
-        if ($compare != 0) return true;
-        // versions match, ids match, check revision number
-        if (
-            $hash == $config->getBatchSerial($this->type) &&
-            $revision < $config->get($this->type . '.DefinitionRev')
-        ) return true;
-        return false;
-    }
-
-    /**
-     * Checks if a definition's type jives with the cache's type
-     * @note Throws an error on failure
-     * @param $def Definition object to check
-     * @return Boolean true if good, false if not
-     */
-    public function checkDefType($def) {
-        if ($def->type !== $this->type) {
-            trigger_error("Cannot use definition of type {$def->type} in cache for {$this->type}");
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Adds a definition object to the cache
-     */
-    abstract public function add($def, $config);
-
-    /**
-     * Unconditionally saves a definition object to the cache
-     */
-    abstract public function set($def, $config);
-
-    /**
-     * Replace an object in the cache
-     */
-    abstract public function replace($def, $config);
-
-    /**
-     * Retrieves a definition object from the cache
-     */
-    abstract public function get($config);
-
-    /**
-     * Removes a definition object to the cache
-     */
-    abstract public function remove($config);
-
-    /**
-     * Clears all objects from cache
-     */
-    abstract public function flush($config);
-
-    /**
-     * Clears all expired (older version or revision) objects from cache
-     * @note Be carefuly implementing this method as flush. Flush must
-     *       not interfere with other Definition types, and cleanup()
-     *       should not be repeatedly called by userland code.
-     */
-    abstract public function cleanup($config);
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/DefinitionCache/Decorator.php b/library/HTMLPurifier/DefinitionCache/Decorator.php
deleted file mode 100644 (file)
index b0fb6d0..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-class HTMLPurifier_DefinitionCache_Decorator extends HTMLPurifier_DefinitionCache
-{
-
-    /**
-     * Cache object we are decorating
-     */
-    public $cache;
-
-    public function __construct() {}
-
-    /**
-     * Lazy decorator function
-     * @param $cache Reference to cache object to decorate
-     */
-    public function decorate(&$cache) {
-        $decorator = $this->copy();
-        // reference is necessary for mocks in PHP 4
-        $decorator->cache =& $cache;
-        $decorator->type  = $cache->type;
-        return $decorator;
-    }
-
-    /**
-     * Cross-compatible clone substitute
-     */
-    public function copy() {
-        return new HTMLPurifier_DefinitionCache_Decorator();
-    }
-
-    public function add($def, $config) {
-        return $this->cache->add($def, $config);
-    }
-
-    public function set($def, $config) {
-        return $this->cache->set($def, $config);
-    }
-
-    public function replace($def, $config) {
-        return $this->cache->replace($def, $config);
-    }
-
-    public function get($config) {
-        return $this->cache->get($config);
-    }
-
-    public function remove($config) {
-        return $this->cache->remove($config);
-    }
-
-    public function flush($config) {
-        return $this->cache->flush($config);
-    }
-
-    public function cleanup($config) {
-        return $this->cache->cleanup($config);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php b/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php
deleted file mode 100644 (file)
index d4cc35c..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-/**
- * Definition cache decorator class that cleans up the cache
- * whenever there is a cache miss.
- */
-class HTMLPurifier_DefinitionCache_Decorator_Cleanup extends
-      HTMLPurifier_DefinitionCache_Decorator
-{
-
-    public $name = 'Cleanup';
-
-    public function copy() {
-        return new HTMLPurifier_DefinitionCache_Decorator_Cleanup();
-    }
-
-    public function add($def, $config) {
-        $status = parent::add($def, $config);
-        if (!$status) parent::cleanup($config);
-        return $status;
-    }
-
-    public function set($def, $config) {
-        $status = parent::set($def, $config);
-        if (!$status) parent::cleanup($config);
-        return $status;
-    }
-
-    public function replace($def, $config) {
-        $status = parent::replace($def, $config);
-        if (!$status) parent::cleanup($config);
-        return $status;
-    }
-
-    public function get($config) {
-        $ret = parent::get($config);
-        if (!$ret) parent::cleanup($config);
-        return $ret;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/DefinitionCache/Decorator/Memory.php b/library/HTMLPurifier/DefinitionCache/Decorator/Memory.php
deleted file mode 100644 (file)
index 18f16d3..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-/**
- * Definition cache decorator class that saves all cache retrievals
- * to PHP's memory; good for unit tests or circumstances where
- * there are lots of configuration objects floating around.
- */
-class HTMLPurifier_DefinitionCache_Decorator_Memory extends
-      HTMLPurifier_DefinitionCache_Decorator
-{
-
-    protected $definitions;
-    public $name = 'Memory';
-
-    public function copy() {
-        return new HTMLPurifier_DefinitionCache_Decorator_Memory();
-    }
-
-    public function add($def, $config) {
-        $status = parent::add($def, $config);
-        if ($status) $this->definitions[$this->generateKey($config)] = $def;
-        return $status;
-    }
-
-    public function set($def, $config) {
-        $status = parent::set($def, $config);
-        if ($status) $this->definitions[$this->generateKey($config)] = $def;
-        return $status;
-    }
-
-    public function replace($def, $config) {
-        $status = parent::replace($def, $config);
-        if ($status) $this->definitions[$this->generateKey($config)] = $def;
-        return $status;
-    }
-
-    public function get($config) {
-        $key = $this->generateKey($config);
-        if (isset($this->definitions[$key])) return $this->definitions[$key];
-        $this->definitions[$key] = parent::get($config);
-        return $this->definitions[$key];
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in b/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in
deleted file mode 100644 (file)
index 21a8fcf..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-require_once 'HTMLPurifier/DefinitionCache/Decorator.php';
-
-/**
- * Definition cache decorator template.
- */
-class HTMLPurifier_DefinitionCache_Decorator_Template extends
-      HTMLPurifier_DefinitionCache_Decorator
-{
-
-    var $name = 'Template'; // replace this
-
-    function copy() {
-        // replace class name with yours
-        return new HTMLPurifier_DefinitionCache_Decorator_Template();
-    }
-
-    // remove methods you don't need
-
-    function add($def, $config) {
-        return parent::add($def, $config);
-    }
-
-    function set($def, $config) {
-        return parent::set($def, $config);
-    }
-
-    function replace($def, $config) {
-        return parent::replace($def, $config);
-    }
-
-    function get($config) {
-        return parent::get($config);
-    }
-
-    function flush() {
-        return parent::flush();
-    }
-
-    function cleanup($config) {
-        return parent::cleanup($config);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/DefinitionCache/Null.php b/library/HTMLPurifier/DefinitionCache/Null.php
deleted file mode 100644 (file)
index 41d97e7..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
- * Null cache object to use when no caching is on.
- */
-class HTMLPurifier_DefinitionCache_Null extends HTMLPurifier_DefinitionCache
-{
-
-    public function add($def, $config) {
-        return false;
-    }
-
-    public function set($def, $config) {
-        return false;
-    }
-
-    public function replace($def, $config) {
-        return false;
-    }
-
-    public function remove($config) {
-        return false;
-    }
-
-    public function get($config) {
-        return false;
-    }
-
-    public function flush($config) {
-        return false;
-    }
-
-    public function cleanup($config) {
-        return false;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/DefinitionCache/Serializer.php b/library/HTMLPurifier/DefinitionCache/Serializer.php
deleted file mode 100644 (file)
index 7a6aa93..0000000
+++ /dev/null
@@ -1,172 +0,0 @@
-<?php
-
-class HTMLPurifier_DefinitionCache_Serializer extends
-      HTMLPurifier_DefinitionCache
-{
-
-    public function add($def, $config) {
-        if (!$this->checkDefType($def)) return;
-        $file = $this->generateFilePath($config);
-        if (file_exists($file)) return false;
-        if (!$this->_prepareDir($config)) return false;
-        return $this->_write($file, serialize($def));
-    }
-
-    public function set($def, $config) {
-        if (!$this->checkDefType($def)) return;
-        $file = $this->generateFilePath($config);
-        if (!$this->_prepareDir($config)) return false;
-        return $this->_write($file, serialize($def));
-    }
-
-    public function replace($def, $config) {
-        if (!$this->checkDefType($def)) return;
-        $file = $this->generateFilePath($config);
-        if (!file_exists($file)) return false;
-        if (!$this->_prepareDir($config)) return false;
-        return $this->_write($file, serialize($def));
-    }
-
-    public function get($config) {
-        $file = $this->generateFilePath($config);
-        if (!file_exists($file)) return false;
-        return unserialize(file_get_contents($file));
-    }
-
-    public function remove($config) {
-        $file = $this->generateFilePath($config);
-        if (!file_exists($file)) return false;
-        return unlink($file);
-    }
-
-    public function flush($config) {
-        if (!$this->_prepareDir($config)) return false;
-        $dir = $this->generateDirectoryPath($config);
-        $dh  = opendir($dir);
-        while (false !== ($filename = readdir($dh))) {
-            if (empty($filename)) continue;
-            if ($filename[0] === '.') continue;
-            unlink($dir . '/' . $filename);
-        }
-    }
-
-    public function cleanup($config) {
-        if (!$this->_prepareDir($config)) return false;
-        $dir = $this->generateDirectoryPath($config);
-        $dh  = opendir($dir);
-        while (false !== ($filename = readdir($dh))) {
-            if (empty($filename)) continue;
-            if ($filename[0] === '.') continue;
-            $key = substr($filename, 0, strlen($filename) - 4);
-            if ($this->isOld($key, $config)) unlink($dir . '/' . $filename);
-        }
-    }
-
-    /**
-     * Generates the file path to the serial file corresponding to
-     * the configuration and definition name
-     * @todo Make protected
-     */
-    public function generateFilePath($config) {
-        $key = $this->generateKey($config);
-        return $this->generateDirectoryPath($config) . '/' . $key . '.ser';
-    }
-
-    /**
-     * Generates the path to the directory contain this cache's serial files
-     * @note No trailing slash
-     * @todo Make protected
-     */
-    public function generateDirectoryPath($config) {
-        $base = $this->generateBaseDirectoryPath($config);
-        return $base . '/' . $this->type;
-    }
-
-    /**
-     * Generates path to base directory that contains all definition type
-     * serials
-     * @todo Make protected
-     */
-    public function generateBaseDirectoryPath($config) {
-        $base = $config->get('Cache.SerializerPath');
-        $base = is_null($base) ? HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer' : $base;
-        return $base;
-    }
-
-    /**
-     * Convenience wrapper function for file_put_contents
-     * @param $file File name to write to
-     * @param $data Data to write into file
-     * @return Number of bytes written if success, or false if failure.
-     */
-    private function _write($file, $data) {
-        return file_put_contents($file, $data);
-    }
-
-    /**
-     * Prepares the directory that this type stores the serials in
-     * @return True if successful
-     */
-    private function _prepareDir($config) {
-        $directory = $this->generateDirectoryPath($config);
-        if (!is_dir($directory)) {
-            $base = $this->generateBaseDirectoryPath($config);
-            if (!is_dir($base)) {
-                trigger_error('Base directory '.$base.' does not exist,
-                    please create or change using %Cache.SerializerPath',
-                    E_USER_WARNING);
-                return false;
-            } elseif (!$this->_testPermissions($base)) {
-                return false;
-            }
-            $old = umask(0022); // disable group and world writes
-            mkdir($directory);
-            umask($old);
-        } elseif (!$this->_testPermissions($directory)) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Tests permissions on a directory and throws out friendly
-     * error messages and attempts to chmod it itself if possible
-     */
-    private function _testPermissions($dir) {
-        // early abort, if it is writable, everything is hunky-dory
-        if (is_writable($dir)) return true;
-        if (!is_dir($dir)) {
-            // generally, you'll want to handle this beforehand
-            // so a more specific error message can be given
-            trigger_error('Directory '.$dir.' does not exist',
-                E_USER_WARNING);
-            return false;
-        }
-        if (function_exists('posix_getuid')) {
-            // POSIX system, we can give more specific advice
-            if (fileowner($dir) === posix_getuid()) {
-                // we can chmod it ourselves
-                chmod($dir, 0755);
-                return true;
-            } elseif (filegroup($dir) === posix_getgid()) {
-                $chmod = '775';
-            } else {
-                // PHP's probably running as nobody, so we'll
-                // need to give global permissions
-                $chmod = '777';
-            }
-            trigger_error('Directory '.$dir.' not writable, '.
-                'please chmod to ' . $chmod,
-                E_USER_WARNING);
-        } else {
-            // generic error message
-            trigger_error('Directory '.$dir.' not writable, '.
-                'please alter file permissions',
-                E_USER_WARNING);
-        }
-        return false;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/DefinitionCache/Serializer/README b/library/HTMLPurifier/DefinitionCache/Serializer/README
deleted file mode 100644 (file)
index 2e35c1c..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-This is a dummy file to prevent Git from ignoring this empty directory.
-
-    vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/DefinitionCacheFactory.php b/library/HTMLPurifier/DefinitionCacheFactory.php
deleted file mode 100644 (file)
index a6ead62..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-<?php
-
-/**
- * Responsible for creating definition caches.
- */
-class HTMLPurifier_DefinitionCacheFactory
-{
-
-    protected $caches = array('Serializer' => array());
-    protected $implementations = array();
-    protected $decorators = array();
-
-    /**
-     * Initialize default decorators
-     */
-    public function setup() {
-        $this->addDecorator('Cleanup');
-    }
-
-    /**
-     * Retrieves an instance of global definition cache factory.
-     */
-    public static function instance($prototype = null) {
-        static $instance;
-        if ($prototype !== null) {
-            $instance = $prototype;
-        } elseif ($instance === null || $prototype === true) {
-            $instance = new HTMLPurifier_DefinitionCacheFactory();
-            $instance->setup();
-        }
-        return $instance;
-    }
-
-    /**
-     * Registers a new definition cache object
-     * @param $short Short name of cache object, for reference
-     * @param $long Full class name of cache object, for construction
-     */
-    public function register($short, $long) {
-        $this->implementations[$short] = $long;
-    }
-
-    /**
-     * Factory method that creates a cache object based on configuration
-     * @param $name Name of definitions handled by cache
-     * @param $config Instance of HTMLPurifier_Config
-     */
-    public function create($type, $config) {
-        $method = $config->get('Cache.DefinitionImpl');
-        if ($method === null) {
-            return new HTMLPurifier_DefinitionCache_Null($type);
-        }
-        if (!empty($this->caches[$method][$type])) {
-            return $this->caches[$method][$type];
-        }
-        if (
-          isset($this->implementations[$method]) &&
-          class_exists($class = $this->implementations[$method], false)
-        ) {
-            $cache = new $class($type);
-        } else {
-            if ($method != 'Serializer') {
-                trigger_error("Unrecognized DefinitionCache $method, using Serializer instead", E_USER_WARNING);
-            }
-            $cache = new HTMLPurifier_DefinitionCache_Serializer($type);
-        }
-        foreach ($this->decorators as $decorator) {
-            $new_cache = $decorator->decorate($cache);
-            // prevent infinite recursion in PHP 4
-            unset($cache);
-            $cache = $new_cache;
-        }
-        $this->caches[$method][$type] = $cache;
-        return $this->caches[$method][$type];
-    }
-
-    /**
-     * Registers a decorator to add to all new cache objects
-     * @param
-     */
-    public function addDecorator($decorator) {
-        if (is_string($decorator)) {
-            $class = "HTMLPurifier_DefinitionCache_Decorator_$decorator";
-            $decorator = new $class;
-        }
-        $this->decorators[$decorator->name] = $decorator;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Doctype.php b/library/HTMLPurifier/Doctype.php
deleted file mode 100644 (file)
index 1e3c574..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-
-/**
- * Represents a document type, contains information on which modules
- * need to be loaded.
- * @note This class is inspected by Printer_HTMLDefinition->renderDoctype.
- *       If structure changes, please update that function.
- */
-class HTMLPurifier_Doctype
-{
-    /**
-     * Full name of doctype
-     */
-    public $name;
-
-    /**
-     * List of standard modules (string identifiers or literal objects)
-     * that this doctype uses
-     */
-    public $modules = array();
-
-    /**
-     * List of modules to use for tidying up code
-     */
-    public $tidyModules = array();
-
-    /**
-     * Is the language derived from XML (i.e. XHTML)?
-     */
-    public $xml = true;
-
-    /**
-     * List of aliases for this doctype
-     */
-    public $aliases = array();
-
-    /**
-     * Public DTD identifier
-     */
-    public $dtdPublic;
-
-    /**
-     * System DTD identifier
-     */
-    public $dtdSystem;
-
-    public function __construct($name = null, $xml = true, $modules = array(),
-        $tidyModules = array(), $aliases = array(), $dtd_public = null, $dtd_system = null
-    ) {
-        $this->name         = $name;
-        $this->xml          = $xml;
-        $this->modules      = $modules;
-        $this->tidyModules  = $tidyModules;
-        $this->aliases      = $aliases;
-        $this->dtdPublic    = $dtd_public;
-        $this->dtdSystem    = $dtd_system;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/DoctypeRegistry.php b/library/HTMLPurifier/DoctypeRegistry.php
deleted file mode 100644 (file)
index 86049e9..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-<?php
-
-class HTMLPurifier_DoctypeRegistry
-{
-
-    /**
-     * Hash of doctype names to doctype objects
-     */
-    protected $doctypes;
-
-    /**
-     * Lookup table of aliases to real doctype names
-     */
-    protected $aliases;
-
-    /**
-     * Registers a doctype to the registry
-     * @note Accepts a fully-formed doctype object, or the
-     *       parameters for constructing a doctype object
-     * @param $doctype Name of doctype or literal doctype object
-     * @param $modules Modules doctype will load
-     * @param $modules_for_modes Modules doctype will load for certain modes
-     * @param $aliases Alias names for doctype
-     * @return Editable registered doctype
-     */
-    public function register($doctype, $xml = true, $modules = array(),
-        $tidy_modules = array(), $aliases = array(), $dtd_public = null, $dtd_system = null
-    ) {
-        if (!is_array($modules)) $modules = array($modules);
-        if (!is_array($tidy_modules)) $tidy_modules = array($tidy_modules);
-        if (!is_array($aliases)) $aliases = array($aliases);
-        if (!is_object($doctype)) {
-            $doctype = new HTMLPurifier_Doctype(
-                $doctype, $xml, $modules, $tidy_modules, $aliases, $dtd_public, $dtd_system
-            );
-        }
-        $this->doctypes[$doctype->name] = $doctype;
-        $name = $doctype->name;
-        // hookup aliases
-        foreach ($doctype->aliases as $alias) {
-            if (isset($this->doctypes[$alias])) continue;
-            $this->aliases[$alias] = $name;
-        }
-        // remove old aliases
-        if (isset($this->aliases[$name])) unset($this->aliases[$name]);
-        return $doctype;
-    }
-
-    /**
-     * Retrieves reference to a doctype of a certain name
-     * @note This function resolves aliases
-     * @note When possible, use the more fully-featured make()
-     * @param $doctype Name of doctype
-     * @return Editable doctype object
-     */
-    public function get($doctype) {
-        if (isset($this->aliases[$doctype])) $doctype = $this->aliases[$doctype];
-        if (!isset($this->doctypes[$doctype])) {
-            trigger_error('Doctype ' . htmlspecialchars($doctype) . ' does not exist', E_USER_ERROR);
-            $anon = new HTMLPurifier_Doctype($doctype);
-            return $anon;
-        }
-        return $this->doctypes[$doctype];
-    }
-
-    /**
-     * Creates a doctype based on a configuration object,
-     * will perform initialization on the doctype
-     * @note Use this function to get a copy of doctype that config
-     *       can hold on to (this is necessary in order to tell
-     *       Generator whether or not the current document is XML
-     *       based or not).
-     */
-    public function make($config) {
-        return clone $this->get($this->getDoctypeFromConfig($config));
-    }
-
-    /**
-     * Retrieves the doctype from the configuration object
-     */
-    public function getDoctypeFromConfig($config) {
-        // recommended test
-        $doctype = $config->get('HTML.Doctype');
-        if (!empty($doctype)) return $doctype;
-        $doctype = $config->get('HTML.CustomDoctype');
-        if (!empty($doctype)) return $doctype;
-        // backwards-compatibility
-        if ($config->get('HTML.XHTML')) {
-            $doctype = 'XHTML 1.0';
-        } else {
-            $doctype = 'HTML 4.01';
-        }
-        if ($config->get('HTML.Strict')) {
-            $doctype .= ' Strict';
-        } else {
-            $doctype .= ' Transitional';
-        }
-        return $doctype;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ElementDef.php b/library/HTMLPurifier/ElementDef.php
deleted file mode 100644 (file)
index 5498d95..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-<?php
-
-/**
- * Structure that stores an HTML element definition. Used by
- * HTMLPurifier_HTMLDefinition and HTMLPurifier_HTMLModule.
- * @note This class is inspected by HTMLPurifier_Printer_HTMLDefinition.
- *       Please update that class too.
- * @warning If you add new properties to this class, you MUST update
- *          the mergeIn() method.
- */
-class HTMLPurifier_ElementDef
-{
-
-    /**
-     * Does the definition work by itself, or is it created solely
-     * for the purpose of merging into another definition?
-     */
-    public $standalone = true;
-
-    /**
-     * Associative array of attribute name to HTMLPurifier_AttrDef
-     * @note Before being processed by HTMLPurifier_AttrCollections
-     *       when modules are finalized during
-     *       HTMLPurifier_HTMLDefinition->setup(), this array may also
-     *       contain an array at index 0 that indicates which attribute
-     *       collections to load into the full array. It may also
-     *       contain string indentifiers in lieu of HTMLPurifier_AttrDef,
-     *       see HTMLPurifier_AttrTypes on how they are expanded during
-     *       HTMLPurifier_HTMLDefinition->setup() processing.
-     */
-    public $attr = array();
-
-    /**
-     * Indexed list of tag's HTMLPurifier_AttrTransform to be done before validation
-     */
-    public $attr_transform_pre = array();
-
-    /**
-     * Indexed list of tag's HTMLPurifier_AttrTransform to be done after validation
-     */
-    public $attr_transform_post = array();
-
-    /**
-     * HTMLPurifier_ChildDef of this tag.
-     */
-    public $child;
-
-    /**
-     * Abstract string representation of internal ChildDef rules. See
-     * HTMLPurifier_ContentSets for how this is parsed and then transformed
-     * into an HTMLPurifier_ChildDef.
-     * @warning This is a temporary variable that is not available after
-     *      being processed by HTMLDefinition
-     */
-    public $content_model;
-
-    /**
-     * Value of $child->type, used to determine which ChildDef to use,
-     * used in combination with $content_model.
-     * @warning This must be lowercase
-     * @warning This is a temporary variable that is not available after
-     *      being processed by HTMLDefinition
-     */
-    public $content_model_type;
-
-
-
-    /**
-     * Does the element have a content model (#PCDATA | Inline)*? This
-     * is important for chameleon ins and del processing in
-     * HTMLPurifier_ChildDef_Chameleon. Dynamically set: modules don't
-     * have to worry about this one.
-     */
-    public $descendants_are_inline = false;
-
-    /**
-     * List of the names of required attributes this element has. Dynamically
-     * populated by HTMLPurifier_HTMLDefinition::getElement
-     */
-    public $required_attr = array();
-
-    /**
-     * Lookup table of tags excluded from all descendants of this tag.
-     * @note SGML permits exclusions for all descendants, but this is
-     *       not possible with DTDs or XML Schemas. W3C has elected to
-     *       use complicated compositions of content_models to simulate
-     *       exclusion for children, but we go the simpler, SGML-style
-     *       route of flat-out exclusions, which correctly apply to
-     *       all descendants and not just children. Note that the XHTML
-     *       Modularization Abstract Modules are blithely unaware of such
-     *       distinctions.
-     */
-    public $excludes = array();
-
-    /**
-     * This tag is explicitly auto-closed by the following tags.
-     */
-    public $autoclose = array();
-
-    /**
-     * If a foreign element is found in this element, test if it is
-     * allowed by this sub-element; if it is, instead of closing the
-     * current element, place it inside this element.
-     */
-    public $wrap;
-
-    /**
-     * Whether or not this is a formatting element affected by the
-     * "Active Formatting Elements" algorithm.
-     */
-    public $formatting;
-
-    /**
-     * Low-level factory constructor for creating new standalone element defs
-     */
-    public static function create($content_model, $content_model_type, $attr) {
-        $def = new HTMLPurifier_ElementDef();
-        $def->content_model = $content_model;
-        $def->content_model_type = $content_model_type;
-        $def->attr = $attr;
-        return $def;
-    }
-
-    /**
-     * Merges the values of another element definition into this one.
-     * Values from the new element def take precedence if a value is
-     * not mergeable.
-     */
-    public function mergeIn($def) {
-
-        // later keys takes precedence
-        foreach($def->attr as $k => $v) {
-            if ($k === 0) {
-                // merge in the includes
-                // sorry, no way to override an include
-                foreach ($v as $v2) {
-                    $this->attr[0][] = $v2;
-                }
-                continue;
-            }
-            if ($v === false) {
-                if (isset($this->attr[$k])) unset($this->attr[$k]);
-                continue;
-            }
-            $this->attr[$k] = $v;
-        }
-        $this->_mergeAssocArray($this->attr_transform_pre, $def->attr_transform_pre);
-        $this->_mergeAssocArray($this->attr_transform_post, $def->attr_transform_post);
-        $this->_mergeAssocArray($this->excludes, $def->excludes);
-
-        if(!empty($def->content_model)) {
-            $this->content_model =
-                str_replace("#SUPER", $this->content_model, $def->content_model);
-            $this->child = false;
-        }
-        if(!empty($def->content_model_type)) {
-            $this->content_model_type = $def->content_model_type;
-            $this->child = false;
-        }
-        if(!is_null($def->child)) $this->child = $def->child;
-        if(!is_null($def->formatting)) $this->formatting = $def->formatting;
-        if($def->descendants_are_inline) $this->descendants_are_inline = $def->descendants_are_inline;
-
-    }
-
-    /**
-     * Merges one array into another, removes values which equal false
-     * @param $a1 Array by reference that is merged into
-     * @param $a2 Array that merges into $a1
-     */
-    private function _mergeAssocArray(&$a1, $a2) {
-        foreach ($a2 as $k => $v) {
-            if ($v === false) {
-                if (isset($a1[$k])) unset($a1[$k]);
-                continue;
-            }
-            $a1[$k] = $v;
-        }
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Encoder.php b/library/HTMLPurifier/Encoder.php
deleted file mode 100644 (file)
index 2b3140c..0000000
+++ /dev/null
@@ -1,426 +0,0 @@
-<?php
-
-/**
- * A UTF-8 specific character encoder that handles cleaning and transforming.
- * @note All functions in this class should be static.
- */
-class HTMLPurifier_Encoder
-{
-
-    /**
-     * Constructor throws fatal error if you attempt to instantiate class
-     */
-    private function __construct() {
-        trigger_error('Cannot instantiate encoder, call methods statically', E_USER_ERROR);
-    }
-
-    /**
-     * Error-handler that mutes errors, alternative to shut-up operator.
-     */
-    public static function muteErrorHandler() {}
-
-    /**
-     * Cleans a UTF-8 string for well-formedness and SGML validity
-     *
-     * It will parse according to UTF-8 and return a valid UTF8 string, with
-     * non-SGML codepoints excluded.
-     *
-     * @note Just for reference, the non-SGML code points are 0 to 31 and
-     *       127 to 159, inclusive.  However, we allow code points 9, 10
-     *       and 13, which are the tab, line feed and carriage return
-     *       respectively. 128 and above the code points map to multibyte
-     *       UTF-8 representations.
-     *
-     * @note Fallback code adapted from utf8ToUnicode by Henri Sivonen and
-     *       hsivonen@iki.fi at <http://iki.fi/hsivonen/php-utf8/> under the
-     *       LGPL license.  Notes on what changed are inside, but in general,
-     *       the original code transformed UTF-8 text into an array of integer
-     *       Unicode codepoints. Understandably, transforming that back to
-     *       a string would be somewhat expensive, so the function was modded to
-     *       directly operate on the string.  However, this discourages code
-     *       reuse, and the logic enumerated here would be useful for any
-     *       function that needs to be able to understand UTF-8 characters.
-     *       As of right now, only smart lossless character encoding converters
-     *       would need that, and I'm probably not going to implement them.
-     *       Once again, PHP 6 should solve all our problems.
-     */
-    public static function cleanUTF8($str, $force_php = false) {
-
-        // UTF-8 validity is checked since PHP 4.3.5
-        // This is an optimization: if the string is already valid UTF-8, no
-        // need to do PHP stuff. 99% of the time, this will be the case.
-        // The regexp matches the XML char production, as well as well as excluding
-        // non-SGML codepoints U+007F to U+009F
-        if (preg_match('/^[\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]*$/Du', $str)) {
-            return $str;
-        }
-
-        $mState = 0; // cached expected number of octets after the current octet
-                     // until the beginning of the next UTF8 character sequence
-        $mUcs4  = 0; // cached Unicode character
-        $mBytes = 1; // cached expected number of octets in the current sequence
-
-        // original code involved an $out that was an array of Unicode
-        // codepoints.  Instead of having to convert back into UTF-8, we've
-        // decided to directly append valid UTF-8 characters onto a string
-        // $out once they're done.  $char accumulates raw bytes, while $mUcs4
-        // turns into the Unicode code point, so there's some redundancy.
-
-        $out = '';
-        $char = '';
-
-        $len = strlen($str);
-        for($i = 0; $i < $len; $i++) {
-            $in = ord($str{$i});
-            $char .= $str[$i]; // append byte to char
-            if (0 == $mState) {
-                // When mState is zero we expect either a US-ASCII character
-                // or a multi-octet sequence.
-                if (0 == (0x80 & ($in))) {
-                    // US-ASCII, pass straight through.
-                    if (($in <= 31 || $in == 127) &&
-                        !($in == 9 || $in == 13 || $in == 10) // save \r\t\n
-                    ) {
-                        // control characters, remove
-                    } else {
-                        $out .= $char;
-                    }
-                    // reset
-                    $char = '';
-                    $mBytes = 1;
-                } elseif (0xC0 == (0xE0 & ($in))) {
-                    // First octet of 2 octet sequence
-                    $mUcs4 = ($in);
-                    $mUcs4 = ($mUcs4 & 0x1F) << 6;
-                    $mState = 1;
-                    $mBytes = 2;
-                } elseif (0xE0 == (0xF0 & ($in))) {
-                    // First octet of 3 octet sequence
-                    $mUcs4 = ($in);
-                    $mUcs4 = ($mUcs4 & 0x0F) << 12;
-                    $mState = 2;
-                    $mBytes = 3;
-                } elseif (0xF0 == (0xF8 & ($in))) {
-                    // First octet of 4 octet sequence
-                    $mUcs4 = ($in);
-                    $mUcs4 = ($mUcs4 & 0x07) << 18;
-                    $mState = 3;
-                    $mBytes = 4;
-                } elseif (0xF8 == (0xFC & ($in))) {
-                    // First octet of 5 octet sequence.
-                    //
-                    // This is illegal because the encoded codepoint must be
-                    // either:
-                    // (a) not the shortest form or
-                    // (b) outside the Unicode range of 0-0x10FFFF.
-                    // Rather than trying to resynchronize, we will carry on
-                    // until the end of the sequence and let the later error
-                    // handling code catch it.
-                    $mUcs4 = ($in);
-                    $mUcs4 = ($mUcs4 & 0x03) << 24;
-                    $mState = 4;
-                    $mBytes = 5;
-                } elseif (0xFC == (0xFE & ($in))) {
-                    // First octet of 6 octet sequence, see comments for 5
-                    // octet sequence.
-                    $mUcs4 = ($in);
-                    $mUcs4 = ($mUcs4 & 1) << 30;
-                    $mState = 5;
-                    $mBytes = 6;
-                } else {
-                    // Current octet is neither in the US-ASCII range nor a
-                    // legal first octet of a multi-octet sequence.
-                    $mState = 0;
-                    $mUcs4  = 0;
-                    $mBytes = 1;
-                    $char = '';
-                }
-            } else {
-                // When mState is non-zero, we expect a continuation of the
-                // multi-octet sequence
-                if (0x80 == (0xC0 & ($in))) {
-                    // Legal continuation.
-                    $shift = ($mState - 1) * 6;
-                    $tmp = $in;
-                    $tmp = ($tmp & 0x0000003F) << $shift;
-                    $mUcs4 |= $tmp;
-
-                    if (0 == --$mState) {
-                        // End of the multi-octet sequence. mUcs4 now contains
-                        // the final Unicode codepoint to be output
-
-                        // Check for illegal sequences and codepoints.
-
-                        // From Unicode 3.1, non-shortest form is illegal
-                        if (((2 == $mBytes) && ($mUcs4 < 0x0080)) ||
-                            ((3 == $mBytes) && ($mUcs4 < 0x0800)) ||
-                            ((4 == $mBytes) && ($mUcs4 < 0x10000)) ||
-                            (4 < $mBytes) ||
-                            // From Unicode 3.2, surrogate characters = illegal
-                            (($mUcs4 & 0xFFFFF800) == 0xD800) ||
-                            // Codepoints outside the Unicode range are illegal
-                            ($mUcs4 > 0x10FFFF)
-                        ) {
-
-                        } elseif (0xFEFF != $mUcs4 && // omit BOM
-                            // check for valid Char unicode codepoints
-                            (
-                                0x9 == $mUcs4 ||
-                                0xA == $mUcs4 ||
-                                0xD == $mUcs4 ||
-                                (0x20 <= $mUcs4 && 0x7E >= $mUcs4) ||
-                                // 7F-9F is not strictly prohibited by XML,
-                                // but it is non-SGML, and thus we don't allow it
-                                (0xA0 <= $mUcs4 && 0xD7FF >= $mUcs4) ||
-                                (0x10000 <= $mUcs4 && 0x10FFFF >= $mUcs4)
-                            )
-                        ) {
-                            $out .= $char;
-                        }
-                        // initialize UTF8 cache (reset)
-                        $mState = 0;
-                        $mUcs4  = 0;
-                        $mBytes = 1;
-                        $char = '';
-                    }
-                } else {
-                    // ((0xC0 & (*in) != 0x80) && (mState != 0))
-                    // Incomplete multi-octet sequence.
-                    // used to result in complete fail, but we'll reset
-                    $mState = 0;
-                    $mUcs4  = 0;
-                    $mBytes = 1;
-                    $char ='';
-                }
-            }
-        }
-        return $out;
-    }
-
-    /**
-     * Translates a Unicode codepoint into its corresponding UTF-8 character.
-     * @note Based on Feyd's function at
-     *       <http://forums.devnetwork.net/viewtopic.php?p=191404#191404>,
-     *       which is in public domain.
-     * @note While we're going to do code point parsing anyway, a good
-     *       optimization would be to refuse to translate code points that
-     *       are non-SGML characters.  However, this could lead to duplication.
-     * @note This is very similar to the unichr function in
-     *       maintenance/generate-entity-file.php (although this is superior,
-     *       due to its sanity checks).
-     */
-
-    // +----------+----------+----------+----------+
-    // | 33222222 | 22221111 | 111111   |          |
-    // | 10987654 | 32109876 | 54321098 | 76543210 | bit
-    // +----------+----------+----------+----------+
-    // |          |          |          | 0xxxxxxx | 1 byte 0x00000000..0x0000007F
-    // |          |          | 110yyyyy | 10xxxxxx | 2 byte 0x00000080..0x000007FF
-    // |          | 1110zzzz | 10yyyyyy | 10xxxxxx | 3 byte 0x00000800..0x0000FFFF
-    // | 11110www | 10wwzzzz | 10yyyyyy | 10xxxxxx | 4 byte 0x00010000..0x0010FFFF
-    // +----------+----------+----------+----------+
-    // | 00000000 | 00011111 | 11111111 | 11111111 | Theoretical upper limit of legal scalars: 2097151 (0x001FFFFF)
-    // | 00000000 | 00010000 | 11111111 | 11111111 | Defined upper limit of legal scalar codes
-    // +----------+----------+----------+----------+
-
-    public static function unichr($code) {
-        if($code > 1114111 or $code < 0 or
-          ($code >= 55296 and $code <= 57343) ) {
-            // bits are set outside the "valid" range as defined
-            // by UNICODE 4.1.0
-            return '';
-        }
-
-        $x = $y = $z = $w = 0;
-        if ($code < 128) {
-            // regular ASCII character
-            $x = $code;
-        } else {
-            // set up bits for UTF-8
-            $x = ($code & 63) | 128;
-            if ($code < 2048) {
-                $y = (($code & 2047) >> 6) | 192;
-            } else {
-                $y = (($code & 4032) >> 6) | 128;
-                if($code < 65536) {
-                    $z = (($code >> 12) & 15) | 224;
-                } else {
-                    $z = (($code >> 12) & 63) | 128;
-                    $w = (($code >> 18) & 7)  | 240;
-                }
-            }
-        }
-        // set up the actual character
-        $ret = '';
-        if($w) $ret .= chr($w);
-        if($z) $ret .= chr($z);
-        if($y) $ret .= chr($y);
-        $ret .= chr($x);
-
-        return $ret;
-    }
-
-    /**
-     * Converts a string to UTF-8 based on configuration.
-     */
-    public static function convertToUTF8($str, $config, $context) {
-        $encoding = $config->get('Core.Encoding');
-        if ($encoding === 'utf-8') return $str;
-        static $iconv = null;
-        if ($iconv === null) $iconv = function_exists('iconv');
-        set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
-        if ($iconv && !$config->get('Test.ForceNoIconv')) {
-            $str = iconv($encoding, 'utf-8//IGNORE', $str);
-            if ($str === false) {
-                // $encoding is not a valid encoding
-                restore_error_handler();
-                trigger_error('Invalid encoding ' . $encoding, E_USER_ERROR);
-                return '';
-            }
-            // If the string is bjorked by Shift_JIS or a similar encoding
-            // that doesn't support all of ASCII, convert the naughty
-            // characters to their true byte-wise ASCII/UTF-8 equivalents.
-            $str = strtr($str, HTMLPurifier_Encoder::testEncodingSupportsASCII($encoding));
-            restore_error_handler();
-            return $str;
-        } elseif ($encoding === 'iso-8859-1') {
-            $str = utf8_encode($str);
-            restore_error_handler();
-            return $str;
-        }
-        trigger_error('Encoding not supported, please install iconv', E_USER_ERROR);
-    }
-
-    /**
-     * Converts a string from UTF-8 based on configuration.
-     * @note Currently, this is a lossy conversion, with unexpressable
-     *       characters being omitted.
-     */
-    public static function convertFromUTF8($str, $config, $context) {
-        $encoding = $config->get('Core.Encoding');
-        if ($encoding === 'utf-8') return $str;
-        static $iconv = null;
-        if ($iconv === null) $iconv = function_exists('iconv');
-        if ($escape = $config->get('Core.EscapeNonASCIICharacters')) {
-            $str = HTMLPurifier_Encoder::convertToASCIIDumbLossless($str);
-        }
-        set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
-        if ($iconv && !$config->get('Test.ForceNoIconv')) {
-            // Undo our previous fix in convertToUTF8, otherwise iconv will barf
-            $ascii_fix = HTMLPurifier_Encoder::testEncodingSupportsASCII($encoding);
-            if (!$escape && !empty($ascii_fix)) {
-                $clear_fix = array();
-                foreach ($ascii_fix as $utf8 => $native) $clear_fix[$utf8] = '';
-                $str = strtr($str, $clear_fix);
-            }
-            $str = strtr($str, array_flip($ascii_fix));
-            // Normal stuff
-            $str = iconv('utf-8', $encoding . '//IGNORE', $str);
-            restore_error_handler();
-            return $str;
-        } elseif ($encoding === 'iso-8859-1') {
-            $str = utf8_decode($str);
-            restore_error_handler();
-            return $str;
-        }
-        trigger_error('Encoding not supported', E_USER_ERROR);
-    }
-
-    /**
-     * Lossless (character-wise) conversion of HTML to ASCII
-     * @param $str UTF-8 string to be converted to ASCII
-     * @returns ASCII encoded string with non-ASCII character entity-ized
-     * @warning Adapted from MediaWiki, claiming fair use: this is a common
-     *       algorithm. If you disagree with this license fudgery,
-     *       implement it yourself.
-     * @note Uses decimal numeric entities since they are best supported.
-     * @note This is a DUMB function: it has no concept of keeping
-     *       character entities that the projected character encoding
-     *       can allow. We could possibly implement a smart version
-     *       but that would require it to also know which Unicode
-     *       codepoints the charset supported (not an easy task).
-     * @note Sort of with cleanUTF8() but it assumes that $str is
-     *       well-formed UTF-8
-     */
-    public static function convertToASCIIDumbLossless($str) {
-        $bytesleft = 0;
-        $result = '';
-        $working = 0;
-        $len = strlen($str);
-        for( $i = 0; $i < $len; $i++ ) {
-            $bytevalue = ord( $str[$i] );
-            if( $bytevalue <= 0x7F ) { //0xxx xxxx
-                $result .= chr( $bytevalue );
-                $bytesleft = 0;
-            } elseif( $bytevalue <= 0xBF ) { //10xx xxxx
-                $working = $working << 6;
-                $working += ($bytevalue & 0x3F);
-                $bytesleft--;
-                if( $bytesleft <= 0 ) {
-                    $result .= "&#" . $working . ";";
-                }
-            } elseif( $bytevalue <= 0xDF ) { //110x xxxx
-                $working = $bytevalue & 0x1F;
-                $bytesleft = 1;
-            } elseif( $bytevalue <= 0xEF ) { //1110 xxxx
-                $working = $bytevalue & 0x0F;
-                $bytesleft = 2;
-            } else { //1111 0xxx
-                $working = $bytevalue & 0x07;
-                $bytesleft = 3;
-            }
-        }
-        return $result;
-    }
-
-    /**
-     * This expensive function tests whether or not a given character
-     * encoding supports ASCII. 7/8-bit encodings like Shift_JIS will
-     * fail this test, and require special processing. Variable width
-     * encodings shouldn't ever fail.
-     *
-     * @param string $encoding Encoding name to test, as per iconv format
-     * @param bool $bypass Whether or not to bypass the precompiled arrays.
-     * @return Array of UTF-8 characters to their corresponding ASCII,
-     *      which can be used to "undo" any overzealous iconv action.
-     */
-    public static function testEncodingSupportsASCII($encoding, $bypass = false) {
-        static $encodings = array();
-        if (!$bypass) {
-            if (isset($encodings[$encoding])) return $encodings[$encoding];
-            $lenc = strtolower($encoding);
-            switch ($lenc) {
-                case 'shift_jis':
-                    return array("\xC2\xA5" => '\\', "\xE2\x80\xBE" => '~');
-                case 'johab':
-                    return array("\xE2\x82\xA9" => '\\');
-            }
-            if (strpos($lenc, 'iso-8859-') === 0) return array();
-        }
-        $ret = array();
-        set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
-        if (iconv('UTF-8', $encoding, 'a') === false) return false;
-        for ($i = 0x20; $i <= 0x7E; $i++) { // all printable ASCII chars
-            $c = chr($i); // UTF-8 char
-            $r = iconv('UTF-8', "$encoding//IGNORE", $c); // initial conversion
-            if (
-                $r === '' ||
-                // This line is needed for iconv implementations that do not
-                // omit characters that do not exist in the target character set
-                ($r === $c && iconv($encoding, 'UTF-8//IGNORE', $r) !== $c)
-            ) {
-                // Reverse engineer: what's the UTF-8 equiv of this byte
-                // sequence? This assumes that there's no variable width
-                // encoding that doesn't support ASCII.
-                $ret[iconv($encoding, 'UTF-8//IGNORE', $c)] = $c;
-            }
-        }
-        restore_error_handler();
-        $encodings[$encoding] = $ret;
-        return $ret;
-    }
-
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/EntityLookup.php b/library/HTMLPurifier/EntityLookup.php
deleted file mode 100644 (file)
index b4dfce9..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
- * Object that provides entity lookup table from entity name to character
- */
-class HTMLPurifier_EntityLookup {
-
-    /**
-     * Assoc array of entity name to character represented.
-     */
-    public $table;
-
-    /**
-     * Sets up the entity lookup table from the serialized file contents.
-     * @note The serialized contents are versioned, but were generated
-     *       using the maintenance script generate_entity_file.php
-     * @warning This is not in constructor to help enforce the Singleton
-     */
-    public function setup($file = false) {
-        if (!$file) {
-            $file = HTMLPURIFIER_PREFIX . '/HTMLPurifier/EntityLookup/entities.ser';
-        }
-        $this->table = unserialize(file_get_contents($file));
-    }
-
-    /**
-     * Retrieves sole instance of the object.
-     * @param Optional prototype of custom lookup table to overload with.
-     */
-    public static function instance($prototype = false) {
-        // no references, since PHP doesn't copy unless modified
-        static $instance = null;
-        if ($prototype) {
-            $instance = $prototype;
-        } elseif (!$instance) {
-            $instance = new HTMLPurifier_EntityLookup();
-            $instance->setup();
-        }
-        return $instance;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/EntityLookup/entities.ser b/library/HTMLPurifier/EntityLookup/entities.ser
deleted file mode 100644 (file)
index f2b8b8f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-a:246:{s:4:"nbsp";s:2:" ";s:5:"iexcl";s:2:"¡";s:4:"cent";s:2:"¢";s:5:"pound";s:2:"£";s:6:"curren";s:2:"¤";s:3:"yen";s:2:"¥";s:6:"brvbar";s:2:"¦";s:4:"sect";s:2:"§";s:3:"uml";s:2:"¨";s:4:"copy";s:2:"©";s:4:"ordf";s:2:"ª";s:5:"laquo";s:2:"«";s:3:"not";s:2:"¬";s:3:"shy";s:2:"­";s:3:"reg";s:2:"®";s:4:"macr";s:2:"¯";s:3:"deg";s:2:"°";s:6:"plusmn";s:2:"±";s:5:"acute";s:2:"´";s:5:"micro";s:2:"µ";s:4:"para";s:2:"¶";s:6:"middot";s:2:"·";s:5:"cedil";s:2:"¸";s:4:"ordm";s:2:"º";s:5:"raquo";s:2:"»";s:6:"iquest";s:2:"¿";s:6:"Agrave";s:2:"À";s:6:"Aacute";s:2:"Á";s:5:"Acirc";s:2:"Â";s:6:"Atilde";s:2:"Ã";s:4:"Auml";s:2:"Ä";s:5:"Aring";s:2:"Å";s:5:"AElig";s:2:"Æ";s:6:"Ccedil";s:2:"Ç";s:6:"Egrave";s:2:"È";s:6:"Eacute";s:2:"É";s:5:"Ecirc";s:2:"Ê";s:4:"Euml";s:2:"Ë";s:6:"Igrave";s:2:"Ì";s:6:"Iacute";s:2:"Í";s:5:"Icirc";s:2:"Î";s:4:"Iuml";s:2:"Ï";s:3:"ETH";s:2:"Ð";s:6:"Ntilde";s:2:"Ñ";s:6:"Ograve";s:2:"Ò";s:6:"Oacute";s:2:"Ó";s:5:"Ocirc";s:2:"Ô";s:6:"Otilde";s:2:"Õ";s:4:"Ouml";s:2:"Ö";s:5:"times";s:2:"×";s:6:"Oslash";s:2:"Ø";s:6:"Ugrave";s:2:"Ù";s:6:"Uacute";s:2:"Ú";s:5:"Ucirc";s:2:"Û";s:4:"Uuml";s:2:"Ü";s:6:"Yacute";s:2:"Ý";s:5:"THORN";s:2:"Þ";s:5:"szlig";s:2:"ß";s:6:"agrave";s:2:"à";s:6:"aacute";s:2:"á";s:5:"acirc";s:2:"â";s:6:"atilde";s:2:"ã";s:4:"auml";s:2:"ä";s:5:"aring";s:2:"å";s:5:"aelig";s:2:"æ";s:6:"ccedil";s:2:"ç";s:6:"egrave";s:2:"è";s:6:"eacute";s:2:"é";s:5:"ecirc";s:2:"ê";s:4:"euml";s:2:"ë";s:6:"igrave";s:2:"ì";s:6:"iacute";s:2:"í";s:5:"icirc";s:2:"î";s:4:"iuml";s:2:"ï";s:3:"eth";s:2:"ð";s:6:"ntilde";s:2:"ñ";s:6:"ograve";s:2:"ò";s:6:"oacute";s:2:"ó";s:5:"ocirc";s:2:"ô";s:6:"otilde";s:2:"õ";s:4:"ouml";s:2:"ö";s:6:"divide";s:2:"÷";s:6:"oslash";s:2:"ø";s:6:"ugrave";s:2:"ù";s:6:"uacute";s:2:"ú";s:5:"ucirc";s:2:"û";s:4:"uuml";s:2:"ü";s:6:"yacute";s:2:"ý";s:5:"thorn";s:2:"þ";s:4:"yuml";s:2:"ÿ";s:4:"quot";s:1:""";s:3:"amp";s:1:"&";s:2:"lt";s:1:"<";s:2:"gt";s:1:">";s:4:"apos";s:1:"'";s:5:"OElig";s:2:"Œ";s:5:"oelig";s:2:"œ";s:6:"Scaron";s:2:"Š";s:6:"scaron";s:2:"š";s:4:"Yuml";s:2:"Ÿ";s:4:"circ";s:2:"ˆ";s:5:"tilde";s:2:"˜";s:4:"ensp";s:3:" ";s:4:"emsp";s:3:" ";s:6:"thinsp";s:3:" ";s:4:"zwnj";s:3:"‌";s:3:"zwj";s:3:"‍";s:3:"lrm";s:3:"‎";s:3:"rlm";s:3:"‏";s:5:"ndash";s:3:"–";s:5:"mdash";s:3:"—";s:5:"lsquo";s:3:"‘";s:5:"rsquo";s:3:"’";s:5:"sbquo";s:3:"‚";s:5:"ldquo";s:3:"“";s:5:"rdquo";s:3:"”";s:5:"bdquo";s:3:"„";s:6:"dagger";s:3:"†";s:6:"Dagger";s:3:"‡";s:6:"permil";s:3:"‰";s:6:"lsaquo";s:3:"‹";s:6:"rsaquo";s:3:"›";s:4:"euro";s:3:"€";s:4:"fnof";s:2:"ƒ";s:5:"Alpha";s:2:"Α";s:4:"Beta";s:2:"Β";s:5:"Gamma";s:2:"Γ";s:5:"Delta";s:2:"Δ";s:7:"Epsilon";s:2:"Ε";s:4:"Zeta";s:2:"Ζ";s:3:"Eta";s:2:"Η";s:5:"Theta";s:2:"Θ";s:4:"Iota";s:2:"Ι";s:5:"Kappa";s:2:"Κ";s:6:"Lambda";s:2:"Λ";s:2:"Mu";s:2:"Μ";s:2:"Nu";s:2:"Ν";s:2:"Xi";s:2:"Ξ";s:7:"Omicron";s:2:"Ο";s:2:"Pi";s:2:"Π";s:3:"Rho";s:2:"Ρ";s:5:"Sigma";s:2:"Σ";s:3:"Tau";s:2:"Τ";s:7:"Upsilon";s:2:"Υ";s:3:"Phi";s:2:"Φ";s:3:"Chi";s:2:"Χ";s:3:"Psi";s:2:"Ψ";s:5:"Omega";s:2:"Ω";s:5:"alpha";s:2:"α";s:4:"beta";s:2:"β";s:5:"gamma";s:2:"γ";s:5:"delta";s:2:"δ";s:7:"epsilon";s:2:"ε";s:4:"zeta";s:2:"ζ";s:3:"eta";s:2:"η";s:5:"theta";s:2:"θ";s:4:"iota";s:2:"ι";s:5:"kappa";s:2:"κ";s:6:"lambda";s:2:"λ";s:2:"mu";s:2:"μ";s:2:"nu";s:2:"ν";s:2:"xi";s:2:"ξ";s:7:"omicron";s:2:"ο";s:2:"pi";s:2:"π";s:3:"rho";s:2:"ρ";s:6:"sigmaf";s:2:"ς";s:5:"sigma";s:2:"σ";s:3:"tau";s:2:"τ";s:7:"upsilon";s:2:"υ";s:3:"phi";s:2:"φ";s:3:"chi";s:2:"χ";s:3:"psi";s:2:"ψ";s:5:"omega";s:2:"ω";s:8:"thetasym";s:2:"ϑ";s:5:"upsih";s:2:"ϒ";s:3:"piv";s:2:"ϖ";s:4:"bull";s:3:"•";s:6:"hellip";s:3:"…";s:5:"prime";s:3:"′";s:5:"Prime";s:3:"″";s:5:"oline";s:3:"‾";s:5:"frasl";s:3:"⁄";s:6:"weierp";s:3:"℘";s:5:"image";s:3:"ℑ";s:4:"real";s:3:"ℜ";s:5:"trade";s:3:"™";s:7:"alefsym";s:3:"ℵ";s:4:"larr";s:3:"←";s:4:"uarr";s:3:"↑";s:4:"rarr";s:3:"→";s:4:"darr";s:3:"↓";s:4:"harr";s:3:"↔";s:5:"crarr";s:3:"↵";s:4:"lArr";s:3:"⇐";s:4:"uArr";s:3:"⇑";s:4:"rArr";s:3:"⇒";s:4:"dArr";s:3:"⇓";s:4:"hArr";s:3:"⇔";s:6:"forall";s:3:"∀";s:4:"part";s:3:"∂";s:5:"exist";s:3:"∃";s:5:"empty";s:3:"∅";s:5:"nabla";s:3:"∇";s:4:"isin";s:3:"∈";s:5:"notin";s:3:"∉";s:2:"ni";s:3:"∋";s:4:"prod";s:3:"∏";s:3:"sum";s:3:"∑";s:5:"minus";s:3:"−";s:6:"lowast";s:3:"∗";s:5:"radic";s:3:"√";s:4:"prop";s:3:"∝";s:5:"infin";s:3:"∞";s:3:"ang";s:3:"∠";s:3:"and";s:3:"∧";s:2:"or";s:3:"∨";s:3:"cap";s:3:"∩";s:3:"cup";s:3:"∪";s:3:"int";s:3:"∫";s:3:"sim";s:3:"∼";s:4:"cong";s:3:"≅";s:5:"asymp";s:3:"≈";s:2:"ne";s:3:"≠";s:5:"equiv";s:3:"≡";s:2:"le";s:3:"≤";s:2:"ge";s:3:"≥";s:3:"sub";s:3:"⊂";s:3:"sup";s:3:"⊃";s:4:"nsub";s:3:"⊄";s:4:"sube";s:3:"⊆";s:4:"supe";s:3:"⊇";s:5:"oplus";s:3:"⊕";s:6:"otimes";s:3:"⊗";s:4:"perp";s:3:"⊥";s:4:"sdot";s:3:"⋅";s:5:"lceil";s:3:"⌈";s:5:"rceil";s:3:"⌉";s:6:"lfloor";s:3:"⌊";s:6:"rfloor";s:3:"⌋";s:4:"lang";s:3:"〈";s:4:"rang";s:3:"〉";s:3:"loz";s:3:"◊";s:6:"spades";s:3:"♠";s:5:"clubs";s:3:"♣";s:6:"hearts";s:3:"♥";s:5:"diams";s:3:"♦";}
\ No newline at end of file
diff --git a/library/HTMLPurifier/EntityParser.php b/library/HTMLPurifier/EntityParser.php
deleted file mode 100644 (file)
index 8c38447..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-<?php
-
-// if want to implement error collecting here, we'll need to use some sort
-// of global data (probably trigger_error) because it's impossible to pass
-// $config or $context to the callback functions.
-
-/**
- * Handles referencing and derefencing character entities
- */
-class HTMLPurifier_EntityParser
-{
-
-    /**
-     * Reference to entity lookup table.
-     */
-    protected $_entity_lookup;
-
-    /**
-     * Callback regex string for parsing entities.
-     */
-    protected $_substituteEntitiesRegex =
-'/&(?:[#]x([a-fA-F0-9]+)|[#]0*(\d+)|([A-Za-z_:][A-Za-z0-9.\-_:]*));?/';
-//     1. hex             2. dec      3. string (XML style)
-
-
-    /**
-     * Decimal to parsed string conversion table for special entities.
-     */
-    protected $_special_dec2str =
-            array(
-                    34 => '"',
-                    38 => '&',
-                    39 => "'",
-                    60 => '<',
-                    62 => '>'
-            );
-
-    /**
-     * Stripped entity names to decimal conversion table for special entities.
-     */
-    protected $_special_ent2dec =
-            array(
-                    'quot' => 34,
-                    'amp'  => 38,
-                    'lt'   => 60,
-                    'gt'   => 62
-            );
-
-    /**
-     * Substitutes non-special entities with their parsed equivalents. Since
-     * running this whenever you have parsed character is t3h 5uck, we run
-     * it before everything else.
-     *
-     * @param $string String to have non-special entities parsed.
-     * @returns Parsed string.
-     */
-    public function substituteNonSpecialEntities($string) {
-        // it will try to detect missing semicolons, but don't rely on it
-        return preg_replace_callback(
-            $this->_substituteEntitiesRegex,
-            array($this, 'nonSpecialEntityCallback'),
-            $string
-            );
-    }
-
-    /**
-     * Callback function for substituteNonSpecialEntities() that does the work.
-     *
-     * @param $matches  PCRE matches array, with 0 the entire match, and
-     *                  either index 1, 2 or 3 set with a hex value, dec value,
-     *                  or string (respectively).
-     * @returns Replacement string.
-     */
-
-    protected function nonSpecialEntityCallback($matches) {
-        // replaces all but big five
-        $entity = $matches[0];
-        $is_num = (@$matches[0][1] === '#');
-        if ($is_num) {
-            $is_hex = (@$entity[2] === 'x');
-            $code = $is_hex ? hexdec($matches[1]) : (int) $matches[2];
-
-            // abort for special characters
-            if (isset($this->_special_dec2str[$code]))  return $entity;
-
-            return HTMLPurifier_Encoder::unichr($code);
-        } else {
-            if (isset($this->_special_ent2dec[$matches[3]])) return $entity;
-            if (!$this->_entity_lookup) {
-                $this->_entity_lookup = HTMLPurifier_EntityLookup::instance();
-            }
-            if (isset($this->_entity_lookup->table[$matches[3]])) {
-                return $this->_entity_lookup->table[$matches[3]];
-            } else {
-                return $entity;
-            }
-        }
-    }
-
-    /**
-     * Substitutes only special entities with their parsed equivalents.
-     *
-     * @notice We try to avoid calling this function because otherwise, it
-     * would have to be called a lot (for every parsed section).
-     *
-     * @param $string String to have non-special entities parsed.
-     * @returns Parsed string.
-     */
-    public function substituteSpecialEntities($string) {
-        return preg_replace_callback(
-            $this->_substituteEntitiesRegex,
-            array($this, 'specialEntityCallback'),
-            $string);
-    }
-
-    /**
-     * Callback function for substituteSpecialEntities() that does the work.
-     *
-     * This callback has same syntax as nonSpecialEntityCallback().
-     *
-     * @param $matches  PCRE-style matches array, with 0 the entire match, and
-     *                  either index 1, 2 or 3 set with a hex value, dec value,
-     *                  or string (respectively).
-     * @returns Replacement string.
-     */
-    protected function specialEntityCallback($matches) {
-        $entity = $matches[0];
-        $is_num = (@$matches[0][1] === '#');
-        if ($is_num) {
-            $is_hex = (@$entity[2] === 'x');
-            $int = $is_hex ? hexdec($matches[1]) : (int) $matches[2];
-            return isset($this->_special_dec2str[$int]) ?
-                $this->_special_dec2str[$int] :
-                $entity;
-        } else {
-            return isset($this->_special_ent2dec[$matches[3]]) ?
-                $this->_special_ent2dec[$matches[3]] :
-                $entity;
-        }
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ErrorCollector.php b/library/HTMLPurifier/ErrorCollector.php
deleted file mode 100644 (file)
index 6713eaf..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-<?php
-
-/**
- * Error collection class that enables HTML Purifier to report HTML
- * problems back to the user
- */
-class HTMLPurifier_ErrorCollector
-{
-
-    /**
-     * Identifiers for the returned error array. These are purposely numeric
-     * so list() can be used.
-     */
-    const LINENO   = 0;
-    const SEVERITY = 1;
-    const MESSAGE  = 2;
-    const CHILDREN = 3;
-
-    protected $errors;
-    protected $_current;
-    protected $_stacks = array(array());
-    protected $locale;
-    protected $generator;
-    protected $context;
-
-    protected $lines = array();
-
-    public function __construct($context) {
-        $this->locale    =& $context->get('Locale');
-        $this->context   = $context;
-        $this->_current  =& $this->_stacks[0];
-        $this->errors    =& $this->_stacks[0];
-    }
-
-    /**
-     * Sends an error message to the collector for later use
-     * @param $severity int Error severity, PHP error style (don't use E_USER_)
-     * @param $msg string Error message text
-     * @param $subst1 string First substitution for $msg
-     * @param $subst2 string ...
-     */
-    public function send($severity, $msg) {
-
-        $args = array();
-        if (func_num_args() > 2) {
-            $args = func_get_args();
-            array_shift($args);
-            unset($args[0]);
-        }
-
-        $token = $this->context->get('CurrentToken', true);
-        $line  = $token ? $token->line : $this->context->get('CurrentLine', true);
-        $col   = $token ? $token->col  : $this->context->get('CurrentCol',  true);
-        $attr  = $this->context->get('CurrentAttr', true);
-
-        // perform special substitutions, also add custom parameters
-        $subst = array();
-        if (!is_null($token)) {
-            $args['CurrentToken'] = $token;
-        }
-        if (!is_null($attr)) {
-            $subst['$CurrentAttr.Name'] = $attr;
-            if (isset($token->attr[$attr])) $subst['$CurrentAttr.Value'] = $token->attr[$attr];
-        }
-
-        if (empty($args)) {
-            $msg = $this->locale->getMessage($msg);
-        } else {
-            $msg = $this->locale->formatMessage($msg, $args);
-        }
-
-        if (!empty($subst)) $msg = strtr($msg, $subst);
-
-        // (numerically indexed)
-        $error = array(
-            self::LINENO   => $line,
-            self::SEVERITY => $severity,
-            self::MESSAGE  => $msg,
-            self::CHILDREN => array()
-        );
-        $this->_current[] = $error;
-
-
-        // NEW CODE BELOW ...
-
-        $struct = null;
-        // Top-level errors are either:
-        //  TOKEN type, if $value is set appropriately, or
-        //  "syntax" type, if $value is null
-        $new_struct = new HTMLPurifier_ErrorStruct();
-        $new_struct->type = HTMLPurifier_ErrorStruct::TOKEN;
-        if ($token) $new_struct->value = clone $token;
-        if (is_int($line) && is_int($col)) {
-            if (isset($this->lines[$line][$col])) {
-                $struct = $this->lines[$line][$col];
-            } else {
-                $struct = $this->lines[$line][$col] = $new_struct;
-            }
-            // These ksorts may present a performance problem
-            ksort($this->lines[$line], SORT_NUMERIC);
-        } else {
-            if (isset($this->lines[-1])) {
-                $struct = $this->lines[-1];
-            } else {
-                $struct = $this->lines[-1] = $new_struct;
-            }
-        }
-        ksort($this->lines, SORT_NUMERIC);
-
-        // Now, check if we need to operate on a lower structure
-        if (!empty($attr)) {
-            $struct = $struct->getChild(HTMLPurifier_ErrorStruct::ATTR, $attr);
-            if (!$struct->value) {
-                $struct->value = array($attr, 'PUT VALUE HERE');
-            }
-        }
-        if (!empty($cssprop)) {
-            $struct = $struct->getChild(HTMLPurifier_ErrorStruct::CSSPROP, $cssprop);
-            if (!$struct->value) {
-                // if we tokenize CSS this might be a little more difficult to do
-                $struct->value = array($cssprop, 'PUT VALUE HERE');
-            }
-        }
-
-        // Ok, structs are all setup, now time to register the error
-        $struct->addError($severity, $msg);
-    }
-
-    /**
-     * Retrieves raw error data for custom formatter to use
-     * @param List of arrays in format of array(line of error,
-     *        error severity, error message,
-     *        recursive sub-errors array)
-     */
-    public function getRaw() {
-        return $this->errors;
-    }
-
-    /**
-     * Default HTML formatting implementation for error messages
-     * @param $config Configuration array, vital for HTML output nature
-     * @param $errors Errors array to display; used for recursion.
-     */
-    public function getHTMLFormatted($config, $errors = null) {
-        $ret = array();
-
-        $this->generator = new HTMLPurifier_Generator($config, $this->context);
-        if ($errors === null) $errors = $this->errors;
-
-        // 'At line' message needs to be removed
-
-        // generation code for new structure goes here. It needs to be recursive.
-        foreach ($this->lines as $line => $col_array) {
-            if ($line == -1) continue;
-            foreach ($col_array as $col => $struct) {
-                $this->_renderStruct($ret, $struct, $line, $col);
-            }
-        }
-        if (isset($this->lines[-1])) {
-            $this->_renderStruct($ret, $this->lines[-1]);
-        }
-
-        if (empty($errors)) {
-            return '<p>' . $this->locale->getMessage('ErrorCollector: No errors') . '</p>';
-        } else {
-            return '<ul><li>' . implode('</li><li>', $ret) . '</li></ul>';
-        }
-
-    }
-
-    private function _renderStruct(&$ret, $struct, $line = null, $col = null) {
-        $stack = array($struct);
-        $context_stack = array(array());
-        while ($current = array_pop($stack)) {
-            $context = array_pop($context_stack);
-            foreach ($current->errors as $error) {
-                list($severity, $msg) = $error;
-                $string = '';
-                $string .= '<div>';
-                // W3C uses an icon to indicate the severity of the error.
-                $error = $this->locale->getErrorName($severity);
-                $string .= "<span class=\"error e$severity\"><strong>$error</strong></span> ";
-                if (!is_null($line) && !is_null($col)) {
-                    $string .= "<em class=\"location\">Line $line, Column $col: </em> ";
-                } else {
-                    $string .= '<em class="location">End of Document: </em> ';
-                }
-                $string .= '<strong class="description">' . $this->generator->escape($msg) . '</strong> ';
-                $string .= '</div>';
-                // Here, have a marker for the character on the column appropriate.
-                // Be sure to clip extremely long lines.
-                //$string .= '<pre>';
-                //$string .= '';
-                //$string .= '</pre>';
-                $ret[] = $string;
-            }
-            foreach ($current->children as $type => $array) {
-                $context[] = $current;
-                $stack = array_merge($stack, array_reverse($array, true));
-                for ($i = count($array); $i > 0; $i--) {
-                    $context_stack[] = $context;
-                }
-            }
-        }
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/ErrorStruct.php b/library/HTMLPurifier/ErrorStruct.php
deleted file mode 100644 (file)
index 9bc8996..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-
-/**
- * Records errors for particular segments of an HTML document such as tokens,
- * attributes or CSS properties. They can contain error structs (which apply
- * to components of what they represent), but their main purpose is to hold
- * errors applying to whatever struct is being used.
- */
-class HTMLPurifier_ErrorStruct
-{
-
-    /**
-     * Possible values for $children first-key. Note that top-level structures
-     * are automatically token-level.
-     */
-    const TOKEN     = 0;
-    const ATTR      = 1;
-    const CSSPROP   = 2;
-
-    /**
-     * Type of this struct.
-     */
-    public $type;
-
-    /**
-     * Value of the struct we are recording errors for. There are various
-     * values for this:
-     *  - TOKEN: Instance of HTMLPurifier_Token
-     *  - ATTR: array('attr-name', 'value')
-     *  - CSSPROP: array('prop-name', 'value')
-     */
-    public $value;
-
-    /**
-     * Errors registered for this structure.
-     */
-    public $errors = array();
-
-    /**
-     * Child ErrorStructs that are from this structure. For example, a TOKEN
-     * ErrorStruct would contain ATTR ErrorStructs. This is a multi-dimensional
-     * array in structure: [TYPE]['identifier']
-     */
-    public $children = array();
-
-    public function getChild($type, $id) {
-        if (!isset($this->children[$type][$id])) {
-            $this->children[$type][$id] = new HTMLPurifier_ErrorStruct();
-            $this->children[$type][$id]->type = $type;
-        }
-        return $this->children[$type][$id];
-    }
-
-    public function addError($severity, $message) {
-        $this->errors[] = array($severity, $message);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Exception.php b/library/HTMLPurifier/Exception.php
deleted file mode 100644 (file)
index be85b4c..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-/**
- * Global exception class for HTML Purifier; any exceptions we throw
- * are from here.
- */
-class HTMLPurifier_Exception extends Exception
-{
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Filter.php b/library/HTMLPurifier/Filter.php
deleted file mode 100644 (file)
index 9a0e7b0..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-/**
- * Represents a pre or post processing filter on HTML Purifier's output
- *
- * Sometimes, a little ad-hoc fixing of HTML has to be done before
- * it gets sent through HTML Purifier: you can use filters to acheive
- * this effect. For instance, YouTube videos can be preserved using
- * this manner. You could have used a decorator for this task, but
- * PHP's support for them is not terribly robust, so we're going
- * to just loop through the filters.
- *
- * Filters should be exited first in, last out. If there are three filters,
- * named 1, 2 and 3, the order of execution should go 1->preFilter,
- * 2->preFilter, 3->preFilter, purify, 3->postFilter, 2->postFilter,
- * 1->postFilter.
- *
- * @note Methods are not declared abstract as it is perfectly legitimate
- *       for an implementation not to want anything to happen on a step
- */
-
-class HTMLPurifier_Filter
-{
-
-    /**
-     * Name of the filter for identification purposes
-     */
-    public $name;
-
-    /**
-     * Pre-processor function, handles HTML before HTML Purifier
-     */
-    public function preFilter($html, $config, $context) {
-        return $html;
-    }
-
-    /**
-     * Post-processor function, handles HTML after HTML Purifier
-     */
-    public function postFilter($html, $config, $context) {
-        return $html;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Filter/ExtractStyleBlocks.php b/library/HTMLPurifier/Filter/ExtractStyleBlocks.php
deleted file mode 100644 (file)
index bbf78a6..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-<?php
-
-/**
- * This filter extracts <style> blocks from input HTML, cleans them up
- * using CSSTidy, and then places them in $purifier->context->get('StyleBlocks')
- * so they can be used elsewhere in the document.
- *
- * @note
- *      See tests/HTMLPurifier/Filter/ExtractStyleBlocksTest.php for
- *      sample usage.
- *
- * @note
- *      This filter can also be used on stylesheets not included in the
- *      document--something purists would probably prefer. Just directly
- *      call HTMLPurifier_Filter_ExtractStyleBlocks->cleanCSS()
- */
-class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
-{
-
-    public $name = 'ExtractStyleBlocks';
-    private $_styleMatches = array();
-    private $_tidy;
-
-    public function __construct() {
-        $this->_tidy = new csstidy();
-    }
-
-    /**
-     * Save the contents of CSS blocks to style matches
-     * @param $matches preg_replace style $matches array
-     */
-    protected function styleCallback($matches) {
-        $this->_styleMatches[] = $matches[1];
-    }
-
-    /**
-     * Removes inline <style> tags from HTML, saves them for later use
-     * @todo Extend to indicate non-text/css style blocks
-     */
-    public function preFilter($html, $config, $context) {
-        $tidy = $config->get('Filter.ExtractStyleBlocks.TidyImpl');
-        if ($tidy !== null) $this->_tidy = $tidy;
-        $html = preg_replace_callback('#<style(?:\s.*)?>(.+)</style>#isU', array($this, 'styleCallback'), $html);
-        $style_blocks = $this->_styleMatches;
-        $this->_styleMatches = array(); // reset
-        $context->register('StyleBlocks', $style_blocks); // $context must not be reused
-        if ($this->_tidy) {
-            foreach ($style_blocks as &$style) {
-                $style = $this->cleanCSS($style, $config, $context);
-            }
-        }
-        return $html;
-    }
-
-    /**
-     * Takes CSS (the stuff found in <style>) and cleans it.
-     * @warning Requires CSSTidy <http://csstidy.sourceforge.net/>
-     * @param $css     CSS styling to clean
-     * @param $config  Instance of HTMLPurifier_Config
-     * @param $context Instance of HTMLPurifier_Context
-     * @return Cleaned CSS
-     */
-    public function cleanCSS($css, $config, $context) {
-        // prepare scope
-        $scope = $config->get('Filter.ExtractStyleBlocks.Scope');
-        if ($scope !== null) {
-            $scopes = array_map('trim', explode(',', $scope));
-        } else {
-            $scopes = array();
-        }
-        // remove comments from CSS
-        $css = trim($css);
-        if (strncmp('<!--', $css, 4) === 0) {
-            $css = substr($css, 4);
-        }
-        if (strlen($css) > 3 && substr($css, -3) == '-->') {
-            $css = substr($css, 0, -3);
-        }
-        $css = trim($css);
-        $this->_tidy->parse($css);
-        $css_definition = $config->getDefinition('CSS');
-        foreach ($this->_tidy->css as $k => $decls) {
-            // $decls are all CSS declarations inside an @ selector
-            $new_decls = array();
-            foreach ($decls as $selector => $style) {
-                $selector = trim($selector);
-                if ($selector === '') continue; // should not happen
-                if ($selector[0] === '+') {
-                    if ($selector !== '' && $selector[0] === '+') continue;
-                }
-                if (!empty($scopes)) {
-                    $new_selector = array(); // because multiple ones are possible
-                    $selectors = array_map('trim', explode(',', $selector));
-                    foreach ($scopes as $s1) {
-                        foreach ($selectors as $s2) {
-                            $new_selector[] = "$s1 $s2";
-                        }
-                    }
-                    $selector = implode(', ', $new_selector); // now it's a string
-                }
-                foreach ($style as $name => $value) {
-                    if (!isset($css_definition->info[$name])) {
-                        unset($style[$name]);
-                        continue;
-                    }
-                    $def = $css_definition->info[$name];
-                    $ret = $def->validate($value, $config, $context);
-                    if ($ret === false) unset($style[$name]);
-                    else $style[$name] = $ret;
-                }
-                $new_decls[$selector] = $style;
-            }
-            $this->_tidy->css[$k] = $new_decls;
-        }
-        // remove stuff that shouldn't be used, could be reenabled
-        // after security risks are analyzed
-        $this->_tidy->import = array();
-        $this->_tidy->charset = null;
-        $this->_tidy->namespace = null;
-        $css = $this->_tidy->print->plain();
-        // we are going to escape any special characters <>& to ensure
-        // that no funny business occurs (i.e. </style> in a font-family prop).
-        if ($config->get('Filter.ExtractStyleBlocks.Escaping')) {
-            $css = str_replace(
-                array('<',    '>',    '&'),
-                array('\3C ', '\3E ', '\26 '),
-                $css
-            );
-        }
-        return $css;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Filter/YouTube.php b/library/HTMLPurifier/Filter/YouTube.php
deleted file mode 100644 (file)
index 23df221..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter
-{
-
-    public $name = 'YouTube';
-
-    public function preFilter($html, $config, $context) {
-        $pre_regex = '#<object[^>]+>.+?'.
-            'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s';
-        $pre_replace = '<span class="youtube-embed">\1</span>';
-        return preg_replace($pre_regex, $pre_replace, $html);
-    }
-
-    public function postFilter($html, $config, $context) {
-        $post_regex = '#<span class="youtube-embed">((?:v|cp)/[A-Za-z0-9\-_=]+)</span>#';
-        return preg_replace_callback($post_regex, array($this, 'postFilterCallback'), $html);
-    }
-
-    protected function armorUrl($url) {
-        return str_replace('--', '-&#45;', $url);
-    }
-
-    protected function postFilterCallback($matches) {
-        $url = $this->armorUrl($matches[1]);
-        return '<object width="425" height="350" type="application/x-shockwave-flash" '.
-            'data="http://www.youtube.com/'.$url.'">'.
-            '<param name="movie" value="http://www.youtube.com/'.$url.'"></param>'.
-            '<!--[if IE]>'.
-            '<embed src="http://www.youtube.com/'.$url.'"'.
-            'type="application/x-shockwave-flash"'.
-            'wmode="transparent" width="425" height="350" />'.
-            '<![endif]-->'.
-            '</object>';
-
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Generator.php b/library/HTMLPurifier/Generator.php
deleted file mode 100644 (file)
index 4a62417..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-<?php
-
-/**
- * Generates HTML from tokens.
- * @todo Refactor interface so that configuration/context is determined
- *       upon instantiation, no need for messy generateFromTokens() calls
- * @todo Make some of the more internal functions protected, and have
- *       unit tests work around that
- */
-class HTMLPurifier_Generator
-{
-
-    /**
-     * Whether or not generator should produce XML output
-     */
-    private $_xhtml = true;
-
-    /**
-     * :HACK: Whether or not generator should comment the insides of <script> tags
-     */
-    private $_scriptFix = false;
-
-    /**
-     * Cache of HTMLDefinition during HTML output to determine whether or
-     * not attributes should be minimized.
-     */
-    private $_def;
-
-    /**
-     * Cache of %Output.SortAttr
-     */
-    private $_sortAttr;
-
-    /**
-     * Cache of %Output.FlashCompat
-     */
-    private $_flashCompat;
-
-    /**
-     * Stack for keeping track of object information when outputting IE
-     * compatibility code.
-     */
-    private $_flashStack = array();
-
-    /**
-     * Configuration for the generator
-     */
-    protected $config;
-
-    /**
-     * @param $config Instance of HTMLPurifier_Config
-     * @param $context Instance of HTMLPurifier_Context
-     */
-    public function __construct($config, $context) {
-        $this->config = $config;
-        $this->_scriptFix = $config->get('Output.CommentScriptContents');
-        $this->_sortAttr = $config->get('Output.SortAttr');
-        $this->_flashCompat = $config->get('Output.FlashCompat');
-        $this->_def = $config->getHTMLDefinition();
-        $this->_xhtml = $this->_def->doctype->xml;
-    }
-
-    /**
-     * Generates HTML from an array of tokens.
-     * @param $tokens Array of HTMLPurifier_Token
-     * @param $config HTMLPurifier_Config object
-     * @return Generated HTML
-     */
-    public function generateFromTokens($tokens) {
-        if (!$tokens) return '';
-
-        // Basic algorithm
-        $html = '';
-        for ($i = 0, $size = count($tokens); $i < $size; $i++) {
-            if ($this->_scriptFix && $tokens[$i]->name === 'script'
-                && $i + 2 < $size && $tokens[$i+2] instanceof HTMLPurifier_Token_End) {
-                // script special case
-                // the contents of the script block must be ONE token
-                // for this to work.
-                $html .= $this->generateFromToken($tokens[$i++]);
-                $html .= $this->generateScriptFromToken($tokens[$i++]);
-            }
-            $html .= $this->generateFromToken($tokens[$i]);
-        }
-
-        // Tidy cleanup
-        if (extension_loaded('tidy') && $this->config->get('Output.TidyFormat')) {
-            $tidy = new Tidy;
-            $tidy->parseString($html, array(
-               'indent'=> true,
-               'output-xhtml' => $this->_xhtml,
-               'show-body-only' => true,
-               'indent-spaces' => 2,
-               'wrap' => 68,
-            ), 'utf8');
-            $tidy->cleanRepair();
-            $html = (string) $tidy; // explicit cast necessary
-        }
-
-        // Normalize newlines to system defined value
-        $nl = $this->config->get('Output.Newline');
-        if ($nl === null) $nl = PHP_EOL;
-        if ($nl !== "\n") $html = str_replace("\n", $nl, $html);
-        return $html;
-    }
-
-    /**
-     * Generates HTML from a single token.
-     * @param $token HTMLPurifier_Token object.
-     * @return Generated HTML
-     */
-    public function generateFromToken($token) {
-        if (!$token instanceof HTMLPurifier_Token) {
-            trigger_error('Cannot generate HTML from non-HTMLPurifier_Token object', E_USER_WARNING);
-            return '';
-
-        } elseif ($token instanceof HTMLPurifier_Token_Start) {
-            $attr = $this->generateAttributes($token->attr, $token->name);
-            if ($this->_flashCompat) {
-                if ($token->name == "object") {
-                    $flash = new stdclass();
-                    $flash->attr = $token->attr;
-                    $flash->param = array();
-                    $this->_flashStack[] = $flash;
-                }
-            }
-            return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>';
-
-        } elseif ($token instanceof HTMLPurifier_Token_End) {
-            $_extra = '';
-            if ($this->_flashCompat) {
-                if ($token->name == "object" && !empty($this->_flashStack)) {
-                    $flash = array_pop($this->_flashStack);
-                    $compat_token = new HTMLPurifier_Token_Empty("embed");
-                    foreach ($flash->attr as $name => $val) {
-                        if ($name == "classid") continue;
-                        if ($name == "type") continue;
-                        if ($name == "data") $name = "src";
-                        $compat_token->attr[$name] = $val;
-                    }
-                    foreach ($flash->param as $name => $val) {
-                        if ($name == "movie") $name = "src";
-                        $compat_token->attr[$name] = $val;
-                    }
-                    $_extra = "<!--[if IE]>".$this->generateFromToken($compat_token)."<![endif]-->";
-                }
-            }
-            return $_extra . '</' . $token->name . '>';
-
-        } elseif ($token instanceof HTMLPurifier_Token_Empty) {
-            if ($this->_flashCompat && $token->name == "param" && !empty($this->_flashStack)) {
-                $this->_flashStack[count($this->_flashStack)-1]->param[$token->attr['name']] = $token->attr['value'];
-            }
-            $attr = $this->generateAttributes($token->attr, $token->name);
-             return '<' . $token->name . ($attr ? ' ' : '') . $attr .
-                ( $this->_xhtml ? ' /': '' ) // <br /> v. <br>
-                . '>';
-
-        } elseif ($token instanceof HTMLPurifier_Token_Text) {
-            return $this->escape($token->data, ENT_NOQUOTES);
-
-        } elseif ($token instanceof HTMLPurifier_Token_Comment) {
-            return '<!--' . $token->data . '-->';
-        } else {
-            return '';
-
-        }
-    }
-
-    /**
-     * Special case processor for the contents of script tags
-     * @warning This runs into problems if there's already a literal
-     *          --> somewhere inside the script contents.
-     */
-    public function generateScriptFromToken($token) {
-        if (!$token instanceof HTMLPurifier_Token_Text) return $this->generateFromToken($token);
-        // Thanks <http://lachy.id.au/log/2005/05/script-comments>
-        $data = preg_replace('#//\s*$#', '', $token->data);
-        return '<!--//--><![CDATA[//><!--' . "\n" . trim($data) . "\n" . '//--><!]]>';
-    }
-
-    /**
-     * Generates attribute declarations from attribute array.
-     * @note This does not include the leading or trailing space.
-     * @param $assoc_array_of_attributes Attribute array
-     * @param $element Name of element attributes are for, used to check
-     *        attribute minimization.
-     * @return Generate HTML fragment for insertion.
-     */
-    public function generateAttributes($assoc_array_of_attributes, $element = false) {
-        $html = '';
-        if ($this->_sortAttr) ksort($assoc_array_of_attributes);
-        foreach ($assoc_array_of_attributes as $key => $value) {
-            if (!$this->_xhtml) {
-                // Remove namespaced attributes
-                if (strpos($key, ':') !== false) continue;
-                // Check if we should minimize the attribute: val="val" -> val
-                if ($element && !empty($this->_def->info[$element]->attr[$key]->minimized)) {
-                    $html .= $key . ' ';
-                    continue;
-                }
-            }
-            $html .= $key.'="'.$this->escape($value).'" ';
-        }
-        return rtrim($html);
-    }
-
-    /**
-     * Escapes raw text data.
-     * @todo This really ought to be protected, but until we have a facility
-     *       for properly generating HTML here w/o using tokens, it stays
-     *       public.
-     * @param $string String data to escape for HTML.
-     * @param $quote Quoting style, like htmlspecialchars. ENT_NOQUOTES is
-     *               permissible for non-attribute output.
-     * @return String escaped data.
-     */
-    public function escape($string, $quote = ENT_COMPAT) {
-        return htmlspecialchars($string, $quote, 'UTF-8');
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLDefinition.php b/library/HTMLPurifier/HTMLDefinition.php
deleted file mode 100644 (file)
index c99ac11..0000000
+++ /dev/null
@@ -1,420 +0,0 @@
-<?php
-
-/**
- * Definition of the purified HTML that describes allowed children,
- * attributes, and many other things.
- *
- * Conventions:
- *
- * All member variables that are prefixed with info
- * (including the main $info array) are used by HTML Purifier internals
- * and should not be directly edited when customizing the HTMLDefinition.
- * They can usually be set via configuration directives or custom
- * modules.
- *
- * On the other hand, member variables without the info prefix are used
- * internally by the HTMLDefinition and MUST NOT be used by other HTML
- * Purifier internals. Many of them, however, are public, and may be
- * edited by userspace code to tweak the behavior of HTMLDefinition.
- *
- * @note This class is inspected by Printer_HTMLDefinition; please
- *       update that class if things here change.
- *
- * @warning Directives that change this object's structure must be in
- *          the HTML or Attr namespace!
- */
-class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
-{
-
-    // FULLY-PUBLIC VARIABLES ---------------------------------------------
-
-    /**
-     * Associative array of element names to HTMLPurifier_ElementDef
-     */
-    public $info = array();
-
-    /**
-     * Associative array of global attribute name to attribute definition.
-     */
-    public $info_global_attr = array();
-
-    /**
-     * String name of parent element HTML will be going into.
-     */
-    public $info_parent = 'div';
-
-    /**
-     * Definition for parent element, allows parent element to be a
-     * tag that's not allowed inside the HTML fragment.
-     */
-    public $info_parent_def;
-
-    /**
-     * String name of element used to wrap inline elements in block context
-     * @note This is rarely used except for BLOCKQUOTEs in strict mode
-     */
-    public $info_block_wrapper = 'p';
-
-    /**
-     * Associative array of deprecated tag name to HTMLPurifier_TagTransform
-     */
-    public $info_tag_transform = array();
-
-    /**
-     * Indexed list of HTMLPurifier_AttrTransform to be performed before validation.
-     */
-    public $info_attr_transform_pre = array();
-
-    /**
-     * Indexed list of HTMLPurifier_AttrTransform to be performed after validation.
-     */
-    public $info_attr_transform_post = array();
-
-    /**
-     * Nested lookup array of content set name (Block, Inline) to
-     * element name to whether or not it belongs in that content set.
-     */
-    public $info_content_sets = array();
-
-    /**
-     * Indexed list of HTMLPurifier_Injector to be used.
-     */
-    public $info_injector = array();
-
-    /**
-     * Doctype object
-     */
-    public $doctype;
-
-
-
-    // RAW CUSTOMIZATION STUFF --------------------------------------------
-
-    /**
-     * Adds a custom attribute to a pre-existing element
-     * @note This is strictly convenience, and does not have a corresponding
-     *       method in HTMLPurifier_HTMLModule
-     * @param $element_name String element name to add attribute to
-     * @param $attr_name String name of attribute
-     * @param $def Attribute definition, can be string or object, see
-     *             HTMLPurifier_AttrTypes for details
-     */
-    public function addAttribute($element_name, $attr_name, $def) {
-        $module = $this->getAnonymousModule();
-        if (!isset($module->info[$element_name])) {
-            $element = $module->addBlankElement($element_name);
-        } else {
-            $element = $module->info[$element_name];
-        }
-        $element->attr[$attr_name] = $def;
-    }
-
-    /**
-     * Adds a custom element to your HTML definition
-     * @note See HTMLPurifier_HTMLModule::addElement for detailed
-     *       parameter and return value descriptions.
-     */
-    public function addElement($element_name, $type, $contents, $attr_collections, $attributes = array()) {
-        $module = $this->getAnonymousModule();
-        // assume that if the user is calling this, the element
-        // is safe. This may not be a good idea
-        $element = $module->addElement($element_name, $type, $contents, $attr_collections, $attributes);
-        return $element;
-    }
-
-    /**
-     * Adds a blank element to your HTML definition, for overriding
-     * existing behavior
-     * @note See HTMLPurifier_HTMLModule::addBlankElement for detailed
-     *       parameter and return value descriptions.
-     */
-    public function addBlankElement($element_name) {
-        $module  = $this->getAnonymousModule();
-        $element = $module->addBlankElement($element_name);
-        return $element;
-    }
-
-    /**
-     * Retrieves a reference to the anonymous module, so you can
-     * bust out advanced features without having to make your own
-     * module.
-     */
-    public function getAnonymousModule() {
-        if (!$this->_anonModule) {
-            $this->_anonModule = new HTMLPurifier_HTMLModule();
-            $this->_anonModule->name = 'Anonymous';
-        }
-        return $this->_anonModule;
-    }
-
-    private $_anonModule;
-
-
-    // PUBLIC BUT INTERNAL VARIABLES --------------------------------------
-
-    public $type = 'HTML';
-    public $manager; /**< Instance of HTMLPurifier_HTMLModuleManager */
-
-    /**
-     * Performs low-cost, preliminary initialization.
-     */
-    public function __construct() {
-        $this->manager = new HTMLPurifier_HTMLModuleManager();
-    }
-
-    protected function doSetup($config) {
-        $this->processModules($config);
-        $this->setupConfigStuff($config);
-        unset($this->manager);
-
-        // cleanup some of the element definitions
-        foreach ($this->info as $k => $v) {
-            unset($this->info[$k]->content_model);
-            unset($this->info[$k]->content_model_type);
-        }
-    }
-
-    /**
-     * Extract out the information from the manager
-     */
-    protected function processModules($config) {
-
-        if ($this->_anonModule) {
-            // for user specific changes
-            // this is late-loaded so we don't have to deal with PHP4
-            // reference wonky-ness
-            $this->manager->addModule($this->_anonModule);
-            unset($this->_anonModule);
-        }
-
-        $this->manager->setup($config);
-        $this->doctype = $this->manager->doctype;
-
-        foreach ($this->manager->modules as $module) {
-            foreach($module->info_tag_transform as $k => $v) {
-                if ($v === false) unset($this->info_tag_transform[$k]);
-                else $this->info_tag_transform[$k] = $v;
-            }
-            foreach($module->info_attr_transform_pre as $k => $v) {
-                if ($v === false) unset($this->info_attr_transform_pre[$k]);
-                else $this->info_attr_transform_pre[$k] = $v;
-            }
-            foreach($module->info_attr_transform_post as $k => $v) {
-                if ($v === false) unset($this->info_attr_transform_post[$k]);
-                else $this->info_attr_transform_post[$k] = $v;
-            }
-            foreach ($module->info_injector as $k => $v) {
-                if ($v === false) unset($this->info_injector[$k]);
-                else $this->info_injector[$k] = $v;
-            }
-        }
-
-        $this->info = $this->manager->getElements();
-        $this->info_content_sets = $this->manager->contentSets->lookup;
-
-    }
-
-    /**
-     * Sets up stuff based on config. We need a better way of doing this.
-     */
-    protected function setupConfigStuff($config) {
-
-        $block_wrapper = $config->get('HTML.BlockWrapper');
-        if (isset($this->info_content_sets['Block'][$block_wrapper])) {
-            $this->info_block_wrapper = $block_wrapper;
-        } else {
-            trigger_error('Cannot use non-block element as block wrapper',
-                E_USER_ERROR);
-        }
-
-        $parent = $config->get('HTML.Parent');
-        $def = $this->manager->getElement($parent, true);
-        if ($def) {
-            $this->info_parent = $parent;
-            $this->info_parent_def = $def;
-        } else {
-            trigger_error('Cannot use unrecognized element as parent',
-                E_USER_ERROR);
-            $this->info_parent_def = $this->manager->getElement($this->info_parent, true);
-        }
-
-        // support template text
-        $support = "(for information on implementing this, see the ".
-                   "support forums) ";
-
-        // setup allowed elements -----------------------------------------
-
-        $allowed_elements = $config->get('HTML.AllowedElements');
-        $allowed_attributes = $config->get('HTML.AllowedAttributes'); // retrieve early
-
-        if (!is_array($allowed_elements) && !is_array($allowed_attributes)) {
-            $allowed = $config->get('HTML.Allowed');
-            if (is_string($allowed)) {
-                list($allowed_elements, $allowed_attributes) = $this->parseTinyMCEAllowedList($allowed);
-            }
-        }
-
-        if (is_array($allowed_elements)) {
-            foreach ($this->info as $name => $d) {
-                if(!isset($allowed_elements[$name])) unset($this->info[$name]);
-                unset($allowed_elements[$name]);
-            }
-            // emit errors
-            foreach ($allowed_elements as $element => $d) {
-                $element = htmlspecialchars($element); // PHP doesn't escape errors, be careful!
-                trigger_error("Element '$element' is not supported $support", E_USER_WARNING);
-            }
-        }
-
-        // setup allowed attributes ---------------------------------------
-
-        $allowed_attributes_mutable = $allowed_attributes; // by copy!
-        if (is_array($allowed_attributes)) {
-
-            // This actually doesn't do anything, since we went away from
-            // global attributes. It's possible that userland code uses
-            // it, but HTMLModuleManager doesn't!
-            foreach ($this->info_global_attr as $attr => $x) {
-                $keys = array($attr, "*@$attr", "*.$attr");
-                $delete = true;
-                foreach ($keys as $key) {
-                    if ($delete && isset($allowed_attributes[$key])) {
-                        $delete = false;
-                    }
-                    if (isset($allowed_attributes_mutable[$key])) {
-                        unset($allowed_attributes_mutable[$key]);
-                    }
-                }
-                if ($delete) unset($this->info_global_attr[$attr]);
-            }
-
-            foreach ($this->info as $tag => $info) {
-                foreach ($info->attr as $attr => $x) {
-                    $keys = array("$tag@$attr", $attr, "*@$attr", "$tag.$attr", "*.$attr");
-                    $delete = true;
-                    foreach ($keys as $key) {
-                        if ($delete && isset($allowed_attributes[$key])) {
-                            $delete = false;
-                        }
-                        if (isset($allowed_attributes_mutable[$key])) {
-                            unset($allowed_attributes_mutable[$key]);
-                        }
-                    }
-                    if ($delete) unset($this->info[$tag]->attr[$attr]);
-                }
-            }
-            // emit errors
-            foreach ($allowed_attributes_mutable as $elattr => $d) {
-                $bits = preg_split('/[.@]/', $elattr, 2);
-                $c = count($bits);
-                switch ($c) {
-                    case 2:
-                        if ($bits[0] !== '*') {
-                            $element = htmlspecialchars($bits[0]);
-                            $attribute = htmlspecialchars($bits[1]);
-                            if (!isset($this->info[$element])) {
-                                trigger_error("Cannot allow attribute '$attribute' if element '$element' is not allowed/supported $support");
-                            } else {
-                                trigger_error("Attribute '$attribute' in element '$element' not supported $support",
-                                    E_USER_WARNING);
-                            }
-                            break;
-                        }
-                        // otherwise fall through
-                    case 1:
-                        $attribute = htmlspecialchars($bits[0]);
-                        trigger_error("Global attribute '$attribute' is not ".
-                            "supported in any elements $support",
-                            E_USER_WARNING);
-                        break;
-                }
-            }
-
-        }
-
-        // setup forbidden elements ---------------------------------------
-
-        $forbidden_elements   = $config->get('HTML.ForbiddenElements');
-        $forbidden_attributes = $config->get('HTML.ForbiddenAttributes');
-
-        foreach ($this->info as $tag => $info) {
-            if (isset($forbidden_elements[$tag])) {
-                unset($this->info[$tag]);
-                continue;
-            }
-            foreach ($info->attr as $attr => $x) {
-                if (
-                    isset($forbidden_attributes["$tag@$attr"]) ||
-                    isset($forbidden_attributes["*@$attr"]) ||
-                    isset($forbidden_attributes[$attr])
-                ) {
-                    unset($this->info[$tag]->attr[$attr]);
-                    continue;
-                } // this segment might get removed eventually
-                elseif (isset($forbidden_attributes["$tag.$attr"])) {
-                    // $tag.$attr are not user supplied, so no worries!
-                    trigger_error("Error with $tag.$attr: tag.attr syntax not supported for HTML.ForbiddenAttributes; use tag@attr instead", E_USER_WARNING);
-                }
-            }
-        }
-        foreach ($forbidden_attributes as $key => $v) {
-            if (strlen($key) < 2) continue;
-            if ($key[0] != '*') continue;
-            if ($key[1] == '.') {
-                trigger_error("Error with $key: *.attr syntax not supported for HTML.ForbiddenAttributes; use attr instead", E_USER_WARNING);
-            }
-        }
-
-        // setup injectors -----------------------------------------------------
-        foreach ($this->info_injector as $i => $injector) {
-            if ($injector->checkNeeded($config) !== false) {
-                // remove injector that does not have it's required
-                // elements/attributes present, and is thus not needed.
-                unset($this->info_injector[$i]);
-            }
-        }
-    }
-
-    /**
-     * Parses a TinyMCE-flavored Allowed Elements and Attributes list into
-     * separate lists for processing. Format is element[attr1|attr2],element2...
-     * @warning Although it's largely drawn from TinyMCE's implementation,
-     *      it is different, and you'll probably have to modify your lists
-     * @param $list String list to parse
-     * @param array($allowed_elements, $allowed_attributes)
-     * @todo Give this its own class, probably static interface
-     */
-    public function parseTinyMCEAllowedList($list) {
-
-        $list = str_replace(array(' ', "\t"), '', $list);
-
-        $elements = array();
-        $attributes = array();
-
-        $chunks = preg_split('/(,|[\n\r]+)/', $list);
-        foreach ($chunks as $chunk) {
-            if (empty($chunk)) continue;
-            // remove TinyMCE element control characters
-            if (!strpos($chunk, '[')) {
-                $element = $chunk;
-                $attr = false;
-            } else {
-                list($element, $attr) = explode('[', $chunk);
-            }
-            if ($element !== '*') $elements[$element] = true;
-            if (!$attr) continue;
-            $attr = substr($attr, 0, strlen($attr) - 1); // remove trailing ]
-            $attr = explode('|', $attr);
-            foreach ($attr as $key) {
-                $attributes["$element.$key"] = true;
-            }
-        }
-
-        return array($elements, $attributes);
-
-    }
-
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule.php b/library/HTMLPurifier/HTMLModule.php
deleted file mode 100644 (file)
index 072cf68..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-<?php
-
-/**
- * Represents an XHTML 1.1 module, with information on elements, tags
- * and attributes.
- * @note Even though this is technically XHTML 1.1, it is also used for
- *       regular HTML parsing. We are using modulization as a convenient
- *       way to represent the internals of HTMLDefinition, and our
- *       implementation is by no means conforming and does not directly
- *       use the normative DTDs or XML schemas.
- * @note The public variables in a module should almost directly
- *       correspond to the variables in HTMLPurifier_HTMLDefinition.
- *       However, the prefix info carries no special meaning in these
- *       objects (include it anyway if that's the correspondence though).
- * @todo Consider making some member functions protected
- */
-
-class HTMLPurifier_HTMLModule
-{
-
-    // -- Overloadable ----------------------------------------------------
-
-    /**
-     * Short unique string identifier of the module
-     */
-    public $name;
-
-    /**
-     * Informally, a list of elements this module changes. Not used in
-     * any significant way.
-     */
-    public $elements = array();
-
-    /**
-     * Associative array of element names to element definitions.
-     * Some definitions may be incomplete, to be merged in later
-     * with the full definition.
-     */
-    public $info = array();
-
-    /**
-     * Associative array of content set names to content set additions.
-     * This is commonly used to, say, add an A element to the Inline
-     * content set. This corresponds to an internal variable $content_sets
-     * and NOT info_content_sets member variable of HTMLDefinition.
-     */
-    public $content_sets = array();
-
-    /**
-     * Associative array of attribute collection names to attribute
-     * collection additions. More rarely used for adding attributes to
-     * the global collections. Example is the StyleAttribute module adding
-     * the style attribute to the Core. Corresponds to HTMLDefinition's
-     * attr_collections->info, since the object's data is only info,
-     * with extra behavior associated with it.
-     */
-    public $attr_collections = array();
-
-    /**
-     * Associative array of deprecated tag name to HTMLPurifier_TagTransform
-     */
-    public $info_tag_transform = array();
-
-    /**
-     * List of HTMLPurifier_AttrTransform to be performed before validation.
-     */
-    public $info_attr_transform_pre = array();
-
-    /**
-     * List of HTMLPurifier_AttrTransform to be performed after validation.
-     */
-    public $info_attr_transform_post = array();
-
-    /**
-     * List of HTMLPurifier_Injector to be performed during well-formedness fixing.
-     * An injector will only be invoked if all of it's pre-requisites are met;
-     * if an injector fails setup, there will be no error; it will simply be
-     * silently disabled.
-     */
-    public $info_injector = array();
-
-    /**
-     * Boolean flag that indicates whether or not getChildDef is implemented.
-     * For optimization reasons: may save a call to a function. Be sure
-     * to set it if you do implement getChildDef(), otherwise it will have
-     * no effect!
-     */
-    public $defines_child_def = false;
-
-    /**
-     * Boolean flag whether or not this module is safe. If it is not safe, all
-     * of its members are unsafe. Modules are safe by default (this might be
-     * slightly dangerous, but it doesn't make much sense to force HTML Purifier,
-     * which is based off of safe HTML, to explicitly say, "This is safe," even
-     * though there are modules which are "unsafe")
-     *
-     * @note Previously, safety could be applied at an element level granularity.
-     *       We've removed this ability, so in order to add "unsafe" elements
-     *       or attributes, a dedicated module with this property set to false
-     *       must be used.
-     */
-    public $safe = true;
-
-    /**
-     * Retrieves a proper HTMLPurifier_ChildDef subclass based on
-     * content_model and content_model_type member variables of
-     * the HTMLPurifier_ElementDef class. There is a similar function
-     * in HTMLPurifier_HTMLDefinition.
-     * @param $def HTMLPurifier_ElementDef instance
-     * @return HTMLPurifier_ChildDef subclass
-     */
-    public function getChildDef($def) {return false;}
-
-    // -- Convenience -----------------------------------------------------
-
-    /**
-     * Convenience function that sets up a new element
-     * @param $element Name of element to add
-     * @param $type What content set should element be registered to?
-     *              Set as false to skip this step.
-     * @param $contents Allowed children in form of:
-     *              "$content_model_type: $content_model"
-     * @param $attr_includes What attribute collections to register to
-     *              element?
-     * @param $attr What unique attributes does the element define?
-     * @note See ElementDef for in-depth descriptions of these parameters.
-     * @return Created element definition object, so you
-     *         can set advanced parameters
-     */
-    public function addElement($element, $type, $contents, $attr_includes = array(), $attr = array()) {
-        $this->elements[] = $element;
-        // parse content_model
-        list($content_model_type, $content_model) = $this->parseContents($contents);
-        // merge in attribute inclusions
-        $this->mergeInAttrIncludes($attr, $attr_includes);
-        // add element to content sets
-        if ($type) $this->addElementToContentSet($element, $type);
-        // create element
-        $this->info[$element] = HTMLPurifier_ElementDef::create(
-            $content_model, $content_model_type, $attr
-        );
-        // literal object $contents means direct child manipulation
-        if (!is_string($contents)) $this->info[$element]->child = $contents;
-        return $this->info[$element];
-    }
-
-    /**
-     * Convenience function that creates a totally blank, non-standalone
-     * element.
-     * @param $element Name of element to create
-     * @return Created element
-     */
-    public function addBlankElement($element) {
-        if (!isset($this->info[$element])) {
-            $this->elements[] = $element;
-            $this->info[$element] = new HTMLPurifier_ElementDef();
-            $this->info[$element]->standalone = false;
-        } else {
-            trigger_error("Definition for $element already exists in module, cannot redefine");
-        }
-        return $this->info[$element];
-    }
-
-    /**
-     * Convenience function that registers an element to a content set
-     * @param Element to register
-     * @param Name content set (warning: case sensitive, usually upper-case
-     *        first letter)
-     */
-    public function addElementToContentSet($element, $type) {
-        if (!isset($this->content_sets[$type])) $this->content_sets[$type] = '';
-        else $this->content_sets[$type] .= ' | ';
-        $this->content_sets[$type] .= $element;
-    }
-
-    /**
-     * Convenience function that transforms single-string contents
-     * into separate content model and content model type
-     * @param $contents Allowed children in form of:
-     *                  "$content_model_type: $content_model"
-     * @note If contents is an object, an array of two nulls will be
-     *       returned, and the callee needs to take the original $contents
-     *       and use it directly.
-     */
-    public function parseContents($contents) {
-        if (!is_string($contents)) return array(null, null); // defer
-        switch ($contents) {
-            // check for shorthand content model forms
-            case 'Empty':
-                return array('empty', '');
-            case 'Inline':
-                return array('optional', 'Inline | #PCDATA');
-            case 'Flow':
-                return array('optional', 'Flow | #PCDATA');
-        }
-        list($content_model_type, $content_model) = explode(':', $contents);
-        $content_model_type = strtolower(trim($content_model_type));
-        $content_model = trim($content_model);
-        return array($content_model_type, $content_model);
-    }
-
-    /**
-     * Convenience function that merges a list of attribute includes into
-     * an attribute array.
-     * @param $attr Reference to attr array to modify
-     * @param $attr_includes Array of includes / string include to merge in
-     */
-    public function mergeInAttrIncludes(&$attr, $attr_includes) {
-        if (!is_array($attr_includes)) {
-            if (empty($attr_includes)) $attr_includes = array();
-            else $attr_includes = array($attr_includes);
-        }
-        $attr[0] = $attr_includes;
-    }
-
-    /**
-     * Convenience function that generates a lookup table with boolean
-     * true as value.
-     * @param $list List of values to turn into a lookup
-     * @note You can also pass an arbitrary number of arguments in
-     *       place of the regular argument
-     * @return Lookup array equivalent of list
-     */
-    public function makeLookup($list) {
-        if (is_string($list)) $list = func_get_args();
-        $ret = array();
-        foreach ($list as $value) {
-            if (is_null($value)) continue;
-            $ret[$value] = true;
-        }
-        return $ret;
-    }
-
-    /**
-     * Lazy load construction of the module after determining whether
-     * or not it's needed, and also when a finalized configuration object
-     * is available.
-     * @param $config Instance of HTMLPurifier_Config
-     */
-    public function setup($config) {}
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Bdo.php b/library/HTMLPurifier/HTMLModule/Bdo.php
deleted file mode 100644 (file)
index 3d66f1b..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Bi-directional Text Module, defines elements that
- * declare directionality of content. Text Extension Module.
- */
-class HTMLPurifier_HTMLModule_Bdo extends HTMLPurifier_HTMLModule
-{
-
-    public $name = 'Bdo';
-    public $attr_collections = array(
-        'I18N' => array('dir' => false)
-    );
-
-    public function setup($config) {
-        $bdo = $this->addElement(
-            'bdo', 'Inline', 'Inline', array('Core', 'Lang'),
-            array(
-                'dir' => 'Enum#ltr,rtl', // required
-                // The Abstract Module specification has the attribute
-                // inclusions wrong for bdo: bdo allows Lang
-            )
-        );
-        $bdo->attr_transform_post['required-dir'] = new HTMLPurifier_AttrTransform_BdoDir();
-
-        $this->attr_collections['I18N']['dir'] = 'Enum#ltr,rtl';
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/CommonAttributes.php b/library/HTMLPurifier/HTMLModule/CommonAttributes.php
deleted file mode 100644 (file)
index 7c15da8..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_CommonAttributes extends HTMLPurifier_HTMLModule
-{
-    public $name = 'CommonAttributes';
-
-    public $attr_collections = array(
-        'Core' => array(
-            0 => array('Style'),
-            // 'xml:space' => false,
-            'class' => 'Class',
-            'id' => 'ID',
-            'title' => 'CDATA',
-        ),
-        'Lang' => array(),
-        'I18N' => array(
-            0 => array('Lang'), // proprietary, for xml:lang/lang
-        ),
-        'Common' => array(
-            0 => array('Core', 'I18N')
-        )
-    );
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Edit.php b/library/HTMLPurifier/HTMLModule/Edit.php
deleted file mode 100644 (file)
index ff93690..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Edit Module, defines editing-related elements. Text Extension
- * Module.
- */
-class HTMLPurifier_HTMLModule_Edit extends HTMLPurifier_HTMLModule
-{
-
-    public $name = 'Edit';
-
-    public function setup($config) {
-        $contents = 'Chameleon: #PCDATA | Inline ! #PCDATA | Flow';
-        $attr = array(
-            'cite' => 'URI',
-            // 'datetime' => 'Datetime', // not implemented
-        );
-        $this->addElement('del', 'Inline', $contents, 'Common', $attr);
-        $this->addElement('ins', 'Inline', $contents, 'Common', $attr);
-    }
-
-    // HTML 4.01 specifies that ins/del must not contain block
-    // elements when used in an inline context, chameleon is
-    // a complicated workaround to acheive this effect
-
-    // Inline context ! Block context (exclamation mark is
-    // separator, see getChildDef for parsing)
-
-    public $defines_child_def = true;
-    public function getChildDef($def) {
-        if ($def->content_model_type != 'chameleon') return false;
-        $value = explode('!', $def->content_model);
-        return new HTMLPurifier_ChildDef_Chameleon($value[0], $value[1]);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Forms.php b/library/HTMLPurifier/HTMLModule/Forms.php
deleted file mode 100644 (file)
index 44c22f6..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Forms module, defines all form-related elements found in HTML 4.
- */
-class HTMLPurifier_HTMLModule_Forms extends HTMLPurifier_HTMLModule
-{
-    public $name = 'Forms';
-    public $safe = false;
-
-    public $content_sets = array(
-        'Block' => 'Form',
-        'Inline' => 'Formctrl',
-    );
-
-    public function setup($config) {
-        $form = $this->addElement('form', 'Form',
-          'Required: Heading | List | Block | fieldset', 'Common', array(
-            'accept' => 'ContentTypes',
-            'accept-charset' => 'Charsets',
-            'action*' => 'URI',
-            'method' => 'Enum#get,post',
-            // really ContentType, but these two are the only ones used today
-            'enctype' => 'Enum#application/x-www-form-urlencoded,multipart/form-data',
-        ));
-        $form->excludes = array('form' => true);
-
-        $input = $this->addElement('input', 'Formctrl', 'Empty', 'Common', array(
-            'accept' => 'ContentTypes',
-            'accesskey' => 'Character',
-            'alt' => 'Text',
-            'checked' => 'Bool#checked',
-            'disabled' => 'Bool#disabled',
-            'maxlength' => 'Number',
-            'name' => 'CDATA',
-            'readonly' => 'Bool#readonly',
-            'size' => 'Number',
-            'src' => 'URI#embeds',
-            'tabindex' => 'Number',
-            'type' => 'Enum#text,password,checkbox,button,radio,submit,reset,file,hidden,image',
-            'value' => 'CDATA',
-        ));
-        $input->attr_transform_post[] = new HTMLPurifier_AttrTransform_Input();
-
-        $this->addElement('select', 'Formctrl', 'Required: optgroup | option', 'Common', array(
-            'disabled' => 'Bool#disabled',
-            'multiple' => 'Bool#multiple',
-            'name' => 'CDATA',
-            'size' => 'Number',
-            'tabindex' => 'Number',
-        ));
-
-        $this->addElement('option', false, 'Optional: #PCDATA', 'Common', array(
-            'disabled' => 'Bool#disabled',
-            'label' => 'Text',
-            'selected' => 'Bool#selected',
-            'value' => 'CDATA',
-        ));
-        // It's illegal for there to be more than one selected, but not
-        // be multiple. Also, no selected means undefined behavior. This might
-        // be difficult to implement; perhaps an injector, or a context variable.
-
-        $textarea = $this->addElement('textarea', 'Formctrl', 'Optional: #PCDATA', 'Common', array(
-            'accesskey' => 'Character',
-            'cols*' => 'Number',
-            'disabled' => 'Bool#disabled',
-            'name' => 'CDATA',
-            'readonly' => 'Bool#readonly',
-            'rows*' => 'Number',
-            'tabindex' => 'Number',
-        ));
-        $textarea->attr_transform_pre[] = new HTMLPurifier_AttrTransform_Textarea();
-
-        $button = $this->addElement('button', 'Formctrl', 'Optional: #PCDATA | Heading | List | Block | Inline', 'Common', array(
-            'accesskey' => 'Character',
-            'disabled' => 'Bool#disabled',
-            'name' => 'CDATA',
-            'tabindex' => 'Number',
-            'type' => 'Enum#button,submit,reset',
-            'value' => 'CDATA',
-        ));
-
-        // For exclusions, ideally we'd specify content sets, not literal elements
-        $button->excludes = $this->makeLookup(
-            'form', 'fieldset', // Form
-            'input', 'select', 'textarea', 'label', 'button', // Formctrl
-            'a' // as per HTML 4.01 spec, this is omitted by modularization
-        );
-
-        // Extra exclusion: img usemap="" is not permitted within this element.
-        // We'll omit this for now, since we don't have any good way of
-        // indicating it yet.
-
-        // This is HIGHLY user-unfriendly; we need a custom child-def for this
-        $this->addElement('fieldset', 'Form', 'Custom: (#WS?,legend,(Flow|#PCDATA)*)', 'Common');
-
-        $label = $this->addElement('label', 'Formctrl', 'Optional: #PCDATA | Inline', 'Common', array(
-            'accesskey' => 'Character',
-            // 'for' => 'IDREF', // IDREF not implemented, cannot allow
-        ));
-        $label->excludes = array('label' => true);
-
-        $this->addElement('legend', false, 'Optional: #PCDATA | Inline', 'Common', array(
-            'accesskey' => 'Character',
-        ));
-
-        $this->addElement('optgroup', false, 'Required: option', 'Common', array(
-            'disabled' => 'Bool#disabled',
-            'label*' => 'Text',
-        ));
-
-        // Don't forget an injector for <isindex>. This one's a little complex
-        // because it maps to multiple elements.
-
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Hypertext.php b/library/HTMLPurifier/HTMLModule/Hypertext.php
deleted file mode 100644 (file)
index d7e9bdd..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Hypertext Module, defines hypertext links. Core Module.
- */
-class HTMLPurifier_HTMLModule_Hypertext extends HTMLPurifier_HTMLModule
-{
-
-    public $name = 'Hypertext';
-
-    public function setup($config) {
-        $a = $this->addElement(
-            'a', 'Inline', 'Inline', 'Common',
-            array(
-                // 'accesskey' => 'Character',
-                // 'charset' => 'Charset',
-                'href' => 'URI',
-                // 'hreflang' => 'LanguageCode',
-                'rel' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rel'),
-                'rev' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rev'),
-                // 'tabindex' => 'Number',
-                // 'type' => 'ContentType',
-            )
-        );
-        $a->formatting = true;
-        $a->excludes = array('a' => true);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Image.php b/library/HTMLPurifier/HTMLModule/Image.php
deleted file mode 100644 (file)
index 948d435..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Image Module provides basic image embedding.
- * @note There is specialized code for removing empty images in
- *       HTMLPurifier_Strategy_RemoveForeignElements
- */
-class HTMLPurifier_HTMLModule_Image extends HTMLPurifier_HTMLModule
-{
-
-    public $name = 'Image';
-
-    public function setup($config) {
-        $max = $config->get('HTML.MaxImgLength');
-        $img = $this->addElement(
-            'img', 'Inline', 'Empty', 'Common',
-            array(
-                'alt*' => 'Text',
-                // According to the spec, it's Length, but percents can
-                // be abused, so we allow only Pixels.
-                'height' => 'Pixels#' . $max,
-                'width'  => 'Pixels#' . $max,
-                'longdesc' => 'URI',
-                'src*' => new HTMLPurifier_AttrDef_URI(true), // embedded
-            )
-        );
-        if ($max === null || $config->get('HTML.Trusted')) {
-            $img->attr['height'] =
-            $img->attr['width'] = 'Length';
-        }
-
-        // kind of strange, but splitting things up would be inefficient
-        $img->attr_transform_pre[] =
-        $img->attr_transform_post[] =
-            new HTMLPurifier_AttrTransform_ImgRequired();
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Legacy.php b/library/HTMLPurifier/HTMLModule/Legacy.php
deleted file mode 100644 (file)
index df33927..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Legacy module defines elements that were previously
- * deprecated.
- *
- * @note Not all legacy elements have been implemented yet, which
- *       is a bit of a reverse problem as compared to browsers! In
- *       addition, this legacy module may implement a bit more than
- *       mandated by XHTML 1.1.
- *
- * This module can be used in combination with TransformToStrict in order
- * to transform as many deprecated elements as possible, but retain
- * questionably deprecated elements that do not have good alternatives
- * as well as transform elements that don't have an implementation.
- * See docs/ref-strictness.txt for more details.
- */
-
-class HTMLPurifier_HTMLModule_Legacy extends HTMLPurifier_HTMLModule
-{
-
-    public $name = 'Legacy';
-
-    public function setup($config) {
-
-        $this->addElement('basefont', 'Inline', 'Empty', false, array(
-            'color' => 'Color',
-            'face' => 'Text', // extremely broad, we should
-            'size' => 'Text', // tighten it
-            'id' => 'ID'
-        ));
-        $this->addElement('center', 'Block', 'Flow', 'Common');
-        $this->addElement('dir', 'Block', 'Required: li', 'Common', array(
-            'compact' => 'Bool#compact'
-        ));
-        $this->addElement('font', 'Inline', 'Inline', array('Core', 'I18N'), array(
-            'color' => 'Color',
-            'face' => 'Text', // extremely broad, we should
-            'size' => 'Text', // tighten it
-        ));
-        $this->addElement('menu', 'Block', 'Required: li', 'Common', array(
-            'compact' => 'Bool#compact'
-        ));
-
-        $s = $this->addElement('s', 'Inline', 'Inline', 'Common');
-        $s->formatting = true;
-
-        $strike = $this->addElement('strike', 'Inline', 'Inline', 'Common');
-        $strike->formatting = true;
-
-        $u = $this->addElement('u', 'Inline', 'Inline', 'Common');
-        $u->formatting = true;
-
-        // setup modifications to old elements
-
-        $align = 'Enum#left,right,center,justify';
-
-        $address = $this->addBlankElement('address');
-        $address->content_model = 'Inline | #PCDATA | p';
-        $address->content_model_type = 'optional';
-        $address->child = false;
-
-        $blockquote = $this->addBlankElement('blockquote');
-        $blockquote->content_model = 'Flow | #PCDATA';
-        $blockquote->content_model_type = 'optional';
-        $blockquote->child = false;
-
-        $br = $this->addBlankElement('br');
-        $br->attr['clear'] = 'Enum#left,all,right,none';
-
-        $caption = $this->addBlankElement('caption');
-        $caption->attr['align'] = 'Enum#top,bottom,left,right';
-
-        $div = $this->addBlankElement('div');
-        $div->attr['align'] = $align;
-
-        $dl = $this->addBlankElement('dl');
-        $dl->attr['compact'] = 'Bool#compact';
-
-        for ($i = 1; $i <= 6; $i++) {
-            $h = $this->addBlankElement("h$i");
-            $h->attr['align'] = $align;
-        }
-
-        $hr = $this->addBlankElement('hr');
-        $hr->attr['align'] = $align;
-        $hr->attr['noshade'] = 'Bool#noshade';
-        $hr->attr['size'] = 'Pixels';
-        $hr->attr['width'] = 'Length';
-
-        $img = $this->addBlankElement('img');
-        $img->attr['align'] = 'Enum#top,middle,bottom,left,right';
-        $img->attr['border'] = 'Pixels';
-        $img->attr['hspace'] = 'Pixels';
-        $img->attr['vspace'] = 'Pixels';
-
-        // figure out this integer business
-
-        $li = $this->addBlankElement('li');
-        $li->attr['value'] = new HTMLPurifier_AttrDef_Integer();
-        $li->attr['type']  = 'Enum#s:1,i,I,a,A,disc,square,circle';
-
-        $ol = $this->addBlankElement('ol');
-        $ol->attr['compact'] = 'Bool#compact';
-        $ol->attr['start'] = new HTMLPurifier_AttrDef_Integer();
-        $ol->attr['type'] = 'Enum#s:1,i,I,a,A';
-
-        $p = $this->addBlankElement('p');
-        $p->attr['align'] = $align;
-
-        $pre = $this->addBlankElement('pre');
-        $pre->attr['width'] = 'Number';
-
-        // script omitted
-
-        $table = $this->addBlankElement('table');
-        $table->attr['align'] = 'Enum#left,center,right';
-        $table->attr['bgcolor'] = 'Color';
-
-        $tr = $this->addBlankElement('tr');
-        $tr->attr['bgcolor'] = 'Color';
-
-        $th = $this->addBlankElement('th');
-        $th->attr['bgcolor'] = 'Color';
-        $th->attr['height'] = 'Length';
-        $th->attr['nowrap'] = 'Bool#nowrap';
-        $th->attr['width'] = 'Length';
-
-        $td = $this->addBlankElement('td');
-        $td->attr['bgcolor'] = 'Color';
-        $td->attr['height'] = 'Length';
-        $td->attr['nowrap'] = 'Bool#nowrap';
-        $td->attr['width'] = 'Length';
-
-        $ul = $this->addBlankElement('ul');
-        $ul->attr['compact'] = 'Bool#compact';
-        $ul->attr['type'] = 'Enum#square,disc,circle';
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/List.php b/library/HTMLPurifier/HTMLModule/List.php
deleted file mode 100644 (file)
index 74d4522..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 List Module, defines list-oriented elements. Core Module.
- */
-class HTMLPurifier_HTMLModule_List extends HTMLPurifier_HTMLModule
-{
-
-    public $name = 'List';
-
-    // According to the abstract schema, the List content set is a fully formed
-    // one or more expr, but it invariably occurs in an optional declaration
-    // so we're not going to do that subtlety. It might cause trouble
-    // if a user defines "List" and expects that multiple lists are
-    // allowed to be specified, but then again, that's not very intuitive.
-    // Furthermore, the actual XML Schema may disagree. Regardless,
-    // we don't have support for such nested expressions without using
-    // the incredibly inefficient and draconic Custom ChildDef.
-
-    public $content_sets = array('Flow' => 'List');
-
-    public function setup($config) {
-        $ol = $this->addElement('ol', 'List', 'Required: li', 'Common');
-        $ol->wrap = "li";
-        $ul = $this->addElement('ul', 'List', 'Required: li', 'Common');
-        $ul->wrap = "li";
-        $this->addElement('dl', 'List', 'Required: dt | dd', 'Common');
-
-        $this->addElement('li', false, 'Flow', 'Common');
-
-        $this->addElement('dd', false, 'Flow', 'Common');
-        $this->addElement('dt', false, 'Inline', 'Common');
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Name.php b/library/HTMLPurifier/HTMLModule/Name.php
deleted file mode 100644 (file)
index 05694b4..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_Name extends HTMLPurifier_HTMLModule
-{
-
-    public $name = 'Name';
-
-    public function setup($config) {
-        $elements = array('a', 'applet', 'form', 'frame', 'iframe', 'img', 'map');
-        foreach ($elements as $name) {
-            $element = $this->addBlankElement($name);
-            $element->attr['name'] = 'CDATA';
-            if (!$config->get('HTML.Attr.Name.UseCDATA')) {
-                $element->attr_transform_post['NameSync'] = new HTMLPurifier_AttrTransform_NameSync();
-            }
-        }
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php b/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php
deleted file mode 100644 (file)
index 5f1b14a..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_NonXMLCommonAttributes extends HTMLPurifier_HTMLModule
-{
-    public $name = 'NonXMLCommonAttributes';
-
-    public $attr_collections = array(
-        'Lang' => array(
-            'lang' => 'LanguageCode',
-        )
-    );
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Object.php b/library/HTMLPurifier/HTMLModule/Object.php
deleted file mode 100644 (file)
index 193c101..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Object Module, defines elements for generic object inclusion
- * @warning Users will commonly use <embed> to cater to legacy browsers: this
- *      module does not allow this sort of behavior
- */
-class HTMLPurifier_HTMLModule_Object extends HTMLPurifier_HTMLModule
-{
-
-    public $name = 'Object';
-    public $safe = false;
-
-    public function setup($config) {
-
-        $this->addElement('object', 'Inline', 'Optional: #PCDATA | Flow | param', 'Common',
-            array(
-                'archive' => 'URI',
-                'classid' => 'URI',
-                'codebase' => 'URI',
-                'codetype' => 'Text',
-                'data' => 'URI',
-                'declare' => 'Bool#declare',
-                'height' => 'Length',
-                'name' => 'CDATA',
-                'standby' => 'Text',
-                'tabindex' => 'Number',
-                'type' => 'ContentType',
-                'width' => 'Length'
-            )
-        );
-
-        $this->addElement('param', false, 'Empty', false,
-            array(
-                'id' => 'ID',
-                'name*' => 'Text',
-                'type' => 'Text',
-                'value' => 'Text',
-                'valuetype' => 'Enum#data,ref,object'
-           )
-        );
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Presentation.php b/library/HTMLPurifier/HTMLModule/Presentation.php
deleted file mode 100644 (file)
index 8ff0b5e..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Presentation Module, defines simple presentation-related
- * markup. Text Extension Module.
- * @note The official XML Schema and DTD specs further divide this into
- *       two modules:
- *          - Block Presentation (hr)
- *          - Inline Presentation (b, big, i, small, sub, sup, tt)
- *       We have chosen not to heed this distinction, as content_sets
- *       provides satisfactory disambiguation.
- */
-class HTMLPurifier_HTMLModule_Presentation extends HTMLPurifier_HTMLModule
-{
-
-    public $name = 'Presentation';
-
-    public function setup($config) {
-        $this->addElement('hr',     'Block',  'Empty',  'Common');
-        $this->addElement('sub',    'Inline', 'Inline', 'Common');
-        $this->addElement('sup',    'Inline', 'Inline', 'Common');
-        $b = $this->addElement('b',      'Inline', 'Inline', 'Common');
-        $b->formatting = true;
-        $big = $this->addElement('big',    'Inline', 'Inline', 'Common');
-        $big->formatting = true;
-        $i = $this->addElement('i',      'Inline', 'Inline', 'Common');
-        $i->formatting = true;
-        $small = $this->addElement('small',  'Inline', 'Inline', 'Common');
-        $small->formatting = true;
-        $tt = $this->addElement('tt',     'Inline', 'Inline', 'Common');
-        $tt->formatting = true;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Proprietary.php b/library/HTMLPurifier/HTMLModule/Proprietary.php
deleted file mode 100644 (file)
index dd36a3d..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-
-/**
- * Module defines proprietary tags and attributes in HTML.
- * @warning If this module is enabled, standards-compliance is off!
- */
-class HTMLPurifier_HTMLModule_Proprietary extends HTMLPurifier_HTMLModule
-{
-
-    public $name = 'Proprietary';
-
-    public function setup($config) {
-
-        $this->addElement('marquee', 'Inline', 'Flow', 'Common',
-            array(
-                'direction' => 'Enum#left,right,up,down',
-                'behavior' => 'Enum#alternate',
-                'width' => 'Length',
-                'height' => 'Length',
-                'scrolldelay' => 'Number',
-                'scrollamount' => 'Number',
-                'loop' => 'Number',
-                'bgcolor' => 'Color',
-                'hspace' => 'Pixels',
-                'vspace' => 'Pixels',
-            )
-        );
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Ruby.php b/library/HTMLPurifier/HTMLModule/Ruby.php
deleted file mode 100644 (file)
index b26a0a3..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Ruby Annotation Module, defines elements that indicate
- * short runs of text alongside base text for annotation or pronounciation.
- */
-class HTMLPurifier_HTMLModule_Ruby extends HTMLPurifier_HTMLModule
-{
-
-    public $name = 'Ruby';
-
-    public function setup($config) {
-        $this->addElement('ruby', 'Inline',
-            'Custom: ((rb, (rt | (rp, rt, rp))) | (rbc, rtc, rtc?))',
-            'Common');
-        $this->addElement('rbc', false, 'Required: rb', 'Common');
-        $this->addElement('rtc', false, 'Required: rt', 'Common');
-        $rb = $this->addElement('rb', false, 'Inline', 'Common');
-        $rb->excludes = array('ruby' => true);
-        $rt = $this->addElement('rt', false, 'Inline', 'Common', array('rbspan' => 'Number'));
-        $rt->excludes = array('ruby' => true);
-        $this->addElement('rp', false, 'Optional: #PCDATA', 'Common');
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/SafeEmbed.php b/library/HTMLPurifier/HTMLModule/SafeEmbed.php
deleted file mode 100644 (file)
index ea25671..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-/**
- * A "safe" embed module. See SafeObject. This is a proprietary element.
- */
-class HTMLPurifier_HTMLModule_SafeEmbed extends HTMLPurifier_HTMLModule
-{
-
-    public $name = 'SafeEmbed';
-
-    public function setup($config) {
-
-        $max = $config->get('HTML.MaxImgLength');
-        $embed = $this->addElement(
-            'embed', 'Inline', 'Empty', 'Common',
-            array(
-                'src*' => 'URI#embedded',
-                'type' => 'Enum#application/x-shockwave-flash',
-                'width' => 'Pixels#' . $max,
-                'height' => 'Pixels#' . $max,
-                'allowscriptaccess' => 'Enum#never',
-                'allownetworking' => 'Enum#internal',
-                'flashvars' => 'Text',
-                'wmode' => 'Enum#window',
-                'name' => 'ID',
-            )
-        );
-        $embed->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeEmbed();
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/SafeObject.php b/library/HTMLPurifier/HTMLModule/SafeObject.php
deleted file mode 100644 (file)
index 64ab8c0..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-/**
- * A "safe" object module. In theory, objects permitted by this module will
- * be safe, and untrusted users can be allowed to embed arbitrary flash objects
- * (maybe other types too, but only Flash is supported as of right now).
- * Highly experimental.
- */
-class HTMLPurifier_HTMLModule_SafeObject extends HTMLPurifier_HTMLModule
-{
-
-    public $name = 'SafeObject';
-
-    public function setup($config) {
-
-        // These definitions are not intrinsically safe: the attribute transforms
-        // are a vital part of ensuring safety.
-
-        $max = $config->get('HTML.MaxImgLength');
-        $object = $this->addElement(
-            'object',
-            'Inline',
-            'Optional: param | Flow | #PCDATA',
-            'Common',
-            array(
-                // While technically not required by the spec, we're forcing
-                // it to this value.
-                'type'   => 'Enum#application/x-shockwave-flash',
-                'width'  => 'Pixels#' . $max,
-                'height' => 'Pixels#' . $max,
-                'data'   => 'URI#embedded',
-                'classid' => 'Enum#clsid:d27cdb6e-ae6d-11cf-96b8-444553540000',
-                'codebase' => new HTMLPurifier_AttrDef_Enum(array(
-                    'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0')),
-            )
-        );
-        $object->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeObject();
-
-        $param = $this->addElement('param', false, 'Empty', false,
-            array(
-                'id' => 'ID',
-                'name*' => 'Text',
-                'value' => 'Text'
-            )
-        );
-        $param->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeParam();
-        $this->info_injector[] = 'SafeObject';
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Scripting.php b/library/HTMLPurifier/HTMLModule/Scripting.php
deleted file mode 100644 (file)
index cecdea6..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-
-/*
-
-WARNING: THIS MODULE IS EXTREMELY DANGEROUS AS IT ENABLES INLINE SCRIPTING
-INSIDE HTML PURIFIER DOCUMENTS. USE ONLY WITH TRUSTED USER INPUT!!!
-
-*/
-
-/**
- * XHTML 1.1 Scripting module, defines elements that are used to contain
- * information pertaining to executable scripts or the lack of support
- * for executable scripts.
- * @note This module does not contain inline scripting elements
- */
-class HTMLPurifier_HTMLModule_Scripting extends HTMLPurifier_HTMLModule
-{
-    public $name = 'Scripting';
-    public $elements = array('script', 'noscript');
-    public $content_sets = array('Block' => 'script | noscript', 'Inline' => 'script | noscript');
-    public $safe = false;
-
-    public function setup($config) {
-        // TODO: create custom child-definition for noscript that
-        // auto-wraps stray #PCDATA in a similar manner to
-        // blockquote's custom definition (we would use it but
-        // blockquote's contents are optional while noscript's contents
-        // are required)
-
-        // TODO: convert this to new syntax, main problem is getting
-        // both content sets working
-
-        // In theory, this could be safe, but I don't see any reason to
-        // allow it.
-        $this->info['noscript'] = new HTMLPurifier_ElementDef();
-        $this->info['noscript']->attr = array( 0 => array('Common') );
-        $this->info['noscript']->content_model = 'Heading | List | Block';
-        $this->info['noscript']->content_model_type = 'required';
-
-        $this->info['script'] = new HTMLPurifier_ElementDef();
-        $this->info['script']->attr = array(
-            'defer' => new HTMLPurifier_AttrDef_Enum(array('defer')),
-            'src'   => new HTMLPurifier_AttrDef_URI(true),
-            'type'  => new HTMLPurifier_AttrDef_Enum(array('text/javascript'))
-        );
-        $this->info['script']->content_model = '#PCDATA';
-        $this->info['script']->content_model_type = 'optional';
-        $this->info['script']->attr_transform_pre['type'] =
-        $this->info['script']->attr_transform_post['type'] =
-            new HTMLPurifier_AttrTransform_ScriptRequired();
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/StyleAttribute.php b/library/HTMLPurifier/HTMLModule/StyleAttribute.php
deleted file mode 100644 (file)
index eb78464..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Edit Module, defines editing-related elements. Text Extension
- * Module.
- */
-class HTMLPurifier_HTMLModule_StyleAttribute extends HTMLPurifier_HTMLModule
-{
-
-    public $name = 'StyleAttribute';
-    public $attr_collections = array(
-        // The inclusion routine differs from the Abstract Modules but
-        // is in line with the DTD and XML Schemas.
-        'Style' => array('style' => false), // see constructor
-        'Core' => array(0 => array('Style'))
-    );
-
-    public function setup($config) {
-        $this->attr_collections['Style']['style'] = new HTMLPurifier_AttrDef_CSS();
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Tables.php b/library/HTMLPurifier/HTMLModule/Tables.php
deleted file mode 100644 (file)
index f314ced..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Tables Module, fully defines accessible table elements.
- */
-class HTMLPurifier_HTMLModule_Tables extends HTMLPurifier_HTMLModule
-{
-
-    public $name = 'Tables';
-
-    public function setup($config) {
-
-        $this->addElement('caption', false, 'Inline', 'Common');
-
-        $this->addElement('table', 'Block',
-            new HTMLPurifier_ChildDef_Table(),  'Common',
-            array(
-                'border' => 'Pixels',
-                'cellpadding' => 'Length',
-                'cellspacing' => 'Length',
-                'frame' => 'Enum#void,above,below,hsides,lhs,rhs,vsides,box,border',
-                'rules' => 'Enum#none,groups,rows,cols,all',
-                'summary' => 'Text',
-                'width' => 'Length'
-            )
-        );
-
-        // common attributes
-        $cell_align = array(
-            'align' => 'Enum#left,center,right,justify,char',
-            'charoff' => 'Length',
-            'valign' => 'Enum#top,middle,bottom,baseline',
-        );
-
-        $cell_t = array_merge(
-            array(
-                'abbr'    => 'Text',
-                'colspan' => 'Number',
-                'rowspan' => 'Number',
-            ),
-            $cell_align
-        );
-        $this->addElement('td', false, 'Flow', 'Common', $cell_t);
-        $this->addElement('th', false, 'Flow', 'Common', $cell_t);
-
-        $this->addElement('tr', false, 'Required: td | th', 'Common', $cell_align);
-
-        $cell_col = array_merge(
-            array(
-                'span'  => 'Number',
-                'width' => 'MultiLength',
-            ),
-            $cell_align
-        );
-        $this->addElement('col',      false, 'Empty',         'Common', $cell_col);
-        $this->addElement('colgroup', false, 'Optional: col', 'Common', $cell_col);
-
-        $this->addElement('tbody', false, 'Required: tr', 'Common', $cell_align);
-        $this->addElement('thead', false, 'Required: tr', 'Common', $cell_align);
-        $this->addElement('tfoot', false, 'Required: tr', 'Common', $cell_align);
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Target.php b/library/HTMLPurifier/HTMLModule/Target.php
deleted file mode 100644 (file)
index 2b844ec..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Target Module, defines target attribute in link elements.
- */
-class HTMLPurifier_HTMLModule_Target extends HTMLPurifier_HTMLModule
-{
-
-    public $name = 'Target';
-
-    public function setup($config) {
-        $elements = array('a');
-        foreach ($elements as $name) {
-            $e = $this->addBlankElement($name);
-            $e->attr = array(
-                'target' => new HTMLPurifier_AttrDef_HTML_FrameTarget()
-            );
-        }
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Text.php b/library/HTMLPurifier/HTMLModule/Text.php
deleted file mode 100644 (file)
index ae77c71..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Text Module, defines basic text containers. Core Module.
- * @note In the normative XML Schema specification, this module
- *       is further abstracted into the following modules:
- *          - Block Phrasal (address, blockquote, pre, h1, h2, h3, h4, h5, h6)
- *          - Block Structural (div, p)
- *          - Inline Phrasal (abbr, acronym, cite, code, dfn, em, kbd, q, samp, strong, var)
- *          - Inline Structural (br, span)
- *       This module, functionally, does not distinguish between these
- *       sub-modules, but the code is internally structured to reflect
- *       these distinctions.
- */
-class HTMLPurifier_HTMLModule_Text extends HTMLPurifier_HTMLModule
-{
-
-    public $name = 'Text';
-    public $content_sets = array(
-        'Flow' => 'Heading | Block | Inline'
-    );
-
-    public function setup($config) {
-
-        // Inline Phrasal -------------------------------------------------
-        $this->addElement('abbr',    'Inline', 'Inline', 'Common');
-        $this->addElement('acronym', 'Inline', 'Inline', 'Common');
-        $this->addElement('cite',    'Inline', 'Inline', 'Common');
-        $this->addElement('dfn',     'Inline', 'Inline', 'Common');
-        $this->addElement('kbd',     'Inline', 'Inline', 'Common');
-        $this->addElement('q',       'Inline', 'Inline', 'Common', array('cite' => 'URI'));
-        $this->addElement('samp',    'Inline', 'Inline', 'Common');
-        $this->addElement('var',     'Inline', 'Inline', 'Common');
-
-        $em = $this->addElement('em',      'Inline', 'Inline', 'Common');
-        $em->formatting = true;
-
-        $strong = $this->addElement('strong',  'Inline', 'Inline', 'Common');
-        $strong->formatting = true;
-
-        $code = $this->addElement('code',    'Inline', 'Inline', 'Common');
-        $code->formatting = true;
-
-        // Inline Structural ----------------------------------------------
-        $this->addElement('span', 'Inline', 'Inline', 'Common');
-        $this->addElement('br',   'Inline', 'Empty',  'Core');
-
-        // Block Phrasal --------------------------------------------------
-        $this->addElement('address',     'Block', 'Inline', 'Common');
-        $this->addElement('blockquote',  'Block', 'Optional: Heading | Block | List', 'Common', array('cite' => 'URI') );
-        $pre = $this->addElement('pre', 'Block', 'Inline', 'Common');
-        $pre->excludes = $this->makeLookup(
-            'img', 'big', 'small', 'object', 'applet', 'font', 'basefont' );
-        $this->addElement('h1', 'Heading', 'Inline', 'Common');
-        $this->addElement('h2', 'Heading', 'Inline', 'Common');
-        $this->addElement('h3', 'Heading', 'Inline', 'Common');
-        $this->addElement('h4', 'Heading', 'Inline', 'Common');
-        $this->addElement('h5', 'Heading', 'Inline', 'Common');
-        $this->addElement('h6', 'Heading', 'Inline', 'Common');
-
-        // Block Structural -----------------------------------------------
-        $p = $this->addElement('p', 'Block', 'Inline', 'Common');
-        $p->autoclose = array_flip(array("address", "blockquote", "center", "dir", "div", "dl", "fieldset", "ol", "p", "ul"));
-
-        $this->addElement('div', 'Block', 'Flow', 'Common');
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Tidy.php b/library/HTMLPurifier/HTMLModule/Tidy.php
deleted file mode 100644 (file)
index 21783f1..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-<?php
-
-/**
- * Abstract class for a set of proprietary modules that clean up (tidy)
- * poorly written HTML.
- * @todo Figure out how to protect some of these methods/properties
- */
-class HTMLPurifier_HTMLModule_Tidy extends HTMLPurifier_HTMLModule
-{
-
-    /**
-     * List of supported levels. Index zero is a special case "no fixes"
-     * level.
-     */
-    public $levels = array(0 => 'none', 'light', 'medium', 'heavy');
-
-    /**
-     * Default level to place all fixes in. Disabled by default
-     */
-    public $defaultLevel = null;
-
-    /**
-     * Lists of fixes used by getFixesForLevel(). Format is:
-     *      HTMLModule_Tidy->fixesForLevel[$level] = array('fix-1', 'fix-2');
-     */
-    public $fixesForLevel = array(
-        'light'  => array(),
-        'medium' => array(),
-        'heavy'  => array()
-    );
-
-    /**
-     * Lazy load constructs the module by determining the necessary
-     * fixes to create and then delegating to the populate() function.
-     * @todo Wildcard matching and error reporting when an added or
-     *       subtracted fix has no effect.
-     */
-    public function setup($config) {
-
-        // create fixes, initialize fixesForLevel
-        $fixes = $this->makeFixes();
-        $this->makeFixesForLevel($fixes);
-
-        // figure out which fixes to use
-        $level = $config->get('HTML.TidyLevel');
-        $fixes_lookup = $this->getFixesForLevel($level);
-
-        // get custom fix declarations: these need namespace processing
-        $add_fixes    = $config->get('HTML.TidyAdd');
-        $remove_fixes = $config->get('HTML.TidyRemove');
-
-        foreach ($fixes as $name => $fix) {
-            // needs to be refactored a little to implement globbing
-            if (
-                isset($remove_fixes[$name]) ||
-                (!isset($add_fixes[$name]) && !isset($fixes_lookup[$name]))
-            ) {
-                unset($fixes[$name]);
-            }
-        }
-
-        // populate this module with necessary fixes
-        $this->populate($fixes);
-
-    }
-
-    /**
-     * Retrieves all fixes per a level, returning fixes for that specific
-     * level as well as all levels below it.
-     * @param $level String level identifier, see $levels for valid values
-     * @return Lookup up table of fixes
-     */
-    public function getFixesForLevel($level) {
-        if ($level == $this->levels[0]) {
-            return array();
-        }
-        $activated_levels = array();
-        for ($i = 1, $c = count($this->levels); $i < $c; $i++) {
-            $activated_levels[] = $this->levels[$i];
-            if ($this->levels[$i] == $level) break;
-        }
-        if ($i == $c) {
-            trigger_error(
-                'Tidy level ' . htmlspecialchars($level) . ' not recognized',
-                E_USER_WARNING
-            );
-            return array();
-        }
-        $ret = array();
-        foreach ($activated_levels as $level) {
-            foreach ($this->fixesForLevel[$level] as $fix) {
-                $ret[$fix] = true;
-            }
-        }
-        return $ret;
-    }
-
-    /**
-     * Dynamically populates the $fixesForLevel member variable using
-     * the fixes array. It may be custom overloaded, used in conjunction
-     * with $defaultLevel, or not used at all.
-     */
-    public function makeFixesForLevel($fixes) {
-        if (!isset($this->defaultLevel)) return;
-        if (!isset($this->fixesForLevel[$this->defaultLevel])) {
-            trigger_error(
-                'Default level ' . $this->defaultLevel . ' does not exist',
-                E_USER_ERROR
-            );
-            return;
-        }
-        $this->fixesForLevel[$this->defaultLevel] = array_keys($fixes);
-    }
-
-    /**
-     * Populates the module with transforms and other special-case code
-     * based on a list of fixes passed to it
-     * @param $lookup Lookup table of fixes to activate
-     */
-    public function populate($fixes) {
-        foreach ($fixes as $name => $fix) {
-            // determine what the fix is for
-            list($type, $params) = $this->getFixType($name);
-            switch ($type) {
-                case 'attr_transform_pre':
-                case 'attr_transform_post':
-                    $attr = $params['attr'];
-                    if (isset($params['element'])) {
-                        $element = $params['element'];
-                        if (empty($this->info[$element])) {
-                            $e = $this->addBlankElement($element);
-                        } else {
-                            $e = $this->info[$element];
-                        }
-                    } else {
-                        $type = "info_$type";
-                        $e = $this;
-                    }
-                    // PHP does some weird parsing when I do
-                    // $e->$type[$attr], so I have to assign a ref.
-                    $f =& $e->$type;
-                    $f[$attr] = $fix;
-                    break;
-                case 'tag_transform':
-                    $this->info_tag_transform[$params['element']] = $fix;
-                    break;
-                case 'child':
-                case 'content_model_type':
-                    $element = $params['element'];
-                    if (empty($this->info[$element])) {
-                        $e = $this->addBlankElement($element);
-                    } else {
-                        $e = $this->info[$element];
-                    }
-                    $e->$type = $fix;
-                    break;
-                default:
-                    trigger_error("Fix type $type not supported", E_USER_ERROR);
-                    break;
-            }
-        }
-    }
-
-    /**
-     * Parses a fix name and determines what kind of fix it is, as well
-     * as other information defined by the fix
-     * @param $name String name of fix
-     * @return array(string $fix_type, array $fix_parameters)
-     * @note $fix_parameters is type dependant, see populate() for usage
-     *       of these parameters
-     */
-    public function getFixType($name) {
-        // parse it
-        $property = $attr = null;
-        if (strpos($name, '#') !== false) list($name, $property) = explode('#', $name);
-        if (strpos($name, '@') !== false) list($name, $attr)     = explode('@', $name);
-
-        // figure out the parameters
-        $params = array();
-        if ($name !== '')    $params['element'] = $name;
-        if (!is_null($attr)) $params['attr'] = $attr;
-
-        // special case: attribute transform
-        if (!is_null($attr)) {
-            if (is_null($property)) $property = 'pre';
-            $type = 'attr_transform_' . $property;
-            return array($type, $params);
-        }
-
-        // special case: tag transform
-        if (is_null($property)) {
-            return array('tag_transform', $params);
-        }
-
-        return array($property, $params);
-
-    }
-
-    /**
-     * Defines all fixes the module will perform in a compact
-     * associative array of fix name to fix implementation.
-     */
-    public function makeFixes() {}
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Tidy/Name.php b/library/HTMLPurifier/HTMLModule/Tidy/Name.php
deleted file mode 100644 (file)
index 61ff85c..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-/**
- * Name is deprecated, but allowed in strict doctypes, so onl
- */
-class HTMLPurifier_HTMLModule_Tidy_Name extends HTMLPurifier_HTMLModule_Tidy
-{
-    public $name = 'Tidy_Name';
-    public $defaultLevel = 'heavy';
-    public function makeFixes() {
-
-        $r = array();
-
-        // @name for img, a -----------------------------------------------
-        // Technically, it's allowed even on strict, so we allow authors to use
-        // it. However, it's deprecated in future versions of XHTML.
-        $r['img@name'] =
-        $r['a@name'] = new HTMLPurifier_AttrTransform_Name();
-
-        return $r;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Tidy/Proprietary.php b/library/HTMLPurifier/HTMLModule/Tidy/Proprietary.php
deleted file mode 100644 (file)
index 14c15c4..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_Tidy_Proprietary extends HTMLPurifier_HTMLModule_Tidy
-{
-
-    public $name = 'Tidy_Proprietary';
-    public $defaultLevel = 'light';
-
-    public function makeFixes() {
-        $r = array();
-        $r['table@background'] = new HTMLPurifier_AttrTransform_Background();
-        $r['td@background']    = new HTMLPurifier_AttrTransform_Background();
-        $r['th@background']    = new HTMLPurifier_AttrTransform_Background();
-        $r['tr@background']    = new HTMLPurifier_AttrTransform_Background();
-        $r['thead@background'] = new HTMLPurifier_AttrTransform_Background();
-        $r['tfoot@background'] = new HTMLPurifier_AttrTransform_Background();
-        $r['tbody@background'] = new HTMLPurifier_AttrTransform_Background();
-        $r['table@height']     = new HTMLPurifier_AttrTransform_Length('height');
-        return $r;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Tidy/Strict.php b/library/HTMLPurifier/HTMLModule/Tidy/Strict.php
deleted file mode 100644 (file)
index c73dc3c..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_Tidy_Strict extends HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4
-{
-    public $name = 'Tidy_Strict';
-    public $defaultLevel = 'light';
-
-    public function makeFixes() {
-        $r = parent::makeFixes();
-        $r['blockquote#content_model_type'] = 'strictblockquote';
-        return $r;
-    }
-
-    public $defines_child_def = true;
-    public function getChildDef($def) {
-        if ($def->content_model_type != 'strictblockquote') return parent::getChildDef($def);
-        return new HTMLPurifier_ChildDef_StrictBlockquote($def->content_model);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Tidy/Transitional.php b/library/HTMLPurifier/HTMLModule/Tidy/Transitional.php
deleted file mode 100644 (file)
index 9960b1d..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_Tidy_Transitional extends HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4
-{
-    public $name = 'Tidy_Transitional';
-    public $defaultLevel = 'heavy';
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Tidy/XHTML.php b/library/HTMLPurifier/HTMLModule/Tidy/XHTML.php
deleted file mode 100644 (file)
index db5a378..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_Tidy_XHTML extends HTMLPurifier_HTMLModule_Tidy
-{
-
-    public $name = 'Tidy_XHTML';
-    public $defaultLevel = 'medium';
-
-    public function makeFixes() {
-        $r = array();
-        $r['@lang'] = new HTMLPurifier_AttrTransform_Lang();
-        return $r;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php b/library/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php
deleted file mode 100644 (file)
index 02e9438..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4 extends HTMLPurifier_HTMLModule_Tidy
-{
-
-    public function makeFixes() {
-
-        $r = array();
-
-        // == deprecated tag transforms ===================================
-
-        $r['font']   = new HTMLPurifier_TagTransform_Font();
-        $r['menu']   = new HTMLPurifier_TagTransform_Simple('ul');
-        $r['dir']    = new HTMLPurifier_TagTransform_Simple('ul');
-        $r['center'] = new HTMLPurifier_TagTransform_Simple('div',  'text-align:center;');
-        $r['u']      = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:underline;');
-        $r['s']      = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:line-through;');
-        $r['strike'] = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:line-through;');
-
-        // == deprecated attribute transforms =============================
-
-        $r['caption@align'] =
-            new HTMLPurifier_AttrTransform_EnumToCSS('align', array(
-                // we're following IE's behavior, not Firefox's, due
-                // to the fact that no one supports caption-side:right,
-                // W3C included (with CSS 2.1). This is a slightly
-                // unreasonable attribute!
-                'left'   => 'text-align:left;',
-                'right'  => 'text-align:right;',
-                'top'    => 'caption-side:top;',
-                'bottom' => 'caption-side:bottom;' // not supported by IE
-            ));
-
-        // @align for img -------------------------------------------------
-        $r['img@align'] =
-            new HTMLPurifier_AttrTransform_EnumToCSS('align', array(
-                'left'   => 'float:left;',
-                'right'  => 'float:right;',
-                'top'    => 'vertical-align:top;',
-                'middle' => 'vertical-align:middle;',
-                'bottom' => 'vertical-align:baseline;',
-            ));
-
-        // @align for table -----------------------------------------------
-        $r['table@align'] =
-            new HTMLPurifier_AttrTransform_EnumToCSS('align', array(
-                'left'   => 'float:left;',
-                'center' => 'margin-left:auto;margin-right:auto;',
-                'right'  => 'float:right;'
-            ));
-
-        // @align for hr -----------------------------------------------
-        $r['hr@align'] =
-            new HTMLPurifier_AttrTransform_EnumToCSS('align', array(
-                // we use both text-align and margin because these work
-                // for different browsers (IE and Firefox, respectively)
-                // and the melange makes for a pretty cross-compatible
-                // solution
-                'left'   => 'margin-left:0;margin-right:auto;text-align:left;',
-                'center' => 'margin-left:auto;margin-right:auto;text-align:center;',
-                'right'  => 'margin-left:auto;margin-right:0;text-align:right;'
-            ));
-
-        // @align for h1, h2, h3, h4, h5, h6, p, div ----------------------
-        // {{{
-            $align_lookup = array();
-            $align_values = array('left', 'right', 'center', 'justify');
-            foreach ($align_values as $v) $align_lookup[$v] = "text-align:$v;";
-        // }}}
-        $r['h1@align'] =
-        $r['h2@align'] =
-        $r['h3@align'] =
-        $r['h4@align'] =
-        $r['h5@align'] =
-        $r['h6@align'] =
-        $r['p@align']  =
-        $r['div@align'] =
-            new HTMLPurifier_AttrTransform_EnumToCSS('align', $align_lookup);
-
-        // @bgcolor for table, tr, td, th ---------------------------------
-        $r['table@bgcolor'] =
-        $r['td@bgcolor'] =
-        $r['th@bgcolor'] =
-            new HTMLPurifier_AttrTransform_BgColor();
-
-        // @border for img ------------------------------------------------
-        $r['img@border'] = new HTMLPurifier_AttrTransform_Border();
-
-        // @clear for br --------------------------------------------------
-        $r['br@clear'] =
-            new HTMLPurifier_AttrTransform_EnumToCSS('clear', array(
-                'left'  => 'clear:left;',
-                'right' => 'clear:right;',
-                'all'   => 'clear:both;',
-                'none'  => 'clear:none;',
-            ));
-
-        // @height for td, th ---------------------------------------------
-        $r['td@height'] =
-        $r['th@height'] =
-            new HTMLPurifier_AttrTransform_Length('height');
-
-        // @hspace for img ------------------------------------------------
-        $r['img@hspace'] = new HTMLPurifier_AttrTransform_ImgSpace('hspace');
-
-        // @noshade for hr ------------------------------------------------
-        // this transformation is not precise but often good enough.
-        // different browsers use different styles to designate noshade
-        $r['hr@noshade'] =
-            new HTMLPurifier_AttrTransform_BoolToCSS(
-                'noshade',
-                'color:#808080;background-color:#808080;border:0;'
-            );
-
-        // @nowrap for td, th ---------------------------------------------
-        $r['td@nowrap'] =
-        $r['th@nowrap'] =
-            new HTMLPurifier_AttrTransform_BoolToCSS(
-                'nowrap',
-                'white-space:nowrap;'
-            );
-
-        // @size for hr  --------------------------------------------------
-        $r['hr@size'] = new HTMLPurifier_AttrTransform_Length('size', 'height');
-
-        // @type for li, ol, ul -------------------------------------------
-        // {{{
-            $ul_types = array(
-                'disc'   => 'list-style-type:disc;',
-                'square' => 'list-style-type:square;',
-                'circle' => 'list-style-type:circle;'
-            );
-            $ol_types = array(
-                '1'   => 'list-style-type:decimal;',
-                'i'   => 'list-style-type:lower-roman;',
-                'I'   => 'list-style-type:upper-roman;',
-                'a'   => 'list-style-type:lower-alpha;',
-                'A'   => 'list-style-type:upper-alpha;'
-            );
-            $li_types = $ul_types + $ol_types;
-        // }}}
-
-        $r['ul@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $ul_types);
-        $r['ol@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $ol_types, true);
-        $r['li@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $li_types, true);
-
-        // @vspace for img ------------------------------------------------
-        $r['img@vspace'] = new HTMLPurifier_AttrTransform_ImgSpace('vspace');
-
-        // @width for hr, td, th ------------------------------------------
-        $r['td@width'] =
-        $r['th@width'] =
-        $r['hr@width'] = new HTMLPurifier_AttrTransform_Length('width');
-
-        return $r;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModule/XMLCommonAttributes.php b/library/HTMLPurifier/HTMLModule/XMLCommonAttributes.php
deleted file mode 100644 (file)
index 9c0e031..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_XMLCommonAttributes extends HTMLPurifier_HTMLModule
-{
-    public $name = 'XMLCommonAttributes';
-
-    public $attr_collections = array(
-        'Lang' => array(
-            'xml:lang' => 'LanguageCode',
-        )
-    );
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/HTMLModuleManager.php b/library/HTMLPurifier/HTMLModuleManager.php
deleted file mode 100644 (file)
index f5c4a1d..0000000
+++ /dev/null
@@ -1,403 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModuleManager
-{
-
-    /**
-     * Instance of HTMLPurifier_DoctypeRegistry
-     */
-    public $doctypes;
-
-    /**
-     * Instance of current doctype
-     */
-    public $doctype;
-
-    /**
-     * Instance of HTMLPurifier_AttrTypes
-     */
-    public $attrTypes;
-
-    /**
-     * Active instances of modules for the specified doctype are
-     * indexed, by name, in this array.
-     */
-    public $modules = array();
-
-    /**
-     * Array of recognized HTMLPurifier_Module instances, indexed by
-     * module's class name. This array is usually lazy loaded, but a
-     * user can overload a module by pre-emptively registering it.
-     */
-    public $registeredModules = array();
-
-    /**
-     * List of extra modules that were added by the user using addModule().
-     * These get unconditionally merged into the current doctype, whatever
-     * it may be.
-     */
-    public $userModules = array();
-
-    /**
-     * Associative array of element name to list of modules that have
-     * definitions for the element; this array is dynamically filled.
-     */
-    public $elementLookup = array();
-
-    /** List of prefixes we should use for registering small names */
-    public $prefixes = array('HTMLPurifier_HTMLModule_');
-
-    public $contentSets;     /**< Instance of HTMLPurifier_ContentSets */
-    public $attrCollections; /**< Instance of HTMLPurifier_AttrCollections */
-
-    /** If set to true, unsafe elements and attributes will be allowed */
-    public $trusted = false;
-
-    public function __construct() {
-
-        // editable internal objects
-        $this->attrTypes = new HTMLPurifier_AttrTypes();
-        $this->doctypes  = new HTMLPurifier_DoctypeRegistry();
-
-        // setup basic modules
-        $common = array(
-            'CommonAttributes', 'Text', 'Hypertext', 'List',
-            'Presentation', 'Edit', 'Bdo', 'Tables', 'Image',
-            'StyleAttribute',
-            // Unsafe:
-            'Scripting', 'Object',  'Forms',
-            // Sorta legacy, but present in strict:
-            'Name',
-        );
-        $transitional = array('Legacy', 'Target');
-        $xml = array('XMLCommonAttributes');
-        $non_xml = array('NonXMLCommonAttributes');
-
-        // setup basic doctypes
-        $this->doctypes->register(
-            'HTML 4.01 Transitional', false,
-            array_merge($common, $transitional, $non_xml),
-            array('Tidy_Transitional', 'Tidy_Proprietary'),
-            array(),
-            '-//W3C//DTD HTML 4.01 Transitional//EN',
-            'http://www.w3.org/TR/html4/loose.dtd'
-        );
-
-        $this->doctypes->register(
-            'HTML 4.01 Strict', false,
-            array_merge($common, $non_xml),
-            array('Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'),
-            array(),
-            '-//W3C//DTD HTML 4.01//EN',
-            'http://www.w3.org/TR/html4/strict.dtd'
-        );
-
-        $this->doctypes->register(
-            'XHTML 1.0 Transitional', true,
-            array_merge($common, $transitional, $xml, $non_xml),
-            array('Tidy_Transitional', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Name'),
-            array(),
-            '-//W3C//DTD XHTML 1.0 Transitional//EN',
-            'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'
-        );
-
-        $this->doctypes->register(
-            'XHTML 1.0 Strict', true,
-            array_merge($common, $xml, $non_xml),
-            array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'),
-            array(),
-            '-//W3C//DTD XHTML 1.0 Strict//EN',
-            'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
-        );
-
-        $this->doctypes->register(
-            'XHTML 1.1', true,
-            array_merge($common, $xml, array('Ruby')),
-            array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Strict', 'Tidy_Name'), // Tidy_XHTML1_1
-            array(),
-            '-//W3C//DTD XHTML 1.1//EN',
-            'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'
-        );
-
-    }
-
-    /**
-     * Registers a module to the recognized module list, useful for
-     * overloading pre-existing modules.
-     * @param $module Mixed: string module name, with or without
-     *                HTMLPurifier_HTMLModule prefix, or instance of
-     *                subclass of HTMLPurifier_HTMLModule.
-     * @param $overload Boolean whether or not to overload previous modules.
-     *                  If this is not set, and you do overload a module,
-     *                  HTML Purifier will complain with a warning.
-     * @note This function will not call autoload, you must instantiate
-     *       (and thus invoke) autoload outside the method.
-     * @note If a string is passed as a module name, different variants
-     *       will be tested in this order:
-     *          - Check for HTMLPurifier_HTMLModule_$name
-     *          - Check all prefixes with $name in order they were added
-     *          - Check for literal object name
-     *          - Throw fatal error
-     *       If your object name collides with an internal class, specify
-     *       your module manually. All modules must have been included
-     *       externally: registerModule will not perform inclusions for you!
-     */
-    public function registerModule($module, $overload = false) {
-        if (is_string($module)) {
-            // attempt to load the module
-            $original_module = $module;
-            $ok = false;
-            foreach ($this->prefixes as $prefix) {
-                $module = $prefix . $original_module;
-                if (class_exists($module)) {
-                    $ok = true;
-                    break;
-                }
-            }
-            if (!$ok) {
-                $module = $original_module;
-                if (!class_exists($module)) {
-                    trigger_error($original_module . ' module does not exist',
-                        E_USER_ERROR);
-                    return;
-                }
-            }
-            $module = new $module();
-        }
-        if (empty($module->name)) {
-            trigger_error('Module instance of ' . get_class($module) . ' must have name');
-            return;
-        }
-        if (!$overload && isset($this->registeredModules[$module->name])) {
-            trigger_error('Overloading ' . $module->name . ' without explicit overload parameter', E_USER_WARNING);
-        }
-        $this->registeredModules[$module->name] = $module;
-    }
-
-    /**
-     * Adds a module to the current doctype by first registering it,
-     * and then tacking it on to the active doctype
-     */
-    public function addModule($module) {
-        $this->registerModule($module);
-        if (is_object($module)) $module = $module->name;
-        $this->userModules[] = $module;
-    }
-
-    /**
-     * Adds a class prefix that registerModule() will use to resolve a
-     * string name to a concrete class
-     */
-    public function addPrefix($prefix) {
-        $this->prefixes[] = $prefix;
-    }
-
-    /**
-     * Performs processing on modules, after being called you may
-     * use getElement() and getElements()
-     * @param $config Instance of HTMLPurifier_Config
-     */
-    public function setup($config) {
-
-        $this->trusted = $config->get('HTML.Trusted');
-
-        // generate
-        $this->doctype = $this->doctypes->make($config);
-        $modules = $this->doctype->modules;
-
-        // take out the default modules that aren't allowed
-        $lookup = $config->get('HTML.AllowedModules');
-        $special_cases = $config->get('HTML.CoreModules');
-
-        if (is_array($lookup)) {
-            foreach ($modules as $k => $m) {
-                if (isset($special_cases[$m])) continue;
-                if (!isset($lookup[$m])) unset($modules[$k]);
-            }
-        }
-
-        // add proprietary module (this gets special treatment because
-        // it is completely removed from doctypes, etc.)
-        if ($config->get('HTML.Proprietary')) {
-            $modules[] = 'Proprietary';
-        }
-
-        // add SafeObject/Safeembed modules
-        if ($config->get('HTML.SafeObject')) {
-            $modules[] = 'SafeObject';
-        }
-        if ($config->get('HTML.SafeEmbed')) {
-            $modules[] = 'SafeEmbed';
-        }
-
-        // merge in custom modules
-        $modules = array_merge($modules, $this->userModules);
-
-        foreach ($modules as $module) {
-            $this->processModule($module);
-            $this->modules[$module]->setup($config);
-        }
-
-        foreach ($this->doctype->tidyModules as $module) {
-            $this->processModule($module);
-            $this->modules[$module]->setup($config);
-        }
-
-        // prepare any injectors
-        foreach ($this->modules as $module) {
-            $n = array();
-            foreach ($module->info_injector as $i => $injector) {
-                if (!is_object($injector)) {
-                    $class = "HTMLPurifier_Injector_$injector";
-                    $injector = new $class;
-                }
-                $n[$injector->name] = $injector;
-            }
-            $module->info_injector = $n;
-        }
-
-        // setup lookup table based on all valid modules
-        foreach ($this->modules as $module) {
-            foreach ($module->info as $name => $def) {
-                if (!isset($this->elementLookup[$name])) {
-                    $this->elementLookup[$name] = array();
-                }
-                $this->elementLookup[$name][] = $module->name;
-            }
-        }
-
-        // note the different choice
-        $this->contentSets = new HTMLPurifier_ContentSets(
-            // content set assembly deals with all possible modules,
-            // not just ones deemed to be "safe"
-            $this->modules
-        );
-        $this->attrCollections = new HTMLPurifier_AttrCollections(
-            $this->attrTypes,
-            // there is no way to directly disable a global attribute,
-            // but using AllowedAttributes or simply not including
-            // the module in your custom doctype should be sufficient
-            $this->modules
-        );
-    }
-
-    /**
-     * Takes a module and adds it to the active module collection,
-     * registering it if necessary.
-     */
-    public function processModule($module) {
-        if (!isset($this->registeredModules[$module]) || is_object($module)) {
-            $this->registerModule($module);
-        }
-        $this->modules[$module] = $this->registeredModules[$module];
-    }
-
-    /**
-     * Retrieves merged element definitions.
-     * @return Array of HTMLPurifier_ElementDef
-     */
-    public function getElements() {
-
-        $elements = array();
-        foreach ($this->modules as $module) {
-            if (!$this->trusted && !$module->safe) continue;
-            foreach ($module->info as $name => $v) {
-                if (isset($elements[$name])) continue;
-                $elements[$name] = $this->getElement($name);
-            }
-        }
-
-        // remove dud elements, this happens when an element that
-        // appeared to be safe actually wasn't
-        foreach ($elements as $n => $v) {
-            if ($v === false) unset($elements[$n]);
-        }
-
-        return $elements;
-
-    }
-
-    /**
-     * Retrieves a single merged element definition
-     * @param $name Name of element
-     * @param $trusted Boolean trusted overriding parameter: set to true
-     *                 if you want the full version of an element
-     * @return Merged HTMLPurifier_ElementDef
-     * @note You may notice that modules are getting iterated over twice (once
-     *       in getElements() and once here). This
-     *       is because
-     */
-    public function getElement($name, $trusted = null) {
-
-        if (!isset($this->elementLookup[$name])) {
-            return false;
-        }
-
-        // setup global state variables
-        $def = false;
-        if ($trusted === null) $trusted = $this->trusted;
-
-        // iterate through each module that has registered itself to this
-        // element
-        foreach($this->elementLookup[$name] as $module_name) {
-
-            $module = $this->modules[$module_name];
-
-            // refuse to create/merge from a module that is deemed unsafe--
-            // pretend the module doesn't exist--when trusted mode is not on.
-            if (!$trusted && !$module->safe) {
-                continue;
-            }
-
-            // clone is used because, ideally speaking, the original
-            // definition should not be modified. Usually, this will
-            // make no difference, but for consistency's sake
-            $new_def = clone $module->info[$name];
-
-            if (!$def && $new_def->standalone) {
-                $def = $new_def;
-            } elseif ($def) {
-                // This will occur even if $new_def is standalone. In practice,
-                // this will usually result in a full replacement.
-                $def->mergeIn($new_def);
-            } else {
-                // :TODO:
-                // non-standalone definitions that don't have a standalone
-                // to merge into could be deferred to the end
-                continue;
-            }
-
-            // attribute value expansions
-            $this->attrCollections->performInclusions($def->attr);
-            $this->attrCollections->expandIdentifiers($def->attr, $this->attrTypes);
-
-            // descendants_are_inline, for ChildDef_Chameleon
-            if (is_string($def->content_model) &&
-                strpos($def->content_model, 'Inline') !== false) {
-                if ($name != 'del' && $name != 'ins') {
-                    // this is for you, ins/del
-                    $def->descendants_are_inline = true;
-                }
-            }
-
-            $this->contentSets->generateChildDef($def, $module);
-        }
-
-        // This can occur if there is a blank definition, but no base to
-        // mix it in with
-        if (!$def) return false;
-
-        // add information on required attributes
-        foreach ($def->attr as $attr_name => $attr_def) {
-            if ($attr_def->required) {
-                $def->required_attr[] = $attr_name;
-            }
-        }
-
-        return $def;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/IDAccumulator.php b/library/HTMLPurifier/IDAccumulator.php
deleted file mode 100644 (file)
index 7321529..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-/**
- * Component of HTMLPurifier_AttrContext that accumulates IDs to prevent dupes
- * @note In Slashdot-speak, dupe means duplicate.
- * @note The default constructor does not accept $config or $context objects:
- *       use must use the static build() factory method to perform initialization.
- */
-class HTMLPurifier_IDAccumulator
-{
-
-    /**
-     * Lookup table of IDs we've accumulated.
-     * @public
-     */
-    public $ids = array();
-
-    /**
-     * Builds an IDAccumulator, also initializing the default blacklist
-     * @param $config Instance of HTMLPurifier_Config
-     * @param $context Instance of HTMLPurifier_Context
-     * @return Fully initialized HTMLPurifier_IDAccumulator
-     */
-    public static function build($config, $context) {
-        $id_accumulator = new HTMLPurifier_IDAccumulator();
-        $id_accumulator->load($config->get('Attr.IDBlacklist'));
-        return $id_accumulator;
-    }
-
-    /**
-     * Add an ID to the lookup table.
-     * @param $id ID to be added.
-     * @return Bool status, true if success, false if there's a dupe
-     */
-    public function add($id) {
-        if (isset($this->ids[$id])) return false;
-        return $this->ids[$id] = true;
-    }
-
-    /**
-     * Load a list of IDs into the lookup table
-     * @param $array_of_ids Array of IDs to load
-     * @note This function doesn't care about duplicates
-     */
-    public function load($array_of_ids) {
-        foreach ($array_of_ids as $id) {
-            $this->ids[$id] = true;
-        }
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Injector.php b/library/HTMLPurifier/Injector.php
deleted file mode 100644 (file)
index 5922f81..0000000
+++ /dev/null
@@ -1,239 +0,0 @@
-<?php
-
-/**
- * Injects tokens into the document while parsing for well-formedness.
- * This enables "formatter-like" functionality such as auto-paragraphing,
- * smiley-ification and linkification to take place.
- *
- * A note on how handlers create changes; this is done by assigning a new
- * value to the $token reference. These values can take a variety of forms and
- * are best described HTMLPurifier_Strategy_MakeWellFormed->processToken()
- * documentation.
- *
- * @todo Allow injectors to request a re-run on their output. This
- *       would help if an operation is recursive.
- */
-abstract class HTMLPurifier_Injector
-{
-
-    /**
-     * Advisory name of injector, this is for friendly error messages
-     */
-    public $name;
-
-    /**
-     * Instance of HTMLPurifier_HTMLDefinition
-     */
-    protected $htmlDefinition;
-
-    /**
-     * Reference to CurrentNesting variable in Context. This is an array
-     * list of tokens that we are currently "inside"
-     */
-    protected $currentNesting;
-
-    /**
-     * Reference to InputTokens variable in Context. This is an array
-     * list of the input tokens that are being processed.
-     */
-    protected $inputTokens;
-
-    /**
-     * Reference to InputIndex variable in Context. This is an integer
-     * array index for $this->inputTokens that indicates what token
-     * is currently being processed.
-     */
-    protected $inputIndex;
-
-    /**
-     * Array of elements and attributes this injector creates and therefore
-     * need to be allowed by the definition. Takes form of
-     * array('element' => array('attr', 'attr2'), 'element2')
-     */
-    public $needed = array();
-
-    /**
-     * Index of inputTokens to rewind to.
-     */
-    protected $rewind = false;
-
-    /**
-     * Rewind to a spot to re-perform processing. This is useful if you
-     * deleted a node, and now need to see if this change affected any
-     * earlier nodes. Rewinding does not affect other injectors, and can
-     * result in infinite loops if not used carefully.
-     * @warning HTML Purifier will prevent you from fast-forwarding with this
-     *          function.
-     */
-    public function rewind($index) {
-        $this->rewind = $index;
-    }
-
-    /**
-     * Retrieves rewind, and then unsets it.
-     */
-    public function getRewind() {
-        $r = $this->rewind;
-        $this->rewind = false;
-        return $r;
-    }
-
-    /**
-     * Prepares the injector by giving it the config and context objects:
-     * this allows references to important variables to be made within
-     * the injector. This function also checks if the HTML environment
-     * will work with the Injector (see checkNeeded()).
-     * @param $config Instance of HTMLPurifier_Config
-     * @param $context Instance of HTMLPurifier_Context
-     * @return Boolean false if success, string of missing needed element/attribute if failure
-     */
-    public function prepare($config, $context) {
-        $this->htmlDefinition = $config->getHTMLDefinition();
-        // Even though this might fail, some unit tests ignore this and
-        // still test checkNeeded, so be careful. Maybe get rid of that
-        // dependency.
-        $result = $this->checkNeeded($config);
-        if ($result !== false) return $result;
-        $this->currentNesting =& $context->get('CurrentNesting');
-        $this->inputTokens    =& $context->get('InputTokens');
-        $this->inputIndex     =& $context->get('InputIndex');
-        return false;
-    }
-
-    /**
-     * This function checks if the HTML environment
-     * will work with the Injector: if p tags are not allowed, the
-     * Auto-Paragraphing injector should not be enabled.
-     * @param $config Instance of HTMLPurifier_Config
-     * @param $context Instance of HTMLPurifier_Context
-     * @return Boolean false if success, string of missing needed element/attribute if failure
-     */
-    public function checkNeeded($config) {
-        $def = $config->getHTMLDefinition();
-        foreach ($this->needed as $element => $attributes) {
-            if (is_int($element)) $element = $attributes;
-            if (!isset($def->info[$element])) return $element;
-            if (!is_array($attributes)) continue;
-            foreach ($attributes as $name) {
-                if (!isset($def->info[$element]->attr[$name])) return "$element.$name";
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Tests if the context node allows a certain element
-     * @param $name Name of element to test for
-     * @return True if element is allowed, false if it is not
-     */
-    public function allowsElement($name) {
-        if (!empty($this->currentNesting)) {
-            $parent_token = array_pop($this->currentNesting);
-            $this->currentNesting[] = $parent_token;
-            $parent = $this->htmlDefinition->info[$parent_token->name];
-        } else {
-            $parent = $this->htmlDefinition->info_parent_def;
-        }
-        if (!isset($parent->child->elements[$name]) || isset($parent->excludes[$name])) {
-            return false;
-        }
-        // check for exclusion
-        for ($i = count($this->currentNesting) - 2; $i >= 0; $i--) {
-            $node = $this->currentNesting[$i];
-            $def  = $this->htmlDefinition->info[$node->name];
-            if (isset($def->excludes[$name])) return false;
-        }
-        return true;
-    }
-
-    /**
-     * Iterator function, which starts with the next token and continues until
-     * you reach the end of the input tokens.
-     * @warning Please prevent previous references from interfering with this
-     *          functions by setting $i = null beforehand!
-     * @param &$i Current integer index variable for inputTokens
-     * @param &$current Current token variable. Do NOT use $token, as that variable is also a reference
-     */
-    protected function forward(&$i, &$current) {
-        if ($i === null) $i = $this->inputIndex + 1;
-        else $i++;
-        if (!isset($this->inputTokens[$i])) return false;
-        $current = $this->inputTokens[$i];
-        return true;
-    }
-
-    /**
-     * Similar to _forward, but accepts a third parameter $nesting (which
-     * should be initialized at 0) and stops when we hit the end tag
-     * for the node $this->inputIndex starts in.
-     */
-    protected function forwardUntilEndToken(&$i, &$current, &$nesting) {
-        $result = $this->forward($i, $current);
-        if (!$result) return false;
-        if ($nesting === null) $nesting = 0;
-        if     ($current instanceof HTMLPurifier_Token_Start) $nesting++;
-        elseif ($current instanceof HTMLPurifier_Token_End) {
-            if ($nesting <= 0) return false;
-            $nesting--;
-        }
-        return true;
-    }
-
-    /**
-     * Iterator function, starts with the previous token and continues until
-     * you reach the beginning of input tokens.
-     * @warning Please prevent previous references from interfering with this
-     *          functions by setting $i = null beforehand!
-     * @param &$i Current integer index variable for inputTokens
-     * @param &$current Current token variable. Do NOT use $token, as that variable is also a reference
-     */
-    protected function backward(&$i, &$current) {
-        if ($i === null) $i = $this->inputIndex - 1;
-        else $i--;
-        if ($i < 0) return false;
-        $current = $this->inputTokens[$i];
-        return true;
-    }
-
-    /**
-     * Initializes the iterator at the current position. Use in a do {} while;
-     * loop to force the _forward and _backward functions to start at the
-     * current location.
-     * @warning Please prevent previous references from interfering with this
-     *          functions by setting $i = null beforehand!
-     * @param &$i Current integer index variable for inputTokens
-     * @param &$current Current token variable. Do NOT use $token, as that variable is also a reference
-     */
-    protected function current(&$i, &$current) {
-        if ($i === null) $i = $this->inputIndex;
-        $current = $this->inputTokens[$i];
-    }
-
-    /**
-     * Handler that is called when a text token is processed
-     */
-    public function handleText(&$token) {}
-
-    /**
-     * Handler that is called when a start or empty token is processed
-     */
-    public function handleElement(&$token) {}
-
-    /**
-     * Handler that is called when an end token is processed
-     */
-    public function handleEnd(&$token) {
-        $this->notifyEnd($token);
-    }
-
-    /**
-     * Notifier that is called when an end token is processed
-     * @note This differs from handlers in that the token is read-only
-     * @deprecated
-     */
-    public function notifyEnd($token) {}
-
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Injector/AutoParagraph.php b/library/HTMLPurifier/Injector/AutoParagraph.php
deleted file mode 100644 (file)
index afa7608..0000000
+++ /dev/null
@@ -1,345 +0,0 @@
-<?php
-
-/**
- * Injector that auto paragraphs text in the root node based on
- * double-spacing.
- * @todo Ensure all states are unit tested, including variations as well.
- * @todo Make a graph of the flow control for this Injector.
- */
-class HTMLPurifier_Injector_AutoParagraph extends HTMLPurifier_Injector
-{
-
-    public $name = 'AutoParagraph';
-    public $needed = array('p');
-
-    private function _pStart() {
-        $par = new HTMLPurifier_Token_Start('p');
-        $par->armor['MakeWellFormed_TagClosedError'] = true;
-        return $par;
-    }
-
-    public function handleText(&$token) {
-        $text = $token->data;
-        // Does the current parent allow <p> tags?
-        if ($this->allowsElement('p')) {
-            if (empty($this->currentNesting) || strpos($text, "\n\n") !== false) {
-                // Note that we have differing behavior when dealing with text
-                // in the anonymous root node, or a node inside the document.
-                // If the text as a double-newline, the treatment is the same;
-                // if it doesn't, see the next if-block if you're in the document.
-
-                $i = $nesting = null;
-                if (!$this->forwardUntilEndToken($i, $current, $nesting) && $token->is_whitespace) {
-                    // State 1.1: ...    ^ (whitespace, then document end)
-                    //               ----
-                    // This is a degenerate case
-                } else {
-                    if (!$token->is_whitespace || $this->_isInline($current)) {
-                        // State 1.2: PAR1
-                        //            ----
-
-                        // State 1.3: PAR1\n\nPAR2
-                        //            ------------
-
-                        // State 1.4: <div>PAR1\n\nPAR2 (see State 2)
-                        //                 ------------
-                        $token = array($this->_pStart());
-                        $this->_splitText($text, $token);
-                    } else {
-                        // State 1.5: \n<hr />
-                        //            --
-                    }
-                }
-            } else {
-                // State 2:   <div>PAR1... (similar to 1.4)
-                //                 ----
-
-                // We're in an element that allows paragraph tags, but we're not
-                // sure if we're going to need them.
-                if ($this->_pLookAhead()) {
-                    // State 2.1: <div>PAR1<b>PAR1\n\nPAR2
-                    //                 ----
-                    // Note: This will always be the first child, since any
-                    // previous inline element would have triggered this very
-                    // same routine, and found the double newline. One possible
-                    // exception would be a comment.
-                    $token = array($this->_pStart(), $token);
-                } else {
-                    // State 2.2.1: <div>PAR1<div>
-                    //                   ----
-
-                    // State 2.2.2: <div>PAR1<b>PAR1</b></div>
-                    //                   ----
-                }
-            }
-        // Is the current parent a <p> tag?
-        } elseif (
-            !empty($this->currentNesting) &&
-            $this->currentNesting[count($this->currentNesting)-1]->name == 'p'
-        ) {
-            // State 3.1: ...<p>PAR1
-            //                  ----
-
-            // State 3.2: ...<p>PAR1\n\nPAR2
-            //                  ------------
-            $token = array();
-            $this->_splitText($text, $token);
-        // Abort!
-        } else {
-            // State 4.1: ...<b>PAR1
-            //                  ----
-
-            // State 4.2: ...<b>PAR1\n\nPAR2
-            //                  ------------
-        }
-    }
-
-    public function handleElement(&$token) {
-        // We don't have to check if we're already in a <p> tag for block
-        // tokens, because the tag would have been autoclosed by MakeWellFormed.
-        if ($this->allowsElement('p')) {
-            if (!empty($this->currentNesting)) {
-                if ($this->_isInline($token)) {
-                    // State 1: <div>...<b>
-                    //                  ---
-
-                    // Check if this token is adjacent to the parent token
-                    // (seek backwards until token isn't whitespace)
-                    $i = null;
-                    $this->backward($i, $prev);
-
-                    if (!$prev instanceof HTMLPurifier_Token_Start) {
-                        // Token wasn't adjacent
-
-                        if (
-                            $prev instanceof HTMLPurifier_Token_Text &&
-                            substr($prev->data, -2) === "\n\n"
-                        ) {
-                            // State 1.1.4: <div><p>PAR1</p>\n\n<b>
-                            //                                  ---
-
-                            // Quite frankly, this should be handled by splitText
-                            $token = array($this->_pStart(), $token);
-                        } else {
-                            // State 1.1.1: <div><p>PAR1</p><b>
-                            //                              ---
-
-                            // State 1.1.2: <div><br /><b>
-                            //                         ---
-
-                            // State 1.1.3: <div>PAR<b>
-                            //                      ---
-                        }
-
-                    } else {
-                        // State 1.2.1: <div><b>
-                        //                   ---
-
-                        // Lookahead to see if <p> is needed.
-                        if ($this->_pLookAhead()) {
-                            // State 1.3.1: <div><b>PAR1\n\nPAR2
-                            //                   ---
-                            $token = array($this->_pStart(), $token);
-                        } else {
-                            // State 1.3.2: <div><b>PAR1</b></div>
-                            //                   ---
-
-                            // State 1.3.3: <div><b>PAR1</b><div></div>\n\n</div>
-                            //                   ---
-                        }
-                    }
-                } else {
-                    // State 2.3: ...<div>
-                    //               -----
-                }
-            } else {
-                if ($this->_isInline($token)) {
-                    // State 3.1: <b>
-                    //            ---
-                    // This is where the {p} tag is inserted, not reflected in
-                    // inputTokens yet, however.
-                    $token = array($this->_pStart(), $token);
-                } else {
-                    // State 3.2: <div>
-                    //            -----
-                }
-
-                $i = null;
-                if ($this->backward($i, $prev)) {
-                    if (
-                        !$prev instanceof HTMLPurifier_Token_Text
-                    ) {
-                        // State 3.1.1: ...</p>{p}<b>
-                        //                        ---
-
-                        // State 3.2.1: ...</p><div>
-                        //                     -----
-
-                        if (!is_array($token)) $token = array($token);
-                        array_unshift($token, new HTMLPurifier_Token_Text("\n\n"));
-                    } else {
-                        // State 3.1.2: ...</p>\n\n{p}<b>
-                        //                            ---
-
-                        // State 3.2.2: ...</p>\n\n<div>
-                        //                         -----
-
-                        // Note: PAR<ELEM> cannot occur because PAR would have been
-                        // wrapped in <p> tags.
-                    }
-                }
-            }
-        } else {
-            // State 2.2: <ul><li>
-            //                ----
-
-            // State 2.4: <p><b>
-            //               ---
-        }
-    }
-
-    /**
-     * Splits up a text in paragraph tokens and appends them
-     * to the result stream that will replace the original
-     * @param $data String text data that will be processed
-     *    into paragraphs
-     * @param $result Reference to array of tokens that the
-     *    tags will be appended onto
-     * @param $config Instance of HTMLPurifier_Config
-     * @param $context Instance of HTMLPurifier_Context
-     */
-    private function _splitText($data, &$result) {
-        $raw_paragraphs = explode("\n\n", $data);
-        $paragraphs  = array(); // without empty paragraphs
-        $needs_start = false;
-        $needs_end   = false;
-
-        $c = count($raw_paragraphs);
-        if ($c == 1) {
-            // There were no double-newlines, abort quickly. In theory this
-            // should never happen.
-            $result[] = new HTMLPurifier_Token_Text($data);
-            return;
-        }
-        for ($i = 0; $i < $c; $i++) {
-            $par = $raw_paragraphs[$i];
-            if (trim($par) !== '') {
-                $paragraphs[] = $par;
-            } else {
-                if ($i == 0) {
-                    // Double newline at the front
-                    if (empty($result)) {
-                        // The empty result indicates that the AutoParagraph
-                        // injector did not add any start paragraph tokens.
-                        // This means that we have been in a paragraph for
-                        // a while, and the newline means we should start a new one.
-                        $result[] = new HTMLPurifier_Token_End('p');
-                        $result[] = new HTMLPurifier_Token_Text("\n\n");
-                        // However, the start token should only be added if
-                        // there is more processing to be done (i.e. there are
-                        // real paragraphs in here). If there are none, the
-                        // next start paragraph tag will be handled by the
-                        // next call to the injector
-                        $needs_start = true;
-                    } else {
-                        // We just started a new paragraph!
-                        // Reinstate a double-newline for presentation's sake, since
-                        // it was in the source code.
-                        array_unshift($result, new HTMLPurifier_Token_Text("\n\n"));
-                    }
-                } elseif ($i + 1 == $c) {
-                    // Double newline at the end
-                    // There should be a trailing </p> when we're finally done.
-                    $needs_end = true;
-                }
-            }
-        }
-
-        // Check if this was just a giant blob of whitespace. Move this earlier,
-        // perhaps?
-        if (empty($paragraphs)) {
-            return;
-        }
-
-        // Add the start tag indicated by \n\n at the beginning of $data
-        if ($needs_start) {
-            $result[] = $this->_pStart();
-        }
-
-        // Append the paragraphs onto the result
-        foreach ($paragraphs as $par) {
-            $result[] = new HTMLPurifier_Token_Text($par);
-            $result[] = new HTMLPurifier_Token_End('p');
-            $result[] = new HTMLPurifier_Token_Text("\n\n");
-            $result[] = $this->_pStart();
-        }
-
-        // Remove trailing start token; Injector will handle this later if
-        // it was indeed needed. This prevents from needing to do a lookahead,
-        // at the cost of a lookbehind later.
-        array_pop($result);
-
-        // If there is no need for an end tag, remove all of it and let
-        // MakeWellFormed close it later.
-        if (!$needs_end) {
-            array_pop($result); // removes \n\n
-            array_pop($result); // removes </p>
-        }
-
-    }
-
-    /**
-     * Returns true if passed token is inline (and, ergo, allowed in
-     * paragraph tags)
-     */
-    private function _isInline($token) {
-        return isset($this->htmlDefinition->info['p']->child->elements[$token->name]);
-    }
-
-    /**
-     * Looks ahead in the token list and determines whether or not we need
-     * to insert a <p> tag.
-     */
-    private function _pLookAhead() {
-        $this->current($i, $current);
-        if ($current instanceof HTMLPurifier_Token_Start) $nesting = 1;
-        else $nesting = 0;
-        $ok = false;
-        while ($this->forwardUntilEndToken($i, $current, $nesting)) {
-            $result = $this->_checkNeedsP($current);
-            if ($result !== null) {
-                $ok = $result;
-                break;
-            }
-        }
-        return $ok;
-    }
-
-    /**
-     * Determines if a particular token requires an earlier inline token
-     * to get a paragraph. This should be used with _forwardUntilEndToken
-     */
-    private function _checkNeedsP($current) {
-        if ($current instanceof HTMLPurifier_Token_Start){
-            if (!$this->_isInline($current)) {
-                // <div>PAR1<div>
-                //      ----
-                // Terminate early, since we hit a block element
-                return false;
-            }
-        } elseif ($current instanceof HTMLPurifier_Token_Text) {
-            if (strpos($current->data, "\n\n") !== false) {
-                // <div>PAR1<b>PAR1\n\nPAR2
-                //      ----
-                return true;
-            } else {
-                // <div>PAR1<b>PAR1...
-                //      ----
-            }
-        }
-        return null;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Injector/DisplayLinkURI.php b/library/HTMLPurifier/Injector/DisplayLinkURI.php
deleted file mode 100644 (file)
index 9dce9bd..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-/**
- * Injector that displays the URL of an anchor instead of linking to it, in addition to showing the text of the link.
- */
-class HTMLPurifier_Injector_DisplayLinkURI extends HTMLPurifier_Injector
-{
-
-    public $name = 'DisplayLinkURI';
-    public $needed = array('a');
-
-    public function handleElement(&$token) {
-    }
-
-    public function handleEnd(&$token) {
-        if (isset($token->start->attr['href'])){
-            $url = $token->start->attr['href'];
-            unset($token->start->attr['href']);
-            $token = array($token, new HTMLPurifier_Token_Text(" ($url)"));
-        } else {
-            // nothing to display
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Injector/Linkify.php b/library/HTMLPurifier/Injector/Linkify.php
deleted file mode 100644 (file)
index 296dac2..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-/**
- * Injector that converts http, https and ftp text URLs to actual links.
- */
-class HTMLPurifier_Injector_Linkify extends HTMLPurifier_Injector
-{
-
-    public $name = 'Linkify';
-    public $needed = array('a' => array('href'));
-
-    public function handleText(&$token) {
-        if (!$this->allowsElement('a')) return;
-
-        if (strpos($token->data, '://') === false) {
-            // our really quick heuristic failed, abort
-            // this may not work so well if we want to match things like
-            // "google.com", but then again, most people don't
-            return;
-        }
-
-        // there is/are URL(s). Let's split the string:
-        // Note: this regex is extremely permissive
-        $bits = preg_split('#((?:https?|ftp)://[^\s\'"<>()]+)#S', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
-
-        $token = array();
-
-        // $i = index
-        // $c = count
-        // $l = is link
-        for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) {
-            if (!$l) {
-                if ($bits[$i] === '') continue;
-                $token[] = new HTMLPurifier_Token_Text($bits[$i]);
-            } else {
-                $token[] = new HTMLPurifier_Token_Start('a', array('href' => $bits[$i]));
-                $token[] = new HTMLPurifier_Token_Text($bits[$i]);
-                $token[] = new HTMLPurifier_Token_End('a');
-            }
-        }
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Injector/PurifierLinkify.php b/library/HTMLPurifier/Injector/PurifierLinkify.php
deleted file mode 100644 (file)
index ad2455a..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-/**
- * Injector that converts configuration directive syntax %Namespace.Directive
- * to links
- */
-class HTMLPurifier_Injector_PurifierLinkify extends HTMLPurifier_Injector
-{
-
-    public $name = 'PurifierLinkify';
-    public $docURL;
-    public $needed = array('a' => array('href'));
-
-    public function prepare($config, $context) {
-        $this->docURL = $config->get('AutoFormat.PurifierLinkify.DocURL');
-        return parent::prepare($config, $context);
-    }
-
-    public function handleText(&$token) {
-        if (!$this->allowsElement('a')) return;
-        if (strpos($token->data, '%') === false) return;
-
-        $bits = preg_split('#%([a-z0-9]+\.[a-z0-9]+)#Si', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
-        $token = array();
-
-        // $i = index
-        // $c = count
-        // $l = is link
-        for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) {
-            if (!$l) {
-                if ($bits[$i] === '') continue;
-                $token[] = new HTMLPurifier_Token_Text($bits[$i]);
-            } else {
-                $token[] = new HTMLPurifier_Token_Start('a',
-                    array('href' => str_replace('%s', $bits[$i], $this->docURL)));
-                $token[] = new HTMLPurifier_Token_Text('%' . $bits[$i]);
-                $token[] = new HTMLPurifier_Token_End('a');
-            }
-        }
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Injector/RemoveEmpty.php b/library/HTMLPurifier/Injector/RemoveEmpty.php
deleted file mode 100644 (file)
index 638bfca..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
-{
-
-    private $context, $config, $attrValidator, $removeNbsp, $removeNbspExceptions;
-
-    public function prepare($config, $context) {
-        parent::prepare($config, $context);
-        $this->config = $config;
-        $this->context = $context;
-        $this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp');
-        $this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions');
-        $this->attrValidator = new HTMLPurifier_AttrValidator();
-    }
-
-    public function handleElement(&$token) {
-        if (!$token instanceof HTMLPurifier_Token_Start) return;
-        $next = false;
-        for ($i = $this->inputIndex + 1, $c = count($this->inputTokens); $i < $c; $i++) {
-            $next = $this->inputTokens[$i];
-            if ($next instanceof HTMLPurifier_Token_Text) {
-                if ($next->is_whitespace) continue;
-                if ($this->removeNbsp && !isset($this->removeNbspExceptions[$token->name])) {
-                    $plain = str_replace("\xC2\xA0", "", $next->data);
-                    $isWsOrNbsp = $plain === '' || ctype_space($plain);
-                    if ($isWsOrNbsp) continue;
-                }
-            }
-            break;
-        }
-        if (!$next || ($next instanceof HTMLPurifier_Token_End && $next->name == $token->name)) {
-            if ($token->name == 'colgroup') return;
-            $this->attrValidator->validateToken($token, $this->config, $this->context);
-            $token->armor['ValidateAttributes'] = true;
-            if (isset($token->attr['id']) || isset($token->attr['name'])) return;
-            $token = $i - $this->inputIndex + 1;
-            for ($b = $this->inputIndex - 1; $b > 0; $b--) {
-                $prev = $this->inputTokens[$b];
-                if ($prev instanceof HTMLPurifier_Token_Text && $prev->is_whitespace) continue;
-                break;
-            }
-            // This is safe because we removed the token that triggered this.
-            $this->rewind($b - 1);
-            return;
-        }
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php b/library/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php
deleted file mode 100644 (file)
index b213134..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-
-/**
- * Injector that removes spans with no attributes
- */
-class HTMLPurifier_Injector_RemoveSpansWithoutAttributes extends HTMLPurifier_Injector
-{
-    public $name = 'RemoveSpansWithoutAttributes';
-    public $needed = array('span');
-
-    private $attrValidator;
-
-    /**
-     * Used by AttrValidator
-     */
-    private $config;
-    private $context;
-
-    public function prepare($config, $context) {
-        $this->attrValidator = new HTMLPurifier_AttrValidator();
-        $this->config = $config;
-        $this->context = $context;
-        return parent::prepare($config, $context);
-    }
-
-    public function handleElement(&$token) {
-        if ($token->name !== 'span' || !$token instanceof HTMLPurifier_Token_Start) {
-            return;
-        }
-
-        // We need to validate the attributes now since this doesn't normally
-        // happen until after MakeWellFormed. If all the attributes are removed
-        // the span needs to be removed too.
-        $this->attrValidator->validateToken($token, $this->config, $this->context);
-        $token->armor['ValidateAttributes'] = true;
-
-        if (!empty($token->attr)) {
-            return;
-        }
-
-        $nesting = 0;
-        $spanContentTokens = array();
-        while ($this->forwardUntilEndToken($i, $current, $nesting)) {}
-
-        if ($current instanceof HTMLPurifier_Token_End && $current->name === 'span') {
-            // Mark closing span tag for deletion
-            $current->markForDeletion = true;
-            // Delete open span tag
-            $token = false;
-        }
-    }
-
-    public function handleEnd(&$token) {
-        if ($token->markForDeletion) {
-            $token = false;
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Injector/SafeObject.php b/library/HTMLPurifier/Injector/SafeObject.php
deleted file mode 100644 (file)
index 9e178ce..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-<?php
-
-/**
- * Adds important param elements to inside of object in order to make
- * things safe.
- */
-class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector
-{
-    public $name = 'SafeObject';
-    public $needed = array('object', 'param');
-
-    protected $objectStack = array();
-    protected $paramStack  = array();
-
-    // Keep this synchronized with AttrTransform/SafeParam.php
-    protected $addParam = array(
-        'allowScriptAccess' => 'never',
-        'allowNetworking' => 'internal',
-    );
-    protected $allowedParam = array(
-        'wmode' => true,
-        'movie' => true,
-        'flashvars' => true,
-        'src' => true,
-    );
-
-    public function prepare($config, $context) {
-        parent::prepare($config, $context);
-    }
-
-    public function handleElement(&$token) {
-        if ($token->name == 'object') {
-            $this->objectStack[] = $token;
-            $this->paramStack[] = array();
-            $new = array($token);
-            foreach ($this->addParam as $name => $value) {
-                $new[] = new HTMLPurifier_Token_Empty('param', array('name' => $name, 'value' => $value));
-            }
-            $token = $new;
-        } elseif ($token->name == 'param') {
-            $nest = count($this->currentNesting) - 1;
-            if ($nest >= 0 && $this->currentNesting[$nest]->name === 'object') {
-                $i = count($this->objectStack) - 1;
-                if (!isset($token->attr['name'])) {
-                    $token = false;
-                    return;
-                }
-                $n = $token->attr['name'];
-                // We need this fix because YouTube doesn't supply a data
-                // attribute, which we need if a type is specified. This is
-                // *very* Flash specific.
-                if (!isset($this->objectStack[$i]->attr['data']) &&
-                    ($token->attr['name'] == 'movie' || $token->attr['name'] == 'src')) {
-                    $this->objectStack[$i]->attr['data'] = $token->attr['value'];
-                }
-                // Check if the parameter is the correct value but has not
-                // already been added
-                if (
-                    !isset($this->paramStack[$i][$n]) &&
-                    isset($this->addParam[$n]) &&
-                    $token->attr['name'] === $this->addParam[$n]
-                ) {
-                    // keep token, and add to param stack
-                    $this->paramStack[$i][$n] = true;
-                } elseif (isset($this->allowedParam[$n])) {
-                    // keep token, don't do anything to it
-                    // (could possibly check for duplicates here)
-                } else {
-                    $token = false;
-                }
-            } else {
-                // not directly inside an object, DENY!
-                $token = false;
-            }
-        }
-    }
-
-    public function handleEnd(&$token) {
-        // This is the WRONG way of handling the object and param stacks;
-        // we should be inserting them directly on the relevant object tokens
-        // so that the global stack handling handles it.
-        if ($token->name == 'object') {
-            array_pop($this->objectStack);
-            array_pop($this->paramStack);
-        }
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Language.php b/library/HTMLPurifier/Language.php
deleted file mode 100644 (file)
index 3e2be03..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-<?php
-
-/**
- * Represents a language and defines localizable string formatting and
- * other functions, as well as the localized messages for HTML Purifier.
- */
-class HTMLPurifier_Language
-{
-
-    /**
-     * ISO 639 language code of language. Prefers shortest possible version
-     */
-    public $code = 'en';
-
-    /**
-     * Fallback language code
-     */
-    public $fallback = false;
-
-    /**
-     * Array of localizable messages
-     */
-    public $messages = array();
-
-    /**
-     * Array of localizable error codes
-     */
-    public $errorNames = array();
-
-    /**
-     * True if no message file was found for this language, so English
-     * is being used instead. Check this if you'd like to notify the
-     * user that they've used a non-supported language.
-     */
-    public $error = false;
-
-    /**
-     * Has the language object been loaded yet?
-     * @todo Make it private, fix usage in HTMLPurifier_LanguageTest
-     */
-    public $_loaded = false;
-
-    /**
-     * Instances of HTMLPurifier_Config and HTMLPurifier_Context
-     */
-    protected $config, $context;
-
-    public function __construct($config, $context) {
-        $this->config  = $config;
-        $this->context = $context;
-    }
-
-    /**
-     * Loads language object with necessary info from factory cache
-     * @note This is a lazy loader
-     */
-    public function load() {
-        if ($this->_loaded) return;
-        $factory = HTMLPurifier_LanguageFactory::instance();
-        $factory->loadLanguage($this->code);
-        foreach ($factory->keys as $key) {
-            $this->$key = $factory->cache[$this->code][$key];
-        }
-        $this->_loaded = true;
-    }
-
-    /**
-     * Retrieves a localised message.
-     * @param $key string identifier of message
-     * @return string localised message
-     */
-    public function getMessage($key) {
-        if (!$this->_loaded) $this->load();
-        if (!isset($this->messages[$key])) return "[$key]";
-        return $this->messages[$key];
-    }
-
-    /**
-     * Retrieves a localised error name.
-     * @param $int integer error number, corresponding to PHP's error
-     *             reporting
-     * @return string localised message
-     */
-    public function getErrorName($int) {
-        if (!$this->_loaded) $this->load();
-        if (!isset($this->errorNames[$int])) return "[Error: $int]";
-        return $this->errorNames[$int];
-    }
-
-    /**
-     * Converts an array list into a string readable representation
-     */
-    public function listify($array) {
-        $sep      = $this->getMessage('Item separator');
-        $sep_last = $this->getMessage('Item separator last');
-        $ret = '';
-        for ($i = 0, $c = count($array); $i < $c; $i++) {
-            if ($i == 0) {
-            } elseif ($i + 1 < $c) {
-                $ret .= $sep;
-            } else {
-                $ret .= $sep_last;
-            }
-            $ret .= $array[$i];
-        }
-        return $ret;
-    }
-
-    /**
-     * Formats a localised message with passed parameters
-     * @param $key string identifier of message
-     * @param $args Parameters to substitute in
-     * @return string localised message
-     * @todo Implement conditionals? Right now, some messages make
-     *     reference to line numbers, but those aren't always available
-     */
-    public function formatMessage($key, $args = array()) {
-        if (!$this->_loaded) $this->load();
-        if (!isset($this->messages[$key])) return "[$key]";
-        $raw = $this->messages[$key];
-        $subst = array();
-        $generator = false;
-        foreach ($args as $i => $value) {
-            if (is_object($value)) {
-                if ($value instanceof HTMLPurifier_Token) {
-                    // factor this out some time
-                    if (!$generator) $generator = $this->context->get('Generator');
-                    if (isset($value->name)) $subst['$'.$i.'.Name'] = $value->name;
-                    if (isset($value->data)) $subst['$'.$i.'.Data'] = $value->data;
-                    $subst['$'.$i.'.Compact'] =
-                    $subst['$'.$i.'.Serialized'] = $generator->generateFromToken($value);
-                    // a more complex algorithm for compact representation
-                    // could be introduced for all types of tokens. This
-                    // may need to be factored out into a dedicated class
-                    if (!empty($value->attr)) {
-                        $stripped_token = clone $value;
-                        $stripped_token->attr = array();
-                        $subst['$'.$i.'.Compact'] = $generator->generateFromToken($stripped_token);
-                    }
-                    $subst['$'.$i.'.Line'] = $value->line ? $value->line : 'unknown';
-                }
-                continue;
-            } elseif (is_array($value)) {
-                $keys = array_keys($value);
-                if (array_keys($keys) === $keys) {
-                    // list
-                    $subst['$'.$i] = $this->listify($value);
-                } else {
-                    // associative array
-                    // no $i implementation yet, sorry
-                    $subst['$'.$i.'.Keys'] = $this->listify($keys);
-                    $subst['$'.$i.'.Values'] = $this->listify(array_values($value));
-                }
-                continue;
-            }
-            $subst['$' . $i] = $value;
-        }
-        return strtr($raw, $subst);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Language/classes/en-x-test.php b/library/HTMLPurifier/Language/classes/en-x-test.php
deleted file mode 100644 (file)
index d52fcb7..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-// private class for unit testing
-
-class HTMLPurifier_Language_en_x_test extends HTMLPurifier_Language
-{
-
-
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Language/messages/en-x-test.php b/library/HTMLPurifier/Language/messages/en-x-test.php
deleted file mode 100644 (file)
index 1c046f3..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-
-// private language message file for unit testing purposes
-
-$fallback = 'en';
-
-$messages = array(
-    'HTMLPurifier' => 'HTML Purifier X'
-);
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Language/messages/en-x-testmini.php b/library/HTMLPurifier/Language/messages/en-x-testmini.php
deleted file mode 100644 (file)
index 806c83f..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-// private language message file for unit testing purposes
-// this language file has no class associated with it
-
-$fallback = 'en';
-
-$messages = array(
-    'HTMLPurifier' => 'HTML Purifier XNone'
-);
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Language/messages/en.php b/library/HTMLPurifier/Language/messages/en.php
deleted file mode 100644 (file)
index 8d7b573..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php
-
-$fallback = false;
-
-$messages = array(
-
-'HTMLPurifier' => 'HTML Purifier',
-
-// for unit testing purposes
-'LanguageFactoryTest: Pizza' => 'Pizza',
-'LanguageTest: List' => '$1',
-'LanguageTest: Hash' => '$1.Keys; $1.Values',
-
-'Item separator' => ', ',
-'Item separator last' => ' and ', // non-Harvard style
-
-'ErrorCollector: No errors' => 'No errors detected. However, because error reporting is still incomplete, there may have been errors that the error collector was not notified of; please inspect the output HTML carefully.',
-'ErrorCollector: At line'   => ' at line $line',
-'ErrorCollector: Incidental errors'  => 'Incidental errors',
-
-'Lexer: Unclosed comment'      => 'Unclosed comment',
-'Lexer: Unescaped lt'          => 'Unescaped less-than sign (<) should be &lt;',
-'Lexer: Missing gt'            => 'Missing greater-than sign (>), previous less-than sign (<) should be escaped',
-'Lexer: Missing attribute key' => 'Attribute declaration has no key',
-'Lexer: Missing end quote'     => 'Attribute declaration has no end quote',
-'Lexer: Extracted body'        => 'Removed document metadata tags',
-
-'Strategy_RemoveForeignElements: Tag transform'              => '<$1> element transformed into $CurrentToken.Serialized',
-'Strategy_RemoveForeignElements: Missing required attribute' => '$CurrentToken.Compact element missing required attribute $1',
-'Strategy_RemoveForeignElements: Foreign element to text'    => 'Unrecognized $CurrentToken.Serialized tag converted to text',
-'Strategy_RemoveForeignElements: Foreign element removed'    => 'Unrecognized $CurrentToken.Serialized tag removed',
-'Strategy_RemoveForeignElements: Comment removed'            => 'Comment containing "$CurrentToken.Data" removed',
-'Strategy_RemoveForeignElements: Foreign meta element removed' => 'Unrecognized $CurrentToken.Serialized meta tag and all descendants removed',
-'Strategy_RemoveForeignElements: Token removed to end'       => 'Tags and text starting from $1 element where removed to end',
-'Strategy_RemoveForeignElements: Trailing hyphen in comment removed' => 'Trailing hyphen(s) in comment removed',
-'Strategy_RemoveForeignElements: Hyphens in comment collapsed' => 'Double hyphens in comments are not allowed, and were collapsed into single hyphens',
-
-'Strategy_MakeWellFormed: Unnecessary end tag removed' => 'Unnecessary $CurrentToken.Serialized tag removed',
-'Strategy_MakeWellFormed: Unnecessary end tag to text' => 'Unnecessary $CurrentToken.Serialized tag converted to text',
-'Strategy_MakeWellFormed: Tag auto closed'             => '$1.Compact started on line $1.Line auto-closed by $CurrentToken.Compact',
-'Strategy_MakeWellFormed: Tag carryover'               => '$1.Compact started on line $1.Line auto-continued into $CurrentToken.Compact',
-'Strategy_MakeWellFormed: Stray end tag removed'       => 'Stray $CurrentToken.Serialized tag removed',
-'Strategy_MakeWellFormed: Stray end tag to text'       => 'Stray $CurrentToken.Serialized tag converted to text',
-'Strategy_MakeWellFormed: Tag closed by element end'   => '$1.Compact tag started on line $1.Line closed by end of $CurrentToken.Serialized',
-'Strategy_MakeWellFormed: Tag closed by document end'  => '$1.Compact tag started on line $1.Line closed by end of document',
-
-'Strategy_FixNesting: Node removed'          => '$CurrentToken.Compact node removed',
-'Strategy_FixNesting: Node excluded'         => '$CurrentToken.Compact node removed due to descendant exclusion by ancestor element',
-'Strategy_FixNesting: Node reorganized'      => 'Contents of $CurrentToken.Compact node reorganized to enforce its content model',
-'Strategy_FixNesting: Node contents removed' => 'Contents of $CurrentToken.Compact node removed',
-
-'AttrValidator: Attributes transformed' => 'Attributes on $CurrentToken.Compact transformed from $1.Keys to $2.Keys',
-'AttrValidator: Attribute removed' => '$CurrentAttr.Name attribute on $CurrentToken.Compact removed',
-
-);
-
-$errorNames = array(
-    E_ERROR   => 'Error',
-    E_WARNING => 'Warning',
-    E_NOTICE  => 'Notice'
-);
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/LanguageFactory.php b/library/HTMLPurifier/LanguageFactory.php
deleted file mode 100644 (file)
index 134ef8c..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-<?php
-
-/**
- * Class responsible for generating HTMLPurifier_Language objects, managing
- * caching and fallbacks.
- * @note Thanks to MediaWiki for the general logic, although this version
- *       has been entirely rewritten
- * @todo Serialized cache for languages
- */
-class HTMLPurifier_LanguageFactory
-{
-
-    /**
-     * Cache of language code information used to load HTMLPurifier_Language objects
-     * Structure is: $factory->cache[$language_code][$key] = $value
-     * @value array map
-     */
-    public $cache;
-
-    /**
-     * Valid keys in the HTMLPurifier_Language object. Designates which
-     * variables to slurp out of a message file.
-     * @value array list
-     */
-    public $keys = array('fallback', 'messages', 'errorNames');
-
-    /**
-     * Instance of HTMLPurifier_AttrDef_Lang to validate language codes
-     * @value object HTMLPurifier_AttrDef_Lang
-     */
-    protected $validator;
-
-    /**
-     * Cached copy of dirname(__FILE__), directory of current file without
-     * trailing slash
-     * @value string filename
-     */
-    protected $dir;
-
-    /**
-     * Keys whose contents are a hash map and can be merged
-     * @value array lookup
-     */
-    protected $mergeable_keys_map = array('messages' => true, 'errorNames' => true);
-
-    /**
-     * Keys whose contents are a list and can be merged
-     * @value array lookup
-     */
-    protected $mergeable_keys_list = array();
-
-    /**
-     * Retrieve sole instance of the factory.
-     * @param $prototype Optional prototype to overload sole instance with,
-     *                   or bool true to reset to default factory.
-     */
-    public static function instance($prototype = null) {
-        static $instance = null;
-        if ($prototype !== null) {
-            $instance = $prototype;
-        } elseif ($instance === null || $prototype == true) {
-            $instance = new HTMLPurifier_LanguageFactory();
-            $instance->setup();
-        }
-        return $instance;
-    }
-
-    /**
-     * Sets up the singleton, much like a constructor
-     * @note Prevents people from getting this outside of the singleton
-     */
-    public function setup() {
-        $this->validator = new HTMLPurifier_AttrDef_Lang();
-        $this->dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier';
-    }
-
-    /**
-     * Creates a language object, handles class fallbacks
-     * @param $config Instance of HTMLPurifier_Config
-     * @param $context Instance of HTMLPurifier_Context
-     * @param $code Code to override configuration with. Private parameter.
-     */
-    public function create($config, $context, $code = false) {
-
-        // validate language code
-        if ($code === false) {
-            $code = $this->validator->validate(
-              $config->get('Core.Language'), $config, $context
-            );
-        } else {
-            $code = $this->validator->validate($code, $config, $context);
-        }
-        if ($code === false) $code = 'en'; // malformed code becomes English
-
-        $pcode = str_replace('-', '_', $code); // make valid PHP classname
-        static $depth = 0; // recursion protection
-
-        if ($code == 'en') {
-            $lang = new HTMLPurifier_Language($config, $context);
-        } else {
-            $class = 'HTMLPurifier_Language_' . $pcode;
-            $file  = $this->dir . '/Language/classes/' . $code . '.php';
-            if (file_exists($file) || class_exists($class, false)) {
-                $lang = new $class($config, $context);
-            } else {
-                // Go fallback
-                $raw_fallback = $this->getFallbackFor($code);
-                $fallback = $raw_fallback ? $raw_fallback : 'en';
-                $depth++;
-                $lang = $this->create($config, $context, $fallback);
-                if (!$raw_fallback) {
-                    $lang->error = true;
-                }
-                $depth--;
-            }
-        }
-
-        $lang->code = $code;
-
-        return $lang;
-
-    }
-
-    /**
-     * Returns the fallback language for language
-     * @note Loads the original language into cache
-     * @param $code string language code
-     */
-    public function getFallbackFor($code) {
-        $this->loadLanguage($code);
-        return $this->cache[$code]['fallback'];
-    }
-
-    /**
-     * Loads language into the cache, handles message file and fallbacks
-     * @param $code string language code
-     */
-    public function loadLanguage($code) {
-        static $languages_seen = array(); // recursion guard
-
-        // abort if we've already loaded it
-        if (isset($this->cache[$code])) return;
-
-        // generate filename
-        $filename = $this->dir . '/Language/messages/' . $code . '.php';
-
-        // default fallback : may be overwritten by the ensuing include
-        $fallback = ($code != 'en') ? 'en' : false;
-
-        // load primary localisation
-        if (!file_exists($filename)) {
-            // skip the include: will rely solely on fallback
-            $filename = $this->dir . '/Language/messages/en.php';
-            $cache = array();
-        } else {
-            include $filename;
-            $cache = compact($this->keys);
-        }
-
-        // load fallback localisation
-        if (!empty($fallback)) {
-
-            // infinite recursion guard
-            if (isset($languages_seen[$code])) {
-                trigger_error('Circular fallback reference in language ' .
-                    $code, E_USER_ERROR);
-                $fallback = 'en';
-            }
-            $language_seen[$code] = true;
-
-            // load the fallback recursively
-            $this->loadLanguage($fallback);
-            $fallback_cache = $this->cache[$fallback];
-
-            // merge fallback with current language
-            foreach ( $this->keys as $key ) {
-                if (isset($cache[$key]) && isset($fallback_cache[$key])) {
-                    if (isset($this->mergeable_keys_map[$key])) {
-                        $cache[$key] = $cache[$key] + $fallback_cache[$key];
-                    } elseif (isset($this->mergeable_keys_list[$key])) {
-                        $cache[$key] = array_merge( $fallback_cache[$key], $cache[$key] );
-                    }
-                } else {
-                    $cache[$key] = $fallback_cache[$key];
-                }
-            }
-
-        }
-
-        // save to cache for later retrieval
-        $this->cache[$code] = $cache;
-
-        return;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Length.php b/library/HTMLPurifier/Length.php
deleted file mode 100644 (file)
index 8d2a46b..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-<?php
-
-/**
- * Represents a measurable length, with a string numeric magnitude
- * and a unit. This object is immutable.
- */
-class HTMLPurifier_Length
-{
-
-    /**
-     * String numeric magnitude.
-     */
-    protected $n;
-
-    /**
-     * String unit. False is permitted if $n = 0.
-     */
-    protected $unit;
-
-    /**
-     * Whether or not this length is valid. Null if not calculated yet.
-     */
-    protected $isValid;
-
-    /**
-     * Lookup array of units recognized by CSS 2.1
-     */
-    protected static $allowedUnits = array(
-        'em' => true, 'ex' => true, 'px' => true, 'in' => true,
-        'cm' => true, 'mm' => true, 'pt' => true, 'pc' => true
-    );
-
-    /**
-     * @param number $n Magnitude
-     * @param string $u Unit
-     */
-    public function __construct($n = '0', $u = false) {
-        $this->n = (string) $n;
-        $this->unit = $u !== false ? (string) $u : false;
-    }
-
-    /**
-     * @param string $s Unit string, like '2em' or '3.4in'
-     * @warning Does not perform validation.
-     */
-    static public function make($s) {
-        if ($s instanceof HTMLPurifier_Length) return $s;
-        $n_length = strspn($s, '1234567890.+-');
-        $n = substr($s, 0, $n_length);
-        $unit = substr($s, $n_length);
-        if ($unit === '') $unit = false;
-        return new HTMLPurifier_Length($n, $unit);
-    }
-
-    /**
-     * Validates the number and unit.
-     */
-    protected function validate() {
-        // Special case:
-        if ($this->n === '+0' || $this->n === '-0') $this->n = '0';
-        if ($this->n === '0' && $this->unit === false) return true;
-        if (!ctype_lower($this->unit)) $this->unit = strtolower($this->unit);
-        if (!isset(HTMLPurifier_Length::$allowedUnits[$this->unit])) return false;
-        // Hack:
-        $def = new HTMLPurifier_AttrDef_CSS_Number();
-        $result = $def->validate($this->n, false, false);
-        if ($result === false) return false;
-        $this->n = $result;
-        return true;
-    }
-
-    /**
-     * Returns string representation of number.
-     */
-    public function toString() {
-        if (!$this->isValid()) return false;
-        return $this->n . $this->unit;
-    }
-
-    /**
-     * Retrieves string numeric magnitude.
-     */
-    public function getN() {return $this->n;}
-
-    /**
-     * Retrieves string unit.
-     */
-    public function getUnit() {return $this->unit;}
-
-    /**
-     * Returns true if this length unit is valid.
-     */
-    public function isValid() {
-        if ($this->isValid === null) $this->isValid = $this->validate();
-        return $this->isValid;
-    }
-
-    /**
-     * Compares two lengths, and returns 1 if greater, -1 if less and 0 if equal.
-     * @warning If both values are too large or small, this calculation will
-     *          not work properly
-     */
-    public function compareTo($l) {
-        if ($l === false) return false;
-        if ($l->unit !== $this->unit) {
-            $converter = new HTMLPurifier_UnitConverter();
-            $l = $converter->convert($l, $this->unit);
-            if ($l === false) return false;
-        }
-        return $this->n - $l->n;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Lexer.php b/library/HTMLPurifier/Lexer.php
deleted file mode 100644 (file)
index b05e115..0000000
+++ /dev/null
@@ -1,306 +0,0 @@
-<?php
-
-/**
- * Forgivingly lexes HTML (SGML-style) markup into tokens.
- *
- * A lexer parses a string of SGML-style markup and converts them into
- * corresponding tokens.  It doesn't check for well-formedness, although its
- * internal mechanism may make this automatic (such as the case of
- * HTMLPurifier_Lexer_DOMLex).  There are several implementations to choose
- * from.
- *
- * A lexer is HTML-oriented: it might work with XML, but it's not
- * recommended, as we adhere to a subset of the specification for optimization
- * reasons. This might change in the future. Also, most tokenizers are not
- * expected to handle DTDs or PIs.
- *
- * This class should not be directly instantiated, but you may use create() to
- * retrieve a default copy of the lexer.  Being a supertype, this class
- * does not actually define any implementation, but offers commonly used
- * convenience functions for subclasses.
- *
- * @note The unit tests will instantiate this class for testing purposes, as
- *       many of the utility functions require a class to be instantiated.
- *       This means that, even though this class is not runnable, it will
- *       not be declared abstract.
- *
- * @par
- *
- * @note
- * We use tokens rather than create a DOM representation because DOM would:
- *
- * @par
- *  -# Require more processing and memory to create,
- *  -# Is not streamable, and
- *  -# Has the entire document structure (html and body not needed).
- *
- * @par
- * However, DOM is helpful in that it makes it easy to move around nodes
- * without a lot of lookaheads to see when a tag is closed. This is a
- * limitation of the token system and some workarounds would be nice.
- */
-class HTMLPurifier_Lexer
-{
-
-    /**
-     * Whether or not this lexer implements line-number/column-number tracking.
-     * If it does, set to true.
-     */
-    public $tracksLineNumbers = false;
-
-    // -- STATIC ----------------------------------------------------------
-
-    /**
-     * Retrieves or sets the default Lexer as a Prototype Factory.
-     *
-     * By default HTMLPurifier_Lexer_DOMLex will be returned. There are
-     * a few exceptions involving special features that only DirectLex
-     * implements.
-     *
-     * @note The behavior of this class has changed, rather than accepting
-     *       a prototype object, it now accepts a configuration object.
-     *       To specify your own prototype, set %Core.LexerImpl to it.
-     *       This change in behavior de-singletonizes the lexer object.
-     *
-     * @param $config Instance of HTMLPurifier_Config
-     * @return Concrete lexer.
-     */
-    public static function create($config) {
-
-        if (!($config instanceof HTMLPurifier_Config)) {
-            $lexer = $config;
-            trigger_error("Passing a prototype to
-              HTMLPurifier_Lexer::create() is deprecated, please instead
-              use %Core.LexerImpl", E_USER_WARNING);
-        } else {
-            $lexer = $config->get('Core.LexerImpl');
-        }
-
-        $needs_tracking =
-            $config->get('Core.MaintainLineNumbers') ||
-            $config->get('Core.CollectErrors');
-
-        $inst = null;
-        if (is_object($lexer)) {
-            $inst = $lexer;
-        } else {
-
-            if (is_null($lexer)) { do {
-                // auto-detection algorithm
-
-                if ($needs_tracking) {
-                    $lexer = 'DirectLex';
-                    break;
-                }
-
-                if (
-                    class_exists('DOMDocument') &&
-                    method_exists('DOMDocument', 'loadHTML') &&
-                    !extension_loaded('domxml')
-                ) {
-                    // check for DOM support, because while it's part of the
-                    // core, it can be disabled compile time. Also, the PECL
-                    // domxml extension overrides the default DOM, and is evil
-                    // and nasty and we shan't bother to support it
-                    $lexer = 'DOMLex';
-                } else {
-                    $lexer = 'DirectLex';
-                }
-
-            } while(0); } // do..while so we can break
-
-            // instantiate recognized string names
-            switch ($lexer) {
-                case 'DOMLex':
-                    $inst = new HTMLPurifier_Lexer_DOMLex();
-                    break;
-                case 'DirectLex':
-                    $inst = new HTMLPurifier_Lexer_DirectLex();
-                    break;
-                case 'PH5P':
-                    $inst = new HTMLPurifier_Lexer_PH5P();
-                    break;
-                default:
-                    throw new HTMLPurifier_Exception("Cannot instantiate unrecognized Lexer type " . htmlspecialchars($lexer));
-            }
-        }
-
-        if (!$inst) throw new HTMLPurifier_Exception('No lexer was instantiated');
-
-        // once PHP DOM implements native line numbers, or we
-        // hack out something using XSLT, remove this stipulation
-        if ($needs_tracking && !$inst->tracksLineNumbers) {
-            throw new HTMLPurifier_Exception('Cannot use lexer that does not support line numbers with Core.MaintainLineNumbers or Core.CollectErrors (use DirectLex instead)');
-        }
-
-        return $inst;
-
-    }
-
-    // -- CONVENIENCE MEMBERS ---------------------------------------------
-
-    public function __construct() {
-        $this->_entity_parser = new HTMLPurifier_EntityParser();
-    }
-
-    /**
-     * Most common entity to raw value conversion table for special entities.
-     */
-    protected $_special_entity2str =
-            array(
-                    '&quot;' => '"',
-                    '&amp;'  => '&',
-                    '&lt;'   => '<',
-                    '&gt;'   => '>',
-                    '&#39;'  => "'",
-                    '&#039;' => "'",
-                    '&#x27;' => "'"
-            );
-
-    /**
-     * Parses special entities into the proper characters.
-     *
-     * This string will translate escaped versions of the special characters
-     * into the correct ones.
-     *
-     * @warning
-     * You should be able to treat the output of this function as
-     * completely parsed, but that's only because all other entities should
-     * have been handled previously in substituteNonSpecialEntities()
-     *
-     * @param $string String character data to be parsed.
-     * @returns Parsed character data.
-     */
-    public function parseData($string) {
-
-        // following functions require at least one character
-        if ($string === '') return '';
-
-        // subtracts amps that cannot possibly be escaped
-        $num_amp = substr_count($string, '&') - substr_count($string, '& ') -
-            ($string[strlen($string)-1] === '&' ? 1 : 0);
-
-        if (!$num_amp) return $string; // abort if no entities
-        $num_esc_amp = substr_count($string, '&amp;');
-        $string = strtr($string, $this->_special_entity2str);
-
-        // code duplication for sake of optimization, see above
-        $num_amp_2 = substr_count($string, '&') - substr_count($string, '& ') -
-            ($string[strlen($string)-1] === '&' ? 1 : 0);
-
-        if ($num_amp_2 <= $num_esc_amp) return $string;
-
-        // hmm... now we have some uncommon entities. Use the callback.
-        $string = $this->_entity_parser->substituteSpecialEntities($string);
-        return $string;
-    }
-
-    /**
-     * Lexes an HTML string into tokens.
-     *
-     * @param $string String HTML.
-     * @return HTMLPurifier_Token array representation of HTML.
-     */
-    public function tokenizeHTML($string, $config, $context) {
-        trigger_error('Call to abstract class', E_USER_ERROR);
-    }
-
-    /**
-     * Translates CDATA sections into regular sections (through escaping).
-     *
-     * @param $string HTML string to process.
-     * @returns HTML with CDATA sections escaped.
-     */
-    protected static function escapeCDATA($string) {
-        return preg_replace_callback(
-            '/<!\[CDATA\[(.+?)\]\]>/s',
-            array('HTMLPurifier_Lexer', 'CDATACallback'),
-            $string
-        );
-    }
-
-    /**
-     * Special CDATA case that is especially convoluted for <script>
-     */
-    protected static function escapeCommentedCDATA($string) {
-        return preg_replace_callback(
-            '#<!--//--><!\[CDATA\[//><!--(.+?)//--><!\]\]>#s',
-            array('HTMLPurifier_Lexer', 'CDATACallback'),
-            $string
-        );
-    }
-
-    /**
-     * Callback function for escapeCDATA() that does the work.
-     *
-     * @warning Though this is public in order to let the callback happen,
-     *          calling it directly is not recommended.
-     * @params $matches PCRE matches array, with index 0 the entire match
-     *                  and 1 the inside of the CDATA section.
-     * @returns Escaped internals of the CDATA section.
-     */
-    protected static function CDATACallback($matches) {
-        // not exactly sure why the character set is needed, but whatever
-        return htmlspecialchars($matches[1], ENT_COMPAT, 'UTF-8');
-    }
-
-    /**
-     * Takes a piece of HTML and normalizes it by converting entities, fixing
-     * encoding, extracting bits, and other good stuff.
-     * @todo Consider making protected
-     */
-    public function normalize($html, $config, $context) {
-
-        // normalize newlines to \n
-        $html = str_replace("\r\n", "\n", $html);
-        $html = str_replace("\r", "\n", $html);
-
-        if ($config->get('HTML.Trusted')) {
-            // escape convoluted CDATA
-            $html = $this->escapeCommentedCDATA($html);
-        }
-
-        // escape CDATA
-        $html = $this->escapeCDATA($html);
-
-        // extract body from document if applicable
-        if ($config->get('Core.ConvertDocumentToFragment')) {
-            $e = false;
-            if ($config->get('Core.CollectErrors')) {
-                $e =& $context->get('ErrorCollector');
-            }
-            $new_html = $this->extractBody($html);
-            if ($e && $new_html != $html) {
-                $e->send(E_WARNING, 'Lexer: Extracted body');
-            }
-            $html = $new_html;
-        }
-
-        // expand entities that aren't the big five
-        $html = $this->_entity_parser->substituteNonSpecialEntities($html);
-
-        // clean into wellformed UTF-8 string for an SGML context: this has
-        // to be done after entity expansion because the entities sometimes
-        // represent non-SGML characters (horror, horror!)
-        $html = HTMLPurifier_Encoder::cleanUTF8($html);
-
-        return $html;
-    }
-
-    /**
-     * Takes a string of HTML (fragment or document) and returns the content
-     * @todo Consider making protected
-     */
-    public function extractBody($html) {
-        $matches = array();
-        $result = preg_match('!<body[^>]*>(.*)</body>!is', $html, $matches);
-        if ($result) {
-            return $matches[1];
-        } else {
-            return $html;
-        }
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Lexer/DOMLex.php b/library/HTMLPurifier/Lexer/DOMLex.php
deleted file mode 100644 (file)
index 20dc2ed..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-<?php
-
-/**
- * Parser that uses PHP 5's DOM extension (part of the core).
- *
- * In PHP 5, the DOM XML extension was revamped into DOM and added to the core.
- * It gives us a forgiving HTML parser, which we use to transform the HTML
- * into a DOM, and then into the tokens.  It is blazingly fast (for large
- * documents, it performs twenty times faster than
- * HTMLPurifier_Lexer_DirectLex,and is the default choice for PHP 5.
- *
- * @note Any empty elements will have empty tokens associated with them, even if
- * this is prohibited by the spec. This is cannot be fixed until the spec
- * comes into play.
- *
- * @note PHP's DOM extension does not actually parse any entities, we use
- *       our own function to do that.
- *
- * @warning DOM tends to drop whitespace, which may wreak havoc on indenting.
- *          If this is a huge problem, due to the fact that HTML is hand
- *          edited and you are unable to get a parser cache that caches the
- *          the output of HTML Purifier while keeping the original HTML lying
- *          around, you may want to run Tidy on the resulting output or use
- *          HTMLPurifier_DirectLex
- */
-
-class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
-{
-
-    private $factory;
-
-    public function __construct() {
-        // setup the factory
-        parent::__construct();
-        $this->factory = new HTMLPurifier_TokenFactory();
-    }
-
-    public function tokenizeHTML($html, $config, $context) {
-
-        $html = $this->normalize($html, $config, $context);
-
-        // attempt to armor stray angled brackets that cannot possibly
-        // form tags and thus are probably being used as emoticons
-        if ($config->get('Core.AggressivelyFixLt')) {
-            $char = '[^a-z!\/]';
-            $comment = "/<!--(.*?)(-->|\z)/is";
-            $html = preg_replace_callback($comment, array($this, 'callbackArmorCommentEntities'), $html);
-            do {
-                $old = $html;
-                $html = preg_replace("/<($char)/i", '&lt;\\1', $html);
-            } while ($html !== $old);
-            $html = preg_replace_callback($comment, array($this, 'callbackUndoCommentSubst'), $html); // fix comments
-        }
-
-        // preprocess html, essential for UTF-8
-        $html = $this->wrapHTML($html, $config, $context);
-
-        $doc = new DOMDocument();
-        $doc->encoding = 'UTF-8'; // theoretically, the above has this covered
-
-        set_error_handler(array($this, 'muteErrorHandler'));
-        $doc->loadHTML($html);
-        restore_error_handler();
-
-        $tokens = array();
-        $this->tokenizeDOM(
-            $doc->getElementsByTagName('html')->item(0)-> // <html>
-                  getElementsByTagName('body')->item(0)-> //   <body>
-                  getElementsByTagName('div')->item(0)    //     <div>
-            , $tokens);
-        return $tokens;
-    }
-
-    /**
-     * Recursive function that tokenizes a node, putting it into an accumulator.
-     *
-     * @param $node     DOMNode to be tokenized.
-     * @param $tokens   Array-list of already tokenized tokens.
-     * @param $collect  Says whether or start and close are collected, set to
-     *                  false at first recursion because it's the implicit DIV
-     *                  tag you're dealing with.
-     * @returns Tokens of node appended to previously passed tokens.
-     */
-    protected function tokenizeDOM($node, &$tokens, $collect = false) {
-
-        // intercept non element nodes. WE MUST catch all of them,
-        // but we're not getting the character reference nodes because
-        // those should have been preprocessed
-        if ($node->nodeType === XML_TEXT_NODE) {
-            $tokens[] = $this->factory->createText($node->data);
-            return;
-        } elseif ($node->nodeType === XML_CDATA_SECTION_NODE) {
-            // undo libxml's special treatment of <script> and <style> tags
-            $last = end($tokens);
-            $data = $node->data;
-            // (note $node->tagname is already normalized)
-            if ($last instanceof HTMLPurifier_Token_Start && ($last->name == 'script' || $last->name == 'style')) {
-                $new_data = trim($data);
-                if (substr($new_data, 0, 4) === '<!--') {
-                    $data = substr($new_data, 4);
-                    if (substr($data, -3) === '-->') {
-                        $data = substr($data, 0, -3);
-                    } else {
-                        // Highly suspicious! Not sure what to do...
-                    }
-                }
-            }
-            $tokens[] = $this->factory->createText($this->parseData($data));
-            return;
-        } elseif ($node->nodeType === XML_COMMENT_NODE) {
-            // this is code is only invoked for comments in script/style in versions
-            // of libxml pre-2.6.28 (regular comments, of course, are still
-            // handled regularly)
-            $tokens[] = $this->factory->createComment($node->data);
-            return;
-        } elseif (
-            // not-well tested: there may be other nodes we have to grab
-            $node->nodeType !== XML_ELEMENT_NODE
-        ) {
-            return;
-        }
-
-        $attr = $node->hasAttributes() ?
-            $this->transformAttrToAssoc($node->attributes) :
-            array();
-
-        // We still have to make sure that the element actually IS empty
-        if (!$node->childNodes->length) {
-            if ($collect) {
-                $tokens[] = $this->factory->createEmpty($node->tagName, $attr);
-            }
-        } else {
-            if ($collect) { // don't wrap on first iteration
-                $tokens[] = $this->factory->createStart(
-                    $tag_name = $node->tagName, // somehow, it get's dropped
-                    $attr
-                );
-            }
-            foreach ($node->childNodes as $node) {
-                // remember, it's an accumulator. Otherwise, we'd have
-                // to use array_merge
-                $this->tokenizeDOM($node, $tokens, true);
-            }
-            if ($collect) {
-                $tokens[] = $this->factory->createEnd($tag_name);
-            }
-        }
-
-    }
-
-    /**
-     * Converts a DOMNamedNodeMap of DOMAttr objects into an assoc array.
-     *
-     * @param $attribute_list DOMNamedNodeMap of DOMAttr objects.
-     * @returns Associative array of attributes.
-     */
-    protected function transformAttrToAssoc($node_map) {
-        // NamedNodeMap is documented very well, so we're using undocumented
-        // features, namely, the fact that it implements Iterator and
-        // has a ->length attribute
-        if ($node_map->length === 0) return array();
-        $array = array();
-        foreach ($node_map as $attr) {
-            $array[$attr->name] = $attr->value;
-        }
-        return $array;
-    }
-
-    /**
-     * An error handler that mutes all errors
-     */
-    public function muteErrorHandler($errno, $errstr) {}
-
-    /**
-     * Callback function for undoing escaping of stray angled brackets
-     * in comments
-     */
-    public function callbackUndoCommentSubst($matches) {
-        return '<!--' . strtr($matches[1], array('&amp;'=>'&','&lt;'=>'<')) . $matches[2];
-    }
-
-    /**
-     * Callback function that entity-izes ampersands in comments so that
-     * callbackUndoCommentSubst doesn't clobber them
-     */
-    public function callbackArmorCommentEntities($matches) {
-        return '<!--' . str_replace('&', '&amp;', $matches[1]) . $matches[2];
-    }
-
-    /**
-     * Wraps an HTML fragment in the necessary HTML
-     */
-    protected function wrapHTML($html, $config, $context) {
-        $def = $config->getDefinition('HTML');
-        $ret = '';
-
-        if (!empty($def->doctype->dtdPublic) || !empty($def->doctype->dtdSystem)) {
-            $ret .= '<!DOCTYPE html ';
-            if (!empty($def->doctype->dtdPublic)) $ret .= 'PUBLIC "' . $def->doctype->dtdPublic . '" ';
-            if (!empty($def->doctype->dtdSystem)) $ret .= '"' . $def->doctype->dtdSystem . '" ';
-            $ret .= '>';
-        }
-
-        $ret .= '<html><head>';
-        $ret .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
-        // No protection if $html contains a stray </div>!
-        $ret .= '</head><body><div>'.$html.'</div></body></html>';
-        return $ret;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Lexer/DirectLex.php b/library/HTMLPurifier/Lexer/DirectLex.php
deleted file mode 100644 (file)
index 456e6e1..0000000
+++ /dev/null
@@ -1,490 +0,0 @@
-<?php
-
-/**
- * Our in-house implementation of a parser.
- *
- * A pure PHP parser, DirectLex has absolutely no dependencies, making
- * it a reasonably good default for PHP4.  Written with efficiency in mind,
- * it can be four times faster than HTMLPurifier_Lexer_PEARSax3, although it
- * pales in comparison to HTMLPurifier_Lexer_DOMLex.
- *
- * @todo Reread XML spec and document differences.
- */
-class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
-{
-
-    public $tracksLineNumbers = true;
-
-    /**
-     * Whitespace characters for str(c)spn.
-     */
-    protected $_whitespace = "\x20\x09\x0D\x0A";
-
-    /**
-     * Callback function for script CDATA fudge
-     * @param $matches, in form of array(opening tag, contents, closing tag)
-     */
-    protected function scriptCallback($matches) {
-        return $matches[1] . htmlspecialchars($matches[2], ENT_COMPAT, 'UTF-8') . $matches[3];
-    }
-
-    public function tokenizeHTML($html, $config, $context) {
-
-        // special normalization for script tags without any armor
-        // our "armor" heurstic is a < sign any number of whitespaces after
-        // the first script tag
-        if ($config->get('HTML.Trusted')) {
-            $html = preg_replace_callback('#(<script[^>]*>)(\s*[^<].+?)(</script>)#si',
-                array($this, 'scriptCallback'), $html);
-        }
-
-        $html = $this->normalize($html, $config, $context);
-
-        $cursor = 0; // our location in the text
-        $inside_tag = false; // whether or not we're parsing the inside of a tag
-        $array = array(); // result array
-
-        // This is also treated to mean maintain *column* numbers too
-        $maintain_line_numbers = $config->get('Core.MaintainLineNumbers');
-
-        if ($maintain_line_numbers === null) {
-            // automatically determine line numbering by checking
-            // if error collection is on
-            $maintain_line_numbers = $config->get('Core.CollectErrors');
-        }
-
-        if ($maintain_line_numbers) {
-            $current_line = 1;
-            $current_col  = 0;
-            $length = strlen($html);
-        } else {
-            $current_line = false;
-            $current_col  = false;
-            $length = false;
-        }
-        $context->register('CurrentLine', $current_line);
-        $context->register('CurrentCol',  $current_col);
-        $nl = "\n";
-        // how often to manually recalculate. This will ALWAYS be right,
-        // but it's pretty wasteful. Set to 0 to turn off
-        $synchronize_interval = $config->get('Core.DirectLexLineNumberSyncInterval');
-
-        $e = false;
-        if ($config->get('Core.CollectErrors')) {
-            $e =& $context->get('ErrorCollector');
-        }
-
-        // for testing synchronization
-        $loops = 0;
-
-        while(++$loops) {
-
-            // $cursor is either at the start of a token, or inside of
-            // a tag (i.e. there was a < immediately before it), as indicated
-            // by $inside_tag
-
-            if ($maintain_line_numbers) {
-
-                // $rcursor, however, is always at the start of a token.
-                $rcursor = $cursor - (int) $inside_tag;
-
-                // Column number is cheap, so we calculate it every round.
-                // We're interested at the *end* of the newline string, so
-                // we need to add strlen($nl) == 1 to $nl_pos before subtracting it
-                // from our "rcursor" position.
-                $nl_pos = strrpos($html, $nl, $rcursor - $length);
-                $current_col = $rcursor - (is_bool($nl_pos) ? 0 : $nl_pos + 1);
-
-                // recalculate lines
-                if (
-                    $synchronize_interval &&  // synchronization is on
-                    $cursor > 0 &&            // cursor is further than zero
-                    $loops % $synchronize_interval === 0 // time to synchronize!
-                ) {
-                    $current_line = 1 + $this->substrCount($html, $nl, 0, $cursor);
-                }
-
-            }
-
-            $position_next_lt = strpos($html, '<', $cursor);
-            $position_next_gt = strpos($html, '>', $cursor);
-
-            // triggers on "<b>asdf</b>" but not "asdf <b></b>"
-            // special case to set up context
-            if ($position_next_lt === $cursor) {
-                $inside_tag = true;
-                $cursor++;
-            }
-
-            if (!$inside_tag && $position_next_lt !== false) {
-                // We are not inside tag and there still is another tag to parse
-                $token = new
-                    HTMLPurifier_Token_Text(
-                        $this->parseData(
-                            substr(
-                                $html, $cursor, $position_next_lt - $cursor
-                            )
-                        )
-                    );
-                if ($maintain_line_numbers) {
-                    $token->rawPosition($current_line, $current_col);
-                    $current_line += $this->substrCount($html, $nl, $cursor, $position_next_lt - $cursor);
-                }
-                $array[] = $token;
-                $cursor  = $position_next_lt + 1;
-                $inside_tag = true;
-                continue;
-            } elseif (!$inside_tag) {
-                // We are not inside tag but there are no more tags
-                // If we're already at the end, break
-                if ($cursor === strlen($html)) break;
-                // Create Text of rest of string
-                $token = new
-                    HTMLPurifier_Token_Text(
-                        $this->parseData(
-                            substr(
-                                $html, $cursor
-                            )
-                        )
-                    );
-                if ($maintain_line_numbers) $token->rawPosition($current_line, $current_col);
-                $array[] = $token;
-                break;
-            } elseif ($inside_tag && $position_next_gt !== false) {
-                // We are in tag and it is well formed
-                // Grab the internals of the tag
-                $strlen_segment = $position_next_gt - $cursor;
-
-                if ($strlen_segment < 1) {
-                    // there's nothing to process!
-                    $token = new HTMLPurifier_Token_Text('<');
-                    $cursor++;
-                    continue;
-                }
-
-                $segment = substr($html, $cursor, $strlen_segment);
-
-                if ($segment === false) {
-                    // somehow, we attempted to access beyond the end of
-                    // the string, defense-in-depth, reported by Nate Abele
-                    break;
-                }
-
-                // Check if it's a comment
-                if (
-                    substr($segment, 0, 3) === '!--'
-                ) {
-                    // re-determine segment length, looking for -->
-                    $position_comment_end = strpos($html, '-->', $cursor);
-                    if ($position_comment_end === false) {
-                        // uh oh, we have a comment that extends to
-                        // infinity. Can't be helped: set comment
-                        // end position to end of string
-                        if ($e) $e->send(E_WARNING, 'Lexer: Unclosed comment');
-                        $position_comment_end = strlen($html);
-                        $end = true;
-                    } else {
-                        $end = false;
-                    }
-                    $strlen_segment = $position_comment_end - $cursor;
-                    $segment = substr($html, $cursor, $strlen_segment);
-                    $token = new
-                        HTMLPurifier_Token_Comment(
-                            substr(
-                                $segment, 3, $strlen_segment - 3
-                            )
-                        );
-                    if ($maintain_line_numbers) {
-                        $token->rawPosition($current_line, $current_col);
-                        $current_line += $this->substrCount($html, $nl, $cursor, $strlen_segment);
-                    }
-                    $array[] = $token;
-                    $cursor = $end ? $position_comment_end : $position_comment_end + 3;
-                    $inside_tag = false;
-                    continue;
-                }
-
-                // Check if it's an end tag
-                $is_end_tag = (strpos($segment,'/') === 0);
-                if ($is_end_tag) {
-                    $type = substr($segment, 1);
-                    $token = new HTMLPurifier_Token_End($type);
-                    if ($maintain_line_numbers) {
-                        $token->rawPosition($current_line, $current_col);
-                        $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
-                    }
-                    $array[] = $token;
-                    $inside_tag = false;
-                    $cursor = $position_next_gt + 1;
-                    continue;
-                }
-
-                // Check leading character is alnum, if not, we may
-                // have accidently grabbed an emoticon. Translate into
-                // text and go our merry way
-                if (!ctype_alpha($segment[0])) {
-                    // XML:  $segment[0] !== '_' && $segment[0] !== ':'
-                    if ($e) $e->send(E_NOTICE, 'Lexer: Unescaped lt');
-                    $token = new HTMLPurifier_Token_Text('<');
-                    if ($maintain_line_numbers) {
-                        $token->rawPosition($current_line, $current_col);
-                        $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
-                    }
-                    $array[] = $token;
-                    $inside_tag = false;
-                    continue;
-                }
-
-                // Check if it is explicitly self closing, if so, remove
-                // trailing slash. Remember, we could have a tag like <br>, so
-                // any later token processing scripts must convert improperly
-                // classified EmptyTags from StartTags.
-                $is_self_closing = (strrpos($segment,'/') === $strlen_segment-1);
-                if ($is_self_closing) {
-                    $strlen_segment--;
-                    $segment = substr($segment, 0, $strlen_segment);
-                }
-
-                // Check if there are any attributes
-                $position_first_space = strcspn($segment, $this->_whitespace);
-
-                if ($position_first_space >= $strlen_segment) {
-                    if ($is_self_closing) {
-                        $token = new HTMLPurifier_Token_Empty($segment);
-                    } else {
-                        $token = new HTMLPurifier_Token_Start($segment);
-                    }
-                    if ($maintain_line_numbers) {
-                        $token->rawPosition($current_line, $current_col);
-                        $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
-                    }
-                    $array[] = $token;
-                    $inside_tag = false;
-                    $cursor = $position_next_gt + 1;
-                    continue;
-                }
-
-                // Grab out all the data
-                $type = substr($segment, 0, $position_first_space);
-                $attribute_string =
-                    trim(
-                        substr(
-                            $segment, $position_first_space
-                        )
-                    );
-                if ($attribute_string) {
-                    $attr = $this->parseAttributeString(
-                                    $attribute_string
-                                  , $config, $context
-                              );
-                } else {
-                    $attr = array();
-                }
-
-                if ($is_self_closing) {
-                    $token = new HTMLPurifier_Token_Empty($type, $attr);
-                } else {
-                    $token = new HTMLPurifier_Token_Start($type, $attr);
-                }
-                if ($maintain_line_numbers) {
-                    $token->rawPosition($current_line, $current_col);
-                    $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
-                }
-                $array[] = $token;
-                $cursor = $position_next_gt + 1;
-                $inside_tag = false;
-                continue;
-            } else {
-                // inside tag, but there's no ending > sign
-                if ($e) $e->send(E_WARNING, 'Lexer: Missing gt');
-                $token = new
-                    HTMLPurifier_Token_Text(
-                        '<' .
-                        $this->parseData(
-                            substr($html, $cursor)
-                        )
-                    );
-                if ($maintain_line_numbers) $token->rawPosition($current_line, $current_col);
-                // no cursor scroll? Hmm...
-                $array[] = $token;
-                break;
-            }
-            break;
-        }
-
-        $context->destroy('CurrentLine');
-        $context->destroy('CurrentCol');
-        return $array;
-    }
-
-    /**
-     * PHP 5.0.x compatible substr_count that implements offset and length
-     */
-    protected function substrCount($haystack, $needle, $offset, $length) {
-        static $oldVersion;
-        if ($oldVersion === null) {
-            $oldVersion = version_compare(PHP_VERSION, '5.1', '<');
-        }
-        if ($oldVersion) {
-            $haystack = substr($haystack, $offset, $length);
-            return substr_count($haystack, $needle);
-        } else {
-            return substr_count($haystack, $needle, $offset, $length);
-        }
-    }
-
-    /**
-     * Takes the inside of an HTML tag and makes an assoc array of attributes.
-     *
-     * @param $string Inside of tag excluding name.
-     * @returns Assoc array of attributes.
-     */
-    public function parseAttributeString($string, $config, $context) {
-        $string = (string) $string; // quick typecast
-
-        if ($string == '') return array(); // no attributes
-
-        $e = false;
-        if ($config->get('Core.CollectErrors')) {
-            $e =& $context->get('ErrorCollector');
-        }
-
-        // let's see if we can abort as quickly as possible
-        // one equal sign, no spaces => one attribute
-        $num_equal = substr_count($string, '=');
-        $has_space = strpos($string, ' ');
-        if ($num_equal === 0 && !$has_space) {
-            // bool attribute
-            return array($string => $string);
-        } elseif ($num_equal === 1 && !$has_space) {
-            // only one attribute
-            list($key, $quoted_value) = explode('=', $string);
-            $quoted_value = trim($quoted_value);
-            if (!$key) {
-                if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key');
-                return array();
-            }
-            if (!$quoted_value) return array($key => '');
-            $first_char = @$quoted_value[0];
-            $last_char  = @$quoted_value[strlen($quoted_value)-1];
-
-            $same_quote = ($first_char == $last_char);
-            $open_quote = ($first_char == '"' || $first_char == "'");
-
-            if ( $same_quote && $open_quote) {
-                // well behaved
-                $value = substr($quoted_value, 1, strlen($quoted_value) - 2);
-            } else {
-                // not well behaved
-                if ($open_quote) {
-                    if ($e) $e->send(E_ERROR, 'Lexer: Missing end quote');
-                    $value = substr($quoted_value, 1);
-                } else {
-                    $value = $quoted_value;
-                }
-            }
-            if ($value === false) $value = '';
-            return array($key => $this->parseData($value));
-        }
-
-        // setup loop environment
-        $array  = array(); // return assoc array of attributes
-        $cursor = 0; // current position in string (moves forward)
-        $size   = strlen($string); // size of the string (stays the same)
-
-        // if we have unquoted attributes, the parser expects a terminating
-        // space, so let's guarantee that there's always a terminating space.
-        $string .= ' ';
-
-        while(true) {
-
-            if ($cursor >= $size) {
-                break;
-            }
-
-            $cursor += ($value = strspn($string, $this->_whitespace, $cursor));
-            // grab the key
-
-            $key_begin = $cursor; //we're currently at the start of the key
-
-            // scroll past all characters that are the key (not whitespace or =)
-            $cursor += strcspn($string, $this->_whitespace . '=', $cursor);
-
-            $key_end = $cursor; // now at the end of the key
-
-            $key = substr($string, $key_begin, $key_end - $key_begin);
-
-            if (!$key) {
-                if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key');
-                $cursor += strcspn($string, $this->_whitespace, $cursor + 1); // prevent infinite loop
-                continue; // empty key
-            }
-
-            // scroll past all whitespace
-            $cursor += strspn($string, $this->_whitespace, $cursor);
-
-            if ($cursor >= $size) {
-                $array[$key] = $key;
-                break;
-            }
-
-            // if the next character is an equal sign, we've got a regular
-            // pair, otherwise, it's a bool attribute
-            $first_char = @$string[$cursor];
-
-            if ($first_char == '=') {
-                // key="value"
-
-                $cursor++;
-                $cursor += strspn($string, $this->_whitespace, $cursor);
-
-                if ($cursor === false) {
-                    $array[$key] = '';
-                    break;
-                }
-
-                // we might be in front of a quote right now
-
-                $char = @$string[$cursor];
-
-                if ($char == '"' || $char == "'") {
-                    // it's quoted, end bound is $char
-                    $cursor++;
-                    $value_begin = $cursor;
-                    $cursor = strpos($string, $char, $cursor);
-                    $value_end = $cursor;
-                } else {
-                    // it's not quoted, end bound is whitespace
-                    $value_begin = $cursor;
-                    $cursor += strcspn($string, $this->_whitespace, $cursor);
-                    $value_end = $cursor;
-                }
-
-                // we reached a premature end
-                if ($cursor === false) {
-                    $cursor = $size;
-                    $value_end = $cursor;
-                }
-
-                $value = substr($string, $value_begin, $value_end - $value_begin);
-                if ($value === false) $value = '';
-                $array[$key] = $this->parseData($value);
-                $cursor++;
-
-            } else {
-                // boolattr
-                if ($key !== '') {
-                    $array[$key] = $key;
-                } else {
-                    // purely theoretical
-                    if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key');
-                }
-
-            }
-        }
-        return $array;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Lexer/PEARSax3.php b/library/HTMLPurifier/Lexer/PEARSax3.php
deleted file mode 100644 (file)
index 1d358c7..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-<?php
-
-/**
- * Proof-of-concept lexer that uses the PEAR package XML_HTMLSax3 to parse HTML.
- *
- * PEAR, not suprisingly, also has a SAX parser for HTML.  I don't know
- * very much about implementation, but it's fairly well written.  However, that
- * abstraction comes at a price: performance. You need to have it installed,
- * and if the API changes, it might break our adapter. Not sure whether or not
- * it's UTF-8 aware, but it has some entity parsing trouble (in all areas,
- * text and attributes).
- *
- * Quite personally, I don't recommend using the PEAR class, and the defaults
- * don't use it. The unit tests do perform the tests on the SAX parser too, but
- * whatever it does for poorly formed HTML is up to it.
- *
- * @todo Generalize so that XML_HTMLSax is also supported.
- *
- * @warning Entity-resolution inside attributes is broken.
- */
-
-class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer
-{
-
-    /**
-     * Internal accumulator array for SAX parsers.
-     */
-    protected $tokens = array();
-    protected $last_token_was_empty;
-
-    private $parent_handler;
-    private $stack = array();
-
-    public function tokenizeHTML($string, $config, $context) {
-
-        $this->tokens = array();
-        $this->last_token_was_empty = false;
-
-        $string = $this->normalize($string, $config, $context);
-
-        $this->parent_handler = set_error_handler(array($this, 'muteStrictErrorHandler'));
-
-        $parser = new XML_HTMLSax3();
-        $parser->set_object($this);
-        $parser->set_element_handler('openHandler','closeHandler');
-        $parser->set_data_handler('dataHandler');
-        $parser->set_escape_handler('escapeHandler');
-
-        // doesn't seem to work correctly for attributes
-        $parser->set_option('XML_OPTION_ENTITIES_PARSED', 1);
-
-        $parser->parse($string);
-
-        restore_error_handler();
-
-        return $this->tokens;
-
-    }
-
-    /**
-     * Open tag event handler, interface is defined by PEAR package.
-     */
-    public function openHandler(&$parser, $name, $attrs, $closed) {
-        // entities are not resolved in attrs
-        foreach ($attrs as $key => $attr) {
-            $attrs[$key] = $this->parseData($attr);
-        }
-        if ($closed) {
-            $this->tokens[] = new HTMLPurifier_Token_Empty($name, $attrs);
-            $this->last_token_was_empty = true;
-        } else {
-            $this->tokens[] = new HTMLPurifier_Token_Start($name, $attrs);
-        }
-        $this->stack[] = $name;
-        return true;
-    }
-
-    /**
-     * Close tag event handler, interface is defined by PEAR package.
-     */
-    public function closeHandler(&$parser, $name) {
-        // HTMLSax3 seems to always send empty tags an extra close tag
-        // check and ignore if you see it:
-        // [TESTME] to make sure it doesn't overreach
-        if ($this->last_token_was_empty) {
-            $this->last_token_was_empty = false;
-            return true;
-        }
-        $this->tokens[] = new HTMLPurifier_Token_End($name);
-        if (!empty($this->stack)) array_pop($this->stack);
-        return true;
-    }
-
-    /**
-     * Data event handler, interface is defined by PEAR package.
-     */
-    public function dataHandler(&$parser, $data) {
-        $this->last_token_was_empty = false;
-        $this->tokens[] = new HTMLPurifier_Token_Text($data);
-        return true;
-    }
-
-    /**
-     * Escaped text handler, interface is defined by PEAR package.
-     */
-    public function escapeHandler(&$parser, $data) {
-        if (strpos($data, '--') === 0) {
-            // remove trailing and leading double-dashes
-            $data = substr($data, 2);
-            if (strlen($data) >= 2 && substr($data, -2) == "--") {
-                $data = substr($data, 0, -2);
-            }
-            if (isset($this->stack[sizeof($this->stack) - 1]) &&
-                $this->stack[sizeof($this->stack) - 1] == "style") {
-                $this->tokens[] = new HTMLPurifier_Token_Text($data);
-            } else {
-                $this->tokens[] = new HTMLPurifier_Token_Comment($data);
-            }
-            $this->last_token_was_empty = false;
-        }
-        // CDATA is handled elsewhere, but if it was handled here:
-        //if (strpos($data, '[CDATA[') === 0) {
-        //    $this->tokens[] = new HTMLPurifier_Token_Text(
-        //        substr($data, 7, strlen($data) - 9) );
-        //}
-        return true;
-    }
-
-    /**
-     * An error handler that mutes strict errors
-     */
-    public function muteStrictErrorHandler($errno, $errstr, $errfile=null, $errline=null, $errcontext=null) {
-        if ($errno == E_STRICT) return;
-        return call_user_func($this->parent_handler, $errno, $errstr, $errfile, $errline, $errcontext);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Lexer/PH5P.php b/library/HTMLPurifier/Lexer/PH5P.php
deleted file mode 100644 (file)
index fa1bf97..0000000
+++ /dev/null
@@ -1,3906 +0,0 @@
-<?php
-
-/**
- * Experimental HTML5-based parser using Jeroen van der Meer's PH5P library.
- * Occupies space in the HTML5 pseudo-namespace, which may cause conflicts.
- * 
- * @note
- *    Recent changes to PHP's DOM extension have resulted in some fatal
- *    error conditions with the original version of PH5P. Pending changes,
- *    this lexer will punt to DirectLex if DOM throughs an exception.
- */
-
-class HTMLPurifier_Lexer_PH5P extends HTMLPurifier_Lexer_DOMLex {
-    
-    public function tokenizeHTML($html, $config, $context) {
-        $new_html = $this->normalize($html, $config, $context);
-        $new_html = $this->wrapHTML($new_html, $config, $context);
-        try {
-            $parser = new HTML5($new_html);
-            $doc = $parser->save();
-        } catch (DOMException $e) {
-            // Uh oh, it failed. Punt to DirectLex.
-            $lexer = new HTMLPurifier_Lexer_DirectLex();
-            $context->register('PH5PError', $e); // save the error, so we can detect it
-            return $lexer->tokenizeHTML($html, $config, $context); // use original HTML
-        }
-        $tokens = array();
-        $this->tokenizeDOM(
-            $doc->getElementsByTagName('html')->item(0)-> // <html>
-                  getElementsByTagName('body')->item(0)-> //   <body>
-                  getElementsByTagName('div')->item(0)    //     <div>
-            , $tokens);
-        return $tokens;
-    }
-    
-}
-
-/*
-
-Copyright 2007 Jeroen van der Meer <http://jero.net/> 
-
-Permission is hereby granted, free of charge, to any person obtaining a 
-copy of this software and associated documentation files (the 
-"Software"), to deal in the Software without restriction, including 
-without limitation the rights to use, copy, modify, merge, publish, 
-distribute, sublicense, and/or sell copies of the Software, and to 
-permit persons to whom the Software is furnished to do so, subject to 
-the following conditions: 
-
-The above copyright notice and this permission notice shall be included 
-in all copies or substantial portions of the Software. 
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
-
-*/
-
-class HTML5 {
-    private $data;
-    private $char;
-    private $EOF;
-    private $state;
-    private $tree;
-    private $token;
-    private $content_model;
-    private $escape = false;
-    private $entities = array('AElig;','AElig','AMP;','AMP','Aacute;','Aacute',
-    'Acirc;','Acirc','Agrave;','Agrave','Alpha;','Aring;','Aring','Atilde;',
-    'Atilde','Auml;','Auml','Beta;','COPY;','COPY','Ccedil;','Ccedil','Chi;',
-    'Dagger;','Delta;','ETH;','ETH','Eacute;','Eacute','Ecirc;','Ecirc','Egrave;',
-    'Egrave','Epsilon;','Eta;','Euml;','Euml','GT;','GT','Gamma;','Iacute;',
-    'Iacute','Icirc;','Icirc','Igrave;','Igrave','Iota;','Iuml;','Iuml','Kappa;',
-    'LT;','LT','Lambda;','Mu;','Ntilde;','Ntilde','Nu;','OElig;','Oacute;',
-    'Oacute','Ocirc;','Ocirc','Ograve;','Ograve','Omega;','Omicron;','Oslash;',
-    'Oslash','Otilde;','Otilde','Ouml;','Ouml','Phi;','Pi;','Prime;','Psi;',
-    'QUOT;','QUOT','REG;','REG','Rho;','Scaron;','Sigma;','THORN;','THORN',
-    'TRADE;','Tau;','Theta;','Uacute;','Uacute','Ucirc;','Ucirc','Ugrave;',
-    'Ugrave','Upsilon;','Uuml;','Uuml','Xi;','Yacute;','Yacute','Yuml;','Zeta;',
-    'aacute;','aacute','acirc;','acirc','acute;','acute','aelig;','aelig',
-    'agrave;','agrave','alefsym;','alpha;','amp;','amp','and;','ang;','apos;',
-    'aring;','aring','asymp;','atilde;','atilde','auml;','auml','bdquo;','beta;',
-    'brvbar;','brvbar','bull;','cap;','ccedil;','ccedil','cedil;','cedil',
-    'cent;','cent','chi;','circ;','clubs;','cong;','copy;','copy','crarr;',
-    'cup;','curren;','curren','dArr;','dagger;','darr;','deg;','deg','delta;',
-    'diams;','divide;','divide','eacute;','eacute','ecirc;','ecirc','egrave;',
-    'egrave','empty;','emsp;','ensp;','epsilon;','equiv;','eta;','eth;','eth',
-    'euml;','euml','euro;','exist;','fnof;','forall;','frac12;','frac12',
-    'frac14;','frac14','frac34;','frac34','frasl;','gamma;','ge;','gt;','gt',
-    'hArr;','harr;','hearts;','hellip;','iacute;','iacute','icirc;','icirc',
-    'iexcl;','iexcl','igrave;','igrave','image;','infin;','int;','iota;',
-    'iquest;','iquest','isin;','iuml;','iuml','kappa;','lArr;','lambda;','lang;',
-    'laquo;','laquo','larr;','lceil;','ldquo;','le;','lfloor;','lowast;','loz;',
-    'lrm;','lsaquo;','lsquo;','lt;','lt','macr;','macr','mdash;','micro;','micro',
-    'middot;','middot','minus;','mu;','nabla;','nbsp;','nbsp','ndash;','ne;',
-    'ni;','not;','not','notin;','nsub;','ntilde;','ntilde','nu;','oacute;',
-    'oacute','ocirc;','ocirc','oelig;','ograve;','ograve','oline;','omega;',
-    'omicron;','oplus;','or;','ordf;','ordf','ordm;','ordm','oslash;','oslash',
-    'otilde;','otilde','otimes;','ouml;','ouml','para;','para','part;','permil;',
-    'perp;','phi;','pi;','piv;','plusmn;','plusmn','pound;','pound','prime;',
-    'prod;','prop;','psi;','quot;','quot','rArr;','radic;','rang;','raquo;',
-    'raquo','rarr;','rceil;','rdquo;','real;','reg;','reg','rfloor;','rho;',
-    'rlm;','rsaquo;','rsquo;','sbquo;','scaron;','sdot;','sect;','sect','shy;',
-    'shy','sigma;','sigmaf;','sim;','spades;','sub;','sube;','sum;','sup1;',
-    'sup1','sup2;','sup2','sup3;','sup3','sup;','supe;','szlig;','szlig','tau;',
-    'there4;','theta;','thetasym;','thinsp;','thorn;','thorn','tilde;','times;',
-    'times','trade;','uArr;','uacute;','uacute','uarr;','ucirc;','ucirc',
-    'ugrave;','ugrave','uml;','uml','upsih;','upsilon;','uuml;','uuml','weierp;',
-    'xi;','yacute;','yacute','yen;','yen','yuml;','yuml','zeta;','zwj;','zwnj;');
-
-    const PCDATA    = 0;
-    const RCDATA    = 1;
-    const CDATA     = 2;
-    const PLAINTEXT = 3;
-
-    const DOCTYPE  = 0;
-    const STARTTAG = 1;
-    const ENDTAG   = 2;
-    const COMMENT  = 3;
-    const CHARACTR = 4;
-    const EOF      = 5;
-
-    public function __construct($data) {
-        $data = str_replace("\r\n", "\n", $data);
-        $data = str_replace("\r", null, $data);
-
-        $this->data = $data;
-        $this->char = -1;
-        $this->EOF  = strlen($data);
-        $this->tree = new HTML5TreeConstructer;
-        $this->content_model = self::PCDATA;
-
-        $this->state = 'data';
-
-        while($this->state !== null) {
-            $this->{$this->state.'State'}();
-        }
-    }
-
-    public function save() {
-        return $this->tree->save();
-    }
-
-    private function char() {
-        return ($this->char < $this->EOF)
-            ? $this->data[$this->char]
-            : false;
-    }
-
-    private function character($s, $l = 0) {
-        if($s + $l < $this->EOF) {
-            if($l === 0) {
-                return $this->data[$s];
-            } else {
-                return substr($this->data, $s, $l);
-            }
-        }
-    }
-
-    private function characters($char_class, $start) {
-        return preg_replace('#^(['.$char_class.']+).*#s', '\\1', substr($this->data, $start));
-    }
-
-    private function dataState() {
-        // Consume the next input character
-        $this->char++;
-        $char = $this->char();
-
-        if($char === '&' && ($this->content_model === self::PCDATA || $this->content_model === self::RCDATA)) {
-            /* U+0026 AMPERSAND (&)
-            When the content model flag is set to one of the PCDATA or RCDATA
-            states: switch to the entity data state. Otherwise: treat it as per
-            the "anything else"    entry below. */
-            $this->state = 'entityData';
-
-        } elseif($char === '-') {
-            /* If the content model flag is set to either the RCDATA state or
-            the CDATA state, and the escape flag is false, and there are at
-            least three characters before this one in the input stream, and the
-            last four characters in the input stream, including this one, are
-            U+003C LESS-THAN SIGN, U+0021 EXCLAMATION MARK, U+002D HYPHEN-MINUS,
-            and U+002D HYPHEN-MINUS ("<!--"), then set the escape flag to true. */
-            if(($this->content_model === self::RCDATA || $this->content_model ===
-            self::CDATA) && $this->escape === false &&
-            $this->char >= 3 && $this->character($this->char - 4, 4) === '<!--') {
-                $this->escape = true;
-            }
-
-            /* In any case, emit the input character as a character token. Stay
-            in the data state. */
-            $this->emitToken(array(
-                'type' => self::CHARACTR,
-                'data' => $char
-            ));
-
-        /* U+003C LESS-THAN SIGN (<) */
-        } elseif($char === '<' && ($this->content_model === self::PCDATA ||
-        (($this->content_model === self::RCDATA ||
-        $this->content_model === self::CDATA) && $this->escape === false))) {
-            /* When the content model flag is set to the PCDATA state: switch
-            to the tag open state.
-
-            When the content model flag is set to either the RCDATA state or
-            the CDATA state and the escape flag is false: switch to the tag
-            open state.
-
-            Otherwise: treat it as per the "anything else" entry below. */
-            $this->state = 'tagOpen';
-
-        /* U+003E GREATER-THAN SIGN (>) */
-        } elseif($char === '>') {
-            /* If the content model flag is set to either the RCDATA state or
-            the CDATA state, and the escape flag is true, and the last three
-            characters in the input stream including this one are U+002D
-            HYPHEN-MINUS, U+002D HYPHEN-MINUS, U+003E GREATER-THAN SIGN ("-->"),
-            set the escape flag to false. */
-            if(($this->content_model === self::RCDATA ||
-            $this->content_model === self::CDATA) && $this->escape === true &&
-            $this->character($this->char, 3) === '-->') {
-                $this->escape = false;
-            }
-
-            /* In any case, emit the input character as a character token.
-            Stay in the data state. */
-            $this->emitToken(array(
-                'type' => self::CHARACTR,
-                'data' => $char
-            ));
-
-        } elseif($this->char === $this->EOF) {
-            /* EOF
-            Emit an end-of-file token. */
-            $this->EOF();
-
-        } elseif($this->content_model === self::PLAINTEXT) {
-            /* When the content model flag is set to the PLAINTEXT state
-            THIS DIFFERS GREATLY FROM THE SPEC: Get the remaining characters of
-            the text and emit it as a character token. */
-            $this->emitToken(array(
-                'type' => self::CHARACTR,
-                'data' => substr($this->data, $this->char)
-            ));
-
-            $this->EOF();
-
-        } else {
-            /* Anything else
-            THIS DIFFERS GREATLY FROM THE SPEC: Get as many character that
-            otherwise would also be treated as a character token and emit it
-            as a single character token. Stay in the data state. */
-            $len  = strcspn($this->data, '<&', $this->char);
-            $char = substr($this->data, $this->char, $len);
-            $this->char += $len - 1;
-
-            $this->emitToken(array(
-                'type' => self::CHARACTR,
-                'data' => $char
-            ));
-
-            $this->state = 'data';
-        }
-    }
-
-    private function entityDataState() {
-        // Attempt to consume an entity.
-        $entity = $this->entity();
-
-        // If nothing is returned, emit a U+0026 AMPERSAND character token.
-        // Otherwise, emit the character token that was returned.
-        $char = (!$entity) ? '&' : $entity;
-        $this->emitToken(array(
-            'type' => self::CHARACTR,
-            'data' => $char
-        ));
-
-        // Finally, switch to the data state.
-        $this->state = 'data';
-    }
-
-    private function tagOpenState() {
-        switch($this->content_model) {
-            case self::RCDATA:
-            case self::CDATA:
-                /* If the next input character is a U+002F SOLIDUS (/) character,
-                consume it and switch to the close tag open state. If the next
-                input character is not a U+002F SOLIDUS (/) character, emit a
-                U+003C LESS-THAN SIGN character token and switch to the data
-                state to process the next input character. */
-                if($this->character($this->char + 1) === '/') {
-                    $this->char++;
-                    $this->state = 'closeTagOpen';
-
-                } else {
-                    $this->emitToken(array(
-                        'type' => self::CHARACTR,
-                        'data' => '<'
-                    ));
-
-                    $this->state = 'data';
-                }
-            break;
-
-            case self::PCDATA:
-                // If the content model flag is set to the PCDATA state
-                // Consume the next input character:
-                $this->char++;
-                $char = $this->char();
-
-                if($char === '!') {
-                    /* U+0021 EXCLAMATION MARK (!)
-                    Switch to the markup declaration open state. */
-                    $this->state = 'markupDeclarationOpen';
-
-                } elseif($char === '/') {
-                    /* U+002F SOLIDUS (/)
-                    Switch to the close tag open state. */
-                    $this->state = 'closeTagOpen';
-
-                } elseif(preg_match('/^[A-Za-z]$/', $char)) {
-                    /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z
-                    Create a new start tag token, set its tag name to the lowercase
-                    version of the input character (add 0x0020 to the character's code
-                    point), then switch to the tag name state. (Don't emit the token
-                    yet; further details will be filled in before it is emitted.) */
-                    $this->token = array(
-                        'name'  => strtolower($char),
-                        'type'  => self::STARTTAG,
-                        'attr'  => array()
-                    );
-
-                    $this->state = 'tagName';
-
-                } elseif($char === '>') {
-                    /* U+003E GREATER-THAN SIGN (>)
-                    Parse error. Emit a U+003C LESS-THAN SIGN character token and a
-                    U+003E GREATER-THAN SIGN character token. Switch to the data state. */
-                    $this->emitToken(array(
-                        'type' => self::CHARACTR,
-                        'data' => '<>'
-                    ));
-
-                    $this->state = 'data';
-
-                } elseif($char === '?') {
-                    /* U+003F QUESTION MARK (?)
-                    Parse error. Switch to the bogus comment state. */
-                    $this->state = 'bogusComment';
-
-                } else {
-                    /* Anything else
-                    Parse error. Emit a U+003C LESS-THAN SIGN character token and
-                    reconsume the current input character in the data state. */
-                    $this->emitToken(array(
-                        'type' => self::CHARACTR,
-                        'data' => '<'
-                    ));
-
-                    $this->char--;
-                    $this->state = 'data';
-                }
-            break;
-        }
-    }
-
-    private function closeTagOpenState() {
-        $next_node = strtolower($this->characters('A-Za-z', $this->char + 1));
-        $the_same = count($this->tree->stack) > 0 && $next_node === end($this->tree->stack)->nodeName;
-
-        if(($this->content_model === self::RCDATA || $this->content_model === self::CDATA) &&
-        (!$the_same || ($the_same && (!preg_match('/[\t\n\x0b\x0c >\/]/',
-        $this->character($this->char + 1 + strlen($next_node))) || $this->EOF === $this->char)))) {
-            /* If the content model flag is set to the RCDATA or CDATA states then
-            examine the next few characters. If they do not match the tag name of
-            the last start tag token emitted (case insensitively), or if they do but
-            they are not immediately followed by one of the following characters:
-                * U+0009 CHARACTER TABULATION
-                * U+000A LINE FEED (LF)
-                * U+000B LINE TABULATION
-                * U+000C FORM FEED (FF)
-                * U+0020 SPACE
-                * U+003E GREATER-THAN SIGN (>)
-                * U+002F SOLIDUS (/)
-                * EOF
-            ...then there is a parse error. Emit a U+003C LESS-THAN SIGN character
-            token, a U+002F SOLIDUS character token, and switch to the data state
-            to process the next input character. */
-            $this->emitToken(array(
-                'type' => self::CHARACTR,
-                'data' => '</'
-            ));
-
-            $this->state = 'data';
-
-        } else {
-            /* Otherwise, if the content model flag is set to the PCDATA state,
-            or if the next few characters do match that tag name, consume the
-            next input character: */
-            $this->char++;
-            $char = $this->char();
-
-            if(preg_match('/^[A-Za-z]$/', $char)) {
-                /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z
-                Create a new end tag token, set its tag name to the lowercase version
-                of the input character (add 0x0020 to the character's code point), then
-                switch to the tag name state. (Don't emit the token yet; further details
-                will be filled in before it is emitted.) */
-                $this->token = array(
-                    'name'  => strtolower($char),
-                    'type'  => self::ENDTAG
-                );
-
-                $this->state = 'tagName';
-
-            } elseif($char === '>') {
-                /* U+003E GREATER-THAN SIGN (>)
-                Parse error. Switch to the data state. */
-                $this->state = 'data';
-
-            } elseif($this->char === $this->EOF) {
-                /* EOF
-                Parse error. Emit a U+003C LESS-THAN SIGN character token and a U+002F
-                SOLIDUS character token. Reconsume the EOF character in the data state. */
-                $this->emitToken(array(
-                    'type' => self::CHARACTR,
-                    'data' => '</'
-                ));
-
-                $this->char--;
-                $this->state = 'data';
-
-            } else {
-                /* Parse error. Switch to the bogus comment state. */
-                $this->state = 'bogusComment';
-            }
-        }
-    }
-
-    private function tagNameState() {
-        // Consume the next input character:
-        $this->char++;
-        $char = $this->character($this->char);
-
-        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            /* U+0009 CHARACTER TABULATION
-            U+000A LINE FEED (LF)
-            U+000B LINE TABULATION
-            U+000C FORM FEED (FF)
-            U+0020 SPACE
-            Switch to the before attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } elseif($char === '>') {
-            /* U+003E GREATER-THAN SIGN (>)
-            Emit the current tag token. Switch to the data state. */
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } elseif($this->char === $this->EOF) {
-            /* EOF
-            Parse error. Emit the current tag token. Reconsume the EOF
-            character in the data state. */
-            $this->emitToken($this->token);
-
-            $this->char--;
-            $this->state = 'data';
-
-        } elseif($char === '/') {
-            /* U+002F SOLIDUS (/)
-            Parse error unless this is a permitted slash. Switch to the before
-            attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } else {
-            /* Anything else
-            Append the current input character to the current tag token's tag name.
-            Stay in the tag name state. */
-            $this->token['name'] .= strtolower($char);
-            $this->state = 'tagName';
-        }
-    }
-
-    private function beforeAttributeNameState() {
-        // Consume the next input character:
-        $this->char++;
-        $char = $this->character($this->char);
-
-        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            /* U+0009 CHARACTER TABULATION
-            U+000A LINE FEED (LF)
-            U+000B LINE TABULATION
-            U+000C FORM FEED (FF)
-            U+0020 SPACE
-            Stay in the before attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } elseif($char === '>') {
-            /* U+003E GREATER-THAN SIGN (>)
-            Emit the current tag token. Switch to the data state. */
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } elseif($char === '/') {
-            /* U+002F SOLIDUS (/)
-            Parse error unless this is a permitted slash. Stay in the before
-            attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } elseif($this->char === $this->EOF) {
-            /* EOF
-            Parse error. Emit the current tag token. Reconsume the EOF
-            character in the data state. */
-            $this->emitToken($this->token);
-
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            /* Anything else
-            Start a new attribute in the current tag token. Set that attribute's
-            name to the current input character, and its value to the empty string.
-            Switch to the attribute name state. */
-            $this->token['attr'][] = array(
-                'name'  => strtolower($char),
-                'value' => null
-            );
-
-            $this->state = 'attributeName';
-        }
-    }
-
-    private function attributeNameState() {
-        // Consume the next input character:
-        $this->char++;
-        $char = $this->character($this->char);
-
-        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            /* U+0009 CHARACTER TABULATION
-            U+000A LINE FEED (LF)
-            U+000B LINE TABULATION
-            U+000C FORM FEED (FF)
-            U+0020 SPACE
-            Stay in the before attribute name state. */
-            $this->state = 'afterAttributeName';
-
-        } elseif($char === '=') {
-            /* U+003D EQUALS SIGN (=)
-            Switch to the before attribute value state. */
-            $this->state = 'beforeAttributeValue';
-
-        } elseif($char === '>') {
-            /* U+003E GREATER-THAN SIGN (>)
-            Emit the current tag token. Switch to the data state. */
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } elseif($char === '/' && $this->character($this->char + 1) !== '>') {
-            /* U+002F SOLIDUS (/)
-            Parse error unless this is a permitted slash. Switch to the before
-            attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } elseif($this->char === $this->EOF) {
-            /* EOF
-            Parse error. Emit the current tag token. Reconsume the EOF
-            character in the data state. */
-            $this->emitToken($this->token);
-
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            /* Anything else
-            Append the current input character to the current attribute's name.
-            Stay in the attribute name state. */
-            $last = count($this->token['attr']) - 1;
-            $this->token['attr'][$last]['name'] .= strtolower($char);
-
-            $this->state = 'attributeName';
-        }
-    }
-
-    private function afterAttributeNameState() {
-        // Consume the next input character:
-        $this->char++;
-        $char = $this->character($this->char);
-
-        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            /* U+0009 CHARACTER TABULATION
-            U+000A LINE FEED (LF)
-            U+000B LINE TABULATION
-            U+000C FORM FEED (FF)
-            U+0020 SPACE
-            Stay in the after attribute name state. */
-            $this->state = 'afterAttributeName';
-
-        } elseif($char === '=') {
-            /* U+003D EQUALS SIGN (=)
-            Switch to the before attribute value state. */
-            $this->state = 'beforeAttributeValue';
-
-        } elseif($char === '>') {
-            /* U+003E GREATER-THAN SIGN (>)
-            Emit the current tag token. Switch to the data state. */
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } elseif($char === '/' && $this->character($this->char + 1) !== '>') {
-            /* U+002F SOLIDUS (/)
-            Parse error unless this is a permitted slash. Switch to the
-            before attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } elseif($this->char === $this->EOF) {
-            /* EOF
-            Parse error. Emit the current tag token. Reconsume the EOF
-            character in the data state. */
-            $this->emitToken($this->token);
-
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            /* Anything else
-            Start a new attribute in the current tag token. Set that attribute's
-            name to the current input character, and its value to the empty string.
-            Switch to the attribute name state. */
-            $this->token['attr'][] = array(
-                'name'  => strtolower($char),
-                'value' => null
-            );
-
-            $this->state = 'attributeName';
-        }
-    }
-
-    private function beforeAttributeValueState() {
-        // Consume the next input character:
-        $this->char++;
-        $char = $this->character($this->char);
-
-        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            /* U+0009 CHARACTER TABULATION
-            U+000A LINE FEED (LF)
-            U+000B LINE TABULATION
-            U+000C FORM FEED (FF)
-            U+0020 SPACE
-            Stay in the before attribute value state. */
-            $this->state = 'beforeAttributeValue';
-
-        } elseif($char === '"') {
-            /* U+0022 QUOTATION MARK (")
-            Switch to the attribute value (double-quoted) state. */
-            $this->state = 'attributeValueDoubleQuoted';
-
-        } elseif($char === '&') {
-            /* U+0026 AMPERSAND (&)
-            Switch to the attribute value (unquoted) state and reconsume
-            this input character. */
-            $this->char--;
-            $this->state = 'attributeValueUnquoted';
-
-        } elseif($char === '\'') {
-            /* U+0027 APOSTROPHE (')
-            Switch to the attribute value (single-quoted) state. */
-            $this->state = 'attributeValueSingleQuoted';
-
-        } elseif($char === '>') {
-            /* U+003E GREATER-THAN SIGN (>)
-            Emit the current tag token. Switch to the data state. */
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } else {
-            /* Anything else
-            Append the current input character to the current attribute's value.
-            Switch to the attribute value (unquoted) state. */
-            $last = count($this->token['attr']) - 1;
-            $this->token['attr'][$last]['value'] .= $char;
-
-            $this->state = 'attributeValueUnquoted';
-        }
-    }
-
-    private function attributeValueDoubleQuotedState() {
-        // Consume the next input character:
-        $this->char++;
-        $char = $this->character($this->char);
-
-        if($char === '"') {
-            /* U+0022 QUOTATION MARK (")
-            Switch to the before attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } elseif($char === '&') {
-            /* U+0026 AMPERSAND (&)
-            Switch to the entity in attribute value state. */
-            $this->entityInAttributeValueState('double');
-
-        } elseif($this->char === $this->EOF) {
-            /* EOF
-            Parse error. Emit the current tag token. Reconsume the character
-            in the data state. */
-            $this->emitToken($this->token);
-
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            /* Anything else
-            Append the current input character to the current attribute's value.
-            Stay in the attribute value (double-quoted) state. */
-            $last = count($this->token['attr']) - 1;
-            $this->token['attr'][$last]['value'] .= $char;
-
-            $this->state = 'attributeValueDoubleQuoted';
-        }
-    }
-
-    private function attributeValueSingleQuotedState() {
-        // Consume the next input character:
-        $this->char++;
-        $char = $this->character($this->char);
-
-        if($char === '\'') {
-            /* U+0022 QUOTATION MARK (')
-            Switch to the before attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } elseif($char === '&') {
-            /* U+0026 AMPERSAND (&)
-            Switch to the entity in attribute value state. */
-            $this->entityInAttributeValueState('single');
-
-        } elseif($this->char === $this->EOF) {
-            /* EOF
-            Parse error. Emit the current tag token. Reconsume the character
-            in the data state. */
-            $this->emitToken($this->token);
-
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            /* Anything else
-            Append the current input character to the current attribute's value.
-            Stay in the attribute value (single-quoted) state. */
-            $last = count($this->token['attr']) - 1;
-            $this->token['attr'][$last]['value'] .= $char;
-
-            $this->state = 'attributeValueSingleQuoted';
-        }
-    }
-
-    private function attributeValueUnquotedState() {
-        // Consume the next input character:
-        $this->char++;
-        $char = $this->character($this->char);
-
-        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            /* U+0009 CHARACTER TABULATION
-            U+000A LINE FEED (LF)
-            U+000B LINE TABULATION
-            U+000C FORM FEED (FF)
-            U+0020 SPACE
-            Switch to the before attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } elseif($char === '&') {
-            /* U+0026 AMPERSAND (&)
-            Switch to the entity in attribute value state. */
-            $this->entityInAttributeValueState();
-
-        } elseif($char === '>') {
-            /* U+003E GREATER-THAN SIGN (>)
-            Emit the current tag token. Switch to the data state. */
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } else {
-            /* Anything else
-            Append the current input character to the current attribute's value.
-            Stay in the attribute value (unquoted) state. */
-            $last = count($this->token['attr']) - 1;
-            $this->token['attr'][$last]['value'] .= $char;
-
-            $this->state = 'attributeValueUnquoted';
-        }
-    }
-
-    private function entityInAttributeValueState() {
-        // Attempt to consume an entity.
-        $entity = $this->entity();
-
-        // If nothing is returned, append a U+0026 AMPERSAND character to the
-        // current attribute's value. Otherwise, emit the character token that
-        // was returned.
-        $char = (!$entity)
-            ? '&'
-            : $entity;
-
-        $last = count($this->token['attr']) - 1;
-        $this->token['attr'][$last]['value'] .= $char;
-    }
-
-    private function bogusCommentState() {
-        /* Consume every character up to the first U+003E GREATER-THAN SIGN
-        character (>) or the end of the file (EOF), whichever comes first. Emit
-        a comment token whose data is the concatenation of all the characters
-        starting from and including the character that caused the state machine
-        to switch into the bogus comment state, up to and including the last
-        consumed character before the U+003E character, if any, or up to the
-        end of the file otherwise. (If the comment was started by the end of
-        the file (EOF), the token is empty.) */
-        $data = $this->characters('^>', $this->char);
-        $this->emitToken(array(
-            'data' => $data,
-            'type' => self::COMMENT
-        ));
-
-        $this->char += strlen($data);
-
-        /* Switch to the data state. */
-        $this->state = 'data';
-
-        /* If the end of the file was reached, reconsume the EOF character. */
-        if($this->char === $this->EOF) {
-            $this->char = $this->EOF - 1;
-        }
-    }
-
-    private function markupDeclarationOpenState() {
-        /* If the next two characters are both U+002D HYPHEN-MINUS (-)
-        characters, consume those two characters, create a comment token whose
-        data is the empty string, and switch to the comment state. */
-        if($this->character($this->char + 1, 2) === '--') {
-            $this->char += 2;
-            $this->state = 'comment';
-            $this->token = array(
-                'data' => null,
-                'type' => self::COMMENT
-            );
-
-        /* Otherwise if the next seven chacacters are a case-insensitive match
-        for the word "DOCTYPE", then consume those characters and switch to the
-        DOCTYPE state. */
-        } elseif(strtolower($this->character($this->char + 1, 7)) === 'doctype') {
-            $this->char += 7;
-            $this->state = 'doctype';
-
-        /* Otherwise, is is a parse error. Switch to the bogus comment state.
-        The next character that is consumed, if any, is the first character
-        that will be in the comment. */
-        } else {
-            $this->char++;
-            $this->state = 'bogusComment';
-        }
-    }
-
-    private function commentState() {
-        /* Consume the next input character: */
-        $this->char++;
-        $char = $this->char();
-
-        /* U+002D HYPHEN-MINUS (-) */
-        if($char === '-') {
-            /* Switch to the comment dash state  */
-            $this->state = 'commentDash';
-
-        /* EOF */
-        } elseif($this->char === $this->EOF) {
-            /* Parse error. Emit the comment token. Reconsume the EOF character
-            in the data state. */
-            $this->emitToken($this->token);
-            $this->char--;
-            $this->state = 'data';
-
-        /* Anything else */
-        } else {
-            /* Append the input character to the comment token's data. Stay in
-            the comment state. */
-            $this->token['data'] .= $char;
-        }
-    }
-
-    private function commentDashState() {
-        /* Consume the next input character: */
-        $this->char++;
-        $char = $this->char();
-
-        /* U+002D HYPHEN-MINUS (-) */
-        if($char === '-') {
-            /* Switch to the comment end state  */
-            $this->state = 'commentEnd';
-
-        /* EOF */
-        } elseif($this->char === $this->EOF) {
-            /* Parse error. Emit the comment token. Reconsume the EOF character
-            in the data state. */
-            $this->emitToken($this->token);
-            $this->char--;
-            $this->state = 'data';
-
-        /* Anything else */
-        } else {
-            /* Append a U+002D HYPHEN-MINUS (-) character and the input
-            character to the comment token's data. Switch to the comment state. */
-            $this->token['data'] .= '-'.$char;
-            $this->state = 'comment';
-        }
-    }
-
-    private function commentEndState() {
-        /* Consume the next input character: */
-        $this->char++;
-        $char = $this->char();
-
-        if($char === '>') {
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } elseif($char === '-') {
-            $this->token['data'] .= '-';
-
-        } elseif($this->char === $this->EOF) {
-            $this->emitToken($this->token);
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            $this->token['data'] .= '--'.$char;
-            $this->state = 'comment';
-        }
-    }
-
-    private function doctypeState() {
-        /* Consume the next input character: */
-        $this->char++;
-        $char = $this->char();
-
-        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            $this->state = 'beforeDoctypeName';
-
-        } else {
-            $this->char--;
-            $this->state = 'beforeDoctypeName';
-        }
-    }
-
-    private function beforeDoctypeNameState() {
-        /* Consume the next input character: */
-        $this->char++;
-        $char = $this->char();
-
-        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            // Stay in the before DOCTYPE name state.
-
-        } elseif(preg_match('/^[a-z]$/', $char)) {
-            $this->token = array(
-                'name' => strtoupper($char),
-                'type' => self::DOCTYPE,
-                'error' => true
-            );
-
-            $this->state = 'doctypeName';
-
-        } elseif($char === '>') {
-            $this->emitToken(array(
-                'name' => null,
-                'type' => self::DOCTYPE,
-                'error' => true
-            ));
-
-            $this->state = 'data';
-
-        } elseif($this->char === $this->EOF) {
-            $this->emitToken(array(
-                'name' => null,
-                'type' => self::DOCTYPE,
-                'error' => true
-            ));
-
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            $this->token = array(
-                'name' => $char,
-                'type' => self::DOCTYPE,
-                'error' => true
-            );
-
-            $this->state = 'doctypeName';
-        }
-    }
-
-    private function doctypeNameState() {
-        /* Consume the next input character: */
-        $this->char++;
-        $char = $this->char();
-
-        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            $this->state = 'AfterDoctypeName';
-
-        } elseif($char === '>') {
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } elseif(preg_match('/^[a-z]$/', $char)) {
-            $this->token['name'] .= strtoupper($char);
-
-        } elseif($this->char === $this->EOF) {
-            $this->emitToken($this->token);
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            $this->token['name'] .= $char;
-        }
-
-        $this->token['error'] = ($this->token['name'] === 'HTML')
-            ? false
-            : true;
-    }
-
-    private function afterDoctypeNameState() {
-        /* Consume the next input character: */
-        $this->char++;
-        $char = $this->char();
-
-        if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            // Stay in the DOCTYPE name state.
-
-        } elseif($char === '>') {
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } elseif($this->char === $this->EOF) {
-            $this->emitToken($this->token);
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            $this->token['error'] = true;
-            $this->state = 'bogusDoctype';
-        }
-    }
-
-    private function bogusDoctypeState() {
-        /* Consume the next input character: */
-        $this->char++;
-        $char = $this->char();
-
-        if($char === '>') {
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } elseif($this->char === $this->EOF) {
-            $this->emitToken($this->token);
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            // Stay in the bogus DOCTYPE state.
-        }
-    }
-
-    private function entity() {
-        $start = $this->char;
-
-        // This section defines how to consume an entity. This definition is
-        // used when parsing entities in text and in attributes.
-
-        // The behaviour depends on the identity of the next character (the
-        // one immediately after the U+0026 AMPERSAND character): 
-
-        switch($this->character($this->char + 1)) {
-            // U+0023 NUMBER SIGN (#)
-            case '#':
-
-                // The behaviour further depends on the character after the
-                // U+0023 NUMBER SIGN:
-                switch($this->character($this->char + 1)) {
-                    // U+0078 LATIN SMALL LETTER X
-                    // U+0058 LATIN CAPITAL LETTER X
-                    case 'x':
-                    case 'X':
-                        // Follow the steps below, but using the range of
-                        // characters U+0030 DIGIT ZERO through to U+0039 DIGIT
-                        // NINE, U+0061 LATIN SMALL LETTER A through to U+0066
-                        // LATIN SMALL LETTER F, and U+0041 LATIN CAPITAL LETTER
-                        // A, through to U+0046 LATIN CAPITAL LETTER F (in other
-                        // words, 0-9, A-F, a-f).
-                        $char = 1;
-                        $char_class = '0-9A-Fa-f';
-                    break;
-
-                    // Anything else
-                    default:
-                        // Follow the steps below, but using the range of
-                        // characters U+0030 DIGIT ZERO through to U+0039 DIGIT
-                        // NINE (i.e. just 0-9).
-                        $char = 0;
-                        $char_class = '0-9';
-                    break;
-                }
-
-                // Consume as many characters as match the range of characters
-                // given above.
-                $this->char++;
-                $e_name = $this->characters($char_class, $this->char + $char + 1);
-                $entity = $this->character($start, $this->char);
-                $cond = strlen($e_name) > 0;
-
-                // The rest of the parsing happens bellow.
-            break;
-
-            // Anything else
-            default:
-                // Consume the maximum number of characters possible, with the
-                // consumed characters case-sensitively matching one of the
-                // identifiers in the first column of the entities table.
-                $e_name = $this->characters('0-9A-Za-z;', $this->char + 1);
-                $len = strlen($e_name);
-
-                for($c = 1; $c <= $len; $c++) {
-                    $id = substr($e_name, 0, $c);
-                    $this->char++;
-
-                    if(in_array($id, $this->entities)) {
-                        if ($e_name[$c-1] !== ';') {
-                            if ($c < $len && $e_name[$c] == ';') {
-                                $this->char++; // consume extra semicolon
-                            }
-                        }
-                        $entity = $id;
-                        break;
-                    }
-                }
-
-                $cond = isset($entity);
-                // The rest of the parsing happens bellow.
-            break;
-        }
-
-        if(!$cond) {
-            // If no match can be made, then this is a parse error. No
-            // characters are consumed, and nothing is returned.
-            $this->char = $start;
-            return false;
-        }
-
-        // Return a character token for the character corresponding to the
-        // entity name (as given by the second column of the entities table).
-        return html_entity_decode('&'.$entity.';', ENT_QUOTES, 'UTF-8');
-    }
-
-    private function emitToken($token) {
-        $emit = $this->tree->emitToken($token);
-
-        if(is_int($emit)) {
-            $this->content_model = $emit;
-
-        } elseif($token['type'] === self::ENDTAG) {
-            $this->content_model = self::PCDATA;
-        }
-    }
-
-    private function EOF() {
-        $this->state = null;
-        $this->tree->emitToken(array(
-            'type' => self::EOF
-        ));
-    }
-}
-
-class HTML5TreeConstructer {
-    public $stack = array();
-
-    private $phase;
-    private $mode;
-    private $dom;
-    private $foster_parent = null;
-    private $a_formatting  = array();
-
-    private $head_pointer = null;
-    private $form_pointer = null;
-
-    private $scoping = array('button','caption','html','marquee','object','table','td','th');
-    private $formatting = array('a','b','big','em','font','i','nobr','s','small','strike','strong','tt','u');
-    private $special = array('address','area','base','basefont','bgsound',
-    'blockquote','body','br','center','col','colgroup','dd','dir','div','dl',
-    'dt','embed','fieldset','form','frame','frameset','h1','h2','h3','h4','h5',
-    'h6','head','hr','iframe','image','img','input','isindex','li','link',
-    'listing','menu','meta','noembed','noframes','noscript','ol','optgroup',
-    'option','p','param','plaintext','pre','script','select','spacer','style',
-    'tbody','textarea','tfoot','thead','title','tr','ul','wbr');
-
-    // The different phases.
-    const INIT_PHASE = 0;
-    const ROOT_PHASE = 1;
-    const MAIN_PHASE = 2;
-    const END_PHASE  = 3;
-
-    // The different insertion modes for the main phase.
-    const BEFOR_HEAD = 0;
-    const IN_HEAD    = 1;
-    const AFTER_HEAD = 2;
-    const IN_BODY    = 3;
-    const IN_TABLE   = 4;
-    const IN_CAPTION = 5;
-    const IN_CGROUP  = 6;
-    const IN_TBODY   = 7;
-    const IN_ROW     = 8;
-    const IN_CELL    = 9;
-    const IN_SELECT  = 10;
-    const AFTER_BODY = 11;
-    const IN_FRAME   = 12;
-    const AFTR_FRAME = 13;
-
-    // The different types of elements.
-    const SPECIAL    = 0;
-    const SCOPING    = 1;
-    const FORMATTING = 2;
-    const PHRASING   = 3;
-
-    const MARKER     = 0;
-
-    public function __construct() {
-        $this->phase = self::INIT_PHASE;
-        $this->mode = self::BEFOR_HEAD;
-        $this->dom = new DOMDocument;
-
-        $this->dom->encoding = 'UTF-8';
-        $this->dom->preserveWhiteSpace = true;
-        $this->dom->substituteEntities = true;
-        $this->dom->strictErrorChecking = false;
-    }
-
-    // Process tag tokens
-    public function emitToken($token) {
-        switch($this->phase) {
-            case self::INIT_PHASE: return $this->initPhase($token); break;
-            case self::ROOT_PHASE: return $this->rootElementPhase($token); break;
-            case self::MAIN_PHASE: return $this->mainPhase($token); break;
-            case self::END_PHASE : return $this->trailingEndPhase($token); break;
-        }
-    }
-
-    private function initPhase($token) {
-        /* Initially, the tree construction stage must handle each token
-        emitted from the tokenisation stage as follows: */
-
-        /* A DOCTYPE token that is marked as being in error
-        A comment token
-        A start tag token
-        An end tag token
-        A character token that is not one of one of U+0009 CHARACTER TABULATION,
-            U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-            or U+0020 SPACE
-        An end-of-file token */
-        if((isset($token['error']) && $token['error']) ||
-        $token['type'] === HTML5::COMMENT ||
-        $token['type'] === HTML5::STARTTAG ||
-        $token['type'] === HTML5::ENDTAG ||
-        $token['type'] === HTML5::EOF ||
-        ($token['type'] === HTML5::CHARACTR && isset($token['data']) &&
-        !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']))) {
-            /* This specification does not define how to handle this case. In
-            particular, user agents may ignore the entirety of this specification
-            altogether for such documents, and instead invoke special parse modes
-            with a greater emphasis on backwards compatibility. */
-
-            $this->phase = self::ROOT_PHASE;
-            return $this->rootElementPhase($token);
-
-        /* A DOCTYPE token marked as being correct */
-        } elseif(isset($token['error']) && !$token['error']) {
-            /* Append a DocumentType node to the Document  node, with the name
-            attribute set to the name given in the DOCTYPE token (which will be
-            "HTML"), and the other attributes specific to DocumentType objects
-            set to null, empty lists, or the empty string as appropriate. */
-            $doctype = new DOMDocumentType(null, null, 'HTML');
-
-            /* Then, switch to the root element phase of the tree construction
-            stage. */
-            $this->phase = self::ROOT_PHASE;
-
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        or U+0020 SPACE */
-        } elseif(isset($token['data']) && preg_match('/^[\t\n\x0b\x0c ]+$/',
-        $token['data'])) {
-            /* Append that character  to the Document node. */
-            $text = $this->dom->createTextNode($token['data']);
-            $this->dom->appendChild($text);
-        }
-    }
-
-    private function rootElementPhase($token) {
-        /* After the initial phase, as each token is emitted from the tokenisation
-        stage, it must be processed as described in this section. */
-
-        /* A DOCTYPE token */
-        if($token['type'] === HTML5::DOCTYPE) {
-            // Parse error. Ignore the token.
-
-        /* A comment token */
-        } elseif($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the Document object with the data
-            attribute set to the data given in the comment token. */
-            $comment = $this->dom->createComment($token['data']);
-            $this->dom->appendChild($comment);
-
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        or U+0020 SPACE */
-        } elseif($token['type'] === HTML5::CHARACTR &&
-        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
-            /* Append that character  to the Document node. */
-            $text = $this->dom->createTextNode($token['data']);
-            $this->dom->appendChild($text);
-
-        /* A character token that is not one of U+0009 CHARACTER TABULATION,
-            U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED
-            (FF), or U+0020 SPACE
-        A start tag token
-        An end tag token
-        An end-of-file token */
-        } elseif(($token['type'] === HTML5::CHARACTR &&
-        !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) ||
-        $token['type'] === HTML5::STARTTAG ||
-        $token['type'] === HTML5::ENDTAG ||
-        $token['type'] === HTML5::EOF) {
-            /* Create an HTMLElement node with the tag name html, in the HTML
-            namespace. Append it to the Document object. Switch to the main
-            phase and reprocess the current token. */
-            $html = $this->dom->createElement('html');
-            $this->dom->appendChild($html);
-            $this->stack[] = $html;
-
-            $this->phase = self::MAIN_PHASE;
-            return $this->mainPhase($token);
-        }
-    }
-
-    private function mainPhase($token) {
-        /* Tokens in the main phase must be handled as follows: */
-
-        /* A DOCTYPE token */
-        if($token['type'] === HTML5::DOCTYPE) {
-            // Parse error. Ignore the token.
-
-        /* A start tag token with the tag name "html" */
-        } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'html') {
-            /* If this start tag token was not the first start tag token, then
-            it is a parse error. */
-
-            /* For each attribute on the token, check to see if the attribute
-            is already present on the top element of the stack of open elements.
-            If it is not, add the attribute and its corresponding value to that
-            element. */
-            foreach($token['attr'] as $attr) {
-                if(!$this->stack[0]->hasAttribute($attr['name'])) {
-                    $this->stack[0]->setAttribute($attr['name'], $attr['value']);
-                }
-            }
-
-        /* An end-of-file token */
-        } elseif($token['type'] === HTML5::EOF) {
-            /* Generate implied end tags. */
-            $this->generateImpliedEndTags();
-
-        /* Anything else. */
-        } else {
-            /* Depends on the insertion mode: */
-            switch($this->mode) {
-                case self::BEFOR_HEAD: return $this->beforeHead($token); break;
-                case self::IN_HEAD:    return $this->inHead($token); break;
-                case self::AFTER_HEAD: return $this->afterHead($token); break;
-                case self::IN_BODY:    return $this->inBody($token); break;
-                case self::IN_TABLE:   return $this->inTable($token); break;
-                case self::IN_CAPTION: return $this->inCaption($token); break;
-                case self::IN_CGROUP:  return $this->inColumnGroup($token); break;
-                case self::IN_TBODY:   return $this->inTableBody($token); break;
-                case self::IN_ROW:     return $this->inRow($token); break;
-                case self::IN_CELL:    return $this->inCell($token); break;
-                case self::IN_SELECT:  return $this->inSelect($token); break;
-                case self::AFTER_BODY: return $this->afterBody($token); break;
-                case self::IN_FRAME:   return $this->inFrameset($token); break;
-                case self::AFTR_FRAME: return $this->afterFrameset($token); break;
-                case self::END_PHASE:  return $this->trailingEndPhase($token); break;
-            }
-        }
-    }
-
-    private function beforeHead($token) {
-        /* Handle the token as follows: */
-
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        or U+0020 SPACE */
-        if($token['type'] === HTML5::CHARACTR &&
-        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
-            /* Append the character to the current node. */
-            $this->insertText($token['data']);
-
-        /* A comment token */
-        } elseif($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the current node with the data attribute
-            set to the data given in the comment token. */
-            $this->insertComment($token['data']);
-
-        /* A start tag token with the tag name "head" */
-        } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') {
-            /* Create an element for the token, append the new element to the
-            current node and push it onto the stack of open elements. */
-            $element = $this->insertElement($token);
-
-            /* Set the head element pointer to this new element node. */
-            $this->head_pointer = $element;
-
-            /* Change the insertion mode to "in head". */
-            $this->mode = self::IN_HEAD;
-
-        /* A start tag token whose tag name is one of: "base", "link", "meta",
-        "script", "style", "title". Or an end tag with the tag name "html".
-        Or a character token that is not one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        or U+0020 SPACE. Or any other start tag token */
-        } elseif($token['type'] === HTML5::STARTTAG ||
-        ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') ||
-        ($token['type'] === HTML5::CHARACTR && !preg_match('/^[\t\n\x0b\x0c ]$/',
-        $token['data']))) {
-            /* Act as if a start tag token with the tag name "head" and no
-            attributes had been seen, then reprocess the current token. */
-            $this->beforeHead(array(
-                'name' => 'head',
-                'type' => HTML5::STARTTAG,
-                'attr' => array()
-            ));
-
-            return $this->inHead($token);
-
-        /* Any other end tag */
-        } elseif($token['type'] === HTML5::ENDTAG) {
-            /* Parse error. Ignore the token. */
-        }
-    }
-
-    private function inHead($token) {
-        /* Handle the token as follows: */
-
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        or U+0020 SPACE.
-
-        THIS DIFFERS FROM THE SPEC: If the current node is either a title, style
-        or script element, append the character to the current node regardless
-        of its content. */
-        if(($token['type'] === HTML5::CHARACTR &&
-        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || (
-        $token['type'] === HTML5::CHARACTR && in_array(end($this->stack)->nodeName,
-        array('title', 'style', 'script')))) {
-            /* Append the character to the current node. */
-            $this->insertText($token['data']);
-
-        /* A comment token */
-        } elseif($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the current node with the data attribute
-            set to the data given in the comment token. */
-            $this->insertComment($token['data']);
-
-        } elseif($token['type'] === HTML5::ENDTAG &&
-        in_array($token['name'], array('title', 'style', 'script'))) {
-            array_pop($this->stack);
-            return HTML5::PCDATA;
-
-        /* A start tag with the tag name "title" */
-        } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'title') {
-            /* Create an element for the token and append the new element to the
-            node pointed to by the head element pointer, or, if that is null
-            (innerHTML case), to the current node. */
-            if($this->head_pointer !== null) {
-                $element = $this->insertElement($token, false);
-                $this->head_pointer->appendChild($element);
-
-            } else {
-                $element = $this->insertElement($token);
-            }
-
-            /* Switch the tokeniser's content model flag  to the RCDATA state. */
-            return HTML5::RCDATA;
-
-        /* A start tag with the tag name "style" */
-        } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'style') {
-            /* Create an element for the token and append the new element to the
-            node pointed to by the head element pointer, or, if that is null
-            (innerHTML case), to the current node. */
-            if($this->head_pointer !== null) {
-                $element = $this->insertElement($token, false);
-                $this->head_pointer->appendChild($element);
-
-            } else {
-                $this->insertElement($token);
-            }
-
-            /* Switch the tokeniser's content model flag  to the CDATA state. */
-            return HTML5::CDATA;
-
-        /* A start tag with the tag name "script" */
-        } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'script') {
-            /* Create an element for the token. */
-            $element = $this->insertElement($token, false);
-            $this->head_pointer->appendChild($element);
-
-            /* Switch the tokeniser's content model flag  to the CDATA state. */
-            return HTML5::CDATA;
-
-        /* A start tag with the tag name "base", "link", or "meta" */
-        } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'],
-        array('base', 'link', 'meta'))) {
-            /* Create an element for the token and append the new element to the
-            node pointed to by the head element pointer, or, if that is null
-            (innerHTML case), to the current node. */
-            if($this->head_pointer !== null) {
-                $element = $this->insertElement($token, false);
-                $this->head_pointer->appendChild($element);
-                array_pop($this->stack);
-
-            } else {
-                $this->insertElement($token);
-            }
-
-        /* An end tag with the tag name "head" */
-        } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'head') {
-            /* If the current node is a head element, pop the current node off
-            the stack of open elements. */
-            if($this->head_pointer->isSameNode(end($this->stack))) {
-                array_pop($this->stack);
-
-            /* Otherwise, this is a parse error. */
-            } else {
-                // k
-            }
-
-            /* Change the insertion mode to "after head". */
-            $this->mode = self::AFTER_HEAD;
-
-        /* A start tag with the tag name "head" or an end tag except "html". */
-        } elseif(($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') ||
-        ($token['type'] === HTML5::ENDTAG && $token['name'] !== 'html')) {
-            // Parse error. Ignore the token.
-
-        /* Anything else */
-        } else {
-            /* If the current node is a head element, act as if an end tag
-            token with the tag name "head" had been seen. */
-            if($this->head_pointer->isSameNode(end($this->stack))) {
-                $this->inHead(array(
-                    'name' => 'head',
-                    'type' => HTML5::ENDTAG
-                ));
-
-            /* Otherwise, change the insertion mode to "after head". */
-            } else {
-                $this->mode = self::AFTER_HEAD;
-            }
-
-            /* Then, reprocess the current token. */
-            return $this->afterHead($token);
-        }
-    }
-
-    private function afterHead($token) {
-        /* Handle the token as follows: */
-
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        or U+0020 SPACE */
-        if($token['type'] === HTML5::CHARACTR &&
-        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
-            /* Append the character to the current node. */
-            $this->insertText($token['data']);
-
-        /* A comment token */
-        } elseif($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the current node with the data attribute
-            set to the data given in the comment token. */
-            $this->insertComment($token['data']);
-
-        /* A start tag token with the tag name "body" */
-        } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'body') {
-            /* Insert a body element for the token. */
-            $this->insertElement($token);
-
-            /* Change the insertion mode to "in body". */
-            $this->mode = self::IN_BODY;
-
-        /* A start tag token with the tag name "frameset" */
-        } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'frameset') {
-            /* Insert a frameset element for the token. */
-            $this->insertElement($token);
-
-            /* Change the insertion mode to "in frameset". */
-            $this->mode = self::IN_FRAME;
-
-        /* A start tag token whose tag name is one of: "base", "link", "meta",
-        "script", "style", "title" */
-        } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'],
-        array('base', 'link', 'meta', 'script', 'style', 'title'))) {
-            /* Parse error. Switch the insertion mode back to "in head" and
-            reprocess the token. */
-            $this->mode = self::IN_HEAD;
-            return $this->inHead($token);
-
-        /* Anything else */
-        } else {
-            /* Act as if a start tag token with the tag name "body" and no
-            attributes had been seen, and then reprocess the current token. */
-            $this->afterHead(array(
-                'name' => 'body',
-                'type' => HTML5::STARTTAG,
-                'attr' => array()
-            ));
-
-            return $this->inBody($token);
-        }
-    }
-
-    private function inBody($token) {
-        /* Handle the token as follows: */
-
-        switch($token['type']) {
-            /* A character token */
-            case HTML5::CHARACTR:
-                /* Reconstruct the active formatting elements, if any. */
-                $this->reconstructActiveFormattingElements();
-
-                /* Append the token's character to the current node. */
-                $this->insertText($token['data']);
-            break;
-
-            /* A comment token */
-            case HTML5::COMMENT:
-                /* Append a Comment node to the current node with the data
-                attribute set to the data given in the comment token. */
-                $this->insertComment($token['data']);
-            break;
-
-            case HTML5::STARTTAG:
-            switch($token['name']) {
-                /* A start tag token whose tag name is one of: "script",
-                "style" */
-                case 'script': case 'style':
-                    /* Process the token as if the insertion mode had been "in
-                    head". */
-                    return $this->inHead($token);
-                break;
-
-                /* A start tag token whose tag name is one of: "base", "link",
-                "meta", "title" */
-                case 'base': case 'link': case 'meta': case 'title':
-                    /* Parse error. Process the token as if the insertion mode
-                    had    been "in head". */
-                    return $this->inHead($token);
-                break;
-
-                /* A start tag token with the tag name "body" */
-                case 'body':
-                    /* Parse error. If the second element on the stack of open
-                    elements is not a body element, or, if the stack of open
-                    elements has only one node on it, then ignore the token.
-                    (innerHTML case) */
-                    if(count($this->stack) === 1 || $this->stack[1]->nodeName !== 'body') {
-                        // Ignore
-
-                    /* Otherwise, for each attribute on the token, check to see
-                    if the attribute is already present on the body element (the
-                    second element)    on the stack of open elements. If it is not,
-                    add the attribute and its corresponding value to that
-                    element. */
-                    } else {
-                        foreach($token['attr'] as $attr) {
-                            if(!$this->stack[1]->hasAttribute($attr['name'])) {
-                                $this->stack[1]->setAttribute($attr['name'], $attr['value']);
-                            }
-                        }
-                    }
-                break;
-
-                /* A start tag whose tag name is one of: "address",
-                "blockquote", "center", "dir", "div", "dl", "fieldset",
-                "listing", "menu", "ol", "p", "ul" */
-                case 'address': case 'blockquote': case 'center': case 'dir':
-                case 'div': case 'dl': case 'fieldset': case 'listing':
-                case 'menu': case 'ol': case 'p': case 'ul':
-                    /* If the stack of open elements has a p element in scope,
-                    then act as if an end tag with the tag name p had been
-                    seen. */
-                    if($this->elementInScope('p')) {
-                        $this->emitToken(array(
-                            'name' => 'p',
-                            'type' => HTML5::ENDTAG
-                        ));
-                    }
-
-                    /* Insert an HTML element for the token. */
-                    $this->insertElement($token);
-                break;
-
-                /* A start tag whose tag name is "form" */
-                case 'form':
-                    /* If the form element pointer is not null, ignore the
-                    token with a parse error. */
-                    if($this->form_pointer !== null) {
-                        // Ignore.
-
-                    /* Otherwise: */
-                    } else {
-                        /* If the stack of open elements has a p element in
-                        scope, then act as if an end tag with the tag name p
-                        had been seen. */
-                        if($this->elementInScope('p')) {
-                            $this->emitToken(array(
-                                'name' => 'p',
-                                'type' => HTML5::ENDTAG
-                            ));
-                        }
-
-                        /* Insert an HTML element for the token, and set the
-                        form element pointer to point to the element created. */
-                        $element = $this->insertElement($token);
-                        $this->form_pointer = $element;
-                    }
-                break;
-
-                /* A start tag whose tag name is "li", "dd" or "dt" */
-                case 'li': case 'dd': case 'dt':
-                    /* If the stack of open elements has a p  element in scope,
-                    then act as if an end tag with the tag name p had been
-                    seen. */
-                    if($this->elementInScope('p')) {
-                        $this->emitToken(array(
-                            'name' => 'p',
-                            'type' => HTML5::ENDTAG
-                        ));
-                    }
-
-                    $stack_length = count($this->stack) - 1;
-
-                    for($n = $stack_length; 0 <= $n; $n--) {
-                        /* 1. Initialise node to be the current node (the
-                        bottommost node of the stack). */
-                        $stop = false;
-                        $node = $this->stack[$n];
-                        $cat  = $this->getElementCategory($node->tagName);
-
-                        /* 2. If node is an li, dd or dt element, then pop all
-                        the    nodes from the current node up to node, including
-                        node, then stop this algorithm. */
-                        if($token['name'] === $node->tagName ||    ($token['name'] !== 'li'
-                        && ($node->tagName === 'dd' || $node->tagName === 'dt'))) {
-                            for($x = $stack_length; $x >= $n ; $x--) {
-                                array_pop($this->stack);
-                            }
-
-                            break;
-                        }
-
-                        /* 3. If node is not in the formatting category, and is
-                        not    in the phrasing category, and is not an address or
-                        div element, then stop this algorithm. */
-                        if($cat !== self::FORMATTING && $cat !== self::PHRASING &&
-                        $node->tagName !== 'address' && $node->tagName !== 'div') {
-                            break;
-                        }
-                    }
-
-                    /* Finally, insert an HTML element with the same tag
-                    name as the    token's. */
-                    $this->insertElement($token);
-                break;
-
-                /* A start tag token whose tag name is "plaintext" */
-                case 'plaintext':
-                    /* If the stack of open elements has a p  element in scope,
-                    then act as if an end tag with the tag name p had been
-                    seen. */
-                    if($this->elementInScope('p')) {
-                        $this->emitToken(array(
-                            'name' => 'p',
-                            'type' => HTML5::ENDTAG
-                        ));
-                    }
-
-                    /* Insert an HTML element for the token. */
-                    $this->insertElement($token);
-
-                    return HTML5::PLAINTEXT;
-                break;
-
-                /* A start tag whose tag name is one of: "h1", "h2", "h3", "h4",
-                "h5", "h6" */
-                case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6':
-                    /* If the stack of open elements has a p  element in scope,
-                    then act as if an end tag with the tag name p had been seen. */
-                    if($this->elementInScope('p')) {
-                        $this->emitToken(array(
-                            'name' => 'p',
-                            'type' => HTML5::ENDTAG
-                        ));
-                    }
-
-                    /* If the stack of open elements has in scope an element whose
-                    tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then
-                    this is a parse error; pop elements from the stack until an
-                    element with one of those tag names has been popped from the
-                    stack. */
-                    while($this->elementInScope(array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))) {
-                        array_pop($this->stack);
-                    }
-
-                    /* Insert an HTML element for the token. */
-                    $this->insertElement($token);
-                break;
-
-                /* A start tag whose tag name is "a" */
-                case 'a':
-                    /* If the list of active formatting elements contains
-                    an element whose tag name is "a" between the end of the
-                    list and the last marker on the list (or the start of
-                    the list if there is no marker on the list), then this
-                    is a parse error; act as if an end tag with the tag name
-                    "a" had been seen, then remove that element from the list
-                    of active formatting elements and the stack of open
-                    elements if the end tag didn't already remove it (it
-                    might not have if the element is not in table scope). */
-                    $leng = count($this->a_formatting);
-
-                    for($n = $leng - 1; $n >= 0; $n--) {
-                        if($this->a_formatting[$n] === self::MARKER) {
-                            break;
-
-                        } elseif($this->a_formatting[$n]->nodeName === 'a') {
-                            $this->emitToken(array(
-                                'name' => 'a',
-                                'type' => HTML5::ENDTAG
-                            ));
-                            break;
-                        }
-                    }
-
-                    /* Reconstruct the active formatting elements, if any. */
-                    $this->reconstructActiveFormattingElements();
-
-                    /* Insert an HTML element for the token. */
-                    $el = $this->insertElement($token);
-
-                    /* Add that element to the list of active formatting
-                    elements. */
-                    $this->a_formatting[] = $el;
-                break;
-
-                /* A start tag whose tag name is one of: "b", "big", "em", "font",
-                "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */
-                case 'b': case 'big': case 'em': case 'font': case 'i':
-                case 'nobr': case 's': case 'small': case 'strike':
-                case 'strong': case 'tt': case 'u':
-                    /* Reconstruct the active formatting elements, if any. */
-                    $this->reconstructActiveFormattingElements();
-
-                    /* Insert an HTML element for the token. */
-                    $el = $this->insertElement($token);
-
-                    /* Add that element to the list of active formatting
-                    elements. */
-                    $this->a_formatting[] = $el;
-                break;
-
-                /* A start tag token whose tag name is "button" */
-                case 'button':
-                    /* If the stack of open elements has a button element in scope,
-                    then this is a parse error; act as if an end tag with the tag
-                    name "button" had been seen, then reprocess the token. (We don't
-                    do that. Unnecessary.) */
-                    if($this->elementInScope('button')) {
-                        $this->inBody(array(
-                            'name' => 'button',
-                            'type' => HTML5::ENDTAG
-                        ));
-                    }
-
-                    /* Reconstruct the active formatting elements, if any. */
-                    $this->reconstructActiveFormattingElements();
-
-                    /* Insert an HTML element for the token. */
-                    $this->insertElement($token);
-
-                    /* Insert a marker at the end of the list of active
-                    formatting elements. */
-                    $this->a_formatting[] = self::MARKER;
-                break;
-
-                /* A start tag token whose tag name is one of: "marquee", "object" */
-                case 'marquee': case 'object':
-                    /* Reconstruct the active formatting elements, if any. */
-                    $this->reconstructActiveFormattingElements();
-
-                    /* Insert an HTML element for the token. */
-                    $this->insertElement($token);
-
-                    /* Insert a marker at the end of the list of active
-                    formatting elements. */
-                    $this->a_formatting[] = self::MARKER;
-                break;
-
-                /* A start tag token whose tag name is "xmp" */
-                case 'xmp':
-                    /* Reconstruct the active formatting elements, if any. */
-                    $this->reconstructActiveFormattingElements();
-
-                    /* Insert an HTML element for the token. */
-                    $this->insertElement($token);
-
-                    /* Switch the content model flag to the CDATA state. */
-                    return HTML5::CDATA;
-                break;
-
-                /* A start tag whose tag name is "table" */
-                case 'table':
-                    /* If the stack of open elements has a p element in scope,
-                    then act as if an end tag with the tag name p had been seen. */
-                    if($this->elementInScope('p')) {
-                        $this->emitToken(array(
-                            'name' => 'p',
-                            'type' => HTML5::ENDTAG
-                        ));
-                    }
-
-                    /* Insert an HTML element for the token. */
-                    $this->insertElement($token);
-
-                    /* Change the insertion mode to "in table". */
-                    $this->mode = self::IN_TABLE;
-                break;
-
-                /* A start tag whose tag name is one of: "area", "basefont",
-                "bgsound", "br", "embed", "img", "param", "spacer", "wbr" */
-                case 'area': case 'basefont': case 'bgsound': case 'br':
-                case 'embed': case 'img': case 'param': case 'spacer':
-                case 'wbr':
-                    /* Reconstruct the active formatting elements, if any. */
-                    $this->reconstructActiveFormattingElements();
-
-                    /* Insert an HTML element for the token. */
-                    $this->insertElement($token);
-
-                    /* Immediately pop the current node off the stack of open elements. */
-                    array_pop($this->stack);
-                break;
-
-                /* A start tag whose tag name is "hr" */
-                case 'hr':
-                    /* If the stack of open elements has a p element in scope,
-                    then act as if an end tag with the tag name p had been seen. */
-                    if($this->elementInScope('p')) {
-                        $this->emitToken(array(
-                            'name' => 'p',
-                            'type' => HTML5::ENDTAG
-                        ));
-                    }
-
-                    /* Insert an HTML element for the token. */
-                    $this->insertElement($token);
-
-                    /* Immediately pop the current node off the stack of open elements. */
-                    array_pop($this->stack);
-                break;
-
-                /* A start tag whose tag name is "image" */
-                case 'image':
-                    /* Parse error. Change the token's tag name to "img" and
-                    reprocess it. (Don't ask.) */
-                    $token['name'] = 'img';
-                    return $this->inBody($token);
-                break;
-
-                /* A start tag whose tag name is "input" */
-                case 'input':
-                    /* Reconstruct the active formatting elements, if any. */
-                    $this->reconstructActiveFormattingElements();
-
-                    /* Insert an input element for the token. */
-                    $element = $this->insertElement($token, false);
-
-                    /* If the form element pointer is not null, then associate the
-                    input element with the form element pointed to by the form
-                    element pointer. */
-                    $this->form_pointer !== null
-                        ? $this->form_pointer->appendChild($element)
-                        : end($this->stack)->appendChild($element);
-
-                    /* Pop that input element off the stack of open elements. */
-                    array_pop($this->stack);
-                break;
-
-                /* A start tag whose tag name is "isindex" */
-                case 'isindex':
-                    /* Parse error. */
-                    // w/e
-
-                    /* If the form element pointer is not null,
-                    then ignore the token. */
-                    if($this->form_pointer === null) {
-                        /* Act as if a start tag token with the tag name "form" had
-                        been seen. */
-                        $this->inBody(array(
-                            'name' => 'body',
-                            'type' => HTML5::STARTTAG,
-                            'attr' => array()
-                        ));
-
-                        /* Act as if a start tag token with the tag name "hr" had
-                        been seen. */
-                        $this->inBody(array(
-                            'name' => 'hr',
-                            'type' => HTML5::STARTTAG,
-                            'attr' => array()
-                        ));
-
-                        /* Act as if a start tag token with the tag name "p" had
-                        been seen. */
-                        $this->inBody(array(
-                            'name' => 'p',
-                            'type' => HTML5::STARTTAG,
-                            'attr' => array()
-                        ));
-
-                        /* Act as if a start tag token with the tag name "label"
-                        had been seen. */
-                        $this->inBody(array(
-                            'name' => 'label',
-                            'type' => HTML5::STARTTAG,
-                            'attr' => array()
-                        ));
-
-                        /* Act as if a stream of character tokens had been seen. */
-                        $this->insertText('This is a searchable index. '.
-                        'Insert your search keywords here: ');
-
-                        /* Act as if a start tag token with the tag name "input"
-                        had been seen, with all the attributes from the "isindex"
-                        token, except with the "name" attribute set to the value
-                        "isindex" (ignoring any explicit "name" attribute). */
-                        $attr = $token['attr'];
-                        $attr[] = array('name' => 'name', 'value' => 'isindex');
-
-                        $this->inBody(array(
-                            'name' => 'input',
-                            'type' => HTML5::STARTTAG,
-                            'attr' => $attr
-                        ));
-
-                        /* Act as if a stream of character tokens had been seen
-                        (see below for what they should say). */
-                        $this->insertText('This is a searchable index. '.
-                        'Insert your search keywords here: ');
-
-                        /* Act as if an end tag token with the tag name "label"
-                        had been seen. */
-                        $this->inBody(array(
-                            'name' => 'label',
-                            'type' => HTML5::ENDTAG
-                        ));
-
-                        /* Act as if an end tag token with the tag name "p" had
-                        been seen. */
-                        $this->inBody(array(
-                            'name' => 'p',
-                            'type' => HTML5::ENDTAG
-                        ));
-
-                        /* Act as if a start tag token with the tag name "hr" had
-                        been seen. */
-                        $this->inBody(array(
-                            'name' => 'hr',
-                            'type' => HTML5::ENDTAG
-                        ));
-
-                        /* Act as if an end tag token with the tag name "form" had
-                        been seen. */
-                        $this->inBody(array(
-                            'name' => 'form',
-                            'type' => HTML5::ENDTAG
-                        ));
-                    }
-                break;
-
-                /* A start tag whose tag name is "textarea" */
-                case 'textarea':
-                    $this->insertElement($token);
-
-                    /* Switch the tokeniser's content model flag to the
-                    RCDATA state. */
-                    return HTML5::RCDATA;
-                break;
-
-                /* A start tag whose tag name is one of: "iframe", "noembed",
-                "noframes" */
-                case 'iframe': case 'noembed': case 'noframes':
-                    $this->insertElement($token);
-
-                    /* Switch the tokeniser's content model flag to the CDATA state. */
-                    return HTML5::CDATA;
-                break;
-
-                /* A start tag whose tag name is "select" */
-                case 'select':
-                    /* Reconstruct the active formatting elements, if any. */
-                    $this->reconstructActiveFormattingElements();
-
-                    /* Insert an HTML element for the token. */
-                    $this->insertElement($token);
-
-                    /* Change the insertion mode to "in select". */
-                    $this->mode = self::IN_SELECT;
-                break;
-
-                /* A start or end tag whose tag name is one of: "caption", "col",
-                "colgroup", "frame", "frameset", "head", "option", "optgroup",
-                "tbody", "td", "tfoot", "th", "thead", "tr". */
-                case 'caption': case 'col': case 'colgroup': case 'frame':
-                case 'frameset': case 'head': case 'option': case 'optgroup':
-                case 'tbody': case 'td': case 'tfoot': case 'th': case 'thead':
-                case 'tr':
-                    // Parse error. Ignore the token.
-                break;
-
-                /* A start or end tag whose tag name is one of: "event-source",
-                "section", "nav", "article", "aside", "header", "footer",
-                "datagrid", "command" */
-                case 'event-source': case 'section': case 'nav': case 'article':
-                case 'aside': case 'header': case 'footer': case 'datagrid':
-                case 'command':
-                    // Work in progress!
-                break;
-
-                /* A start tag token not covered by the previous entries */
-                default:
-                    /* Reconstruct the active formatting elements, if any. */
-                    $this->reconstructActiveFormattingElements();
-
-                    $this->insertElement($token, true, true);
-                break;
-            }
-            break;
-
-            case HTML5::ENDTAG:
-            switch($token['name']) {
-                /* An end tag with the tag name "body" */
-                case 'body':
-                    /* If the second element in the stack of open elements is
-                    not a body element, this is a parse error. Ignore the token.
-                    (innerHTML case) */
-                    if(count($this->stack) < 2 || $this->stack[1]->nodeName !== 'body') {
-                        // Ignore.
-
-                    /* If the current node is not the body element, then this
-                    is a parse error. */
-                    } elseif(end($this->stack)->nodeName !== 'body') {
-                        // Parse error.
-                    }
-
-                    /* Change the insertion mode to "after body". */
-                    $this->mode = self::AFTER_BODY;
-                break;
-
-                /* An end tag with the tag name "html" */
-                case 'html':
-                    /* Act as if an end tag with tag name "body" had been seen,
-                    then, if that token wasn't ignored, reprocess the current
-                    token. */
-                    $this->inBody(array(
-                        'name' => 'body',
-                        'type' => HTML5::ENDTAG
-                    ));
-
-                    return $this->afterBody($token);
-                break;
-
-                /* An end tag whose tag name is one of: "address", "blockquote",
-                "center", "dir", "div", "dl", "fieldset", "listing", "menu",
-                "ol", "pre", "ul" */
-                case 'address': case 'blockquote': case 'center': case 'dir':
-                case 'div': case 'dl': case 'fieldset': case 'listing':
-                case 'menu': case 'ol': case 'pre': case 'ul':
-                    /* If the stack of open elements has an element in scope
-                    with the same tag name as that of the token, then generate
-                    implied end tags. */
-                    if($this->elementInScope($token['name'])) {
-                        $this->generateImpliedEndTags();
-
-                        /* Now, if the current node is not an element with
-                        the same tag name as that of the token, then this
-                        is a parse error. */
-                        // w/e
-
-                        /* If the stack of open elements has an element in
-                        scope with the same tag name as that of the token,
-                        then pop elements from this stack until an element
-                        with that tag name has been popped from the stack. */
-                        for($n = count($this->stack) - 1; $n >= 0; $n--) {
-                            if($this->stack[$n]->nodeName === $token['name']) {
-                                $n = -1;
-                            }
-
-                            array_pop($this->stack);
-                        }
-                    }
-                break;
-
-                /* An end tag whose tag name is "form" */
-                case 'form':
-                    /* If the stack of open elements has an element in scope
-                    with the same tag name as that of the token, then generate
-                    implied    end tags. */
-                    if($this->elementInScope($token['name'])) {
-                        $this->generateImpliedEndTags();
-
-                    } 
-
-                    if(end($this->stack)->nodeName !== $token['name']) {
-                        /* Now, if the current node is not an element with the
-                        same tag name as that of the token, then this is a parse
-                        error. */
-                        // w/e
-
-                    } else {
-                        /* Otherwise, if the current node is an element with
-                        the same tag name as that of the token pop that element
-                        from the stack. */
-                        array_pop($this->stack);
-                    }
-
-                    /* In any case, set the form element pointer to null. */
-                    $this->form_pointer = null;
-                break;
-
-                /* An end tag whose tag name is "p" */
-                case 'p':
-                    /* If the stack of open elements has a p element in scope,
-                    then generate implied end tags, except for p elements. */
-                    if($this->elementInScope('p')) {
-                        $this->generateImpliedEndTags(array('p'));
-
-                        /* If the current node is not a p element, then this is
-                        a parse error. */
-                        // k
-
-                        /* If the stack of open elements has a p element in
-                        scope, then pop elements from this stack until the stack
-                        no longer has a p element in scope. */
-                        for($n = count($this->stack) - 1; $n >= 0; $n--) {
-                            if($this->elementInScope('p')) {
-                                array_pop($this->stack);
-
-                            } else {
-                                break;
-                            }
-                        }
-                    }
-                break;
-
-                /* An end tag whose tag name is "dd", "dt", or "li" */
-                case 'dd': case 'dt': case 'li':
-                    /* If the stack of open elements has an element in scope
-                    whose tag name matches the tag name of the token, then
-                    generate implied end tags, except for elements with the
-                    same tag name as the token. */
-                    if($this->elementInScope($token['name'])) {
-                        $this->generateImpliedEndTags(array($token['name']));
-
-                        /* If the current node is not an element with the same
-                        tag name as the token, then this is a parse error. */
-                        // w/e
-
-                        /* If the stack of open elements has an element in scope
-                        whose tag name matches the tag name of the token, then
-                        pop elements from this stack until an element with that
-                        tag name has been popped from the stack. */
-                        for($n = count($this->stack) - 1; $n >= 0; $n--) {
-                            if($this->stack[$n]->nodeName === $token['name']) {
-                                $n = -1;
-                            }
-
-                            array_pop($this->stack);
-                        }
-                    }
-                break;
-
-                /* An end tag whose tag name is one of: "h1", "h2", "h3", "h4",
-                "h5", "h6" */
-                case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6':
-                    $elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6');
-
-                    /* If the stack of open elements has in scope an element whose
-                    tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then
-                    generate implied end tags. */
-                    if($this->elementInScope($elements)) {
-                        $this->generateImpliedEndTags();
-
-                        /* Now, if the current node is not an element with the same
-                        tag name as that of the token, then this is a parse error. */
-                        // w/e
-
-                        /* If the stack of open elements has in scope an element
-                        whose tag name is one of "h1", "h2", "h3", "h4", "h5", or
-                        "h6", then pop elements from the stack until an element
-                        with one of those tag names has been popped from the stack. */
-                        while($this->elementInScope($elements)) {
-                            array_pop($this->stack);
-                        }
-                    }
-                break;
-
-                /* An end tag whose tag name is one of: "a", "b", "big", "em",
-                "font", "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */
-                case 'a': case 'b': case 'big': case 'em': case 'font':
-                case 'i': case 'nobr': case 's': case 'small': case 'strike':
-                case 'strong': case 'tt': case 'u':
-                    /* 1. Let the formatting element be the last element in
-                    the list of active formatting elements that:
-                        * is between the end of the list and the last scope
-                        marker in the list, if any, or the start of the list
-                        otherwise, and
-                        * has the same tag name as the token.
-                    */
-                    while(true) {
-                        for($a = count($this->a_formatting) - 1; $a >= 0; $a--) {
-                            if($this->a_formatting[$a] === self::MARKER) {
-                                break;
-
-                            } elseif($this->a_formatting[$a]->tagName === $token['name']) {
-                                $formatting_element = $this->a_formatting[$a];
-                                $in_stack = in_array($formatting_element, $this->stack, true);
-                                $fe_af_pos = $a;
-                                break;
-                            }
-                        }
-
-                        /* If there is no such node, or, if that node is
-                        also in the stack of open elements but the element
-                        is not in scope, then this is a parse error. Abort
-                        these steps. The token is ignored. */
-                        if(!isset($formatting_element) || ($in_stack &&
-                        !$this->elementInScope($token['name']))) {
-                            break;
-
-                        /* Otherwise, if there is such a node, but that node
-                        is not in the stack of open elements, then this is a
-                        parse error; remove the element from the list, and
-                        abort these steps. */
-                        } elseif(isset($formatting_element) && !$in_stack) {
-                            unset($this->a_formatting[$fe_af_pos]);
-                            $this->a_formatting = array_merge($this->a_formatting);
-                            break;
-                        }
-
-                        /* 2. Let the furthest block be the topmost node in the
-                        stack of open elements that is lower in the stack
-                        than the formatting element, and is not an element in
-                        the phrasing or formatting categories. There might
-                        not be one. */
-                        $fe_s_pos = array_search($formatting_element, $this->stack, true);
-                        $length = count($this->stack);
-
-                        for($s = $fe_s_pos + 1; $s < $length; $s++) {
-                            $category = $this->getElementCategory($this->stack[$s]->nodeName);
-
-                            if($category !== self::PHRASING && $category !== self::FORMATTING) {
-                                $furthest_block = $this->stack[$s];
-                            }
-                        }
-
-                        /* 3. If there is no furthest block, then the UA must
-                        skip the subsequent steps and instead just pop all
-                        the nodes from the bottom of the stack of open
-                        elements, from the current node up to the formatting
-                        element, and remove the formatting element from the
-                        list of active formatting elements. */
-                        if(!isset($furthest_block)) {
-                            for($n = $length - 1; $n >= $fe_s_pos; $n--) {
-                                array_pop($this->stack);
-                            }
-
-                            unset($this->a_formatting[$fe_af_pos]);
-                            $this->a_formatting = array_merge($this->a_formatting);
-                            break;
-                        }
-
-                        /* 4. Let the common ancestor be the element
-                        immediately above the formatting element in the stack
-                        of open elements. */
-                        $common_ancestor = $this->stack[$fe_s_pos - 1];
-
-                        /* 5. If the furthest block has a parent node, then
-                        remove the furthest block from its parent node. */
-                        if($furthest_block->parentNode !== null) {
-                            $furthest_block->parentNode->removeChild($furthest_block);
-                        }
-
-                        /* 6. Let a bookmark note the position of the
-                        formatting element in the list of active formatting
-                        elements relative to the elements on either side
-                        of it in the list. */
-                        $bookmark = $fe_af_pos;
-
-                        /* 7. Let node and last node  be the furthest block.
-                        Follow these steps: */
-                        $node = $furthest_block;
-                        $last_node = $furthest_block;
-
-                        while(true) {
-                            for($n = array_search($node, $this->stack, true) - 1; $n >= 0; $n--) {
-                                /* 7.1 Let node be the element immediately
-                                prior to node in the stack of open elements. */
-                                $node = $this->stack[$n];
-
-                                /* 7.2 If node is not in the list of active
-                                formatting elements, then remove node from
-                                the stack of open elements and then go back
-                                to step 1. */
-                                if(!in_array($node, $this->a_formatting, true)) {
-                                    unset($this->stack[$n]);
-                                    $this->stack = array_merge($this->stack);
-
-                                } else {
-                                    break;
-                                }
-                            }
-
-                            /* 7.3 Otherwise, if node is the formatting
-                            element, then go to the next step in the overall
-                            algorithm. */
-                            if($node === $formatting_element) {
-                                break;
-
-                            /* 7.4 Otherwise, if last node is the furthest
-                            block, then move the aforementioned bookmark to
-                            be immediately after the node in the list of
-                            active formatting elements. */
-                            } elseif($last_node === $furthest_block) {
-                                $bookmark = array_search($node, $this->a_formatting, true) + 1;
-                            }
-
-                            /* 7.5 If node has any children, perform a
-                            shallow clone of node, replace the entry for
-                            node in the list of active formatting elements
-                            with an entry for the clone, replace the entry
-                            for node in the stack of open elements with an
-                            entry for the clone, and let node be the clone. */
-                            if($node->hasChildNodes()) {
-                                $clone = $node->cloneNode();
-                                $s_pos = array_search($node, $this->stack, true);
-                                $a_pos = array_search($node, $this->a_formatting, true);
-
-                                $this->stack[$s_pos] = $clone;
-                                $this->a_formatting[$a_pos] = $clone;
-                                $node = $clone;
-                            }
-
-                            /* 7.6 Insert last node into node, first removing
-                            it from its previous parent node if any. */
-                            if($last_node->parentNode !== null) {
-                                $last_node->parentNode->removeChild($last_node);
-                            }
-
-                            $node->appendChild($last_node);
-
-                            /* 7.7 Let last node be node. */
-                            $last_node = $node;
-                        }
-
-                        /* 8. Insert whatever last node ended up being in
-                        the previous step into the common ancestor node,
-                        first removing it from its previous parent node if
-                        any. */
-                        if($last_node->parentNode !== null) {
-                            $last_node->parentNode->removeChild($last_node);
-                        }
-
-                        $common_ancestor->appendChild($last_node);
-
-                        /* 9. Perform a shallow clone of the formatting
-                        element. */
-                        $clone = $formatting_element->cloneNode();
-
-                        /* 10. Take all of the child nodes of the furthest
-                        block and append them to the clone created in the
-                        last step. */
-                        while($furthest_block->hasChildNodes()) {
-                            $child = $furthest_block->firstChild;
-                            $furthest_block->removeChild($child);
-                            $clone->appendChild($child);
-                        }
-
-                        /* 11. Append that clone to the furthest block. */
-                        $furthest_block->appendChild($clone);
-
-                        /* 12. Remove the formatting element from the list
-                        of active formatting elements, and insert the clone
-                        into the list of active formatting elements at the
-                        position of the aforementioned bookmark. */
-                        $fe_af_pos = array_search($formatting_element, $this->a_formatting, true);
-                        unset($this->a_formatting[$fe_af_pos]);
-                        $this->a_formatting = array_merge($this->a_formatting);
-
-                        $af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1);
-                        $af_part2 = array_slice($this->a_formatting, $bookmark, count($this->a_formatting));
-                        $this->a_formatting = array_merge($af_part1, array($clone), $af_part2);
-
-                        /* 13. Remove the formatting element from the stack
-                        of open elements, and insert the clone into the stack
-                        of open elements immediately after (i.e. in a more
-                        deeply nested position than) the position of the
-                        furthest block in that stack. */
-                        $fe_s_pos = array_search($formatting_element, $this->stack, true);
-                        $fb_s_pos = array_search($furthest_block, $this->stack, true);
-                        unset($this->stack[$fe_s_pos]);
-
-                        $s_part1 = array_slice($this->stack, 0, $fb_s_pos);
-                        $s_part2 = array_slice($this->stack, $fb_s_pos + 1, count($this->stack));
-                        $this->stack = array_merge($s_part1, array($clone), $s_part2);
-
-                        /* 14. Jump back to step 1 in this series of steps. */
-                        unset($formatting_element, $fe_af_pos, $fe_s_pos, $furthest_block);
-                    }
-                break;
-
-                /* An end tag token whose tag name is one of: "button",
-                "marquee", "object" */
-                case 'button': case 'marquee': case 'object':
-                    /* If the stack of open elements has an element in scope whose
-                    tag name matches the tag name of the token, then generate implied
-                    tags. */
-                    if($this->elementInScope($token['name'])) {
-                        $this->generateImpliedEndTags();
-
-                        /* Now, if the current node is not an element with the same
-                        tag name as the token, then this is a parse error. */
-                        // k
-
-                        /* Now, if the stack of open elements has an element in scope
-                        whose tag name matches the tag name of the token, then pop
-                        elements from the stack until that element has been popped from
-                        the stack, and clear the list of active formatting elements up
-                        to the last marker. */
-                        for($n = count($this->stack) - 1; $n >= 0; $n--) {
-                            if($this->stack[$n]->nodeName === $token['name']) {
-                                $n = -1;
-                            }
-
-                            array_pop($this->stack);
-                        }
-
-                        $marker = end(array_keys($this->a_formatting, self::MARKER, true));
-
-                        for($n = count($this->a_formatting) - 1; $n > $marker; $n--) {
-                            array_pop($this->a_formatting);
-                        }
-                    }
-                break;
-
-                /* Or an end tag whose tag name is one of: "area", "basefont",
-                "bgsound", "br", "embed", "hr", "iframe", "image", "img",
-                "input", "isindex", "noembed", "noframes", "param", "select",
-                "spacer", "table", "textarea", "wbr" */
-                case 'area': case 'basefont': case 'bgsound': case 'br':
-                case 'embed': case 'hr': case 'iframe': case 'image':
-                case 'img': case 'input': case 'isindex': case 'noembed':
-                case 'noframes': case 'param': case 'select': case 'spacer':
-                case 'table': case 'textarea': case 'wbr':
-                    // Parse error. Ignore the token.
-                break;
-
-                /* An end tag token not covered by the previous entries */
-                default:
-                    for($n = count($this->stack) - 1; $n >= 0; $n--) {
-                        /* Initialise node to be the current node (the bottommost
-                        node of the stack). */
-                        $node = end($this->stack);
-
-                        /* If node has the same tag name as the end tag token,
-                        then: */
-                        if($token['name'] === $node->nodeName) {
-                            /* Generate implied end tags. */
-                            $this->generateImpliedEndTags();
-
-                            /* If the tag name of the end tag token does not
-                            match the tag name of the current node, this is a
-                            parse error. */
-                            // k
-
-                            /* Pop all the nodes from the current node up to
-                            node, including node, then stop this algorithm. */
-                            for($x = count($this->stack) - $n; $x >= $n; $x--) {
-                                array_pop($this->stack);
-                            }
-                                    
-                        } else {
-                            $category = $this->getElementCategory($node);
-
-                            if($category !== self::SPECIAL && $category !== self::SCOPING) {
-                                /* Otherwise, if node is in neither the formatting
-                                category nor the phrasing category, then this is a
-                                parse error. Stop this algorithm. The end tag token
-                                is ignored. */
-                                return false;
-                            }
-                        }
-                    }
-                break;
-            }
-            break;
-        }
-    }
-
-    private function inTable($token) {
-        $clear = array('html', 'table');
-
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        or U+0020 SPACE */
-        if($token['type'] === HTML5::CHARACTR &&
-        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
-            /* Append the character to the current node. */
-            $text = $this->dom->createTextNode($token['data']);
-            end($this->stack)->appendChild($text);
-
-        /* A comment token */
-        } elseif($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the current node with the data
-            attribute set to the data given in the comment token. */
-            $comment = $this->dom->createComment($token['data']);
-            end($this->stack)->appendChild($comment);
-
-        /* A start tag whose tag name is "caption" */
-        } elseif($token['type'] === HTML5::STARTTAG &&
-        $token['name'] === 'caption') {
-            /* Clear the stack back to a table context. */
-            $this->clearStackToTableContext($clear);
-
-            /* Insert a marker at the end of the list of active
-            formatting elements. */
-            $this->a_formatting[] = self::MARKER;
-
-            /* Insert an HTML element for the token, then switch the
-            insertion mode to "in caption". */
-            $this->insertElement($token);
-            $this->mode = self::IN_CAPTION;
-
-        /* A start tag whose tag name is "colgroup" */
-        } elseif($token['type'] === HTML5::STARTTAG &&
-        $token['name'] === 'colgroup') {
-            /* Clear the stack back to a table context. */
-            $this->clearStackToTableContext($clear);
-
-            /* Insert an HTML element for the token, then switch the
-            insertion mode to "in column group". */
-            $this->insertElement($token);
-            $this->mode = self::IN_CGROUP;
-
-        /* A start tag whose tag name is "col" */
-        } elseif($token['type'] === HTML5::STARTTAG &&
-        $token['name'] === 'col') {
-            $this->inTable(array(
-                'name' => 'colgroup',
-                'type' => HTML5::STARTTAG,
-                'attr' => array()
-            ));
-
-            $this->inColumnGroup($token);
-
-        /* A start tag whose tag name is one of: "tbody", "tfoot", "thead" */
-        } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'],
-        array('tbody', 'tfoot', 'thead'))) {
-            /* Clear the stack back to a table context. */
-            $this->clearStackToTableContext($clear);
-
-            /* Insert an HTML element for the token, then switch the insertion
-            mode to "in table body". */
-            $this->insertElement($token);
-            $this->mode = self::IN_TBODY;
-
-        /* A start tag whose tag name is one of: "td", "th", "tr" */
-        } elseif($token['type'] === HTML5::STARTTAG &&
-        in_array($token['name'], array('td', 'th', 'tr'))) {
-            /* Act as if a start tag token with the tag name "tbody" had been
-            seen, then reprocess the current token. */
-            $this->inTable(array(
-                'name' => 'tbody',
-                'type' => HTML5::STARTTAG,
-                'attr' => array()
-            ));
-
-            return $this->inTableBody($token);
-
-        /* A start tag whose tag name is "table" */
-        } elseif($token['type'] === HTML5::STARTTAG &&
-        $token['name'] === 'table') {
-            /* Parse error. Act as if an end tag token with the tag name "table"
-            had been seen, then, if that token wasn't ignored, reprocess the
-            current token. */
-            $this->inTable(array(
-                'name' => 'table',
-                'type' => HTML5::ENDTAG
-            ));
-
-            return $this->mainPhase($token);
-
-        /* An end tag whose tag name is "table" */
-        } elseif($token['type'] === HTML5::ENDTAG &&
-        $token['name'] === 'table') {
-            /* If the stack of open elements does not have an element in table
-            scope with the same tag name as the token, this is a parse error.
-            Ignore the token. (innerHTML case) */
-            if(!$this->elementInScope($token['name'], true)) {
-                return false;
-
-            /* Otherwise: */
-            } else {
-                /* Generate implied end tags. */
-                $this->generateImpliedEndTags();
-
-                /* Now, if the current node is not a table element, then this
-                is a parse error. */
-                // w/e
-
-                /* Pop elements from this stack until a table element has been
-                popped from the stack. */
-                while(true) {
-                    $current = end($this->stack)->nodeName;
-                    array_pop($this->stack);
-
-                    if($current === 'table') {
-                        break;
-                    }
-                }
-
-                /* Reset the insertion mode appropriately. */
-                $this->resetInsertionMode();
-            }
-
-        /* An end tag whose tag name is one of: "body", "caption", "col",
-        "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr" */
-        } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'],
-        array('body', 'caption', 'col', 'colgroup', 'html', 'tbody', 'td',
-        'tfoot', 'th', 'thead', 'tr'))) {
-            // Parse error. Ignore the token.
-
-        /* Anything else */
-        } else {
-            /* Parse error. Process the token as if the insertion mode was "in
-            body", with the following exception: */
-
-            /* If the current node is a table, tbody, tfoot, thead, or tr
-            element, then, whenever a node would be inserted into the current
-            node, it must instead be inserted into the foster parent element. */
-            if(in_array(end($this->stack)->nodeName,
-            array('table', 'tbody', 'tfoot', 'thead', 'tr'))) {
-                /* The foster parent element is the parent element of the last
-                table element in the stack of open elements, if there is a
-                table element and it has such a parent element. If there is no
-                table element in the stack of open elements (innerHTML case),
-                then the foster parent element is the first element in the
-                stack of open elements (the html  element). Otherwise, if there
-                is a table element in the stack of open elements, but the last
-                table element in the stack of open elements has no parent, or
-                its parent node is not an element, then the foster parent
-                element is the element before the last table element in the
-                stack of open elements. */
-                for($n = count($this->stack) - 1; $n >= 0; $n--) {
-                    if($this->stack[$n]->nodeName === 'table') {
-                        $table = $this->stack[$n];
-                        break;
-                    }
-                }
-
-                if(isset($table) && $table->parentNode !== null) {
-                    $this->foster_parent = $table->parentNode;
-
-                } elseif(!isset($table)) {
-                    $this->foster_parent = $this->stack[0];
-
-                } elseif(isset($table) && ($table->parentNode === null ||
-                $table->parentNode->nodeType !== XML_ELEMENT_NODE)) {
-                    $this->foster_parent = $this->stack[$n - 1];
-                }
-            }
-
-            $this->inBody($token);
-        }
-    }
-
-    private function inCaption($token) {
-        /* An end tag whose tag name is "caption" */
-        if($token['type'] === HTML5::ENDTAG && $token['name'] === 'caption') {
-            /* If the stack of open elements does not have an element in table
-            scope with the same tag name as the token, this is a parse error.
-            Ignore the token. (innerHTML case) */
-            if(!$this->elementInScope($token['name'], true)) {
-                // Ignore
-
-            /* Otherwise: */
-            } else {
-                /* Generate implied end tags. */
-                $this->generateImpliedEndTags();
-
-                /* Now, if the current node is not a caption element, then this
-                is a parse error. */
-                // w/e
-
-                /* Pop elements from this stack until a caption element has
-                been popped from the stack. */
-                while(true) {
-                    $node = end($this->stack)->nodeName;
-                    array_pop($this->stack);
-
-                    if($node === 'caption') {
-                        break;
-                    }
-                }
-
-                /* Clear the list of active formatting elements up to the last
-                marker. */
-                $this->clearTheActiveFormattingElementsUpToTheLastMarker();
-
-                /* Switch the insertion mode to "in table". */
-                $this->mode = self::IN_TABLE;
-            }
-
-        /* A start tag whose tag name is one of: "caption", "col", "colgroup",
-        "tbody", "td", "tfoot", "th", "thead", "tr", or an end tag whose tag
-        name is "table" */
-        } elseif(($token['type'] === HTML5::STARTTAG && in_array($token['name'],
-        array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
-        'thead', 'tr'))) || ($token['type'] === HTML5::ENDTAG &&
-        $token['name'] === 'table')) {
-            /* Parse error. Act as if an end tag with the tag name "caption"
-            had been seen, then, if that token wasn't ignored, reprocess the
-            current token. */
-            $this->inCaption(array(
-                'name' => 'caption',
-                'type' => HTML5::ENDTAG
-            ));
-
-            return $this->inTable($token);
-
-        /* An end tag whose tag name is one of: "body", "col", "colgroup",
-        "html", "tbody", "td", "tfoot", "th", "thead", "tr" */
-        } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'],
-        array('body', 'col', 'colgroup', 'html', 'tbody', 'tfoot', 'th',
-        'thead', 'tr'))) {
-            // Parse error. Ignore the token.
-
-        /* Anything else */
-        } else {
-            /* Process the token as if the insertion mode was "in body". */
-            $this->inBody($token);
-        }
-    }
-
-    private function inColumnGroup($token) {
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        or U+0020 SPACE */
-        if($token['type'] === HTML5::CHARACTR &&
-        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
-            /* Append the character to the current node. */
-            $text = $this->dom->createTextNode($token['data']);
-            end($this->stack)->appendChild($text);
-
-        /* A comment token */
-        } elseif($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the current node with the data
-            attribute set to the data given in the comment token. */
-            $comment = $this->dom->createComment($token['data']);
-            end($this->stack)->appendChild($comment);
-
-        /* A start tag whose tag name is "col" */
-        } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'col') {
-            /* Insert a col element for the token. Immediately pop the current
-            node off the stack of open elements. */
-            $this->insertElement($token);
-            array_pop($this->stack);
-
-        /* An end tag whose tag name is "colgroup" */
-        } elseif($token['type'] === HTML5::ENDTAG &&
-        $token['name'] === 'colgroup') {
-            /* If the current node is the root html element, then this is a
-            parse error, ignore the token. (innerHTML case) */
-            if(end($this->stack)->nodeName === 'html') {
-                // Ignore
-
-            /* Otherwise, pop the current node (which will be a colgroup
-            element) from the stack of open elements. Switch the insertion
-            mode to "in table". */
-            } else {
-                array_pop($this->stack);
-                $this->mode = self::IN_TABLE;
-            }
-
-        /* An end tag whose tag name is "col" */
-        } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'col') {
-            /* Parse error. Ignore the token. */
-
-        /* Anything else */
-        } else {
-            /* Act as if an end tag with the tag name "colgroup" had been seen,
-            and then, if that token wasn't ignored, reprocess the current token. */
-            $this->inColumnGroup(array(
-                'name' => 'colgroup',
-                'type' => HTML5::ENDTAG
-            ));
-
-            return $this->inTable($token);
-        }
-    }
-
-    private function inTableBody($token) {
-        $clear = array('tbody', 'tfoot', 'thead', 'html');
-
-        /* A start tag whose tag name is "tr" */
-        if($token['type'] === HTML5::STARTTAG && $token['name'] === 'tr') {
-            /* Clear the stack back to a table body context. */
-            $this->clearStackToTableContext($clear);
-
-            /* Insert a tr element for the token, then switch the insertion
-            mode to "in row". */
-            $this->insertElement($token);
-            $this->mode = self::IN_ROW;
-
-        /* A start tag whose tag name is one of: "th", "td" */
-        } elseif($token['type'] === HTML5::STARTTAG &&
-        ($token['name'] === 'th' ||    $token['name'] === 'td')) {
-            /* Parse error. Act as if a start tag with the tag name "tr" had
-            been seen, then reprocess the current token. */
-            $this->inTableBody(array(
-                'name' => 'tr',
-                'type' => HTML5::STARTTAG,
-                'attr' => array()
-            ));
-
-            return $this->inRow($token);
-
-        /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
-        } elseif($token['type'] === HTML5::ENDTAG &&
-        in_array($token['name'], array('tbody', 'tfoot', 'thead'))) {
-            /* If the stack of open elements does not have an element in table
-            scope with the same tag name as the token, this is a parse error.
-            Ignore the token. */
-            if(!$this->elementInScope($token['name'], true)) {
-                // Ignore
-
-            /* Otherwise: */
-            } else {
-                /* Clear the stack back to a table body context. */
-                $this->clearStackToTableContext($clear);
-
-                /* Pop the current node from the stack of open elements. Switch
-                the insertion mode to "in table". */
-                array_pop($this->stack);
-                $this->mode = self::IN_TABLE;
-            }
-
-        /* A start tag whose tag name is one of: "caption", "col", "colgroup",
-        "tbody", "tfoot", "thead", or an end tag whose tag name is "table" */
-        } elseif(($token['type'] === HTML5::STARTTAG && in_array($token['name'],
-        array('caption', 'col', 'colgroup', 'tbody', 'tfoor', 'thead'))) ||
-        ($token['type'] === HTML5::STARTTAG && $token['name'] === 'table')) {
-            /* If the stack of open elements does not have a tbody, thead, or
-            tfoot element in table scope, this is a parse error. Ignore the
-            token. (innerHTML case) */
-            if(!$this->elementInScope(array('tbody', 'thead', 'tfoot'), true)) {
-                // Ignore.
-
-            /* Otherwise: */
-            } else {
-                /* Clear the stack back to a table body context. */
-                $this->clearStackToTableContext($clear);
-
-                /* Act as if an end tag with the same tag name as the current
-                node ("tbody", "tfoot", or "thead") had been seen, then
-                reprocess the current token. */
-                $this->inTableBody(array(
-                    'name' => end($this->stack)->nodeName,
-                    'type' => HTML5::ENDTAG
-                ));
-
-                return $this->mainPhase($token);
-            }
-
-        /* An end tag whose tag name is one of: "body", "caption", "col",
-        "colgroup", "html", "td", "th", "tr" */
-        } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'],
-        array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr'))) {
-            /* Parse error. Ignore the token. */
-
-        /* Anything else */
-        } else {
-            /* Process the token as if the insertion mode was "in table". */
-            $this->inTable($token);
-        }
-    }
-
-    private function inRow($token) {
-        $clear = array('tr', 'html');
-
-        /* A start tag whose tag name is one of: "th", "td" */
-        if($token['type'] === HTML5::STARTTAG &&
-        ($token['name'] === 'th' || $token['name'] === 'td')) {
-            /* Clear the stack back to a table row context. */
-            $this->clearStackToTableContext($clear);
-
-            /* Insert an HTML element for the token, then switch the insertion
-            mode to "in cell". */
-            $this->insertElement($token);
-            $this->mode = self::IN_CELL;
-
-            /* Insert a marker at the end of the list of active formatting
-            elements. */
-            $this->a_formatting[] = self::MARKER;
-
-        /* An end tag whose tag name is "tr" */
-        } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'tr') {
-            /* If the stack of open elements does not have an element in table
-            scope with the same tag name as the token, this is a parse error.
-            Ignore the token. (innerHTML case) */
-            if(!$this->elementInScope($token['name'], true)) {
-                // Ignore.
-
-            /* Otherwise: */
-            } else {
-                /* Clear the stack back to a table row context. */
-                $this->clearStackToTableContext($clear);
-
-                /* Pop the current node (which will be a tr element) from the
-                stack of open elements. Switch the insertion mode to "in table
-                body". */
-                array_pop($this->stack);
-                $this->mode = self::IN_TBODY;
-            }
-
-        /* A start tag whose tag name is one of: "caption", "col", "colgroup",
-        "tbody", "tfoot", "thead", "tr" or an end tag whose tag name is "table" */
-        } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'],
-        array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr'))) {
-            /* Act as if an end tag with the tag name "tr" had been seen, then,
-            if that token wasn't ignored, reprocess the current token. */
-            $this->inRow(array(
-                'name' => 'tr',
-                'type' => HTML5::ENDTAG
-            ));
-
-            return $this->inCell($token);
-
-        /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
-        } elseif($token['type'] === HTML5::ENDTAG &&
-        in_array($token['name'], array('tbody', 'tfoot', 'thead'))) {
-            /* If the stack of open elements does not have an element in table
-            scope with the same tag name as the token, this is a parse error.
-            Ignore the token. */
-            if(!$this->elementInScope($token['name'], true)) {
-                // Ignore.
-
-            /* Otherwise: */
-            } else {
-                /* Otherwise, act as if an end tag with the tag name "tr" had
-                been seen, then reprocess the current token. */
-                $this->inRow(array(
-                    'name' => 'tr',
-                    'type' => HTML5::ENDTAG
-                ));
-
-                return $this->inCell($token);
-            }
-
-        /* An end tag whose tag name is one of: "body", "caption", "col",
-        "colgroup", "html", "td", "th" */
-        } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'],
-        array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr'))) {
-            /* Parse error. Ignore the token. */
-
-        /* Anything else */
-        } else {
-            /* Process the token as if the insertion mode was "in table". */
-            $this->inTable($token);
-        }
-    }
-
-    private function inCell($token) {
-        /* An end tag whose tag name is one of: "td", "th" */
-        if($token['type'] === HTML5::ENDTAG &&
-        ($token['name'] === 'td' || $token['name'] === 'th')) {
-            /* If the stack of open elements does not have an element in table
-            scope with the same tag name as that of the token, then this is a
-            parse error and the token must be ignored. */
-            if(!$this->elementInScope($token['name'], true)) {
-                // Ignore.
-
-            /* Otherwise: */
-            } else {
-                /* Generate implied end tags, except for elements with the same
-                tag name as the token. */
-                $this->generateImpliedEndTags(array($token['name']));
-
-                /* Now, if the current node is not an element with the same tag
-                name as the token, then this is a parse error. */
-                // k
-
-                /* Pop elements from this stack until an element with the same
-                tag name as the token has been popped from the stack. */
-                while(true) {
-                    $node = end($this->stack)->nodeName;
-                    array_pop($this->stack);
-
-                    if($node === $token['name']) {
-                        break;
-                    }
-                }
-
-                /* Clear the list of active formatting elements up to the last
-                marker. */
-                $this->clearTheActiveFormattingElementsUpToTheLastMarker();
-
-                /* Switch the insertion mode to "in row". (The current node
-                will be a tr element at this point.) */
-                $this->mode = self::IN_ROW;
-            }
-
-        /* A start tag whose tag name is one of: "caption", "col", "colgroup",
-        "tbody", "td", "tfoot", "th", "thead", "tr" */
-        } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'],
-        array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
-        'thead', 'tr'))) {
-            /* If the stack of open elements does not have a td or th element
-            in table scope, then this is a parse error; ignore the token.
-            (innerHTML case) */
-            if(!$this->elementInScope(array('td', 'th'), true)) {
-                // Ignore.
-
-            /* Otherwise, close the cell (see below) and reprocess the current
-            token. */
-            } else {
-                $this->closeCell();
-                return $this->inRow($token);
-            }
-
-        /* A start tag whose tag name is one of: "caption", "col", "colgroup",
-        "tbody", "td", "tfoot", "th", "thead", "tr" */
-        } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'],
-        array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
-        'thead', 'tr'))) {
-            /* If the stack of open elements does not have a td or th element
-            in table scope, then this is a parse error; ignore the token.
-            (innerHTML case) */
-            if(!$this->elementInScope(array('td', 'th'), true)) {
-                // Ignore.
-
-            /* Otherwise, close the cell (see below) and reprocess the current
-            token. */
-            } else {
-                $this->closeCell();
-                return $this->inRow($token);
-            }
-
-        /* An end tag whose tag name is one of: "body", "caption", "col",
-        "colgroup", "html" */
-        } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'],
-        array('body', 'caption', 'col', 'colgroup', 'html'))) {
-            /* Parse error. Ignore the token. */
-
-        /* An end tag whose tag name is one of: "table", "tbody", "tfoot",
-        "thead", "tr" */
-        } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'],
-        array('table', 'tbody', 'tfoot', 'thead', 'tr'))) {
-            /* If the stack of open elements does not have an element in table
-            scope with the same tag name as that of the token (which can only
-            happen for "tbody", "tfoot" and "thead", or, in the innerHTML case),
-            then this is a parse error and the token must be ignored. */
-            if(!$this->elementInScope($token['name'], true)) {
-                // Ignore.
-
-            /* Otherwise, close the cell (see below) and reprocess the current
-            token. */
-            } else {
-                $this->closeCell();
-                return $this->inRow($token);
-            }
-
-        /* Anything else */
-        } else {
-            /* Process the token as if the insertion mode was "in body". */
-            $this->inBody($token);
-        }
-    }
-
-    private function inSelect($token) {
-        /* Handle the token as follows: */
-
-        /* A character token */
-        if($token['type'] === HTML5::CHARACTR) {
-            /* Append the token's character to the current node. */
-            $this->insertText($token['data']);
-
-        /* A comment token */
-        } elseif($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the current node with the data
-            attribute set to the data given in the comment token. */
-            $this->insertComment($token['data']);
-
-        /* A start tag token whose tag name is "option" */
-        } elseif($token['type'] === HTML5::STARTTAG &&
-        $token['name'] === 'option') {
-            /* If the current node is an option element, act as if an end tag
-            with the tag name "option" had been seen. */
-            if(end($this->stack)->nodeName === 'option') {
-                $this->inSelect(array(
-                    'name' => 'option',
-                    'type' => HTML5::ENDTAG
-                ));
-            }
-
-            /* Insert an HTML element for the token. */
-            $this->insertElement($token);
-
-        /* A start tag token whose tag name is "optgroup" */
-        } elseif($token['type'] === HTML5::STARTTAG &&
-        $token['name'] === 'optgroup') {
-            /* If the current node is an option element, act as if an end tag
-            with the tag name "option" had been seen. */
-            if(end($this->stack)->nodeName === 'option') {
-                $this->inSelect(array(
-                    'name' => 'option',
-                    'type' => HTML5::ENDTAG
-                ));
-            }
-
-            /* If the current node is an optgroup element, act as if an end tag
-            with the tag name "optgroup" had been seen. */
-            if(end($this->stack)->nodeName === 'optgroup') {
-                $this->inSelect(array(
-                    'name' => 'optgroup',
-                    'type' => HTML5::ENDTAG
-                ));
-            }
-
-            /* Insert an HTML element for the token. */
-            $this->insertElement($token);
-
-        /* An end tag token whose tag name is "optgroup" */
-        } elseif($token['type'] === HTML5::ENDTAG &&
-        $token['name'] === 'optgroup') {
-            /* First, if the current node is an option element, and the node
-            immediately before it in the stack of open elements is an optgroup
-            element, then act as if an end tag with the tag name "option" had
-            been seen. */
-            $elements_in_stack = count($this->stack);
-
-            if($this->stack[$elements_in_stack - 1]->nodeName === 'option' &&
-            $this->stack[$elements_in_stack - 2]->nodeName === 'optgroup') {
-                $this->inSelect(array(
-                    'name' => 'option',
-                    'type' => HTML5::ENDTAG
-                ));
-            }
-
-            /* If the current node is an optgroup element, then pop that node
-            from the stack of open elements. Otherwise, this is a parse error,
-            ignore the token. */
-            if($this->stack[$elements_in_stack - 1] === 'optgroup') {
-                array_pop($this->stack);
-            }
-
-        /* An end tag token whose tag name is "option" */
-        } elseif($token['type'] === HTML5::ENDTAG &&
-        $token['name'] === 'option') {
-            /* If the current node is an option element, then pop that node
-            from the stack of open elements. Otherwise, this is a parse error,
-            ignore the token. */
-            if(end($this->stack)->nodeName === 'option') {
-                array_pop($this->stack);
-            }
-
-        /* An end tag whose tag name is "select" */
-        } elseif($token['type'] === HTML5::ENDTAG &&
-        $token['name'] === 'select') {
-            /* If the stack of open elements does not have an element in table
-            scope with the same tag name as the token, this is a parse error.
-            Ignore the token. (innerHTML case) */
-            if(!$this->elementInScope($token['name'], true)) {
-                // w/e
-
-            /* Otherwise: */
-            } else {
-                /* Pop elements from the stack of open elements until a select
-                element has been popped from the stack. */
-                while(true) {
-                    $current = end($this->stack)->nodeName;
-                    array_pop($this->stack);
-
-                    if($current === 'select') {
-                        break;
-                    }
-                }
-
-                /* Reset the insertion mode appropriately. */
-                $this->resetInsertionMode();
-            }
-
-        /* A start tag whose tag name is "select" */
-        } elseif($token['name'] === 'select' &&
-        $token['type'] === HTML5::STARTTAG) {
-            /* Parse error. Act as if the token had been an end tag with the
-            tag name "select" instead. */
-            $this->inSelect(array(
-                'name' => 'select',
-                'type' => HTML5::ENDTAG
-            ));
-
-        /* An end tag whose tag name is one of: "caption", "table", "tbody",
-        "tfoot", "thead", "tr", "td", "th" */
-        } elseif(in_array($token['name'], array('caption', 'table', 'tbody',
-        'tfoot', 'thead', 'tr', 'td', 'th')) && $token['type'] === HTML5::ENDTAG) {
-            /* Parse error. */
-            // w/e
-
-            /* If the stack of open elements has an element in table scope with
-            the same tag name as that of the token, then act as if an end tag
-            with the tag name "select" had been seen, and reprocess the token.
-            Otherwise, ignore the token. */
-            if($this->elementInScope($token['name'], true)) {
-                $this->inSelect(array(
-                    'name' => 'select',
-                    'type' => HTML5::ENDTAG
-                ));
-
-                $this->mainPhase($token);
-            }
-
-        /* Anything else */
-        } else {
-            /* Parse error. Ignore the token. */
-        }
-    }
-
-    private function afterBody($token) {
-        /* Handle the token as follows: */
-
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        or U+0020 SPACE */
-        if($token['type'] === HTML5::CHARACTR &&
-        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
-            /* Process the token as it would be processed if the insertion mode
-            was "in body". */
-            $this->inBody($token);
-
-        /* A comment token */
-        } elseif($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the first element in the stack of open
-            elements (the html element), with the data attribute set to the
-            data given in the comment token. */
-            $comment = $this->dom->createComment($token['data']);
-            $this->stack[0]->appendChild($comment);
-
-        /* An end tag with the tag name "html" */
-        } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') {
-            /* If the parser was originally created in order to handle the
-            setting of an element's innerHTML attribute, this is a parse error;
-            ignore the token. (The element will be an html element in this
-            case.) (innerHTML case) */
-
-            /* Otherwise, switch to the trailing end phase. */
-            $this->phase = self::END_PHASE;
-
-        /* Anything else */
-        } else {
-            /* Parse error. Set the insertion mode to "in body" and reprocess
-            the token. */
-            $this->mode = self::IN_BODY;
-            return $this->inBody($token);
-        }
-    }
-
-    private function inFrameset($token) {
-        /* Handle the token as follows: */
-
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */
-        if($token['type'] === HTML5::CHARACTR &&
-        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
-            /* Append the character to the current node. */
-            $this->insertText($token['data']);
-
-        /* A comment token */
-        } elseif($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the current node with the data
-            attribute set to the data given in the comment token. */
-            $this->insertComment($token['data']);
-
-        /* A start tag with the tag name "frameset" */
-        } elseif($token['name'] === 'frameset' &&
-        $token['type'] === HTML5::STARTTAG) {
-            $this->insertElement($token);
-
-        /* An end tag with the tag name "frameset" */
-        } elseif($token['name'] === 'frameset' &&
-        $token['type'] === HTML5::ENDTAG) {
-            /* If the current node is the root html element, then this is a
-            parse error; ignore the token. (innerHTML case) */
-            if(end($this->stack)->nodeName === 'html') {
-                // Ignore
-
-            } else {
-                /* Otherwise, pop the current node from the stack of open
-                elements. */
-                array_pop($this->stack);
-
-                /* If the parser was not originally created in order to handle
-                the setting of an element's innerHTML attribute (innerHTML case),
-                and the current node is no longer a frameset element, then change
-                the insertion mode to "after frameset". */
-                $this->mode = self::AFTR_FRAME;
-            }
-
-        /* A start tag with the tag name "frame" */
-        } elseif($token['name'] === 'frame' &&
-        $token['type'] === HTML5::STARTTAG) {
-            /* Insert an HTML element for the token. */
-            $this->insertElement($token);
-
-            /* Immediately pop the current node off the stack of open elements. */
-            array_pop($this->stack);
-
-        /* A start tag with the tag name "noframes" */
-        } elseif($token['name'] === 'noframes' &&
-        $token['type'] === HTML5::STARTTAG) {
-            /* Process the token as if the insertion mode had been "in body". */
-            $this->inBody($token);
-
-        /* Anything else */
-        } else {
-            /* Parse error. Ignore the token. */
-        }
-    }
-
-    private function afterFrameset($token) {
-        /* Handle the token as follows: */
-
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */
-        if($token['type'] === HTML5::CHARACTR &&
-        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
-            /* Append the character to the current node. */
-            $this->insertText($token['data']);
-
-        /* A comment token */
-        } elseif($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the current node with the data
-            attribute set to the data given in the comment token. */
-            $this->insertComment($token['data']);
-
-        /* An end tag with the tag name "html" */
-        } elseif($token['name'] === 'html' &&
-        $token['type'] === HTML5::ENDTAG) {
-            /* Switch to the trailing end phase. */
-            $this->phase = self::END_PHASE;
-
-        /* A start tag with the tag name "noframes" */
-        } elseif($token['name'] === 'noframes' &&
-        $token['type'] === HTML5::STARTTAG) {
-            /* Process the token as if the insertion mode had been "in body". */
-            $this->inBody($token);
-
-        /* Anything else */
-        } else {
-            /* Parse error. Ignore the token. */
-        }
-    }
-
-    private function trailingEndPhase($token) {
-        /* After the main phase, as each token is emitted from the tokenisation
-        stage, it must be processed as described in this section. */
-
-        /* A DOCTYPE token */
-        if($token['type'] === HTML5::DOCTYPE) {
-            // Parse error. Ignore the token.
-
-        /* A comment token */
-        } elseif($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the Document object with the data
-            attribute set to the data given in the comment token. */
-            $comment = $this->dom->createComment($token['data']);
-            $this->dom->appendChild($comment);
-
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        or U+0020 SPACE */
-        } elseif($token['type'] === HTML5::CHARACTR &&
-        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) {
-            /* Process the token as it would be processed in the main phase. */
-            $this->mainPhase($token);
-
-        /* A character token that is not one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        or U+0020 SPACE. Or a start tag token. Or an end tag token. */
-        } elseif(($token['type'] === HTML5::CHARACTR &&
-        preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) ||
-        $token['type'] === HTML5::STARTTAG || $token['type'] === HTML5::ENDTAG) {
-            /* Parse error. Switch back to the main phase and reprocess the
-            token. */
-            $this->phase = self::MAIN_PHASE;
-            return $this->mainPhase($token);
-
-        /* An end-of-file token */
-        } elseif($token['type'] === HTML5::EOF) {
-            /* OMG DONE!! */
-        }
-    }
-
-    private function insertElement($token, $append = true, $check = false) {
-        // Proprietary workaround for libxml2's limitations with tag names
-        if ($check) {
-            // Slightly modified HTML5 tag-name modification,
-            // removing anything that's not an ASCII letter, digit, or hyphen
-            $token['name'] = preg_replace('/[^a-z0-9-]/i', '', $token['name']);
-            // Remove leading hyphens and numbers
-            $token['name'] = ltrim($token['name'], '-0..9');
-            // In theory, this should ever be needed, but just in case
-            if ($token['name'] === '') $token['name'] = 'span'; // arbitrary generic choice
-        }
-        
-        $el = $this->dom->createElement($token['name']);
-
-        foreach($token['attr'] as $attr) {
-            if(!$el->hasAttribute($attr['name'])) {
-                $el->setAttribute($attr['name'], $attr['value']);
-            }
-        }
-
-        $this->appendToRealParent($el);
-        $this->stack[] = $el;
-
-        return $el;
-    }
-
-    private function insertText($data) {
-        $text = $this->dom->createTextNode($data);
-        $this->appendToRealParent($text);
-    }
-
-    private function insertComment($data) {
-        $comment = $this->dom->createComment($data);
-        $this->appendToRealParent($comment);
-    }
-
-    private function appendToRealParent($node) {
-        if($this->foster_parent === null) {
-            end($this->stack)->appendChild($node);
-
-        } elseif($this->foster_parent !== null) {
-            /* If the foster parent element is the parent element of the
-            last table element in the stack of open elements, then the new
-            node must be inserted immediately before the last table element
-            in the stack of open elements in the foster parent element;
-            otherwise, the new node must be appended to the foster parent
-            element. */
-            for($n = count($this->stack) - 1; $n >= 0; $n--) {
-                if($this->stack[$n]->nodeName === 'table' &&
-                $this->stack[$n]->parentNode !== null) {
-                    $table = $this->stack[$n];
-                    break;
-                }
-            }
-
-            if(isset($table) && $this->foster_parent->isSameNode($table->parentNode))
-                $this->foster_parent->insertBefore($node, $table);
-            else
-                $this->foster_parent->appendChild($node);
-
-            $this->foster_parent = null;
-        }
-    }
-
-    private function elementInScope($el, $table = false) {
-        if(is_array($el)) {
-            foreach($el as $element) {
-                if($this->elementInScope($element, $table)) {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
-        $leng = count($this->stack);
-
-        for($n = 0; $n < $leng; $n++) {
-            /* 1. Initialise node to be the current node (the bottommost node of
-            the stack). */
-            $node = $this->stack[$leng - 1 - $n];
-
-            if($node->tagName === $el) {
-                /* 2. If node is the target node, terminate in a match state. */
-                return true;
-
-            } elseif($node->tagName === 'table') {
-                /* 3. Otherwise, if node is a table element, terminate in a failure
-                state. */
-                return false;
-
-            } elseif($table === true && in_array($node->tagName, array('caption', 'td',
-            'th', 'button', 'marquee', 'object'))) {
-                /* 4. Otherwise, if the algorithm is the "has an element in scope"
-                variant (rather than the "has an element in table scope" variant),
-                and node is one of the following, terminate in a failure state. */
-                return false;
-
-            } elseif($node === $node->ownerDocument->documentElement) {
-                /* 5. Otherwise, if node is an html element (root element), terminate
-                in a failure state. (This can only happen if the node is the topmost
-                node of the    stack of open elements, and prevents the next step from
-                being invoked if there are no more elements in the stack.) */
-                return false;
-            }
-
-            /* Otherwise, set node to the previous entry in the stack of open
-            elements and return to step 2. (This will never fail, since the loop
-            will always terminate in the previous step if the top of the stack
-            is reached.) */
-        }
-    }
-
-    private function reconstructActiveFormattingElements() {
-        /* 1. If there are no entries in the list of active formatting elements,
-        then there is nothing to reconstruct; stop this algorithm. */
-        $formatting_elements = count($this->a_formatting);
-
-        if($formatting_elements === 0) {
-            return false;
-        }
-
-        /* 3. Let entry be the last (most recently added) element in the list
-        of active formatting elements. */
-        $entry = end($this->a_formatting);
-
-        /* 2. If the last (most recently added) entry in the list of active
-        formatting elements is a marker, or if it is an element that is in the
-        stack of open elements, then there is nothing to reconstruct; stop this
-        algorithm. */
-        if($entry === self::MARKER || in_array($entry, $this->stack, true)) {
-            return false;
-        }
-
-        for($a = $formatting_elements - 1; $a >= 0; true) {
-            /* 4. If there are no entries before entry in the list of active
-            formatting elements, then jump to step 8. */
-            if($a === 0) {
-                $step_seven = false;
-                break;
-            }
-
-            /* 5. Let entry be the entry one earlier than entry in the list of
-            active formatting elements. */
-            $a--;
-            $entry = $this->a_formatting[$a];
-
-            /* 6. If entry is neither a marker nor an element that is also in
-            thetack of open elements, go to step 4. */
-            if($entry === self::MARKER || in_array($entry, $this->stack, true)) {
-                break;
-            }
-        }
-
-        while(true) {
-            /* 7. Let entry be the element one later than entry in the list of
-            active formatting elements. */
-            if(isset($step_seven) && $step_seven === true) {
-                $a++;
-                $entry = $this->a_formatting[$a];
-            }
-
-            /* 8. Perform a shallow clone of the element entry to obtain clone. */
-            $clone = $entry->cloneNode();
-
-            /* 9. Append clone to the current node and push it onto the stack
-            of open elements  so that it is the new current node. */
-            end($this->stack)->appendChild($clone);
-            $this->stack[] = $clone;
-
-            /* 10. Replace the entry for entry in the list with an entry for
-            clone. */
-            $this->a_formatting[$a] = $clone;
-
-            /* 11. If the entry for clone in the list of active formatting
-            elements is not the last entry in the list, return to step 7. */
-            if(end($this->a_formatting) !== $clone) {
-                $step_seven = true;
-            } else {
-                break;
-            }
-        }
-    }
-
-    private function clearTheActiveFormattingElementsUpToTheLastMarker() {
-        /* When the steps below require the UA to clear the list of active
-        formatting elements up to the last marker, the UA must perform the
-        following steps: */
-
-        while(true) {
-            /* 1. Let entry be the last (most recently added) entry in the list
-            of active formatting elements. */
-            $entry = end($this->a_formatting);
-
-            /* 2. Remove entry from the list of active formatting elements. */
-            array_pop($this->a_formatting);
-
-            /* 3. If entry was a marker, then stop the algorithm at this point.
-            The list has been cleared up to the last marker. */
-            if($entry === self::MARKER) {
-                break;
-            }
-        }
-    }
-
-    private function generateImpliedEndTags($exclude = array()) {
-        /* When the steps below require the UA to generate implied end tags,
-        then, if the current node is a dd element, a dt element, an li element,
-        a p element, a td element, a th  element, or a tr element, the UA must
-        act as if an end tag with the respective tag name had been seen and
-        then generate implied end tags again. */
-        $node = end($this->stack);
-        $elements = array_diff(array('dd', 'dt', 'li', 'p', 'td', 'th', 'tr'), $exclude);
-
-        while(in_array(end($this->stack)->nodeName, $elements)) {
-            array_pop($this->stack);
-        }
-    }
-
-    private function getElementCategory($node) {
-        $name = $node->tagName;
-        if(in_array($name, $this->special))
-            return self::SPECIAL;
-
-        elseif(in_array($name, $this->scoping))
-            return self::SCOPING;
-
-        elseif(in_array($name, $this->formatting))
-            return self::FORMATTING;
-
-        else
-            return self::PHRASING;
-    }
-
-    private function clearStackToTableContext($elements) {
-        /* When the steps above require the UA to clear the stack back to a
-        table context, it means that the UA must, while the current node is not
-        a table element or an html element, pop elements from the stack of open
-        elements. If this causes any elements to be popped from the stack, then
-        this is a parse error. */
-        while(true) {
-            $node = end($this->stack)->nodeName;
-
-            if(in_array($node, $elements)) {
-                break;
-            } else {
-                array_pop($this->stack);
-            }
-        }
-    }
-
-    private function resetInsertionMode() {
-        /* 1. Let last be false. */
-        $last = false;
-        $leng = count($this->stack);
-
-        for($n = $leng - 1; $n >= 0; $n--) {
-            /* 2. Let node be the last node in the stack of open elements. */
-            $node = $this->stack[$n];
-
-            /* 3. If node is the first node in the stack of open elements, then
-            set last to true. If the element whose innerHTML  attribute is being
-            set is neither a td  element nor a th element, then set node to the
-            element whose innerHTML  attribute is being set. (innerHTML  case) */
-            if($this->stack[0]->isSameNode($node)) {
-                $last = true;
-            }
-
-            /* 4. If node is a select element, then switch the insertion mode to
-            "in select" and abort these steps. (innerHTML case) */
-            if($node->nodeName === 'select') {
-                $this->mode = self::IN_SELECT;
-                break;
-
-            /* 5. If node is a td or th element, then switch the insertion mode
-            to "in cell" and abort these steps. */
-            } elseif($node->nodeName === 'td' || $node->nodeName === 'th') {
-                $this->mode = self::IN_CELL;
-                break;
-
-            /* 6. If node is a tr element, then switch the insertion mode to
-            "in    row" and abort these steps. */
-            } elseif($node->nodeName === 'tr') {
-                $this->mode = self::IN_ROW;
-                break;
-
-            /* 7. If node is a tbody, thead, or tfoot element, then switch the
-            insertion mode to "in table body" and abort these steps. */
-            } elseif(in_array($node->nodeName, array('tbody', 'thead', 'tfoot'))) {
-                $this->mode = self::IN_TBODY;
-                break;
-
-            /* 8. If node is a caption element, then switch the insertion mode
-            to "in caption" and abort these steps. */
-            } elseif($node->nodeName === 'caption') {
-                $this->mode = self::IN_CAPTION;
-                break;
-
-            /* 9. If node is a colgroup element, then switch the insertion mode
-            to "in column group" and abort these steps. (innerHTML case) */
-            } elseif($node->nodeName === 'colgroup') {
-                $this->mode = self::IN_CGROUP;
-                break;
-
-            /* 10. If node is a table element, then switch the insertion mode
-            to "in table" and abort these steps. */
-            } elseif($node->nodeName === 'table') {
-                $this->mode = self::IN_TABLE;
-                break;
-
-            /* 11. If node is a head element, then switch the insertion mode
-            to "in body" ("in body"! not "in head"!) and abort these steps.
-            (innerHTML case) */
-            } elseif($node->nodeName === 'head') {
-                $this->mode = self::IN_BODY;
-                break;
-
-            /* 12. If node is a body element, then switch the insertion mode to
-            "in body" and abort these steps. */
-            } elseif($node->nodeName === 'body') {
-                $this->mode = self::IN_BODY;
-                break;
-
-            /* 13. If node is a frameset element, then switch the insertion
-            mode to "in frameset" and abort these steps. (innerHTML case) */
-            } elseif($node->nodeName === 'frameset') {
-                $this->mode = self::IN_FRAME;
-                break;
-
-            /* 14. If node is an html element, then: if the head element
-            pointer is null, switch the insertion mode to "before head",
-            otherwise, switch the insertion mode to "after head". In either
-            case, abort these steps. (innerHTML case) */
-            } elseif($node->nodeName === 'html') {
-                $this->mode = ($this->head_pointer === null)
-                    ? self::BEFOR_HEAD
-                    : self::AFTER_HEAD;
-
-                break;
-
-            /* 15. If last is true, then set the insertion mode to "in body"
-            and    abort these steps. (innerHTML case) */
-            } elseif($last) {
-                $this->mode = self::IN_BODY;
-                break;
-            }
-        }
-    }
-
-    private function closeCell() {
-        /* If the stack of open elements has a td or th element in table scope,
-        then act as if an end tag token with that tag name had been seen. */
-        foreach(array('td', 'th') as $cell) {
-            if($this->elementInScope($cell, true)) {
-                $this->inCell(array(
-                    'name' => $cell,
-                    'type' => HTML5::ENDTAG
-                ));
-
-                break;
-            }
-        }
-    }
-
-    public function save() {
-        return $this->dom;
-    }
-}
-?>
diff --git a/library/HTMLPurifier/PercentEncoder.php b/library/HTMLPurifier/PercentEncoder.php
deleted file mode 100644 (file)
index a43c44f..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-<?php
-
-/**
- * Class that handles operations involving percent-encoding in URIs.
- *
- * @warning
- *      Be careful when reusing instances of PercentEncoder. The object
- *      you use for normalize() SHOULD NOT be used for encode(), or
- *      vice-versa.
- */
-class HTMLPurifier_PercentEncoder
-{
-
-    /**
-     * Reserved characters to preserve when using encode().
-     */
-    protected $preserve = array();
-
-    /**
-     * String of characters that should be preserved while using encode().
-     */
-    public function __construct($preserve = false) {
-        // unreserved letters, ought to const-ify
-        for ($i = 48; $i <= 57;  $i++) $this->preserve[$i] = true; // digits
-        for ($i = 65; $i <= 90;  $i++) $this->preserve[$i] = true; // upper-case
-        for ($i = 97; $i <= 122; $i++) $this->preserve[$i] = true; // lower-case
-        $this->preserve[45] = true; // Dash         -
-        $this->preserve[46] = true; // Period       .
-        $this->preserve[95] = true; // Underscore   _
-        $this->preserve[126]= true; // Tilde        ~
-
-        // extra letters not to escape
-        if ($preserve !== false) {
-            for ($i = 0, $c = strlen($preserve); $i < $c; $i++) {
-                $this->preserve[ord($preserve[$i])] = true;
-            }
-        }
-    }
-
-    /**
-     * Our replacement for urlencode, it encodes all non-reserved characters,
-     * as well as any extra characters that were instructed to be preserved.
-     * @note
-     *      Assumes that the string has already been normalized, making any
-     *      and all percent escape sequences valid. Percents will not be
-     *      re-escaped, regardless of their status in $preserve
-     * @param $string String to be encoded
-     * @return Encoded string.
-     */
-    public function encode($string) {
-        $ret = '';
-        for ($i = 0, $c = strlen($string); $i < $c; $i++) {
-            if ($string[$i] !== '%' && !isset($this->preserve[$int = ord($string[$i])]) ) {
-                $ret .= '%' . sprintf('%02X', $int);
-            } else {
-                $ret .= $string[$i];
-            }
-        }
-        return $ret;
-    }
-
-    /**
-     * Fix up percent-encoding by decoding unreserved characters and normalizing.
-     * @warning This function is affected by $preserve, even though the
-     *          usual desired behavior is for this not to preserve those
-     *          characters. Be careful when reusing instances of PercentEncoder!
-     * @param $string String to normalize
-     */
-    public function normalize($string) {
-        if ($string == '') return '';
-        $parts = explode('%', $string);
-        $ret = array_shift($parts);
-        foreach ($parts as $part) {
-            $length = strlen($part);
-            if ($length < 2) {
-                $ret .= '%25' . $part;
-                continue;
-            }
-            $encoding = substr($part, 0, 2);
-            $text     = substr($part, 2);
-            if (!ctype_xdigit($encoding)) {
-                $ret .= '%25' . $part;
-                continue;
-            }
-            $int = hexdec($encoding);
-            if (isset($this->preserve[$int])) {
-                $ret .= chr($int) . $text;
-                continue;
-            }
-            $encoding = strtoupper($encoding);
-            $ret .= '%' . $encoding . $text;
-        }
-        return $ret;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Printer.php b/library/HTMLPurifier/Printer.php
deleted file mode 100644 (file)
index e7eb82e..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-<?php
-
-// OUT OF DATE, NEEDS UPDATING!
-// USE XMLWRITER!
-
-class HTMLPurifier_Printer
-{
-
-    /**
-     * Instance of HTMLPurifier_Generator for HTML generation convenience funcs
-     */
-    protected $generator;
-
-    /**
-     * Instance of HTMLPurifier_Config, for easy access
-     */
-    protected $config;
-
-    /**
-     * Initialize $generator.
-     */
-    public function __construct() {
-    }
-
-    /**
-     * Give generator necessary configuration if possible
-     */
-    public function prepareGenerator($config) {
-        $all = $config->getAll();
-        $context = new HTMLPurifier_Context();
-        $this->generator = new HTMLPurifier_Generator($config, $context);
-    }
-
-    /**
-     * Main function that renders object or aspect of that object
-     * @note Parameters vary depending on printer
-     */
-    // function render() {}
-
-    /**
-     * Returns a start tag
-     * @param $tag Tag name
-     * @param $attr Attribute array
-     */
-    protected function start($tag, $attr = array()) {
-        return $this->generator->generateFromToken(
-                    new HTMLPurifier_Token_Start($tag, $attr ? $attr : array())
-               );
-    }
-
-    /**
-     * Returns an end teg
-     * @param $tag Tag name
-     */
-    protected function end($tag) {
-        return $this->generator->generateFromToken(
-                    new HTMLPurifier_Token_End($tag)
-               );
-    }
-
-    /**
-     * Prints a complete element with content inside
-     * @param $tag Tag name
-     * @param $contents Element contents
-     * @param $attr Tag attributes
-     * @param $escape Bool whether or not to escape contents
-     */
-    protected function element($tag, $contents, $attr = array(), $escape = true) {
-        return $this->start($tag, $attr) .
-               ($escape ? $this->escape($contents) : $contents) .
-               $this->end($tag);
-    }
-
-    protected function elementEmpty($tag, $attr = array()) {
-        return $this->generator->generateFromToken(
-            new HTMLPurifier_Token_Empty($tag, $attr)
-        );
-    }
-
-    protected function text($text) {
-        return $this->generator->generateFromToken(
-            new HTMLPurifier_Token_Text($text)
-        );
-    }
-
-    /**
-     * Prints a simple key/value row in a table.
-     * @param $name Key
-     * @param $value Value
-     */
-    protected function row($name, $value) {
-        if (is_bool($value)) $value = $value ? 'On' : 'Off';
-        return
-            $this->start('tr') . "\n" .
-                $this->element('th', $name) . "\n" .
-                $this->element('td', $value) . "\n" .
-            $this->end('tr')
-        ;
-    }
-
-    /**
-     * Escapes a string for HTML output.
-     * @param $string String to escape
-     */
-    protected function escape($string) {
-        $string = HTMLPurifier_Encoder::cleanUTF8($string);
-        $string = htmlspecialchars($string, ENT_COMPAT, 'UTF-8');
-        return $string;
-    }
-
-    /**
-     * Takes a list of strings and turns them into a single list
-     * @param $array List of strings
-     * @param $polite Bool whether or not to add an end before the last
-     */
-    protected function listify($array, $polite = false) {
-        if (empty($array)) return 'None';
-        $ret = '';
-        $i = count($array);
-        foreach ($array as $value) {
-            $i--;
-            $ret .= $value;
-            if ($i > 0 && !($polite && $i == 1)) $ret .= ', ';
-            if ($polite && $i == 1) $ret .= 'and ';
-        }
-        return $ret;
-    }
-
-    /**
-     * Retrieves the class of an object without prefixes, as well as metadata
-     * @param $obj Object to determine class of
-     * @param $prefix Further prefix to remove
-     */
-    protected function getClass($obj, $sec_prefix = '') {
-        static $five = null;
-        if ($five === null) $five = version_compare(PHP_VERSION, '5', '>=');
-        $prefix = 'HTMLPurifier_' . $sec_prefix;
-        if (!$five) $prefix = strtolower($prefix);
-        $class = str_replace($prefix, '', get_class($obj));
-        $lclass = strtolower($class);
-        $class .= '(';
-        switch ($lclass) {
-            case 'enum':
-                $values = array();
-                foreach ($obj->valid_values as $value => $bool) {
-                    $values[] = $value;
-                }
-                $class .= implode(', ', $values);
-                break;
-            case 'css_composite':
-                $values = array();
-                foreach ($obj->defs as $def) {
-                    $values[] = $this->getClass($def, $sec_prefix);
-                }
-                $class .= implode(', ', $values);
-                break;
-            case 'css_multiple':
-                $class .= $this->getClass($obj->single, $sec_prefix) . ', ';
-                $class .= $obj->max;
-                break;
-            case 'css_denyelementdecorator':
-                $class .= $this->getClass($obj->def, $sec_prefix) . ', ';
-                $class .= $obj->element;
-                break;
-            case 'css_importantdecorator':
-                $class .= $this->getClass($obj->def, $sec_prefix);
-                if ($obj->allow) $class .= ', !important';
-                break;
-        }
-        $class .= ')';
-        return $class;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Printer/CSSDefinition.php b/library/HTMLPurifier/Printer/CSSDefinition.php
deleted file mode 100644 (file)
index 81f9865..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-class HTMLPurifier_Printer_CSSDefinition extends HTMLPurifier_Printer
-{
-
-    protected $def;
-
-    public function render($config) {
-        $this->def = $config->getCSSDefinition();
-        $ret = '';
-
-        $ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer'));
-        $ret .= $this->start('table');
-
-        $ret .= $this->element('caption', 'Properties ($info)');
-
-        $ret .= $this->start('thead');
-        $ret .= $this->start('tr');
-        $ret .= $this->element('th', 'Property', array('class' => 'heavy'));
-        $ret .= $this->element('th', 'Definition', array('class' => 'heavy', 'style' => 'width:auto;'));
-        $ret .= $this->end('tr');
-        $ret .= $this->end('thead');
-
-        ksort($this->def->info);
-        foreach ($this->def->info as $property => $obj) {
-            $name = $this->getClass($obj, 'AttrDef_');
-            $ret .= $this->row($property, $name);
-        }
-
-        $ret .= $this->end('table');
-        $ret .= $this->end('div');
-
-        return $ret;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Printer/ConfigForm.css b/library/HTMLPurifier/Printer/ConfigForm.css
deleted file mode 100644 (file)
index 3ff1a88..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-
-.hp-config {}
-
-.hp-config tbody th {text-align:right; padding-right:0.5em;}
-.hp-config thead, .hp-config .namespace {background:#3C578C; color:#FFF;}
-.hp-config .namespace th {text-align:center;}
-.hp-config .verbose {display:none;}
-.hp-config .controls {text-align:center;}
-
-/* vim: et sw=4 sts=4 */
diff --git a/library/HTMLPurifier/Printer/ConfigForm.js b/library/HTMLPurifier/Printer/ConfigForm.js
deleted file mode 100644 (file)
index cba00c9..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-function toggleWriteability(id_of_patient, checked) {
-    document.getElementById(id_of_patient).disabled = checked;
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Printer/ConfigForm.php b/library/HTMLPurifier/Printer/ConfigForm.php
deleted file mode 100644 (file)
index 02aa656..0000000
+++ /dev/null
@@ -1,368 +0,0 @@
-<?php
-
-/**
- * @todo Rewrite to use Interchange objects
- */
-class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
-{
-
-    /**
-     * Printers for specific fields
-     */
-    protected $fields = array();
-
-    /**
-     * Documentation URL, can have fragment tagged on end
-     */
-    protected $docURL;
-
-    /**
-     * Name of form element to stuff config in
-     */
-    protected $name;
-
-    /**
-     * Whether or not to compress directive names, clipping them off
-     * after a certain amount of letters. False to disable or integer letters
-     * before clipping.
-     */
-    protected $compress = false;
-
-    /**
-     * @param $name Form element name for directives to be stuffed into
-     * @param $doc_url String documentation URL, will have fragment tagged on
-     * @param $compress Integer max length before compressing a directive name, set to false to turn off
-     */
-    public function __construct(
-        $name, $doc_url = null, $compress = false
-    ) {
-        parent::__construct();
-        $this->docURL = $doc_url;
-        $this->name   = $name;
-        $this->compress = $compress;
-        // initialize sub-printers
-        $this->fields[0]    = new HTMLPurifier_Printer_ConfigForm_default();
-        $this->fields[HTMLPurifier_VarParser::BOOL]       = new HTMLPurifier_Printer_ConfigForm_bool();
-    }
-
-    /**
-     * Sets default column and row size for textareas in sub-printers
-     * @param $cols Integer columns of textarea, null to use default
-     * @param $rows Integer rows of textarea, null to use default
-     */
-    public function setTextareaDimensions($cols = null, $rows = null) {
-        if ($cols) $this->fields['default']->cols = $cols;
-        if ($rows) $this->fields['default']->rows = $rows;
-    }
-
-    /**
-     * Retrieves styling, in case it is not accessible by webserver
-     */
-    public static function getCSS() {
-        return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.css');
-    }
-
-    /**
-     * Retrieves JavaScript, in case it is not accessible by webserver
-     */
-    public static function getJavaScript() {
-        return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.js');
-    }
-
-    /**
-     * Returns HTML output for a configuration form
-     * @param $config Configuration object of current form state, or an array
-     *        where [0] has an HTML namespace and [1] is being rendered.
-     * @param $allowed Optional namespace(s) and directives to restrict form to.
-     */
-    public function render($config, $allowed = true, $render_controls = true) {
-        if (is_array($config) && isset($config[0])) {
-            $gen_config = $config[0];
-            $config = $config[1];
-        } else {
-            $gen_config = $config;
-        }
-
-        $this->config = $config;
-        $this->genConfig = $gen_config;
-        $this->prepareGenerator($gen_config);
-
-        $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $config->def);
-        $all = array();
-        foreach ($allowed as $key) {
-            list($ns, $directive) = $key;
-            $all[$ns][$directive] = $config->get($ns .'.'. $directive);
-        }
-
-        $ret = '';
-        $ret .= $this->start('table', array('class' => 'hp-config'));
-        $ret .= $this->start('thead');
-        $ret .= $this->start('tr');
-            $ret .= $this->element('th', 'Directive', array('class' => 'hp-directive'));
-            $ret .= $this->element('th', 'Value', array('class' => 'hp-value'));
-        $ret .= $this->end('tr');
-        $ret .= $this->end('thead');
-        foreach ($all as $ns => $directives) {
-            $ret .= $this->renderNamespace($ns, $directives);
-        }
-        if ($render_controls) {
-             $ret .= $this->start('tbody');
-             $ret .= $this->start('tr');
-                 $ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls'));
-                     $ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit'));
-                     $ret .= '[<a href="?">Reset</a>]';
-                 $ret .= $this->end('td');
-             $ret .= $this->end('tr');
-             $ret .= $this->end('tbody');
-        }
-        $ret .= $this->end('table');
-        return $ret;
-    }
-
-    /**
-     * Renders a single namespace
-     * @param $ns String namespace name
-     * @param $directive Associative array of directives to values
-     */
-    protected function renderNamespace($ns, $directives) {
-        $ret = '';
-        $ret .= $this->start('tbody', array('class' => 'namespace'));
-        $ret .= $this->start('tr');
-            $ret .= $this->element('th', $ns, array('colspan' => 2));
-        $ret .= $this->end('tr');
-        $ret .= $this->end('tbody');
-        $ret .= $this->start('tbody');
-        foreach ($directives as $directive => $value) {
-            $ret .= $this->start('tr');
-            $ret .= $this->start('th');
-            if ($this->docURL) {
-                $url = str_replace('%s', urlencode("$ns.$directive"), $this->docURL);
-                $ret .= $this->start('a', array('href' => $url));
-            }
-                $attr = array('for' => "{$this->name}:$ns.$directive");
-
-                // crop directive name if it's too long
-                if (!$this->compress || (strlen($directive) < $this->compress)) {
-                    $directive_disp = $directive;
-                } else {
-                    $directive_disp = substr($directive, 0, $this->compress - 2) . '...';
-                    $attr['title'] = $directive;
-                }
-
-                $ret .= $this->element(
-                    'label',
-                    $directive_disp,
-                    // component printers must create an element with this id
-                    $attr
-                );
-            if ($this->docURL) $ret .= $this->end('a');
-            $ret .= $this->end('th');
-
-            $ret .= $this->start('td');
-                $def = $this->config->def->info["$ns.$directive"];
-                if (is_int($def)) {
-                    $allow_null = $def < 0;
-                    $type = abs($def);
-                } else {
-                    $type = $def->type;
-                    $allow_null = isset($def->allow_null);
-                }
-                if (!isset($this->fields[$type])) $type = 0; // default
-                $type_obj = $this->fields[$type];
-                if ($allow_null) {
-                    $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj);
-                }
-                $ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config));
-            $ret .= $this->end('td');
-            $ret .= $this->end('tr');
-        }
-        $ret .= $this->end('tbody');
-        return $ret;
-    }
-
-}
-
-/**
- * Printer decorator for directives that accept null
- */
-class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer {
-    /**
-     * Printer being decorated
-     */
-    protected $obj;
-    /**
-     * @param $obj Printer to decorate
-     */
-    public function __construct($obj) {
-        parent::__construct();
-        $this->obj = $obj;
-    }
-    public function render($ns, $directive, $value, $name, $config) {
-        if (is_array($config) && isset($config[0])) {
-            $gen_config = $config[0];
-            $config = $config[1];
-        } else {
-            $gen_config = $config;
-        }
-        $this->prepareGenerator($gen_config);
-
-        $ret = '';
-        $ret .= $this->start('label', array('for' => "$name:Null_$ns.$directive"));
-        $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
-        $ret .= $this->text(' Null/Disabled');
-        $ret .= $this->end('label');
-        $attr = array(
-            'type' => 'checkbox',
-            'value' => '1',
-            'class' => 'null-toggle',
-            'name' => "$name"."[Null_$ns.$directive]",
-            'id' => "$name:Null_$ns.$directive",
-            'onclick' => "toggleWriteability('$name:$ns.$directive',checked)" // INLINE JAVASCRIPT!!!!
-        );
-        if ($this->obj instanceof HTMLPurifier_Printer_ConfigForm_bool) {
-            // modify inline javascript slightly
-            $attr['onclick'] = "toggleWriteability('$name:Yes_$ns.$directive',checked);toggleWriteability('$name:No_$ns.$directive',checked)";
-        }
-        if ($value === null) $attr['checked'] = 'checked';
-        $ret .= $this->elementEmpty('input', $attr);
-        $ret .= $this->text(' or ');
-        $ret .= $this->elementEmpty('br');
-        $ret .= $this->obj->render($ns, $directive, $value, $name, array($gen_config, $config));
-        return $ret;
-    }
-}
-
-/**
- * Swiss-army knife configuration form field printer
- */
-class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer {
-    public $cols = 18;
-    public $rows = 5;
-    public function render($ns, $directive, $value, $name, $config) {
-        if (is_array($config) && isset($config[0])) {
-            $gen_config = $config[0];
-            $config = $config[1];
-        } else {
-            $gen_config = $config;
-        }
-        $this->prepareGenerator($gen_config);
-        // this should probably be split up a little
-        $ret = '';
-        $def = $config->def->info["$ns.$directive"];
-        if (is_int($def)) {
-            $type = abs($def);
-        } else {
-            $type = $def->type;
-        }
-        if (is_array($value)) {
-            switch ($type) {
-                case HTMLPurifier_VarParser::LOOKUP:
-                    $array = $value;
-                    $value = array();
-                    foreach ($array as $val => $b) {
-                        $value[] = $val;
-                    }
-                case HTMLPurifier_VarParser::ALIST:
-                    $value = implode(PHP_EOL, $value);
-                    break;
-                case HTMLPurifier_VarParser::HASH:
-                    $nvalue = '';
-                    foreach ($value as $i => $v) {
-                        $nvalue .= "$i:$v" . PHP_EOL;
-                    }
-                    $value = $nvalue;
-                    break;
-                default:
-                    $value = '';
-            }
-        }
-        if ($type === HTMLPurifier_VarParser::MIXED) {
-            return 'Not supported';
-            $value = serialize($value);
-        }
-        $attr = array(
-            'name' => "$name"."[$ns.$directive]",
-            'id' => "$name:$ns.$directive"
-        );
-        if ($value === null) $attr['disabled'] = 'disabled';
-        if (isset($def->allowed)) {
-            $ret .= $this->start('select', $attr);
-            foreach ($def->allowed as $val => $b) {
-                $attr = array();
-                if ($value == $val) $attr['selected'] = 'selected';
-                $ret .= $this->element('option', $val, $attr);
-            }
-            $ret .= $this->end('select');
-        } elseif (
-            $type === HTMLPurifier_VarParser::TEXT ||
-            $type === HTMLPurifier_VarParser::ITEXT ||
-            $type === HTMLPurifier_VarParser::ALIST ||
-            $type === HTMLPurifier_VarParser::HASH ||
-            $type === HTMLPurifier_VarParser::LOOKUP
-        ) {
-            $attr['cols'] = $this->cols;
-            $attr['rows'] = $this->rows;
-            $ret .= $this->start('textarea', $attr);
-            $ret .= $this->text($value);
-            $ret .= $this->end('textarea');
-        } else {
-            $attr['value'] = $value;
-            $attr['type'] = 'text';
-            $ret .= $this->elementEmpty('input', $attr);
-        }
-        return $ret;
-    }
-}
-
-/**
- * Bool form field printer
- */
-class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer {
-    public function render($ns, $directive, $value, $name, $config) {
-        if (is_array($config) && isset($config[0])) {
-            $gen_config = $config[0];
-            $config = $config[1];
-        } else {
-            $gen_config = $config;
-        }
-        $this->prepareGenerator($gen_config);
-        $ret = '';
-        $ret .= $this->start('div', array('id' => "$name:$ns.$directive"));
-
-        $ret .= $this->start('label', array('for' => "$name:Yes_$ns.$directive"));
-        $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
-        $ret .= $this->text(' Yes');
-        $ret .= $this->end('label');
-
-        $attr = array(
-            'type' => 'radio',
-            'name' => "$name"."[$ns.$directive]",
-            'id' => "$name:Yes_$ns.$directive",
-            'value' => '1'
-        );
-        if ($value === true) $attr['checked'] = 'checked';
-        if ($value === null) $attr['disabled'] = 'disabled';
-        $ret .= $this->elementEmpty('input', $attr);
-
-        $ret .= $this->start('label', array('for' => "$name:No_$ns.$directive"));
-        $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
-        $ret .= $this->text(' No');
-        $ret .= $this->end('label');
-
-        $attr = array(
-            'type' => 'radio',
-            'name' => "$name"."[$ns.$directive]",
-            'id' => "$name:No_$ns.$directive",
-            'value' => '0'
-        );
-        if ($value === false) $attr['checked'] = 'checked';
-        if ($value === null) $attr['disabled'] = 'disabled';
-        $ret .= $this->elementEmpty('input', $attr);
-
-        $ret .= $this->end('div');
-
-        return $ret;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Printer/HTMLDefinition.php b/library/HTMLPurifier/Printer/HTMLDefinition.php
deleted file mode 100644 (file)
index 8a8f126..0000000
+++ /dev/null
@@ -1,272 +0,0 @@
-<?php
-
-class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
-{
-
-    /**
-     * Instance of HTMLPurifier_HTMLDefinition, for easy access
-     */
-    protected $def;
-
-    public function render($config) {
-        $ret = '';
-        $this->config =& $config;
-
-        $this->def = $config->getHTMLDefinition();
-
-        $ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer'));
-
-        $ret .= $this->renderDoctype();
-        $ret .= $this->renderEnvironment();
-        $ret .= $this->renderContentSets();
-        $ret .= $this->renderInfo();
-
-        $ret .= $this->end('div');
-
-        return $ret;
-    }
-
-    /**
-     * Renders the Doctype table
-     */
-    protected function renderDoctype() {
-        $doctype = $this->def->doctype;
-        $ret = '';
-        $ret .= $this->start('table');
-        $ret .= $this->element('caption', 'Doctype');
-        $ret .= $this->row('Name', $doctype->name);
-        $ret .= $this->row('XML', $doctype->xml ? 'Yes' : 'No');
-        $ret .= $this->row('Default Modules', implode($doctype->modules, ', '));
-        $ret .= $this->row('Default Tidy Modules', implode($doctype->tidyModules, ', '));
-        $ret .= $this->end('table');
-        return $ret;
-    }
-
-
-    /**
-     * Renders environment table, which is miscellaneous info
-     */
-    protected function renderEnvironment() {
-        $def = $this->def;
-
-        $ret = '';
-
-        $ret .= $this->start('table');
-        $ret .= $this->element('caption', 'Environment');
-
-        $ret .= $this->row('Parent of fragment', $def->info_parent);
-        $ret .= $this->renderChildren($def->info_parent_def->child);
-        $ret .= $this->row('Block wrap name', $def->info_block_wrapper);
-
-        $ret .= $this->start('tr');
-            $ret .= $this->element('th', 'Global attributes');
-            $ret .= $this->element('td', $this->listifyAttr($def->info_global_attr),0,0);
-        $ret .= $this->end('tr');
-
-        $ret .= $this->start('tr');
-            $ret .= $this->element('th', 'Tag transforms');
-            $list = array();
-            foreach ($def->info_tag_transform as $old => $new) {
-                $new = $this->getClass($new, 'TagTransform_');
-                $list[] = "<$old> with $new";
-            }
-            $ret .= $this->element('td', $this->listify($list));
-        $ret .= $this->end('tr');
-
-        $ret .= $this->start('tr');
-            $ret .= $this->element('th', 'Pre-AttrTransform');
-            $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_pre));
-        $ret .= $this->end('tr');
-
-        $ret .= $this->start('tr');
-            $ret .= $this->element('th', 'Post-AttrTransform');
-            $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_post));
-        $ret .= $this->end('tr');
-
-        $ret .= $this->end('table');
-        return $ret;
-    }
-
-    /**
-     * Renders the Content Sets table
-     */
-    protected function renderContentSets() {
-        $ret = '';
-        $ret .= $this->start('table');
-        $ret .= $this->element('caption', 'Content Sets');
-        foreach ($this->def->info_content_sets as $name => $lookup) {
-            $ret .= $this->heavyHeader($name);
-            $ret .= $this->start('tr');
-            $ret .= $this->element('td', $this->listifyTagLookup($lookup));
-            $ret .= $this->end('tr');
-        }
-        $ret .= $this->end('table');
-        return $ret;
-    }
-
-    /**
-     * Renders the Elements ($info) table
-     */
-    protected function renderInfo() {
-        $ret = '';
-        $ret .= $this->start('table');
-        $ret .= $this->element('caption', 'Elements ($info)');
-        ksort($this->def->info);
-        $ret .= $this->heavyHeader('Allowed tags', 2);
-        $ret .= $this->start('tr');
-        $ret .= $this->element('td', $this->listifyTagLookup($this->def->info), array('colspan' => 2));
-        $ret .= $this->end('tr');
-        foreach ($this->def->info as $name => $def) {
-            $ret .= $this->start('tr');
-                $ret .= $this->element('th', "<$name>", array('class'=>'heavy', 'colspan' => 2));
-            $ret .= $this->end('tr');
-            $ret .= $this->start('tr');
-                $ret .= $this->element('th', 'Inline content');
-                $ret .= $this->element('td', $def->descendants_are_inline ? 'Yes' : 'No');
-            $ret .= $this->end('tr');
-            if (!empty($def->excludes)) {
-                $ret .= $this->start('tr');
-                    $ret .= $this->element('th', 'Excludes');
-                    $ret .= $this->element('td', $this->listifyTagLookup($def->excludes));
-                $ret .= $this->end('tr');
-            }
-            if (!empty($def->attr_transform_pre)) {
-                $ret .= $this->start('tr');
-                    $ret .= $this->element('th', 'Pre-AttrTransform');
-                    $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_pre));
-                $ret .= $this->end('tr');
-            }
-            if (!empty($def->attr_transform_post)) {
-                $ret .= $this->start('tr');
-                    $ret .= $this->element('th', 'Post-AttrTransform');
-                    $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_post));
-                $ret .= $this->end('tr');
-            }
-            if (!empty($def->auto_close)) {
-                $ret .= $this->start('tr');
-                    $ret .= $this->element('th', 'Auto closed by');
-                    $ret .= $this->element('td', $this->listifyTagLookup($def->auto_close));
-                $ret .= $this->end('tr');
-            }
-            $ret .= $this->start('tr');
-                $ret .= $this->element('th', 'Allowed attributes');
-                $ret .= $this->element('td',$this->listifyAttr($def->attr), array(), 0);
-            $ret .= $this->end('tr');
-
-            if (!empty($def->required_attr)) {
-                $ret .= $this->row('Required attributes', $this->listify($def->required_attr));
-            }
-
-            $ret .= $this->renderChildren($def->child);
-        }
-        $ret .= $this->end('table');
-        return $ret;
-    }
-
-    /**
-     * Renders a row describing the allowed children of an element
-     * @param $def HTMLPurifier_ChildDef of pertinent element
-     */
-    protected function renderChildren($def) {
-        $context = new HTMLPurifier_Context();
-        $ret = '';
-        $ret .= $this->start('tr');
-            $elements = array();
-            $attr = array();
-            if (isset($def->elements)) {
-                if ($def->type == 'strictblockquote') {
-                    $def->validateChildren(array(), $this->config, $context);
-                }
-                $elements = $def->elements;
-            }
-            if ($def->type == 'chameleon') {
-                $attr['rowspan'] = 2;
-            } elseif ($def->type == 'empty') {
-                $elements = array();
-            } elseif ($def->type == 'table') {
-                $elements = array_flip(array('col', 'caption', 'colgroup', 'thead',
-                    'tfoot', 'tbody', 'tr'));
-            }
-            $ret .= $this->element('th', 'Allowed children', $attr);
-
-            if ($def->type == 'chameleon') {
-
-                $ret .= $this->element('td',
-                    '<em>Block</em>: ' .
-                    $this->escape($this->listifyTagLookup($def->block->elements)),0,0);
-                $ret .= $this->end('tr');
-                $ret .= $this->start('tr');
-                $ret .= $this->element('td',
-                    '<em>Inline</em>: ' .
-                    $this->escape($this->listifyTagLookup($def->inline->elements)),0,0);
-
-            } elseif ($def->type == 'custom') {
-
-                $ret .= $this->element('td', '<em>'.ucfirst($def->type).'</em>: ' .
-                    $def->dtd_regex);
-
-            } else {
-                $ret .= $this->element('td',
-                    '<em>'.ucfirst($def->type).'</em>: ' .
-                    $this->escape($this->listifyTagLookup($elements)),0,0);
-            }
-        $ret .= $this->end('tr');
-        return $ret;
-    }
-
-    /**
-     * Listifies a tag lookup table.
-     * @param $array Tag lookup array in form of array('tagname' => true)
-     */
-    protected function listifyTagLookup($array) {
-        ksort($array);
-        $list = array();
-        foreach ($array as $name => $discard) {
-            if ($name !== '#PCDATA' && !isset($this->def->info[$name])) continue;
-            $list[] = $name;
-        }
-        return $this->listify($list);
-    }
-
-    /**
-     * Listifies a list of objects by retrieving class names and internal state
-     * @param $array List of objects
-     * @todo Also add information about internal state
-     */
-    protected function listifyObjectList($array) {
-        ksort($array);
-        $list = array();
-        foreach ($array as $discard => $obj) {
-            $list[] = $this->getClass($obj, 'AttrTransform_');
-        }
-        return $this->listify($list);
-    }
-
-    /**
-     * Listifies a hash of attributes to AttrDef classes
-     * @param $array Array hash in form of array('attrname' => HTMLPurifier_AttrDef)
-     */
-    protected function listifyAttr($array) {
-        ksort($array);
-        $list = array();
-        foreach ($array as $name => $obj) {
-            if ($obj === false) continue;
-            $list[] = "$name&nbsp;=&nbsp;<i>" . $this->getClass($obj, 'AttrDef_') . '</i>';
-        }
-        return $this->listify($list);
-    }
-
-    /**
-     * Creates a heavy header row
-     */
-    protected function heavyHeader($text, $num = 1) {
-        $ret = '';
-        $ret .= $this->start('tr');
-        $ret .= $this->element('th', $text, array('colspan' => $num, 'class' => 'heavy'));
-        $ret .= $this->end('tr');
-        return $ret;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/PropertyList.php b/library/HTMLPurifier/PropertyList.php
deleted file mode 100644 (file)
index 2b99fb7..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-<?php
-
-/**
- * Generic property list implementation
- */
-class HTMLPurifier_PropertyList
-{
-    /**
-     * Internal data-structure for properties
-     */
-    protected $data = array();
-
-    /**
-     * Parent plist
-     */
-    protected $parent;
-
-    protected $cache;
-
-    public function __construct($parent = null) {
-        $this->parent = $parent;
-    }
-
-    /**
-     * Recursively retrieves the value for a key
-     */
-    public function get($name) {
-        if ($this->has($name)) return $this->data[$name];
-        // possible performance bottleneck, convert to iterative if necessary
-        if ($this->parent) return $this->parent->get($name);
-        throw new HTMLPurifier_Exception("Key '$name' not found");
-    }
-
-    /**
-     * Sets the value of a key, for this plist
-     */
-    public function set($name, $value) {
-        $this->data[$name] = $value;
-    }
-
-    /**
-     * Returns true if a given key exists
-     */
-    public function has($name) {
-        return array_key_exists($name, $this->data);
-    }
-
-    /**
-     * Resets a value to the value of it's parent, usually the default. If
-     * no value is specified, the entire plist is reset.
-     */
-    public function reset($name = null) {
-        if ($name == null) $this->data = array();
-        else unset($this->data[$name]);
-    }
-
-    /**
-     * Squashes this property list and all of its property lists into a single
-     * array, and returns the array. This value is cached by default.
-     * @param $force If true, ignores the cache and regenerates the array.
-     */
-    public function squash($force = false) {
-        if ($this->cache !== null && !$force) return $this->cache;
-        if ($this->parent) {
-            return $this->cache = array_merge($this->parent->squash($force), $this->data);
-        } else {
-            return $this->cache = $this->data;
-        }
-    }
-
-    /**
-     * Returns the parent plist.
-     */
-    public function getParent() {
-        return $this->parent;
-    }
-
-    /**
-     * Sets the parent plist.
-     */
-    public function setParent($plist) {
-        $this->parent = $plist;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/PropertyListIterator.php b/library/HTMLPurifier/PropertyListIterator.php
deleted file mode 100644 (file)
index 8f25044..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-/**
- * Property list iterator. Do not instantiate this class directly.
- */
-class HTMLPurifier_PropertyListIterator extends FilterIterator
-{
-
-    protected $l;
-    protected $filter;
-
-    /**
-     * @param $data Array of data to iterate over
-     * @param $filter Optional prefix to only allow values of
-     */
-    public function __construct(Iterator $iterator, $filter = null) {
-        parent::__construct($iterator);
-        $this->l = strlen($filter);
-        $this->filter = $filter;
-    }
-
-    public function accept() {
-        $key = $this->getInnerIterator()->key();
-        if( strncmp($key, $this->filter, $this->l) !== 0 ) {
-            return false;
-        }
-        return true;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Strategy.php b/library/HTMLPurifier/Strategy.php
deleted file mode 100644 (file)
index 2462865..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-/**
- * Supertype for classes that define a strategy for modifying/purifying tokens.
- *
- * While HTMLPurifier's core purpose is fixing HTML into something proper,
- * strategies provide plug points for extra configuration or even extra
- * features, such as custom tags, custom parsing of text, etc.
- */
-
-
-abstract class HTMLPurifier_Strategy
-{
-
-    /**
-     * Executes the strategy on the tokens.
-     *
-     * @param $tokens Array of HTMLPurifier_Token objects to be operated on.
-     * @param $config Configuration options
-     * @returns Processed array of token objects.
-     */
-    abstract public function execute($tokens, $config, $context);
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Strategy/Composite.php b/library/HTMLPurifier/Strategy/Composite.php
deleted file mode 100644 (file)
index 816490b..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-/**
- * Composite strategy that runs multiple strategies on tokens.
- */
-abstract class HTMLPurifier_Strategy_Composite extends HTMLPurifier_Strategy
-{
-
-    /**
-     * List of strategies to run tokens through.
-     */
-    protected $strategies = array();
-
-    abstract public function __construct();
-
-    public function execute($tokens, $config, $context) {
-        foreach ($this->strategies as $strategy) {
-            $tokens = $strategy->execute($tokens, $config, $context);
-        }
-        return $tokens;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Strategy/Core.php b/library/HTMLPurifier/Strategy/Core.php
deleted file mode 100644 (file)
index d90e158..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-
-/**
- * Core strategy composed of the big four strategies.
- */
-class HTMLPurifier_Strategy_Core extends HTMLPurifier_Strategy_Composite
-{
-
-    public function __construct() {
-        $this->strategies[] = new HTMLPurifier_Strategy_RemoveForeignElements();
-        $this->strategies[] = new HTMLPurifier_Strategy_MakeWellFormed();
-        $this->strategies[] = new HTMLPurifier_Strategy_FixNesting();
-        $this->strategies[] = new HTMLPurifier_Strategy_ValidateAttributes();
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Strategy/FixNesting.php b/library/HTMLPurifier/Strategy/FixNesting.php
deleted file mode 100644 (file)
index f818023..0000000
+++ /dev/null
@@ -1,328 +0,0 @@
-<?php
-
-/**
- * Takes a well formed list of tokens and fixes their nesting.
- *
- * HTML elements dictate which elements are allowed to be their children,
- * for example, you can't have a p tag in a span tag.  Other elements have
- * much more rigorous definitions: tables, for instance, require a specific
- * order for their elements.  There are also constraints not expressible by
- * document type definitions, such as the chameleon nature of ins/del
- * tags and global child exclusions.
- *
- * The first major objective of this strategy is to iterate through all the
- * nodes (not tokens) of the list of tokens and determine whether or not
- * their children conform to the element's definition.  If they do not, the
- * child definition may optionally supply an amended list of elements that
- * is valid or require that the entire node be deleted (and the previous
- * node rescanned).
- *
- * The second objective is to ensure that explicitly excluded elements of
- * an element do not appear in its children.  Code that accomplishes this
- * task is pervasive through the strategy, though the two are distinct tasks
- * and could, theoretically, be seperated (although it's not recommended).
- *
- * @note Whether or not unrecognized children are silently dropped or
- *       translated into text depends on the child definitions.
- *
- * @todo Enable nodes to be bubbled out of the structure.
- */
-
-class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
-{
-
-    public function execute($tokens, $config, $context) {
-        //####################################################################//
-        // Pre-processing
-
-        // get a copy of the HTML definition
-        $definition = $config->getHTMLDefinition();
-
-        // insert implicit "parent" node, will be removed at end.
-        // DEFINITION CALL
-        $parent_name = $definition->info_parent;
-        array_unshift($tokens, new HTMLPurifier_Token_Start($parent_name));
-        $tokens[] = new HTMLPurifier_Token_End($parent_name);
-
-        // setup the context variable 'IsInline', for chameleon processing
-        // is 'false' when we are not inline, 'true' when it must always
-        // be inline, and an integer when it is inline for a certain
-        // branch of the document tree
-        $is_inline = $definition->info_parent_def->descendants_are_inline;
-        $context->register('IsInline', $is_inline);
-
-        // setup error collector
-        $e =& $context->get('ErrorCollector', true);
-
-        //####################################################################//
-        // Loop initialization
-
-        // stack that contains the indexes of all parents,
-        // $stack[count($stack)-1] being the current parent
-        $stack = array();
-
-        // stack that contains all elements that are excluded
-        // it is organized by parent elements, similar to $stack,
-        // but it is only populated when an element with exclusions is
-        // processed, i.e. there won't be empty exclusions.
-        $exclude_stack = array();
-
-        // variable that contains the start token while we are processing
-        // nodes. This enables error reporting to do its job
-        $start_token = false;
-        $context->register('CurrentToken', $start_token);
-
-        //####################################################################//
-        // Loop
-
-        // iterate through all start nodes. Determining the start node
-        // is complicated so it has been omitted from the loop construct
-        for ($i = 0, $size = count($tokens) ; $i < $size; ) {
-
-            //################################################################//
-            // Gather information on children
-
-            // child token accumulator
-            $child_tokens = array();
-
-            // scroll to the end of this node, report number, and collect
-            // all children
-            for ($j = $i, $depth = 0; ; $j++) {
-                if ($tokens[$j] instanceof HTMLPurifier_Token_Start) {
-                    $depth++;
-                    // skip token assignment on first iteration, this is the
-                    // token we currently are on
-                    if ($depth == 1) continue;
-                } elseif ($tokens[$j] instanceof HTMLPurifier_Token_End) {
-                    $depth--;
-                    // skip token assignment on last iteration, this is the
-                    // end token of the token we're currently on
-                    if ($depth == 0) break;
-                }
-                $child_tokens[] = $tokens[$j];
-            }
-
-            // $i is index of start token
-            // $j is index of end token
-
-            $start_token = $tokens[$i]; // to make token available via CurrentToken
-
-            //################################################################//
-            // Gather information on parent
-
-            // calculate parent information
-            if ($count = count($stack)) {
-                $parent_index = $stack[$count-1];
-                $parent_name  = $tokens[$parent_index]->name;
-                if ($parent_index == 0) {
-                    $parent_def   = $definition->info_parent_def;
-                } else {
-                    $parent_def   = $definition->info[$parent_name];
-                }
-            } else {
-                // processing as if the parent were the "root" node
-                // unknown info, it won't be used anyway, in the future,
-                // we may want to enforce one element only (this is
-                // necessary for HTML Purifier to clean entire documents
-                $parent_index = $parent_name = $parent_def = null;
-            }
-
-            // calculate context
-            if ($is_inline === false) {
-                // check if conditions make it inline
-                if (!empty($parent_def) && $parent_def->descendants_are_inline) {
-                    $is_inline = $count - 1;
-                }
-            } else {
-                // check if we're out of inline
-                if ($count === $is_inline) {
-                    $is_inline = false;
-                }
-            }
-
-            //################################################################//
-            // Determine whether element is explicitly excluded SGML-style
-
-            // determine whether or not element is excluded by checking all
-            // parent exclusions. The array should not be very large, two
-            // elements at most.
-            $excluded = false;
-            if (!empty($exclude_stack)) {
-                foreach ($exclude_stack as $lookup) {
-                    if (isset($lookup[$tokens[$i]->name])) {
-                        $excluded = true;
-                        // no need to continue processing
-                        break;
-                    }
-                }
-            }
-
-            //################################################################//
-            // Perform child validation
-
-            if ($excluded) {
-                // there is an exclusion, remove the entire node
-                $result = false;
-                $excludes = array(); // not used, but good to initialize anyway
-            } else {
-                // DEFINITION CALL
-                if ($i === 0) {
-                    // special processing for the first node
-                    $def = $definition->info_parent_def;
-                } else {
-                    $def = $definition->info[$tokens[$i]->name];
-
-                }
-
-                if (!empty($def->child)) {
-                    // have DTD child def validate children
-                    $result = $def->child->validateChildren(
-                        $child_tokens, $config, $context);
-                } else {
-                    // weird, no child definition, get rid of everything
-                    $result = false;
-                }
-
-                // determine whether or not this element has any exclusions
-                $excludes = $def->excludes;
-            }
-
-            // $result is now a bool or array
-
-            //################################################################//
-            // Process result by interpreting $result
-
-            if ($result === true || $child_tokens === $result) {
-                // leave the node as is
-
-                // register start token as a parental node start
-                $stack[] = $i;
-
-                // register exclusions if there are any
-                if (!empty($excludes)) $exclude_stack[] = $excludes;
-
-                // move cursor to next possible start node
-                $i++;
-
-            } elseif($result === false) {
-                // remove entire node
-
-                if ($e) {
-                    if ($excluded) {
-                        $e->send(E_ERROR, 'Strategy_FixNesting: Node excluded');
-                    } else {
-                        $e->send(E_ERROR, 'Strategy_FixNesting: Node removed');
-                    }
-                }
-
-                // calculate length of inner tokens and current tokens
-                $length = $j - $i + 1;
-
-                // perform removal
-                array_splice($tokens, $i, $length);
-
-                // update size
-                $size -= $length;
-
-                // there is no start token to register,
-                // current node is now the next possible start node
-                // unless it turns out that we need to do a double-check
-
-                // this is a rought heuristic that covers 100% of HTML's
-                // cases and 99% of all other cases. A child definition
-                // that would be tricked by this would be something like:
-                // ( | a b c) where it's all or nothing. Fortunately,
-                // our current implementation claims that that case would
-                // not allow empty, even if it did
-                if (!$parent_def->child->allow_empty) {
-                    // we need to do a double-check
-                    $i = $parent_index;
-                    array_pop($stack);
-                }
-
-                // PROJECTED OPTIMIZATION: Process all children elements before
-                // reprocessing parent node.
-
-            } else {
-                // replace node with $result
-
-                // calculate length of inner tokens
-                $length = $j - $i - 1;
-
-                if ($e) {
-                    if (empty($result) && $length) {
-                        $e->send(E_ERROR, 'Strategy_FixNesting: Node contents removed');
-                    } else {
-                        $e->send(E_WARNING, 'Strategy_FixNesting: Node reorganized');
-                    }
-                }
-
-                // perform replacement
-                array_splice($tokens, $i + 1, $length, $result);
-
-                // update size
-                $size -= $length;
-                $size += count($result);
-
-                // register start token as a parental node start
-                $stack[] = $i;
-
-                // register exclusions if there are any
-                if (!empty($excludes)) $exclude_stack[] = $excludes;
-
-                // move cursor to next possible start node
-                $i++;
-
-            }
-
-            //################################################################//
-            // Scroll to next start node
-
-            // We assume, at this point, that $i is the index of the token
-            // that is the first possible new start point for a node.
-
-            // Test if the token indeed is a start tag, if not, move forward
-            // and test again.
-            $size = count($tokens);
-            while ($i < $size and !$tokens[$i] instanceof HTMLPurifier_Token_Start) {
-                if ($tokens[$i] instanceof HTMLPurifier_Token_End) {
-                    // pop a token index off the stack if we ended a node
-                    array_pop($stack);
-                    // pop an exclusion lookup off exclusion stack if
-                    // we ended node and that node had exclusions
-                    if ($i == 0 || $i == $size - 1) {
-                        // use specialized var if it's the super-parent
-                        $s_excludes = $definition->info_parent_def->excludes;
-                    } else {
-                        $s_excludes = $definition->info[$tokens[$i]->name]->excludes;
-                    }
-                    if ($s_excludes) {
-                        array_pop($exclude_stack);
-                    }
-                }
-                $i++;
-            }
-
-        }
-
-        //####################################################################//
-        // Post-processing
-
-        // remove implicit parent tokens at the beginning and end
-        array_shift($tokens);
-        array_pop($tokens);
-
-        // remove context variables
-        $context->destroy('IsInline');
-        $context->destroy('CurrentToken');
-
-        //####################################################################//
-        // Return
-
-        return $tokens;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Strategy/MakeWellFormed.php b/library/HTMLPurifier/Strategy/MakeWellFormed.php
deleted file mode 100644 (file)
index c736584..0000000
+++ /dev/null
@@ -1,475 +0,0 @@
-<?php
-
-/**
- * Takes tokens makes them well-formed (balance end tags, etc.)
- */
-class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
-{
-
-    /**
-     * Array stream of tokens being processed.
-     */
-    protected $tokens;
-
-    /**
-     * Current index in $tokens.
-     */
-    protected $t;
-
-    /**
-     * Current nesting of elements.
-     */
-    protected $stack;
-
-    /**
-     * Injectors active in this stream processing.
-     */
-    protected $injectors;
-
-    /**
-     * Current instance of HTMLPurifier_Config.
-     */
-    protected $config;
-
-    /**
-     * Current instance of HTMLPurifier_Context.
-     */
-    protected $context;
-
-    public function execute($tokens, $config, $context) {
-
-        $definition = $config->getHTMLDefinition();
-
-        // local variables
-        $generator = new HTMLPurifier_Generator($config, $context);
-        $escape_invalid_tags = $config->get('Core.EscapeInvalidTags');
-        $e = $context->get('ErrorCollector', true);
-        $t = false; // token index
-        $i = false; // injector index
-        $token      = false; // the current token
-        $reprocess  = false; // whether or not to reprocess the same token
-        $stack = array();
-
-        // member variables
-        $this->stack   =& $stack;
-        $this->t       =& $t;
-        $this->tokens  =& $tokens;
-        $this->config  = $config;
-        $this->context = $context;
-
-        // context variables
-        $context->register('CurrentNesting', $stack);
-        $context->register('InputIndex',     $t);
-        $context->register('InputTokens',    $tokens);
-        $context->register('CurrentToken',   $token);
-
-        // -- begin INJECTOR --
-
-        $this->injectors = array();
-
-        $injectors = $config->getBatch('AutoFormat');
-        $def_injectors = $definition->info_injector;
-        $custom_injectors = $injectors['Custom'];
-        unset($injectors['Custom']); // special case
-        foreach ($injectors as $injector => $b) {
-            // XXX: Fix with a legitimate lookup table of enabled filters
-            if (strpos($injector, '.') !== false) continue;
-            $injector = "HTMLPurifier_Injector_$injector";
-            if (!$b) continue;
-            $this->injectors[] = new $injector;
-        }
-        foreach ($def_injectors as $injector) {
-            // assumed to be objects
-            $this->injectors[] = $injector;
-        }
-        foreach ($custom_injectors as $injector) {
-            if (!$injector) continue;
-            if (is_string($injector)) {
-                $injector = "HTMLPurifier_Injector_$injector";
-                $injector = new $injector;
-            }
-            $this->injectors[] = $injector;
-        }
-
-        // give the injectors references to the definition and context
-        // variables for performance reasons
-        foreach ($this->injectors as $ix => $injector) {
-            $error = $injector->prepare($config, $context);
-            if (!$error) continue;
-            array_splice($this->injectors, $ix, 1); // rm the injector
-            trigger_error("Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING);
-        }
-
-        // -- end INJECTOR --
-
-        // a note on punting:
-        //      In order to reduce code duplication, whenever some code needs
-        //      to make HTML changes in order to make things "correct", the
-        //      new HTML gets sent through the purifier, regardless of its
-        //      status. This means that if we add a start token, because it
-        //      was totally necessary, we don't have to update nesting; we just
-        //      punt ($reprocess = true; continue;) and it does that for us.
-
-        // isset is in loop because $tokens size changes during loop exec
-        for (
-            $t = 0;
-            $t == 0 || isset($tokens[$t - 1]);
-            // only increment if we don't need to reprocess
-            $reprocess ? $reprocess = false : $t++
-        ) {
-
-            // check for a rewind
-            if (is_int($i) && $i >= 0) {
-                // possibility: disable rewinding if the current token has a
-                // rewind set on it already. This would offer protection from
-                // infinite loop, but might hinder some advanced rewinding.
-                $rewind_to = $this->injectors[$i]->getRewind();
-                if (is_int($rewind_to) && $rewind_to < $t) {
-                    if ($rewind_to < 0) $rewind_to = 0;
-                    while ($t > $rewind_to) {
-                        $t--;
-                        $prev = $tokens[$t];
-                        // indicate that other injectors should not process this token,
-                        // but we need to reprocess it
-                        unset($prev->skip[$i]);
-                        $prev->rewind = $i;
-                        if ($prev instanceof HTMLPurifier_Token_Start) array_pop($this->stack);
-                        elseif ($prev instanceof HTMLPurifier_Token_End) $this->stack[] = $prev->start;
-                    }
-                }
-                $i = false;
-            }
-
-            // handle case of document end
-            if (!isset($tokens[$t])) {
-                // kill processing if stack is empty
-                if (empty($this->stack)) break;
-
-                // peek
-                $top_nesting = array_pop($this->stack);
-                $this->stack[] = $top_nesting;
-
-                // send error
-                if ($e && !isset($top_nesting->armor['MakeWellFormed_TagClosedError'])) {
-                    $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $top_nesting);
-                }
-
-                // append, don't splice, since this is the end
-                $tokens[] = new HTMLPurifier_Token_End($top_nesting->name);
-
-                // punt!
-                $reprocess = true;
-                continue;
-            }
-
-            $token = $tokens[$t];
-
-            //echo '<br>'; printTokens($tokens, $t); printTokens($this->stack);
-            //flush();
-
-            // quick-check: if it's not a tag, no need to process
-            if (empty($token->is_tag)) {
-                if ($token instanceof HTMLPurifier_Token_Text) {
-                    foreach ($this->injectors as $i => $injector) {
-                        if (isset($token->skip[$i])) continue;
-                        if ($token->rewind !== null && $token->rewind !== $i) continue;
-                        $injector->handleText($token);
-                        $this->processToken($token, $i);
-                        $reprocess = true;
-                        break;
-                    }
-                }
-                // another possibility is a comment
-                continue;
-            }
-
-            if (isset($definition->info[$token->name])) {
-                $type = $definition->info[$token->name]->child->type;
-            } else {
-                $type = false; // Type is unknown, treat accordingly
-            }
-
-            // quick tag checks: anything that's *not* an end tag
-            $ok = false;
-            if ($type === 'empty' && $token instanceof HTMLPurifier_Token_Start) {
-                // claims to be a start tag but is empty
-                $token = new HTMLPurifier_Token_Empty($token->name, $token->attr);
-                $ok = true;
-            } elseif ($type && $type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) {
-                // claims to be empty but really is a start tag
-                $this->swap(new HTMLPurifier_Token_End($token->name));
-                $this->insertBefore(new HTMLPurifier_Token_Start($token->name, $token->attr));
-                // punt (since we had to modify the input stream in a non-trivial way)
-                $reprocess = true;
-                continue;
-            } elseif ($token instanceof HTMLPurifier_Token_Empty) {
-                // real empty token
-                $ok = true;
-            } elseif ($token instanceof HTMLPurifier_Token_Start) {
-                // start tag
-
-                // ...unless they also have to close their parent
-                if (!empty($this->stack)) {
-
-                    $parent = array_pop($this->stack);
-                    $this->stack[] = $parent;
-
-                    if (isset($definition->info[$parent->name])) {
-                        $elements = $definition->info[$parent->name]->child->getAllowedElements($config);
-                        $autoclose = !isset($elements[$token->name]);
-                    } else {
-                        $autoclose = false;
-                    }
-
-                    if ($autoclose && $definition->info[$token->name]->wrap) {
-                        // Check if an element can be wrapped by another 
-                        // element to make it valid in a context (for 
-                        // example, <ul><ul> needs a <li> in between)
-                        $wrapname = $definition->info[$token->name]->wrap;
-                        $wrapdef = $definition->info[$wrapname];
-                        $elements = $wrapdef->child->getAllowedElements($config);
-                        $parent_elements = $definition->info[$parent->name]->child->getAllowedElements($config);
-                        if (isset($elements[$token->name]) && isset($parent_elements[$wrapname])) {
-                            $newtoken = new HTMLPurifier_Token_Start($wrapname);
-                            $this->insertBefore($newtoken);
-                            $reprocess = true;
-                            continue;
-                        }
-                    }
-
-                    $carryover = false;
-                    if ($autoclose && $definition->info[$parent->name]->formatting) {
-                        $carryover = true;
-                    }
-
-                    if ($autoclose) {
-                        // errors need to be updated
-                        $new_token = new HTMLPurifier_Token_End($parent->name);
-                        $new_token->start = $parent;
-                        if ($carryover) {
-                            $element = clone $parent;
-                            $element->armor['MakeWellFormed_TagClosedError'] = true;
-                            $element->carryover = true;
-                            $this->processToken(array($new_token, $token, $element));
-                        } else {
-                            $this->insertBefore($new_token);
-                        }
-                        if ($e && !isset($parent->armor['MakeWellFormed_TagClosedError'])) {
-                            if (!$carryover) {
-                                $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag auto closed', $parent);
-                            } else {
-                                $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag carryover', $parent);
-                            }
-                        }
-                        $reprocess = true;
-                        continue;
-                    }
-
-                }
-                $ok = true;
-            }
-
-            if ($ok) {
-                foreach ($this->injectors as $i => $injector) {
-                    if (isset($token->skip[$i])) continue;
-                    if ($token->rewind !== null && $token->rewind !== $i) continue;
-                    $injector->handleElement($token);
-                    $this->processToken($token, $i);
-                    $reprocess = true;
-                    break;
-                }
-                if (!$reprocess) {
-                    // ah, nothing interesting happened; do normal processing
-                    $this->swap($token);
-                    if ($token instanceof HTMLPurifier_Token_Start) {
-                        $this->stack[] = $token;
-                    } elseif ($token instanceof HTMLPurifier_Token_End) {
-                        throw new HTMLPurifier_Exception('Improper handling of end tag in start code; possible error in MakeWellFormed');
-                    }
-                }
-                continue;
-            }
-
-            // sanity check: we should be dealing with a closing tag
-            if (!$token instanceof HTMLPurifier_Token_End) {
-                throw new HTMLPurifier_Exception('Unaccounted for tag token in input stream, bug in HTML Purifier');
-            }
-
-            // make sure that we have something open
-            if (empty($this->stack)) {
-                if ($escape_invalid_tags) {
-                    if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text');
-                    $this->swap(new HTMLPurifier_Token_Text(
-                        $generator->generateFromToken($token)
-                    ));
-                } else {
-                    $this->remove();
-                    if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed');
-                }
-                $reprocess = true;
-                continue;
-            }
-
-            // first, check for the simplest case: everything closes neatly.
-            // Eventually, everything passes through here; if there are problems
-            // we modify the input stream accordingly and then punt, so that
-            // the tokens get processed again.
-            $current_parent = array_pop($this->stack);
-            if ($current_parent->name == $token->name) {
-                $token->start = $current_parent;
-                foreach ($this->injectors as $i => $injector) {
-                    if (isset($token->skip[$i])) continue;
-                    if ($token->rewind !== null && $token->rewind !== $i) continue;
-                    $injector->handleEnd($token);
-                    $this->processToken($token, $i);
-                    $this->stack[] = $current_parent;
-                    $reprocess = true;
-                    break;
-                }
-                continue;
-            }
-
-            // okay, so we're trying to close the wrong tag
-
-            // undo the pop previous pop
-            $this->stack[] = $current_parent;
-
-            // scroll back the entire nest, trying to find our tag.
-            // (feature could be to specify how far you'd like to go)
-            $size = count($this->stack);
-            // -2 because -1 is the last element, but we already checked that
-            $skipped_tags = false;
-            for ($j = $size - 2; $j >= 0; $j--) {
-                if ($this->stack[$j]->name == $token->name) {
-                    $skipped_tags = array_slice($this->stack, $j);
-                    break;
-                }
-            }
-
-            // we didn't find the tag, so remove
-            if ($skipped_tags === false) {
-                if ($escape_invalid_tags) {
-                    $this->swap(new HTMLPurifier_Token_Text(
-                        $generator->generateFromToken($token)
-                    ));
-                    if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text');
-                } else {
-                    $this->remove();
-                    if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed');
-                }
-                $reprocess = true;
-                continue;
-            }
-
-            // do errors, in REVERSE $j order: a,b,c with </a></b></c>
-            $c = count($skipped_tags);
-            if ($e) {
-                for ($j = $c - 1; $j > 0; $j--) {
-                    // notice we exclude $j == 0, i.e. the current ending tag, from
-                    // the errors...
-                    if (!isset($skipped_tags[$j]->armor['MakeWellFormed_TagClosedError'])) {
-                        $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', $skipped_tags[$j]);
-                    }
-                }
-            }
-
-            // insert tags, in FORWARD $j order: c,b,a with </a></b></c>
-            $replace = array($token);
-            for ($j = 1; $j < $c; $j++) {
-                // ...as well as from the insertions
-                $new_token = new HTMLPurifier_Token_End($skipped_tags[$j]->name);
-                $new_token->start = $skipped_tags[$j];
-                array_unshift($replace, $new_token);
-                if (isset($definition->info[$new_token->name]) && $definition->info[$new_token->name]->formatting) {
-                    $element = clone $skipped_tags[$j];
-                    $element->carryover = true;
-                    $element->armor['MakeWellFormed_TagClosedError'] = true;
-                    $replace[] = $element;
-                }
-            }
-            $this->processToken($replace);
-            $reprocess = true;
-            continue;
-        }
-
-        $context->destroy('CurrentNesting');
-        $context->destroy('InputTokens');
-        $context->destroy('InputIndex');
-        $context->destroy('CurrentToken');
-
-        unset($this->injectors, $this->stack, $this->tokens, $this->t);
-        return $tokens;
-    }
-
-    /**
-     * Processes arbitrary token values for complicated substitution patterns.
-     * In general:
-     *
-     * If $token is an array, it is a list of tokens to substitute for the
-     * current token. These tokens then get individually processed. If there
-     * is a leading integer in the list, that integer determines how many
-     * tokens from the stream should be removed.
-     *
-     * If $token is a regular token, it is swapped with the current token.
-     *
-     * If $token is false, the current token is deleted.
-     *
-     * If $token is an integer, that number of tokens (with the first token
-     * being the current one) will be deleted.
-     *
-     * @param $token Token substitution value
-     * @param $injector Injector that performed the substitution; default is if
-     *        this is not an injector related operation.
-     */
-    protected function processToken($token, $injector = -1) {
-
-        // normalize forms of token
-        if (is_object($token)) $token = array(1, $token);
-        if (is_int($token))    $token = array($token);
-        if ($token === false)  $token = array(1);
-        if (!is_array($token)) throw new HTMLPurifier_Exception('Invalid token type from injector');
-        if (!is_int($token[0])) array_unshift($token, 1);
-        if ($token[0] === 0) throw new HTMLPurifier_Exception('Deleting zero tokens is not valid');
-
-        // $token is now an array with the following form:
-        // array(number nodes to delete, new node 1, new node 2, ...)
-
-        $delete = array_shift($token);
-        $old = array_splice($this->tokens, $this->t, $delete, $token);
-
-        if ($injector > -1) {
-            // determine appropriate skips
-            $oldskip = isset($old[0]) ? $old[0]->skip : array();
-            foreach ($token as $object) {
-                $object->skip = $oldskip;
-                $object->skip[$injector] = true;
-            }
-        }
-
-    }
-
-    /**
-     * Inserts a token before the current token. Cursor now points to this token
-     */
-    private function insertBefore($token) {
-        array_splice($this->tokens, $this->t, 0, array($token));
-    }
-
-    /**
-     * Removes current token. Cursor now points to new token occupying previously
-     * occupied space.
-     */
-    private function remove() {
-        array_splice($this->tokens, $this->t, 1);
-    }
-
-    /**
-     * Swap current token with new token. Cursor points to new token (no change).
-     */
-    private function swap($token) {
-        $this->tokens[$this->t] = $token;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Strategy/RemoveForeignElements.php b/library/HTMLPurifier/Strategy/RemoveForeignElements.php
deleted file mode 100644 (file)
index cf3a33e..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-<?php
-
-/**
- * Removes all unrecognized tags from the list of tokens.
- *
- * This strategy iterates through all the tokens and removes unrecognized
- * tokens. If a token is not recognized but a TagTransform is defined for
- * that element, the element will be transformed accordingly.
- */
-
-class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
-{
-
-    public function execute($tokens, $config, $context) {
-        $definition = $config->getHTMLDefinition();
-        $generator = new HTMLPurifier_Generator($config, $context);
-        $result = array();
-
-        $escape_invalid_tags = $config->get('Core.EscapeInvalidTags');
-        $remove_invalid_img  = $config->get('Core.RemoveInvalidImg');
-
-        // currently only used to determine if comments should be kept
-        $trusted = $config->get('HTML.Trusted');
-
-        $remove_script_contents = $config->get('Core.RemoveScriptContents');
-        $hidden_elements     = $config->get('Core.HiddenElements');
-
-        // remove script contents compatibility
-        if ($remove_script_contents === true) {
-            $hidden_elements['script'] = true;
-        } elseif ($remove_script_contents === false && isset($hidden_elements['script'])) {
-            unset($hidden_elements['script']);
-        }
-
-        $attr_validator = new HTMLPurifier_AttrValidator();
-
-        // removes tokens until it reaches a closing tag with its value
-        $remove_until = false;
-
-        // converts comments into text tokens when this is equal to a tag name
-        $textify_comments = false;
-
-        $token = false;
-        $context->register('CurrentToken', $token);
-
-        $e = false;
-        if ($config->get('Core.CollectErrors')) {
-            $e =& $context->get('ErrorCollector');
-        }
-
-        foreach($tokens as $token) {
-            if ($remove_until) {
-                if (empty($token->is_tag) || $token->name !== $remove_until) {
-                    continue;
-                }
-            }
-            if (!empty( $token->is_tag )) {
-                // DEFINITION CALL
-
-                // before any processing, try to transform the element
-                if (
-                    isset($definition->info_tag_transform[$token->name])
-                ) {
-                    $original_name = $token->name;
-                    // there is a transformation for this tag
-                    // DEFINITION CALL
-                    $token = $definition->
-                                info_tag_transform[$token->name]->
-                                    transform($token, $config, $context);
-                    if ($e) $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Tag transform', $original_name);
-                }
-
-                if (isset($definition->info[$token->name])) {
-
-                    // mostly everything's good, but
-                    // we need to make sure required attributes are in order
-                    if (
-                        ($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) &&
-                        $definition->info[$token->name]->required_attr &&
-                        ($token->name != 'img' || $remove_invalid_img) // ensure config option still works
-                    ) {
-                        $attr_validator->validateToken($token, $config, $context);
-                        $ok = true;
-                        foreach ($definition->info[$token->name]->required_attr as $name) {
-                            if (!isset($token->attr[$name])) {
-                                $ok = false;
-                                break;
-                            }
-                        }
-                        if (!$ok) {
-                            if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Missing required attribute', $name);
-                            continue;
-                        }
-                        $token->armor['ValidateAttributes'] = true;
-                    }
-
-                    if (isset($hidden_elements[$token->name]) && $token instanceof HTMLPurifier_Token_Start) {
-                        $textify_comments = $token->name;
-                    } elseif ($token->name === $textify_comments && $token instanceof HTMLPurifier_Token_End) {
-                        $textify_comments = false;
-                    }
-
-                } elseif ($escape_invalid_tags) {
-                    // invalid tag, generate HTML representation and insert in
-                    if ($e) $e->send(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text');
-                    $token = new HTMLPurifier_Token_Text(
-                        $generator->generateFromToken($token)
-                    );
-                } else {
-                    // check if we need to destroy all of the tag's children
-                    // CAN BE GENERICIZED
-                    if (isset($hidden_elements[$token->name])) {
-                        if ($token instanceof HTMLPurifier_Token_Start) {
-                            $remove_until = $token->name;
-                        } elseif ($token instanceof HTMLPurifier_Token_Empty) {
-                            // do nothing: we're still looking
-                        } else {
-                            $remove_until = false;
-                        }
-                        if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign meta element removed');
-                    } else {
-                        if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign element removed');
-                    }
-                    continue;
-                }
-            } elseif ($token instanceof HTMLPurifier_Token_Comment) {
-                // textify comments in script tags when they are allowed
-                if ($textify_comments !== false) {
-                    $data = $token->data;
-                    $token = new HTMLPurifier_Token_Text($data);
-                } elseif ($trusted) {
-                    // keep, but perform comment cleaning
-                    if ($e) {
-                        // perform check whether or not there's a trailing hyphen
-                        if (substr($token->data, -1) == '-') {
-                            $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Trailing hyphen in comment removed');
-                        }
-                    }
-                    $token->data = rtrim($token->data, '-');
-                    $found_double_hyphen = false;
-                    while (strpos($token->data, '--') !== false) {
-                        if ($e && !$found_double_hyphen) {
-                            $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Hyphens in comment collapsed');
-                        }
-                        $found_double_hyphen = true; // prevent double-erroring
-                        $token->data = str_replace('--', '-', $token->data);
-                    }
-                } else {
-                    // strip comments
-                    if ($e) $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed');
-                    continue;
-                }
-            } elseif ($token instanceof HTMLPurifier_Token_Text) {
-            } else {
-                continue;
-            }
-            $result[] = $token;
-        }
-        if ($remove_until && $e) {
-            // we removed tokens until the end, throw error
-            $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Token removed to end', $remove_until);
-        }
-
-        $context->destroy('CurrentToken');
-
-        return $result;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Strategy/ValidateAttributes.php b/library/HTMLPurifier/Strategy/ValidateAttributes.php
deleted file mode 100644 (file)
index c3328a9..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
- * Validate all attributes in the tokens.
- */
-
-class HTMLPurifier_Strategy_ValidateAttributes extends HTMLPurifier_Strategy
-{
-
-    public function execute($tokens, $config, $context) {
-
-        // setup validator
-        $validator = new HTMLPurifier_AttrValidator();
-
-        $token = false;
-        $context->register('CurrentToken', $token);
-
-        foreach ($tokens as $key => $token) {
-
-            // only process tokens that have attributes,
-            //   namely start and empty tags
-            if (!$token instanceof HTMLPurifier_Token_Start && !$token instanceof HTMLPurifier_Token_Empty) continue;
-
-            // skip tokens that are armored
-            if (!empty($token->armor['ValidateAttributes'])) continue;
-
-            // note that we have no facilities here for removing tokens
-            $validator->validateToken($token, $config, $context);
-
-            $tokens[$key] = $token; // for PHP 4
-        }
-        $context->destroy('CurrentToken');
-
-        return $tokens;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/StringHash.php b/library/HTMLPurifier/StringHash.php
deleted file mode 100644 (file)
index 62085c5..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
- * This is in almost every respect equivalent to an array except
- * that it keeps track of which keys were accessed.
- *
- * @warning For the sake of backwards compatibility with early versions
- *     of PHP 5, you must not use the $hash[$key] syntax; if you do
- *     our version of offsetGet is never called.
- */
-class HTMLPurifier_StringHash extends ArrayObject
-{
-    protected $accessed = array();
-
-    /**
-     * Retrieves a value, and logs the access.
-     */
-    public function offsetGet($index) {
-        $this->accessed[$index] = true;
-        return parent::offsetGet($index);
-    }
-
-    /**
-     * Returns a lookup array of all array indexes that have been accessed.
-     * @return Array in form array($index => true).
-     */
-    public function getAccessed() {
-        return $this->accessed;
-    }
-
-    /**
-     * Resets the access array.
-     */
-    public function resetAccessed() {
-        $this->accessed = array();
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/StringHashParser.php b/library/HTMLPurifier/StringHashParser.php
deleted file mode 100644 (file)
index f3e70c7..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-<?php
-
-/**
- * Parses string hash files. File format is as such:
- *
- *      DefaultKeyValue
- *      KEY: Value
- *      KEY2: Value2
- *      --MULTILINE-KEY--
- *      Multiline
- *      value.
- *
- * Which would output something similar to:
- *
- *      array(
- *          'ID' => 'DefaultKeyValue',
- *          'KEY' => 'Value',
- *          'KEY2' => 'Value2',
- *          'MULTILINE-KEY' => "Multiline\nvalue.\n",
- *      )
- *
- * We use this as an easy to use file-format for configuration schema
- * files, but the class itself is usage agnostic.
- *
- * You can use ---- to forcibly terminate parsing of a single string-hash;
- * this marker is used in multi string-hashes to delimit boundaries.
- */
-class HTMLPurifier_StringHashParser
-{
-
-    public $default = 'ID';
-
-    /**
-     * Parses a file that contains a single string-hash.
-     */
-    public function parseFile($file) {
-        if (!file_exists($file)) return false;
-        $fh = fopen($file, 'r');
-        if (!$fh) return false;
-        $ret = $this->parseHandle($fh);
-        fclose($fh);
-        return $ret;
-    }
-
-    /**
-     * Parses a file that contains multiple string-hashes delimited by '----'
-     */
-    public function parseMultiFile($file) {
-        if (!file_exists($file)) return false;
-        $ret = array();
-        $fh = fopen($file, 'r');
-        if (!$fh) return false;
-        while (!feof($fh)) {
-            $ret[] = $this->parseHandle($fh);
-        }
-        fclose($fh);
-        return $ret;
-    }
-
-    /**
-     * Internal parser that acepts a file handle.
-     * @note While it's possible to simulate in-memory parsing by using
-     *       custom stream wrappers, if such a use-case arises we should
-     *       factor out the file handle into its own class.
-     * @param $fh File handle with pointer at start of valid string-hash
-     *            block.
-     */
-    protected function parseHandle($fh) {
-        $state   = false;
-        $single  = false;
-        $ret     = array();
-        do {
-            $line = fgets($fh);
-            if ($line === false) break;
-            $line = rtrim($line, "\n\r");
-            if (!$state && $line === '') continue;
-            if ($line === '----') break;
-            if (strncmp('--#', $line, 3) === 0) {
-                // Comment
-                continue;
-            } elseif (strncmp('--', $line, 2) === 0) {
-                // Multiline declaration
-                $state = trim($line, '- ');
-                if (!isset($ret[$state])) $ret[$state] = '';
-                continue;
-            } elseif (!$state) {
-                $single = true;
-                if (strpos($line, ':') !== false) {
-                    // Single-line declaration
-                    list($state, $line) = explode(':', $line, 2);
-                    $line = trim($line);
-                } else {
-                    // Use default declaration
-                    $state  = $this->default;
-                }
-            }
-            if ($single) {
-                $ret[$state] = $line;
-                $single = false;
-                $state  = false;
-            } else {
-                $ret[$state] .= "$line\n";
-            }
-        } while (!feof($fh));
-        return $ret;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/TagTransform.php b/library/HTMLPurifier/TagTransform.php
deleted file mode 100644 (file)
index 210a447..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-/**
- * Defines a mutation of an obsolete tag into a valid tag.
- */
-abstract class HTMLPurifier_TagTransform
-{
-
-    /**
-     * Tag name to transform the tag to.
-     */
-    public $transform_to;
-
-    /**
-     * Transforms the obsolete tag into the valid tag.
-     * @param $tag Tag to be transformed.
-     * @param $config Mandatory HTMLPurifier_Config object
-     * @param $context Mandatory HTMLPurifier_Context object
-     */
-    abstract public function transform($tag, $config, $context);
-
-    /**
-     * Prepends CSS properties to the style attribute, creating the
-     * attribute if it doesn't exist.
-     * @warning Copied over from AttrTransform, be sure to keep in sync
-     * @param $attr Attribute array to process (passed by reference)
-     * @param $css CSS to prepend
-     */
-    protected function prependCSS(&$attr, $css) {
-        $attr['style'] = isset($attr['style']) ? $attr['style'] : '';
-        $attr['style'] = $css . $attr['style'];
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/TagTransform/Font.php b/library/HTMLPurifier/TagTransform/Font.php
deleted file mode 100644 (file)
index ed24637..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php
-
-/**
- * Transforms FONT tags to the proper form (SPAN with CSS styling)
- *
- * This transformation takes the three proprietary attributes of FONT and
- * transforms them into their corresponding CSS attributes.  These are color,
- * face, and size.
- *
- * @note Size is an interesting case because it doesn't map cleanly to CSS.
- *       Thanks to
- *       http://style.cleverchimp.com/font_size_intervals/altintervals.html
- *       for reasonable mappings.
- * @warning This doesn't work completely correctly; specifically, this
- *          TagTransform operates before well-formedness is enforced, so
- *          the "active formatting elements" algorithm doesn't get applied.
- */
-class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform
-{
-
-    public $transform_to = 'span';
-
-    protected $_size_lookup = array(
-        '0' => 'xx-small',
-        '1' => 'xx-small',
-        '2' => 'small',
-        '3' => 'medium',
-        '4' => 'large',
-        '5' => 'x-large',
-        '6' => 'xx-large',
-        '7' => '300%',
-        '-1' => 'smaller',
-        '-2' => '60%',
-        '+1' => 'larger',
-        '+2' => '150%',
-        '+3' => '200%',
-        '+4' => '300%'
-    );
-
-    public function transform($tag, $config, $context) {
-
-        if ($tag instanceof HTMLPurifier_Token_End) {
-            $new_tag = clone $tag;
-            $new_tag->name = $this->transform_to;
-            return $new_tag;
-        }
-
-        $attr = $tag->attr;
-        $prepend_style = '';
-
-        // handle color transform
-        if (isset($attr['color'])) {
-            $prepend_style .= 'color:' . $attr['color'] . ';';
-            unset($attr['color']);
-        }
-
-        // handle face transform
-        if (isset($attr['face'])) {
-            $prepend_style .= 'font-family:' . $attr['face'] . ';';
-            unset($attr['face']);
-        }
-
-        // handle size transform
-        if (isset($attr['size'])) {
-            // normalize large numbers
-            if ($attr['size']{0} == '+' || $attr['size']{0} == '-') {
-                $size = (int) $attr['size'];
-                if ($size < -2) $attr['size'] = '-2';
-                if ($size > 4)  $attr['size'] = '+4';
-            } else {
-                $size = (int) $attr['size'];
-                if ($size > 7) $attr['size'] = '7';
-            }
-            if (isset($this->_size_lookup[$attr['size']])) {
-                $prepend_style .= 'font-size:' .
-                  $this->_size_lookup[$attr['size']] . ';';
-            }
-            unset($attr['size']);
-        }
-
-        if ($prepend_style) {
-            $attr['style'] = isset($attr['style']) ?
-                $prepend_style . $attr['style'] :
-                $prepend_style;
-        }
-
-        $new_tag = clone $tag;
-        $new_tag->name = $this->transform_to;
-        $new_tag->attr = $attr;
-
-        return $new_tag;
-
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/TagTransform/Simple.php b/library/HTMLPurifier/TagTransform/Simple.php
deleted file mode 100644 (file)
index 0e36130..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-/**
- * Simple transformation, just change tag name to something else,
- * and possibly add some styling. This will cover most of the deprecated
- * tag cases.
- */
-class HTMLPurifier_TagTransform_Simple extends HTMLPurifier_TagTransform
-{
-
-    protected $style;
-
-    /**
-     * @param $transform_to Tag name to transform to.
-     * @param $style CSS style to add to the tag
-     */
-    public function __construct($transform_to, $style = null) {
-        $this->transform_to = $transform_to;
-        $this->style = $style;
-    }
-
-    public function transform($tag, $config, $context) {
-        $new_tag = clone $tag;
-        $new_tag->name = $this->transform_to;
-        if (!is_null($this->style) &&
-            ($new_tag instanceof HTMLPurifier_Token_Start || $new_tag instanceof HTMLPurifier_Token_Empty)
-        ) {
-            $this->prependCSS($new_tag->attr, $this->style);
-        }
-        return $new_tag;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Token.php b/library/HTMLPurifier/Token.php
deleted file mode 100644 (file)
index 7900e6c..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-<?php
-
-/**
- * Abstract base token class that all others inherit from.
- */
-class HTMLPurifier_Token {
-    public $line; /**< Line number node was on in source document. Null if unknown. */
-    public $col;  /**< Column of line node was on in source document. Null if unknown. */
-
-    /**
-     * Lookup array of processing that this token is exempt from.
-     * Currently, valid values are "ValidateAttributes" and
-     * "MakeWellFormed_TagClosedError"
-     */
-    public $armor = array();
-
-    /**
-     * Used during MakeWellFormed.
-     */
-    public $skip;
-    public $rewind;
-    public $carryover;
-
-    public function __get($n) {
-      if ($n === 'type') {
-        trigger_error('Deprecated type property called; use instanceof', E_USER_NOTICE);
-        switch (get_class($this)) {
-          case 'HTMLPurifier_Token_Start':      return 'start';
-          case 'HTMLPurifier_Token_Empty':      return 'empty';
-          case 'HTMLPurifier_Token_End':        return 'end';
-          case 'HTMLPurifier_Token_Text':       return 'text';
-          case 'HTMLPurifier_Token_Comment':    return 'comment';
-          default: return null;
-        }
-      }
-    }
-
-    /**
-     * Sets the position of the token in the source document.
-     */
-    public function position($l = null, $c = null) {
-        $this->line = $l;
-        $this->col  = $c;
-    }
-
-    /**
-     * Convenience function for DirectLex settings line/col position.
-     */
-    public function rawPosition($l, $c) {
-        if ($c === -1) $l++;
-        $this->line = $l;
-        $this->col  = $c;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Token/Comment.php b/library/HTMLPurifier/Token/Comment.php
deleted file mode 100644 (file)
index dc6bdca..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/**
- * Concrete comment token class. Generally will be ignored.
- */
-class HTMLPurifier_Token_Comment extends HTMLPurifier_Token
-{
-    public $data; /**< Character data within comment. */
-    public $is_whitespace = true;
-    /**
-     * Transparent constructor.
-     *
-     * @param $data String comment data.
-     */
-    public function __construct($data, $line = null, $col = null) {
-        $this->data = $data;
-        $this->line = $line;
-        $this->col  = $col;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Token/Empty.php b/library/HTMLPurifier/Token/Empty.php
deleted file mode 100644 (file)
index 2a82b47..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-
-/**
- * Concrete empty token class.
- */
-class HTMLPurifier_Token_Empty extends HTMLPurifier_Token_Tag
-{
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Token/End.php b/library/HTMLPurifier/Token/End.php
deleted file mode 100644 (file)
index 353e79d..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<?php
-
-/**
- * Concrete end token class.
- *
- * @warning This class accepts attributes even though end tags cannot. This
- * is for optimization reasons, as under normal circumstances, the Lexers
- * do not pass attributes.
- */
-class HTMLPurifier_Token_End extends HTMLPurifier_Token_Tag
-{
-    /**
-     * Token that started this node. Added by MakeWellFormed. Please
-     * do not edit this!
-     */
-    public $start;
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Token/Start.php b/library/HTMLPurifier/Token/Start.php
deleted file mode 100644 (file)
index e0e14fc..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-
-/**
- * Concrete start token class.
- */
-class HTMLPurifier_Token_Start extends HTMLPurifier_Token_Tag
-{
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Token/Tag.php b/library/HTMLPurifier/Token/Tag.php
deleted file mode 100644 (file)
index 798be02..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-/**
- * Abstract class of a tag token (start, end or empty), and its behavior.
- */
-class HTMLPurifier_Token_Tag extends HTMLPurifier_Token
-{
-    /**
-     * Static bool marker that indicates the class is a tag.
-     *
-     * This allows us to check objects with <tt>!empty($obj->is_tag)</tt>
-     * without having to use a function call <tt>is_a()</tt>.
-     */
-    public $is_tag = true;
-
-    /**
-     * The lower-case name of the tag, like 'a', 'b' or 'blockquote'.
-     *
-     * @note Strictly speaking, XML tags are case sensitive, so we shouldn't
-     * be lower-casing them, but these tokens cater to HTML tags, which are
-     * insensitive.
-     */
-    public $name;
-
-    /**
-     * Associative array of the tag's attributes.
-     */
-    public $attr = array();
-
-    /**
-     * Non-overloaded constructor, which lower-cases passed tag name.
-     *
-     * @param $name String name.
-     * @param $attr Associative array of attributes.
-     */
-    public function __construct($name, $attr = array(), $line = null, $col = null) {
-        $this->name = ctype_lower($name) ? $name : strtolower($name);
-        foreach ($attr as $key => $value) {
-            // normalization only necessary when key is not lowercase
-            if (!ctype_lower($key)) {
-                $new_key = strtolower($key);
-                if (!isset($attr[$new_key])) {
-                    $attr[$new_key] = $attr[$key];
-                }
-                if ($new_key !== $key) {
-                    unset($attr[$key]);
-                }
-            }
-        }
-        $this->attr = $attr;
-        $this->line = $line;
-        $this->col  = $col;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/Token/Text.php b/library/HTMLPurifier/Token/Text.php
deleted file mode 100644 (file)
index 82efd82..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-
-/**
- * Concrete text token class.
- *
- * Text tokens comprise of regular parsed character data (PCDATA) and raw
- * character data (from the CDATA sections). Internally, their
- * data is parsed with all entities expanded. Surprisingly, the text token
- * does have a "tag name" called #PCDATA, which is how the DTD represents it
- * in permissible child nodes.
- */
-class HTMLPurifier_Token_Text extends HTMLPurifier_Token
-{
-
-    public $name = '#PCDATA'; /**< PCDATA tag name compatible with DTD. */
-    public $data; /**< Parsed character data of text. */
-    public $is_whitespace; /**< Bool indicating if node is whitespace. */
-
-    /**
-     * Constructor, accepts data and determines if it is whitespace.
-     *
-     * @param $data String parsed character data.
-     */
-    public function __construct($data, $line = null, $col = null) {
-        $this->data = $data;
-        $this->is_whitespace = ctype_space($data);
-        $this->line = $line;
-        $this->col  = $col;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/TokenFactory.php b/library/HTMLPurifier/TokenFactory.php
deleted file mode 100644 (file)
index 7cf48fb..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-<?php
-
-/**
- * Factory for token generation.
- *
- * @note Doing some benchmarking indicates that the new operator is much
- *       slower than the clone operator (even discounting the cost of the
- *       constructor).  This class is for that optimization.
- *       Other then that, there's not much point as we don't
- *       maintain parallel HTMLPurifier_Token hierarchies (the main reason why
- *       you'd want to use an abstract factory).
- * @todo Port DirectLex to use this
- */
-class HTMLPurifier_TokenFactory
-{
-
-    /**
-     * Prototypes that will be cloned.
-     * @private
-     */
-    // p stands for prototype
-    private $p_start, $p_end, $p_empty, $p_text, $p_comment;
-
-    /**
-     * Generates blank prototypes for cloning.
-     */
-    public function __construct() {
-        $this->p_start  = new HTMLPurifier_Token_Start('', array());
-        $this->p_end    = new HTMLPurifier_Token_End('');
-        $this->p_empty  = new HTMLPurifier_Token_Empty('', array());
-        $this->p_text   = new HTMLPurifier_Token_Text('');
-        $this->p_comment= new HTMLPurifier_Token_Comment('');
-    }
-
-    /**
-     * Creates a HTMLPurifier_Token_Start.
-     * @param $name Tag name
-     * @param $attr Associative array of attributes
-     * @return Generated HTMLPurifier_Token_Start
-     */
-    public function createStart($name, $attr = array()) {
-        $p = clone $this->p_start;
-        $p->__construct($name, $attr);
-        return $p;
-    }
-
-    /**
-     * Creates a HTMLPurifier_Token_End.
-     * @param $name Tag name
-     * @return Generated HTMLPurifier_Token_End
-     */
-    public function createEnd($name) {
-        $p = clone $this->p_end;
-        $p->__construct($name);
-        return $p;
-    }
-
-    /**
-     * Creates a HTMLPurifier_Token_Empty.
-     * @param $name Tag name
-     * @param $attr Associative array of attributes
-     * @return Generated HTMLPurifier_Token_Empty
-     */
-    public function createEmpty($name, $attr = array()) {
-        $p = clone $this->p_empty;
-        $p->__construct($name, $attr);
-        return $p;
-    }
-
-    /**
-     * Creates a HTMLPurifier_Token_Text.
-     * @param $data Data of text token
-     * @return Generated HTMLPurifier_Token_Text
-     */
-    public function createText($data) {
-        $p = clone $this->p_text;
-        $p->__construct($data);
-        return $p;
-    }
-
-    /**
-     * Creates a HTMLPurifier_Token_Comment.
-     * @param $data Data of comment token
-     * @return Generated HTMLPurifier_Token_Comment
-     */
-    public function createComment($data) {
-        $p = clone $this->p_comment;
-        $p->__construct($data);
-        return $p;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/URI.php b/library/HTMLPurifier/URI.php
deleted file mode 100644 (file)
index 8b50d0d..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-<?php
-
-/**
- * HTML Purifier's internal representation of a URI.
- * @note
- *      Internal data-structures are completely escaped. If the data needs
- *      to be used in a non-URI context (which is very unlikely), be sure
- *      to decode it first. The URI may not necessarily be well-formed until
- *      validate() is called.
- */
-class HTMLPurifier_URI
-{
-
-    public $scheme, $userinfo, $host, $port, $path, $query, $fragment;
-
-    /**
-     * @note Automatically normalizes scheme and port
-     */
-    public function __construct($scheme, $userinfo, $host, $port, $path, $query, $fragment) {
-        $this->scheme = is_null($scheme) || ctype_lower($scheme) ? $scheme : strtolower($scheme);
-        $this->userinfo = $userinfo;
-        $this->host = $host;
-        $this->port = is_null($port) ? $port : (int) $port;
-        $this->path = $path;
-        $this->query = $query;
-        $this->fragment = $fragment;
-    }
-
-    /**
-     * Retrieves a scheme object corresponding to the URI's scheme/default
-     * @param $config Instance of HTMLPurifier_Config
-     * @param $context Instance of HTMLPurifier_Context
-     * @return Scheme object appropriate for validating this URI
-     */
-    public function getSchemeObj($config, $context) {
-        $registry = HTMLPurifier_URISchemeRegistry::instance();
-        if ($this->scheme !== null) {
-            $scheme_obj = $registry->getScheme($this->scheme, $config, $context);
-            if (!$scheme_obj) return false; // invalid scheme, clean it out
-        } else {
-            // no scheme: retrieve the default one
-            $def = $config->getDefinition('URI');
-            $scheme_obj = $registry->getScheme($def->defaultScheme, $config, $context);
-            if (!$scheme_obj) {
-                // something funky happened to the default scheme object
-                trigger_error(
-                    'Default scheme object "' . $def->defaultScheme . '" was not readable',
-                    E_USER_WARNING
-                );
-                return false;
-            }
-        }
-        return $scheme_obj;
-    }
-
-    /**
-     * Generic validation method applicable for all schemes. May modify
-     * this URI in order to get it into a compliant form.
-     * @param $config Instance of HTMLPurifier_Config
-     * @param $context Instance of HTMLPurifier_Context
-     * @return True if validation/filtering succeeds, false if failure
-     */
-    public function validate($config, $context) {
-
-        // ABNF definitions from RFC 3986
-        $chars_sub_delims = '!$&\'()*+,;=';
-        $chars_gen_delims = ':/?#[]@';
-        $chars_pchar = $chars_sub_delims . ':@';
-
-        // validate scheme (MUST BE FIRST!)
-        if (!is_null($this->scheme) && is_null($this->host)) {
-            $def = $config->getDefinition('URI');
-            if ($def->defaultScheme === $this->scheme) {
-                $this->scheme = null;
-            }
-        }
-
-        // validate host
-        if (!is_null($this->host)) {
-            $host_def = new HTMLPurifier_AttrDef_URI_Host();
-            $this->host = $host_def->validate($this->host, $config, $context);
-            if ($this->host === false) $this->host = null;
-        }
-
-        // validate username
-        if (!is_null($this->userinfo)) {
-            $encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . ':');
-            $this->userinfo = $encoder->encode($this->userinfo);
-        }
-
-        // validate port
-        if (!is_null($this->port)) {
-            if ($this->port < 1 || $this->port > 65535) $this->port = null;
-        }
-
-        // validate path
-        $path_parts = array();
-        $segments_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/');
-        if (!is_null($this->host)) {
-            // path-abempty (hier and relative)
-            $this->path = $segments_encoder->encode($this->path);
-        } elseif ($this->path !== '' && $this->path[0] === '/') {
-            // path-absolute (hier and relative)
-            if (strlen($this->path) >= 2 && $this->path[1] === '/') {
-                // This shouldn't ever happen!
-                $this->path = '';
-            } else {
-                $this->path = $segments_encoder->encode($this->path);
-            }
-        } elseif (!is_null($this->scheme) && $this->path !== '') {
-            // path-rootless (hier)
-            // Short circuit evaluation means we don't need to check nz
-            $this->path = $segments_encoder->encode($this->path);
-        } elseif (is_null($this->scheme) && $this->path !== '') {
-            // path-noscheme (relative)
-            // (once again, not checking nz)
-            $segment_nc_encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . '@');
-            $c = strpos($this->path, '/');
-            if ($c !== false) {
-                $this->path =
-                    $segment_nc_encoder->encode(substr($this->path, 0, $c)) .
-                    $segments_encoder->encode(substr($this->path, $c));
-            } else {
-                $this->path = $segment_nc_encoder->encode($this->path);
-            }
-        } else {
-            // path-empty (hier and relative)
-            $this->path = ''; // just to be safe
-        }
-
-        // qf = query and fragment
-        $qf_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/?');
-
-        if (!is_null($this->query)) {
-            $this->query = $qf_encoder->encode($this->query);
-        }
-
-        if (!is_null($this->fragment)) {
-            $this->fragment = $qf_encoder->encode($this->fragment);
-        }
-
-        return true;
-
-    }
-
-    /**
-     * Convert URI back to string
-     * @return String URI appropriate for output
-     */
-    public function toString() {
-        // reconstruct authority
-        $authority = null;
-        if (!is_null($this->host)) {
-            $authority = '';
-            if(!is_null($this->userinfo)) $authority .= $this->userinfo . '@';
-            $authority .= $this->host;
-            if(!is_null($this->port))     $authority .= ':' . $this->port;
-        }
-
-        // reconstruct the result
-        $result = '';
-        if (!is_null($this->scheme))    $result .= $this->scheme . ':';
-        if (!is_null($authority))       $result .=  '//' . $authority;
-        $result .= $this->path;
-        if (!is_null($this->query))     $result .= '?' . $this->query;
-        if (!is_null($this->fragment))  $result .= '#' . $this->fragment;
-
-        return $result;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/URIDefinition.php b/library/HTMLPurifier/URIDefinition.php
deleted file mode 100644 (file)
index ea2b8fe..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-
-class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
-{
-
-    public $type = 'URI';
-    protected $filters = array();
-    protected $postFilters = array();
-    protected $registeredFilters = array();
-
-    /**
-     * HTMLPurifier_URI object of the base specified at %URI.Base
-     */
-    public $base;
-
-    /**
-     * String host to consider "home" base, derived off of $base
-     */
-    public $host;
-
-    /**
-     * Name of default scheme based on %URI.DefaultScheme and %URI.Base
-     */
-    public $defaultScheme;
-
-    public function __construct() {
-        $this->registerFilter(new HTMLPurifier_URIFilter_DisableExternal());
-        $this->registerFilter(new HTMLPurifier_URIFilter_DisableExternalResources());
-        $this->registerFilter(new HTMLPurifier_URIFilter_HostBlacklist());
-        $this->registerFilter(new HTMLPurifier_URIFilter_MakeAbsolute());
-        $this->registerFilter(new HTMLPurifier_URIFilter_Munge());
-    }
-
-    public function registerFilter($filter) {
-        $this->registeredFilters[$filter->name] = $filter;
-    }
-
-    public function addFilter($filter, $config) {
-        $r = $filter->prepare($config);
-        if ($r === false) return; // null is ok, for backwards compat
-        if ($filter->post) {
-            $this->postFilters[$filter->name] = $filter;
-        } else {
-            $this->filters[$filter->name] = $filter;
-        }
-    }
-
-    protected function doSetup($config) {
-        $this->setupMemberVariables($config);
-        $this->setupFilters($config);
-    }
-
-    protected function setupFilters($config) {
-        foreach ($this->registeredFilters as $name => $filter) {
-            $conf = $config->get('URI.' . $name);
-            if ($conf !== false && $conf !== null) {
-                $this->addFilter($filter, $config);
-            }
-        }
-        unset($this->registeredFilters);
-    }
-
-    protected function setupMemberVariables($config) {
-        $this->host = $config->get('URI.Host');
-        $base_uri = $config->get('URI.Base');
-        if (!is_null($base_uri)) {
-            $parser = new HTMLPurifier_URIParser();
-            $this->base = $parser->parse($base_uri);
-            $this->defaultScheme = $this->base->scheme;
-            if (is_null($this->host)) $this->host = $this->base->host;
-        }
-        if (is_null($this->defaultScheme)) $this->defaultScheme = $config->get('URI.DefaultScheme');
-    }
-
-    public function filter(&$uri, $config, $context) {
-        foreach ($this->filters as $name => $f) {
-            $result = $f->filter($uri, $config, $context);
-            if (!$result) return false;
-        }
-        return true;
-    }
-
-    public function postFilter(&$uri, $config, $context) {
-        foreach ($this->postFilters as $name => $f) {
-            $result = $f->filter($uri, $config, $context);
-            if (!$result) return false;
-        }
-        return true;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/URIFilter.php b/library/HTMLPurifier/URIFilter.php
deleted file mode 100644 (file)
index c116f93..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-/**
- * Chainable filters for custom URI processing.
- *
- * These filters can perform custom actions on a URI filter object,
- * including transformation or blacklisting.
- *
- * @warning This filter is called before scheme object validation occurs.
- *          Make sure, if you require a specific scheme object, you
- *          you check that it exists. This allows filters to convert
- *          proprietary URI schemes into regular ones.
- */
-abstract class HTMLPurifier_URIFilter
-{
-
-    /**
-     * Unique identifier of filter
-     */
-    public $name;
-
-    /**
-     * True if this filter should be run after scheme validation.
-     */
-    public $post = false;
-
-    /**
-     * Performs initialization for the filter
-     */
-    public function prepare($config) {return true;}
-
-    /**
-     * Filter a URI object
-     * @param $uri Reference to URI object variable
-     * @param $config Instance of HTMLPurifier_Config
-     * @param $context Instance of HTMLPurifier_Context
-     * @return bool Whether or not to continue processing: false indicates
-     *         URL is no good, true indicates continue processing. Note that
-     *         all changes are committed directly on the URI object
-     */
-    abstract public function filter(&$uri, $config, $context);
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/URIFilter/DisableExternal.php b/library/HTMLPurifier/URIFilter/DisableExternal.php
deleted file mode 100644 (file)
index d8a39a5..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-class HTMLPurifier_URIFilter_DisableExternal extends HTMLPurifier_URIFilter
-{
-    public $name = 'DisableExternal';
-    protected $ourHostParts = false;
-    public function prepare($config) {
-        $our_host = $config->getDefinition('URI')->host;
-        if ($our_host !== null) $this->ourHostParts = array_reverse(explode('.', $our_host));
-    }
-    public function filter(&$uri, $config, $context) {
-        if (is_null($uri->host)) return true;
-        if ($this->ourHostParts === false) return false;
-        $host_parts = array_reverse(explode('.', $uri->host));
-        foreach ($this->ourHostParts as $i => $x) {
-            if (!isset($host_parts[$i])) return false;
-            if ($host_parts[$i] != $this->ourHostParts[$i]) return false;
-        }
-        return true;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/URIFilter/DisableExternalResources.php b/library/HTMLPurifier/URIFilter/DisableExternalResources.php
deleted file mode 100644 (file)
index 881abc4..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-class HTMLPurifier_URIFilter_DisableExternalResources extends HTMLPurifier_URIFilter_DisableExternal
-{
-    public $name = 'DisableExternalResources';
-    public function filter(&$uri, $config, $context) {
-        if (!$context->get('EmbeddedURI', true)) return true;
-        return parent::filter($uri, $config, $context);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/URIFilter/HostBlacklist.php b/library/HTMLPurifier/URIFilter/HostBlacklist.php
deleted file mode 100644 (file)
index 045aa09..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-class HTMLPurifier_URIFilter_HostBlacklist extends HTMLPurifier_URIFilter
-{
-    public $name = 'HostBlacklist';
-    protected $blacklist = array();
-    public function prepare($config) {
-        $this->blacklist = $config->get('URI.HostBlacklist');
-        return true;
-    }
-    public function filter(&$uri, $config, $context) {
-        foreach($this->blacklist as $blacklisted_host_fragment) {
-            if (strpos($uri->host, $blacklisted_host_fragment) !== false) {
-                return false;
-            }
-        }
-        return true;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/URIFilter/MakeAbsolute.php b/library/HTMLPurifier/URIFilter/MakeAbsolute.php
deleted file mode 100644 (file)
index f46ab26..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php
-
-// does not support network paths
-
-class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter
-{
-    public $name = 'MakeAbsolute';
-    protected $base;
-    protected $basePathStack = array();
-    public function prepare($config) {
-        $def = $config->getDefinition('URI');
-        $this->base = $def->base;
-        if (is_null($this->base)) {
-            trigger_error('URI.MakeAbsolute is being ignored due to lack of value for URI.Base configuration', E_USER_WARNING);
-            return false;
-        }
-        $this->base->fragment = null; // fragment is invalid for base URI
-        $stack = explode('/', $this->base->path);
-        array_pop($stack); // discard last segment
-        $stack = $this->_collapseStack($stack); // do pre-parsing
-        $this->basePathStack = $stack;
-        return true;
-    }
-    public function filter(&$uri, $config, $context) {
-        if (is_null($this->base)) return true; // abort early
-        if (
-            $uri->path === '' && is_null($uri->scheme) &&
-            is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment)
-        ) {
-            // reference to current document
-            $uri = clone $this->base;
-            return true;
-        }
-        if (!is_null($uri->scheme)) {
-            // absolute URI already: don't change
-            if (!is_null($uri->host)) return true;
-            $scheme_obj = $uri->getSchemeObj($config, $context);
-            if (!$scheme_obj) {
-                // scheme not recognized
-                return false;
-            }
-            if (!$scheme_obj->hierarchical) {
-                // non-hierarchal URI with explicit scheme, don't change
-                return true;
-            }
-            // special case: had a scheme but always is hierarchical and had no authority
-        }
-        if (!is_null($uri->host)) {
-            // network path, don't bother
-            return true;
-        }
-        if ($uri->path === '') {
-            $uri->path = $this->base->path;
-        } elseif ($uri->path[0] !== '/') {
-            // relative path, needs more complicated processing
-            $stack = explode('/', $uri->path);
-            $new_stack = array_merge($this->basePathStack, $stack);
-            if ($new_stack[0] !== '' && !is_null($this->base->host)) {
-                array_unshift($new_stack, '');
-            }
-            $new_stack = $this->_collapseStack($new_stack);
-            $uri->path = implode('/', $new_stack);
-        } else {
-            // absolute path, but still we should collapse
-            $uri->path = implode('/', $this->_collapseStack(explode('/', $uri->path)));
-        }
-        // re-combine
-        $uri->scheme = $this->base->scheme;
-        if (is_null($uri->userinfo)) $uri->userinfo = $this->base->userinfo;
-        if (is_null($uri->host))     $uri->host     = $this->base->host;
-        if (is_null($uri->port))     $uri->port     = $this->base->port;
-        return true;
-    }
-
-    /**
-     * Resolve dots and double-dots in a path stack
-     */
-    private function _collapseStack($stack) {
-        $result = array();
-        $is_folder = false;
-        for ($i = 0; isset($stack[$i]); $i++) {
-            $is_folder = false;
-            // absorb an internally duplicated slash
-            if ($stack[$i] == '' && $i && isset($stack[$i+1])) continue;
-            if ($stack[$i] == '..') {
-                if (!empty($result)) {
-                    $segment = array_pop($result);
-                    if ($segment === '' && empty($result)) {
-                        // error case: attempted to back out too far:
-                        // restore the leading slash
-                        $result[] = '';
-                    } elseif ($segment === '..') {
-                        $result[] = '..'; // cannot remove .. with ..
-                    }
-                } else {
-                    // relative path, preserve the double-dots
-                    $result[] = '..';
-                }
-                $is_folder = true;
-                continue;
-            }
-            if ($stack[$i] == '.') {
-                // silently absorb
-                $is_folder = true;
-                continue;
-            }
-            $result[] = $stack[$i];
-        }
-        if ($is_folder) $result[] = '';
-        return $result;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/URIFilter/Munge.php b/library/HTMLPurifier/URIFilter/Munge.php
deleted file mode 100644 (file)
index efa10a6..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-
-class HTMLPurifier_URIFilter_Munge extends HTMLPurifier_URIFilter
-{
-    public $name = 'Munge';
-    public $post = true;
-    private $target, $parser, $doEmbed, $secretKey;
-
-    protected $replace = array();
-
-    public function prepare($config) {
-        $this->target    = $config->get('URI.' . $this->name);
-        $this->parser    = new HTMLPurifier_URIParser();
-        $this->doEmbed   = $config->get('URI.MungeResources');
-        $this->secretKey = $config->get('URI.MungeSecretKey');
-        return true;
-    }
-    public function filter(&$uri, $config, $context) {
-        if ($context->get('EmbeddedURI', true) && !$this->doEmbed) return true;
-
-        $scheme_obj = $uri->getSchemeObj($config, $context);
-        if (!$scheme_obj) return true; // ignore unknown schemes, maybe another postfilter did it
-        if (is_null($uri->host) || empty($scheme_obj->browsable)) {
-            return true;
-        }
-        // don't redirect if target host is our host
-        if ($uri->host === $config->getDefinition('URI')->host) {
-            return true;
-        }
-
-        $this->makeReplace($uri, $config, $context);
-        $this->replace = array_map('rawurlencode', $this->replace);
-
-        $new_uri = strtr($this->target, $this->replace);
-        $new_uri = $this->parser->parse($new_uri);
-        // don't redirect if the target host is the same as the
-        // starting host
-        if ($uri->host === $new_uri->host) return true;
-        $uri = $new_uri; // overwrite
-        return true;
-    }
-
-    protected function makeReplace($uri, $config, $context) {
-        $string = $uri->toString();
-        // always available
-        $this->replace['%s'] = $string;
-        $this->replace['%r'] = $context->get('EmbeddedURI', true);
-        $token = $context->get('CurrentToken', true);
-        $this->replace['%n'] = $token ? $token->name : null;
-        $this->replace['%m'] = $context->get('CurrentAttr', true);
-        $this->replace['%p'] = $context->get('CurrentCSSProperty', true);
-        // not always available
-        if ($this->secretKey) $this->replace['%t'] = sha1($this->secretKey . ':' . $string);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/URIParser.php b/library/HTMLPurifier/URIParser.php
deleted file mode 100644 (file)
index 7179e4a..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-/**
- * Parses a URI into the components and fragment identifier as specified
- * by RFC 3986.
- */
-class HTMLPurifier_URIParser
-{
-
-    /**
-     * Instance of HTMLPurifier_PercentEncoder to do normalization with.
-     */
-    protected $percentEncoder;
-
-    public function __construct() {
-        $this->percentEncoder = new HTMLPurifier_PercentEncoder();
-    }
-
-    /**
-     * Parses a URI.
-     * @param $uri string URI to parse
-     * @return HTMLPurifier_URI representation of URI. This representation has
-     *         not been validated yet and may not conform to RFC.
-     */
-    public function parse($uri) {
-
-        $uri = $this->percentEncoder->normalize($uri);
-
-        // Regexp is as per Appendix B.
-        // Note that ["<>] are an addition to the RFC's recommended
-        // characters, because they represent external delimeters.
-        $r_URI = '!'.
-            '(([^:/?#"<>]+):)?'. // 2. Scheme
-            '(//([^/?#"<>]*))?'. // 4. Authority
-            '([^?#"<>]*)'.       // 5. Path
-            '(\?([^#"<>]*))?'.   // 7. Query
-            '(#([^"<>]*))?'.     // 8. Fragment
-            '!';
-
-        $matches = array();
-        $result = preg_match($r_URI, $uri, $matches);
-
-        if (!$result) return false; // *really* invalid URI
-
-        // seperate out parts
-        $scheme     = !empty($matches[1]) ? $matches[2] : null;
-        $authority  = !empty($matches[3]) ? $matches[4] : null;
-        $path       = $matches[5]; // always present, can be empty
-        $query      = !empty($matches[6]) ? $matches[7] : null;
-        $fragment   = !empty($matches[8]) ? $matches[9] : null;
-
-        // further parse authority
-        if ($authority !== null) {
-            $r_authority = "/^((.+?)@)?(\[[^\]]+\]|[^:]*)(:(\d*))?/";
-            $matches = array();
-            preg_match($r_authority, $authority, $matches);
-            $userinfo   = !empty($matches[1]) ? $matches[2] : null;
-            $host       = !empty($matches[3]) ? $matches[3] : '';
-            $port       = !empty($matches[4]) ? (int) $matches[5] : null;
-        } else {
-            $port = $host = $userinfo = null;
-        }
-
-        return new HTMLPurifier_URI(
-            $scheme, $userinfo, $host, $port, $path, $query, $fragment);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/URIScheme.php b/library/HTMLPurifier/URIScheme.php
deleted file mode 100644 (file)
index 039710f..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-
-/**
- * Validator for the components of a URI for a specific scheme
- */
-class HTMLPurifier_URIScheme
-{
-
-    /**
-     * Scheme's default port (integer)
-     */
-    public $default_port = null;
-
-    /**
-     * Whether or not URIs of this schem are locatable by a browser
-     * http and ftp are accessible, while mailto and news are not.
-     */
-    public $browsable = false;
-
-    /**
-     * Whether or not the URI always uses <hier_part>, resolves edge cases
-     * with making relative URIs absolute
-     */
-    public $hierarchical = false;
-
-    /**
-     * Validates the components of a URI
-     * @note This implementation should be called by children if they define
-     *       a default port, as it does port processing.
-     * @param $uri Instance of HTMLPurifier_URI
-     * @param $config HTMLPurifier_Config object
-     * @param $context HTMLPurifier_Context object
-     * @return Bool success or failure
-     */
-    public function validate(&$uri, $config, $context) {
-        if ($this->default_port == $uri->port) $uri->port = null;
-        return true;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/URIScheme/data.php b/library/HTMLPurifier/URIScheme/data.php
deleted file mode 100644 (file)
index b7f1989..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-
-/**
- * Implements data: URI for base64 encoded images supported by GD.
- */
-class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme {
-
-    public $browsable = true;
-    public $allowed_types = array(
-        // you better write validation code for other types if you
-        // decide to allow them
-        'image/jpeg' => true,
-        'image/gif' => true,
-        'image/png' => true,
-        );
-
-    public function validate(&$uri, $config, $context) {
-        $result = explode(',', $uri->path, 2);
-        $is_base64 = false;
-        $charset = null;
-        $content_type = null;
-        if (count($result) == 2) {
-            list($metadata, $data) = $result;
-            // do some legwork on the metadata
-            $metas = explode(';', $metadata);
-            while(!empty($metas)) {
-                $cur = array_shift($metas);
-                if ($cur == 'base64') {
-                    $is_base64 = true;
-                    break;
-                }
-                if (substr($cur, 0, 8) == 'charset=') {
-                    // doesn't match if there are arbitrary spaces, but
-                    // whatever dude
-                    if ($charset !== null) continue; // garbage
-                    $charset = substr($cur, 8); // not used
-                } else {
-                    if ($content_type !== null) continue; // garbage
-                    $content_type = $cur;
-                }
-            }
-        } else {
-            $data = $result[0];
-        }
-        if ($content_type !== null && empty($this->allowed_types[$content_type])) {
-            return false;
-        }
-        if ($charset !== null) {
-            // error; we don't allow plaintext stuff
-            $charset = null;
-        }
-        $data = rawurldecode($data);
-        if ($is_base64) {
-            $raw_data = base64_decode($data);
-        } else {
-            $raw_data = $data;
-        }
-        // XXX probably want to refactor this into a general mechanism
-        // for filtering arbitrary content types
-        $file = tempnam("/tmp", "");
-        file_put_contents($file, $raw_data);
-        if (function_exists('exif_imagetype')) {
-            $image_code = exif_imagetype($file);
-        } elseif (function_exists('getimagesize')) {
-            set_error_handler(array($this, 'muteErrorHandler'));
-            $info = getimagesize($file);
-            restore_error_handler();
-            if ($info == false) return false;
-            $image_code = $info[2];
-        } else {
-            trigger_error("could not find exif_imagetype or getimagesize functions", E_USER_ERROR);
-        }
-        $real_content_type = image_type_to_mime_type($image_code);
-        if ($real_content_type != $content_type) {
-            // we're nice guys; if the content type is something else we
-            // support, change it over
-            if (empty($this->allowed_types[$real_content_type])) return false;
-            $content_type = $real_content_type;
-        }
-        // ok, it's kosher, rewrite what we need
-        $uri->userinfo = null;
-        $uri->host = null;
-        $uri->port = null;
-        $uri->fragment = null;
-        $uri->query = null;
-        $uri->path = "$content_type;base64," . base64_encode($raw_data);
-        return true;
-    }
-
-    public function muteErrorHandler($errno, $errstr) {}
-
-}
-
diff --git a/library/HTMLPurifier/URIScheme/ftp.php b/library/HTMLPurifier/URIScheme/ftp.php
deleted file mode 100644 (file)
index 5849bf7..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-/**
- * Validates ftp (File Transfer Protocol) URIs as defined by generic RFC 1738.
- */
-class HTMLPurifier_URIScheme_ftp extends HTMLPurifier_URIScheme {
-
-    public $default_port = 21;
-    public $browsable = true; // usually
-    public $hierarchical = true;
-
-    public function validate(&$uri, $config, $context) {
-        parent::validate($uri, $config, $context);
-        $uri->query    = null;
-
-        // typecode check
-        $semicolon_pos = strrpos($uri->path, ';'); // reverse
-        if ($semicolon_pos !== false) {
-            $type = substr($uri->path, $semicolon_pos + 1); // no semicolon
-            $uri->path = substr($uri->path, 0, $semicolon_pos);
-            $type_ret = '';
-            if (strpos($type, '=') !== false) {
-                // figure out whether or not the declaration is correct
-                list($key, $typecode) = explode('=', $type, 2);
-                if ($key !== 'type') {
-                    // invalid key, tack it back on encoded
-                    $uri->path .= '%3B' . $type;
-                } elseif ($typecode === 'a' || $typecode === 'i' || $typecode === 'd') {
-                    $type_ret = ";type=$typecode";
-                }
-            } else {
-                $uri->path .= '%3B' . $type;
-            }
-            $uri->path = str_replace(';', '%3B', $uri->path);
-            $uri->path .= $type_ret;
-        }
-
-        return true;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/URIScheme/http.php b/library/HTMLPurifier/URIScheme/http.php
deleted file mode 100644 (file)
index b097a31..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-/**
- * Validates http (HyperText Transfer Protocol) as defined by RFC 2616
- */
-class HTMLPurifier_URIScheme_http extends HTMLPurifier_URIScheme {
-
-    public $default_port = 80;
-    public $browsable = true;
-    public $hierarchical = true;
-
-    public function validate(&$uri, $config, $context) {
-        parent::validate($uri, $config, $context);
-        $uri->userinfo = null;
-        return true;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/URIScheme/https.php b/library/HTMLPurifier/URIScheme/https.php
deleted file mode 100644 (file)
index 29e3809..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-/**
- * Validates https (Secure HTTP) according to http scheme.
- */
-class HTMLPurifier_URIScheme_https extends HTMLPurifier_URIScheme_http {
-
-    public $default_port = 443;
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/URIScheme/mailto.php b/library/HTMLPurifier/URIScheme/mailto.php
deleted file mode 100644 (file)
index c1e2cd5..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-// VERY RELAXED! Shouldn't cause problems, not even Firefox checks if the
-// email is valid, but be careful!
-
-/**
- * Validates mailto (for E-mail) according to RFC 2368
- * @todo Validate the email address
- * @todo Filter allowed query parameters
- */
-
-class HTMLPurifier_URIScheme_mailto extends HTMLPurifier_URIScheme {
-
-    public $browsable = false;
-
-    public function validate(&$uri, $config, $context) {
-        parent::validate($uri, $config, $context);
-        $uri->userinfo = null;
-        $uri->host     = null;
-        $uri->port     = null;
-        // we need to validate path against RFC 2368's addr-spec
-        return true;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/URIScheme/news.php b/library/HTMLPurifier/URIScheme/news.php
deleted file mode 100644 (file)
index f5f54f4..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/**
- * Validates news (Usenet) as defined by generic RFC 1738
- */
-class HTMLPurifier_URIScheme_news extends HTMLPurifier_URIScheme {
-
-    public $browsable = false;
-
-    public function validate(&$uri, $config, $context) {
-        parent::validate($uri, $config, $context);
-        $uri->userinfo = null;
-        $uri->host     = null;
-        $uri->port     = null;
-        $uri->query    = null;
-        // typecode check needed on path
-        return true;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/URIScheme/nntp.php b/library/HTMLPurifier/URIScheme/nntp.php
deleted file mode 100644 (file)
index 5bf93ea..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-/**
- * Validates nntp (Network News Transfer Protocol) as defined by generic RFC 1738
- */
-class HTMLPurifier_URIScheme_nntp extends HTMLPurifier_URIScheme {
-
-    public $default_port = 119;
-    public $browsable = false;
-
-    public function validate(&$uri, $config, $context) {
-        parent::validate($uri, $config, $context);
-        $uri->userinfo = null;
-        $uri->query    = null;
-        return true;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/URISchemeRegistry.php b/library/HTMLPurifier/URISchemeRegistry.php
deleted file mode 100644 (file)
index 576bf7b..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-/**
- * Registry for retrieving specific URI scheme validator objects.
- */
-class HTMLPurifier_URISchemeRegistry
-{
-
-    /**
-     * Retrieve sole instance of the registry.
-     * @param $prototype Optional prototype to overload sole instance with,
-     *                   or bool true to reset to default registry.
-     * @note Pass a registry object $prototype with a compatible interface and
-     *       the function will copy it and return it all further times.
-     */
-    public static function instance($prototype = null) {
-        static $instance = null;
-        if ($prototype !== null) {
-            $instance = $prototype;
-        } elseif ($instance === null || $prototype == true) {
-            $instance = new HTMLPurifier_URISchemeRegistry();
-        }
-        return $instance;
-    }
-
-    /**
-     * Cache of retrieved schemes.
-     */
-    protected $schemes = array();
-
-    /**
-     * Retrieves a scheme validator object
-     * @param $scheme String scheme name like http or mailto
-     * @param $config HTMLPurifier_Config object
-     * @param $config HTMLPurifier_Context object
-     */
-    public function getScheme($scheme, $config, $context) {
-        if (!$config) $config = HTMLPurifier_Config::createDefault();
-
-        // important, otherwise attacker could include arbitrary file
-        $allowed_schemes = $config->get('URI.AllowedSchemes');
-        if (!$config->get('URI.OverrideAllowedSchemes') &&
-            !isset($allowed_schemes[$scheme])
-        ) {
-            return;
-        }
-
-        if (isset($this->schemes[$scheme])) return $this->schemes[$scheme];
-        if (!isset($allowed_schemes[$scheme])) return;
-
-        $class = 'HTMLPurifier_URIScheme_' . $scheme;
-        if (!class_exists($class)) return;
-        $this->schemes[$scheme] = new $class();
-        return $this->schemes[$scheme];
-    }
-
-    /**
-     * Registers a custom scheme to the cache, bypassing reflection.
-     * @param $scheme Scheme name
-     * @param $scheme_obj HTMLPurifier_URIScheme object
-     */
-    public function register($scheme, $scheme_obj) {
-        $this->schemes[$scheme] = $scheme_obj;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/UnitConverter.php b/library/HTMLPurifier/UnitConverter.php
deleted file mode 100644 (file)
index 545d426..0000000
+++ /dev/null
@@ -1,254 +0,0 @@
-<?php
-
-/**
- * Class for converting between different unit-lengths as specified by
- * CSS.
- */
-class HTMLPurifier_UnitConverter
-{
-
-    const ENGLISH = 1;
-    const METRIC = 2;
-    const DIGITAL = 3;
-
-    /**
-     * Units information array. Units are grouped into measuring systems
-     * (English, Metric), and are assigned an integer representing
-     * the conversion factor between that unit and the smallest unit in
-     * the system. Numeric indexes are actually magical constants that
-     * encode conversion data from one system to the next, with a O(n^2)
-     * constraint on memory (this is generally not a problem, since
-     * the number of measuring systems is small.)
-     */
-    protected static $units = array(
-        self::ENGLISH => array(
-            'px' => 3, // This is as per CSS 2.1 and Firefox. Your mileage may vary
-            'pt' => 4,
-            'pc' => 48,
-            'in' => 288,
-            self::METRIC => array('pt', '0.352777778', 'mm'),
-        ),
-        self::METRIC => array(
-            'mm' => 1,
-            'cm' => 10,
-            self::ENGLISH => array('mm', '2.83464567', 'pt'),
-        ),
-    );
-
-    /**
-     * Minimum bcmath precision for output.
-     */
-    protected $outputPrecision;
-
-    /**
-     * Bcmath precision for internal calculations.
-     */
-    protected $internalPrecision;
-
-    /**
-     * Whether or not BCMath is available
-     */
-    private $bcmath;
-
-    public function __construct($output_precision = 4, $internal_precision = 10, $force_no_bcmath = false) {
-        $this->outputPrecision = $output_precision;
-        $this->internalPrecision = $internal_precision;
-        $this->bcmath = !$force_no_bcmath && function_exists('bcmul');
-    }
-
-    /**
-     * Converts a length object of one unit into another unit.
-     * @param HTMLPurifier_Length $length
-     *      Instance of HTMLPurifier_Length to convert. You must validate()
-     *      it before passing it here!
-     * @param string $to_unit
-     *      Unit to convert to.
-     * @note
-     *      About precision: This conversion function pays very special
-     *      attention to the incoming precision of values and attempts
-     *      to maintain a number of significant figure. Results are
-     *      fairly accurate up to nine digits. Some caveats:
-     *          - If a number is zero-padded as a result of this significant
-     *            figure tracking, the zeroes will be eliminated.
-     *          - If a number contains less than four sigfigs ($outputPrecision)
-     *            and this causes some decimals to be excluded, those
-     *            decimals will be added on.
-     */
-    public function convert($length, $to_unit) {
-
-        if (!$length->isValid()) return false;
-
-        $n    = $length->getN();
-        $unit = $length->getUnit();
-
-        if ($n === '0' || $unit === false) {
-            return new HTMLPurifier_Length('0', false);
-        }
-
-        $state = $dest_state = false;
-        foreach (self::$units as $k => $x) {
-            if (isset($x[$unit])) $state = $k;
-            if (isset($x[$to_unit])) $dest_state = $k;
-        }
-        if (!$state || !$dest_state) return false;
-
-        // Some calculations about the initial precision of the number;
-        // this will be useful when we need to do final rounding.
-        $sigfigs = $this->getSigFigs($n);
-        if ($sigfigs < $this->outputPrecision) $sigfigs = $this->outputPrecision;
-
-        // BCMath's internal precision deals only with decimals. Use
-        // our default if the initial number has no decimals, or increase
-        // it by how ever many decimals, thus, the number of guard digits
-        // will always be greater than or equal to internalPrecision.
-        $log = (int) floor(log(abs($n), 10));
-        $cp = ($log < 0) ? $this->internalPrecision - $log : $this->internalPrecision; // internal precision
-
-        for ($i = 0; $i < 2; $i++) {
-
-            // Determine what unit IN THIS SYSTEM we need to convert to
-            if ($dest_state === $state) {
-                // Simple conversion
-                $dest_unit = $to_unit;
-            } else {
-                // Convert to the smallest unit, pending a system shift
-                $dest_unit = self::$units[$state][$dest_state][0];
-            }
-
-            // Do the conversion if necessary
-            if ($dest_unit !== $unit) {
-                $factor = $this->div(self::$units[$state][$unit], self::$units[$state][$dest_unit], $cp);
-                $n = $this->mul($n, $factor, $cp);
-                $unit = $dest_unit;
-            }
-
-            // Output was zero, so bail out early. Shouldn't ever happen.
-            if ($n === '') {
-                $n = '0';
-                $unit = $to_unit;
-                break;
-            }
-
-            // It was a simple conversion, so bail out
-            if ($dest_state === $state) {
-                break;
-            }
-
-            if ($i !== 0) {
-                // Conversion failed! Apparently, the system we forwarded
-                // to didn't have this unit. This should never happen!
-                return false;
-            }
-
-            // Pre-condition: $i == 0
-
-            // Perform conversion to next system of units
-            $n = $this->mul($n, self::$units[$state][$dest_state][1], $cp);
-            $unit = self::$units[$state][$dest_state][2];
-            $state = $dest_state;
-
-            // One more loop around to convert the unit in the new system.
-
-        }
-
-        // Post-condition: $unit == $to_unit
-        if ($unit !== $to_unit) return false;
-
-        // Useful for debugging:
-        //echo "<pre>n";
-        //echo "$n\nsigfigs = $sigfigs\nnew_log = $new_log\nlog = $log\nrp = $rp\n</pre>\n";
-
-        $n = $this->round($n, $sigfigs);
-        if (strpos($n, '.') !== false) $n = rtrim($n, '0');
-        $n = rtrim($n, '.');
-
-        return new HTMLPurifier_Length($n, $unit);
-    }
-
-    /**
-     * Returns the number of significant figures in a string number.
-     * @param string $n Decimal number
-     * @return int number of sigfigs
-     */
-    public function getSigFigs($n) {
-        $n = ltrim($n, '0+-');
-        $dp = strpos($n, '.'); // decimal position
-        if ($dp === false) {
-            $sigfigs = strlen(rtrim($n, '0'));
-        } else {
-            $sigfigs = strlen(ltrim($n, '0.')); // eliminate extra decimal character
-            if ($dp !== 0) $sigfigs--;
-        }
-        return $sigfigs;
-    }
-
-    /**
-     * Adds two numbers, using arbitrary precision when available.
-     */
-    private function add($s1, $s2, $scale) {
-        if ($this->bcmath) return bcadd($s1, $s2, $scale);
-        else return $this->scale($s1 + $s2, $scale);
-    }
-
-    /**
-     * Multiples two numbers, using arbitrary precision when available.
-     */
-    private function mul($s1, $s2, $scale) {
-        if ($this->bcmath) return bcmul($s1, $s2, $scale);
-        else return $this->scale($s1 * $s2, $scale);
-    }
-
-    /**
-     * Divides two numbers, using arbitrary precision when available.
-     */
-    private function div($s1, $s2, $scale) {
-        if ($this->bcmath) return bcdiv($s1, $s2, $scale);
-        else return $this->scale($s1 / $s2, $scale);
-    }
-
-    /**
-     * Rounds a number according to the number of sigfigs it should have,
-     * using arbitrary precision when available.
-     */
-    private function round($n, $sigfigs) {
-        $new_log = (int) floor(log(abs($n), 10)); // Number of digits left of decimal - 1
-        $rp = $sigfigs - $new_log - 1; // Number of decimal places needed
-        $neg = $n < 0 ? '-' : ''; // Negative sign
-        if ($this->bcmath) {
-            if ($rp >= 0) {
-                $n = bcadd($n, $neg . '0.' .  str_repeat('0', $rp) . '5', $rp + 1);
-                $n = bcdiv($n, '1', $rp);
-            } else {
-                // This algorithm partially depends on the standardized
-                // form of numbers that comes out of bcmath.
-                $n = bcadd($n, $neg . '5' . str_repeat('0', $new_log - $sigfigs), 0);
-                $n = substr($n, 0, $sigfigs + strlen($neg)) . str_repeat('0', $new_log - $sigfigs + 1);
-            }
-            return $n;
-        } else {
-            return $this->scale(round($n, $sigfigs - $new_log - 1), $rp + 1);
-        }
-    }
-
-    /**
-     * Scales a float to $scale digits right of decimal point, like BCMath.
-     */
-    private function scale($r, $scale) {
-        if ($scale < 0) {
-            // The f sprintf type doesn't support negative numbers, so we
-            // need to cludge things manually. First get the string.
-            $r = sprintf('%.0f', (float) $r);
-            // Due to floating point precision loss, $r will more than likely
-            // look something like 4652999999999.9234. We grab one more digit
-            // than we need to precise from $r and then use that to round
-            // appropriately.
-            $precise = (string) round(substr($r, 0, strlen($r) + $scale), -1);
-            // Now we return it, truncating the zero that was rounded off.
-            return substr($precise, 0, -1) . str_repeat('0', -$scale + 1);
-        }
-        return sprintf('%.' . $scale . 'f', (float) $r);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/VarParser.php b/library/HTMLPurifier/VarParser.php
deleted file mode 100644 (file)
index 68e72ae..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-<?php
-
-/**
- * Parses string representations into their corresponding native PHP
- * variable type. The base implementation does a simple type-check.
- */
-class HTMLPurifier_VarParser
-{
-
-    const STRING    = 1;
-    const ISTRING   = 2;
-    const TEXT      = 3;
-    const ITEXT     = 4;
-    const INT       = 5;
-    const FLOAT     = 6;
-    const BOOL      = 7;
-    const LOOKUP    = 8;
-    const ALIST     = 9;
-    const HASH      = 10;
-    const MIXED     = 11;
-
-    /**
-     * Lookup table of allowed types. Mainly for backwards compatibility, but
-     * also convenient for transforming string type names to the integer constants.
-     */
-    static public $types = array(
-        'string'    => self::STRING,
-        'istring'   => self::ISTRING,
-        'text'      => self::TEXT,
-        'itext'     => self::ITEXT,
-        'int'       => self::INT,
-        'float'     => self::FLOAT,
-        'bool'      => self::BOOL,
-        'lookup'    => self::LOOKUP,
-        'list'      => self::ALIST,
-        'hash'      => self::HASH,
-        'mixed'     => self::MIXED
-    );
-
-    /**
-     * Lookup table of types that are string, and can have aliases or
-     * allowed value lists.
-     */
-    static public $stringTypes = array(
-        self::STRING    => true,
-        self::ISTRING   => true,
-        self::TEXT      => true,
-        self::ITEXT     => true,
-    );
-
-    /**
-     * Validate a variable according to type. Throws
-     * HTMLPurifier_VarParserException if invalid.
-     * It may return NULL as a valid type if $allow_null is true.
-     *
-     * @param $var Variable to validate
-     * @param $type Type of variable, see HTMLPurifier_VarParser->types
-     * @param $allow_null Whether or not to permit null as a value
-     * @return Validated and type-coerced variable
-     */
-    final public function parse($var, $type, $allow_null = false) {
-        if (is_string($type)) {
-            if (!isset(HTMLPurifier_VarParser::$types[$type])) {
-                throw new HTMLPurifier_VarParserException("Invalid type '$type'");
-            } else {
-                $type = HTMLPurifier_VarParser::$types[$type];
-            }
-        }
-        $var = $this->parseImplementation($var, $type, $allow_null);
-        if ($allow_null && $var === null) return null;
-        // These are basic checks, to make sure nothing horribly wrong
-        // happened in our implementations.
-        switch ($type) {
-            case (self::STRING):
-            case (self::ISTRING):
-            case (self::TEXT):
-            case (self::ITEXT):
-                if (!is_string($var)) break;
-                if ($type == self::ISTRING || $type == self::ITEXT) $var = strtolower($var);
-                return $var;
-            case (self::INT):
-                if (!is_int($var)) break;
-                return $var;
-            case (self::FLOAT):
-                if (!is_float($var)) break;
-                return $var;
-            case (self::BOOL):
-                if (!is_bool($var)) break;
-                return $var;
-            case (self::LOOKUP):
-            case (self::ALIST):
-            case (self::HASH):
-                if (!is_array($var)) break;
-                if ($type === self::LOOKUP) {
-                    foreach ($var as $k) if ($k !== true) $this->error('Lookup table contains value other than true');
-                } elseif ($type === self::ALIST) {
-                    $keys = array_keys($var);
-                    if (array_keys($keys) !== $keys) $this->error('Indices for list are not uniform');
-                }
-                return $var;
-            case (self::MIXED):
-                return $var;
-            default:
-                $this->errorInconsistent(get_class($this), $type);
-        }
-        $this->errorGeneric($var, $type);
-    }
-
-    /**
-     * Actually implements the parsing. Base implementation is to not
-     * do anything to $var. Subclasses should overload this!
-     */
-    protected function parseImplementation($var, $type, $allow_null) {
-        return $var;
-    }
-
-    /**
-     * Throws an exception.
-     */
-    protected function error($msg) {
-        throw new HTMLPurifier_VarParserException($msg);
-    }
-
-    /**
-     * Throws an inconsistency exception.
-     * @note This should not ever be called. It would be called if we
-     *       extend the allowed values of HTMLPurifier_VarParser without
-     *       updating subclasses.
-     */
-    protected function errorInconsistent($class, $type) {
-        throw new HTMLPurifier_Exception("Inconsistency in $class: ".HTMLPurifier_VarParser::getTypeName($type)." not implemented");
-    }
-
-    /**
-     * Generic error for if a type didn't work.
-     */
-    protected function errorGeneric($var, $type) {
-        $vtype = gettype($var);
-        $this->error("Expected type ".HTMLPurifier_VarParser::getTypeName($type).", got $vtype");
-    }
-
-    static public function getTypeName($type) {
-        static $lookup;
-        if (!$lookup) {
-            // Lazy load the alternative lookup table
-            $lookup = array_flip(HTMLPurifier_VarParser::$types);
-        }
-        if (!isset($lookup[$type])) return 'unknown';
-        return $lookup[$type];
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/VarParser/Flexible.php b/library/HTMLPurifier/VarParser/Flexible.php
deleted file mode 100644 (file)
index c954250..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php
-
-/**
- * Performs safe variable parsing based on types which can be used by
- * users. This may not be able to represent all possible data inputs,
- * however.
- */
-class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser
-{
-
-    protected function parseImplementation($var, $type, $allow_null) {
-        if ($allow_null && $var === null) return null;
-        switch ($type) {
-            // Note: if code "breaks" from the switch, it triggers a generic
-            // exception to be thrown. Specific errors can be specifically
-            // done here.
-            case self::MIXED :
-            case self::ISTRING :
-            case self::STRING :
-            case self::TEXT :
-            case self::ITEXT :
-                return $var;
-            case self::INT :
-                if (is_string($var) && ctype_digit($var)) $var = (int) $var;
-                return $var;
-            case self::FLOAT :
-                if ((is_string($var) && is_numeric($var)) || is_int($var)) $var = (float) $var;
-                return $var;
-            case self::BOOL :
-                if (is_int($var) && ($var === 0 || $var === 1)) {
-                    $var = (bool) $var;
-                } elseif (is_string($var)) {
-                    if ($var == 'on' || $var == 'true' || $var == '1') {
-                        $var = true;
-                    } elseif ($var == 'off' || $var == 'false' || $var == '0') {
-                        $var = false;
-                    } else {
-                        throw new HTMLPurifier_VarParserException("Unrecognized value '$var' for $type");
-                    }
-                }
-                return $var;
-            case self::ALIST :
-            case self::HASH :
-            case self::LOOKUP :
-                if (is_string($var)) {
-                    // special case: technically, this is an array with
-                    // a single empty string item, but having an empty
-                    // array is more intuitive
-                    if ($var == '') return array();
-                    if (strpos($var, "\n") === false && strpos($var, "\r") === false) {
-                        // simplistic string to array method that only works
-                        // for simple lists of tag names or alphanumeric characters
-                        $var = explode(',',$var);
-                    } else {
-                        $var = preg_split('/(,|[\n\r]+)/', $var);
-                    }
-                    // remove spaces
-                    foreach ($var as $i => $j) $var[$i] = trim($j);
-                    if ($type === self::HASH) {
-                        // key:value,key2:value2
-                        $nvar = array();
-                        foreach ($var as $keypair) {
-                            $c = explode(':', $keypair, 2);
-                            if (!isset($c[1])) continue;
-                            $nvar[$c[0]] = $c[1];
-                        }
-                        $var = $nvar;
-                    }
-                }
-                if (!is_array($var)) break;
-                $keys = array_keys($var);
-                if ($keys === array_keys($keys)) {
-                    if ($type == self::ALIST) return $var;
-                    elseif ($type == self::LOOKUP) {
-                        $new = array();
-                        foreach ($var as $key) {
-                            $new[$key] = true;
-                        }
-                        return $new;
-                    } else break;
-                }
-                if ($type === self::LOOKUP) {
-                    foreach ($var as $key => $value) {
-                        $var[$key] = true;
-                    }
-                }
-                return $var;
-            default:
-                $this->errorInconsistent(__CLASS__, $type);
-        }
-        $this->errorGeneric($var, $type);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/VarParser/Native.php b/library/HTMLPurifier/VarParser/Native.php
deleted file mode 100644 (file)
index b02a6de..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-/**
- * This variable parser uses PHP's internal code engine. Because it does
- * this, it can represent all inputs; however, it is dangerous and cannot
- * be used by users.
- */
-class HTMLPurifier_VarParser_Native extends HTMLPurifier_VarParser
-{
-
-    protected function parseImplementation($var, $type, $allow_null) {
-        return $this->evalExpression($var);
-    }
-
-    protected function evalExpression($expr) {
-        $var = null;
-        $result = eval("\$var = $expr;");
-        if ($result === false) {
-            throw new HTMLPurifier_VarParserException("Fatal error in evaluated code");
-        }
-        return $var;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/HTMLPurifier/VarParserException.php b/library/HTMLPurifier/VarParserException.php
deleted file mode 100644 (file)
index 5df3414..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-
-/**
- * Exception type for HTMLPurifier_VarParser
- */
-class HTMLPurifier_VarParserException extends HTMLPurifier_Exception
-{
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/CREDITS b/library/ezyang/htmlpurifier/CREDITS
new file mode 100644 (file)
index 0000000..7921b45
--- /dev/null
@@ -0,0 +1,9 @@
+
+CREDITS
+
+Almost everything written by Edward Z. Yang (Ambush Commander).  Lots of thanks
+to the DevNetwork Community for their help (see docs/ref-devnetwork.html for
+more details), Feyd especially (namely IPv6 and optimization).  Thanks to RSnake
+for letting me package his fantastic XSS cheatsheet for a smoketest.
+
+    vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/INSTALL b/library/ezyang/htmlpurifier/INSTALL
new file mode 100644 (file)
index 0000000..677c04a
--- /dev/null
@@ -0,0 +1,374 @@
+
+Install
+    How to install HTML Purifier
+
+HTML Purifier is designed to run out of the box, so actually using the
+library is extremely easy.  (Although... if you were looking for a
+step-by-step installation GUI, you've downloaded the wrong software!)
+
+While the impatient can get going immediately with some of the sample
+code at the bottom of this library, it's well worth reading this entire
+document--most of the other documentation assumes that you are familiar
+with these contents.
+
+
+---------------------------------------------------------------------------
+1.  Compatibility
+
+HTML Purifier is PHP 5 only, and is actively tested from PHP 5.0.5 and
+up. It has no core dependencies with other libraries. PHP
+4 support was deprecated on December 31, 2007 with HTML Purifier 3.0.0.
+HTML Purifier is not compatible with zend.ze1_compatibility_mode.
+
+These optional extensions can enhance the capabilities of HTML Purifier:
+
+    * iconv  : Converts text to and from non-UTF-8 encodings
+    * bcmath : Used for unit conversion and imagecrash protection
+    * tidy   : Used for pretty-printing HTML
+
+These optional libraries can enhance the capabilities of HTML Purifier:
+
+    * CSSTidy : Clean CSS stylesheets using %Core.ExtractStyleBlocks
+    * Net_IDNA2 (PEAR) : IRI support using %Core.EnableIDNA
+
+---------------------------------------------------------------------------
+2.  Reconnaissance
+
+A big plus of HTML Purifier is its inerrant support of standards, so
+your web-pages should be standards-compliant.  (They should also use
+semantic markup, but that's another issue altogether, one HTML Purifier
+cannot fix without reading your mind.)
+
+HTML Purifier can process these doctypes:
+
+* XHTML 1.0 Transitional (default)
+* XHTML 1.0 Strict
+* HTML 4.01 Transitional
+* HTML 4.01 Strict
+* XHTML 1.1
+
+...and these character encodings:
+
+* UTF-8 (default)
+* Any encoding iconv supports (with crippled internationalization support)
+
+These defaults reflect what my choices would be if I were authoring an
+HTML document, however, what you choose depends on the nature of your
+codebase.  If you don't know what doctype you are using, you can determine
+the doctype from this identifier at the top of your source code:
+
+    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+...and the character encoding from this code:
+
+    <meta http-equiv="Content-type" content="text/html;charset=ENCODING">
+
+If the character encoding declaration is missing, STOP NOW, and
+read 'docs/enduser-utf8.html' (web accessible at
+http://htmlpurifier.org/docs/enduser-utf8.html).  In fact, even if it is
+present, read this document anyway, as many websites specify their
+document's character encoding incorrectly.
+
+
+---------------------------------------------------------------------------
+3.  Including the library
+
+The procedure is quite simple:
+
+    require_once '/path/to/library/HTMLPurifier.auto.php';
+
+This will setup an autoloader, so the library's files are only included
+when you use them.
+
+Only the contents in the library/ folder are necessary, so you can remove
+everything else when using HTML Purifier in a production environment.
+
+If you installed HTML Purifier via PEAR, all you need to do is:
+
+    require_once 'HTMLPurifier.auto.php';
+
+Please note that the usual PEAR practice of including just the classes you
+want will not work with HTML Purifier's autoloading scheme.
+
+Advanced users, read on; other users can skip to section 4.
+
+Autoload compatibility
+----------------------
+
+    HTML Purifier attempts to be as smart as possible when registering an
+    autoloader, but there are some cases where you will need to change
+    your own code to accomodate HTML Purifier. These are those cases:
+
+    PHP VERSION IS LESS THAN 5.1.2, AND YOU'VE DEFINED __autoload
+        Because spl_autoload_register() doesn't exist in early versions
+        of PHP 5, HTML Purifier has no way of adding itself to the autoload
+        stack. Modify your __autoload function to test
+        HTMLPurifier_Bootstrap::autoload($class)
+
+        For example, suppose your autoload function looks like this:
+
+            function __autoload($class) {
+                require str_replace('_', '/', $class) . '.php';
+                return true;
+            }
+
+        A modified version with HTML Purifier would look like this:
+
+            function __autoload($class) {
+                if (HTMLPurifier_Bootstrap::autoload($class)) return true;
+                require str_replace('_', '/', $class) . '.php';
+                return true;
+            }
+
+        Note that there *is* some custom behavior in our autoloader; the
+        original autoloader in our example would work for 99% of the time,
+        but would fail when including language files.
+
+    AN __autoload FUNCTION IS DECLARED AFTER OUR AUTOLOADER IS REGISTERED
+        spl_autoload_register() has the curious behavior of disabling
+        the existing __autoload() handler. Users need to explicitly
+        spl_autoload_register('__autoload'). Because we use SPL when it
+        is available, __autoload() will ALWAYS be disabled. If __autoload()
+        is declared before HTML Purifier is loaded, this is not a problem:
+        HTML Purifier will register the function for you. But if it is
+        declared afterwards, it will mysteriously not work. This
+        snippet of code (after your autoloader is defined) will fix it:
+
+            spl_autoload_register('__autoload')
+
+    Users should also be on guard if they use a version of PHP previous
+    to 5.1.2 without an autoloader--HTML Purifier will define __autoload()
+    for you, which can collide with an autoloader that was added by *you*
+    later.
+
+
+For better performance
+----------------------
+
+    Opcode caches, which greatly speed up PHP initialization for scripts
+    with large amounts of code (HTML Purifier included), don't like
+    autoloaders. We offer an include file that includes all of HTML Purifier's
+    files in one go in an opcode cache friendly manner:
+
+        // If /path/to/library isn't already in your include path, uncomment
+        // the below line:
+        // require '/path/to/library/HTMLPurifier.path.php';
+
+        require 'HTMLPurifier.includes.php';
+
+    Optional components still need to be included--you'll know if you try to
+    use a feature and you get a class doesn't exists error! The autoloader
+    can be used in conjunction with this approach to catch classes that are
+    missing. Simply add this afterwards:
+
+        require 'HTMLPurifier.autoload.php';
+
+Standalone version
+------------------
+
+    HTML Purifier has a standalone distribution; you can also generate
+    a standalone file from the full version by running the script
+    maintenance/generate-standalone.php . The standalone version has the
+    benefit of having most of its code in one file, so parsing is much
+    faster and the library is easier to manage.
+
+    If HTMLPurifier.standalone.php exists in the library directory, you
+    can use it like this:
+
+        require '/path/to/HTMLPurifier.standalone.php';
+
+    This is equivalent to including HTMLPurifier.includes.php, except that
+    the contents of standalone/ will be added to your path. To override this
+    behavior, specify a new HTMLPURIFIER_PREFIX where standalone files can
+    be found (usually, this will be one directory up, the "true" library
+    directory in full distributions). Don't forget to set your path too!
+
+    The autoloader can be added to the end to ensure the classes are
+    loaded when necessary; otherwise you can manually include them.
+    To use the autoloader, use this:
+
+        require 'HTMLPurifier.autoload.php';
+
+For advanced users
+------------------
+
+    HTMLPurifier.auto.php performs a number of operations that can be done
+    individually. These are:
+
+        HTMLPurifier.path.php
+            Puts /path/to/library in the include path. For high performance,
+            this should be done in php.ini.
+
+        HTMLPurifier.autoload.php
+            Registers our autoload handler HTMLPurifier_Bootstrap::autoload($class).
+
+    You can do these operations by yourself--in fact, you must modify your own
+    autoload handler if you are using a version of PHP earlier than PHP 5.1.2
+    (See "Autoload compatibility" above).
+
+
+---------------------------------------------------------------------------
+4. Configuration
+
+HTML Purifier is designed to run out-of-the-box, but occasionally HTML
+Purifier needs to be told what to do.  If you answer no to any of these
+questions, read on; otherwise, you can skip to the next section (or, if you're
+into configuring things just for the heck of it, skip to 4.3).
+
+* Am I using UTF-8?
+* Am I using XHTML 1.0 Transitional?
+
+If you answered no to any of these questions, instantiate a configuration
+object and read on:
+
+    $config = HTMLPurifier_Config::createDefault();
+
+
+4.1. Setting a different character encoding
+
+You really shouldn't use any other encoding except UTF-8, especially if you
+plan to support multilingual websites (read section three for more details).
+However, switching to UTF-8 is not always immediately feasible, so we can
+adapt.
+
+HTML Purifier uses iconv to support other character encodings, as such,
+any encoding that iconv supports <http://www.gnu.org/software/libiconv/>
+HTML Purifier supports with this code:
+
+    $config->set('Core.Encoding', /* put your encoding here */);
+
+An example usage for Latin-1 websites (the most common encoding for English
+websites):
+
+    $config->set('Core.Encoding', 'ISO-8859-1');
+
+Note that HTML Purifier's support for non-Unicode encodings is crippled by the
+fact that any character not supported by that encoding will be silently
+dropped, EVEN if it is ampersand escaped.  If you want to work around
+this, you are welcome to read docs/enduser-utf8.html for a fix,
+but please be cognizant of the issues the "solution" creates (for this
+reason, I do not include the solution in this document).
+
+
+4.2. Setting a different doctype
+
+For those of you using HTML 4.01 Transitional, you can disable
+XHTML output like this:
+
+    $config->set('HTML.Doctype', 'HTML 4.01 Transitional');
+
+Other supported doctypes include:
+
+    * HTML 4.01 Strict
+    * HTML 4.01 Transitional
+    * XHTML 1.0 Strict
+    * XHTML 1.0 Transitional
+    * XHTML 1.1
+
+
+4.3. Other settings
+
+There are more configuration directives which can be read about
+here: <http://htmlpurifier.org/live/configdoc/plain.html>  They're a bit boring,
+but they can help out for those of you who like to exert maximum control over
+your code.  Some of the more interesting ones are configurable at the
+demo <http://htmlpurifier.org/demo.php> and are well worth looking into
+for your own system.
+
+For example, you can fine tune allowed elements and attributes, convert
+relative URLs to absolute ones, and even autoparagraph input text! These
+are, respectively, %HTML.Allowed, %URI.MakeAbsolute and %URI.Base, and
+%AutoFormat.AutoParagraph. The %Namespace.Directive naming convention
+translates to:
+
+    $config->set('Namespace.Directive', $value);
+
+E.g.
+
+    $config->set('HTML.Allowed', 'p,b,a[href],i');
+    $config->set('URI.Base', 'http://www.example.com');
+    $config->set('URI.MakeAbsolute', true);
+    $config->set('AutoFormat.AutoParagraph', true);
+
+
+---------------------------------------------------------------------------
+5. Caching
+
+HTML Purifier generates some cache files (generally one or two) to speed up
+its execution. For maximum performance, make sure that
+library/HTMLPurifier/DefinitionCache/Serializer is writeable by the webserver.
+
+If you are in the library/ folder of HTML Purifier, you can set the
+appropriate permissions using:
+
+    chmod -R 0755 HTMLPurifier/DefinitionCache/Serializer
+
+If the above command doesn't work, you may need to assign write permissions
+to all. This may be necessary if your webserver runs as nobody, but is
+not recommended since it means any other user can write files in the
+directory. Use:
+
+    chmod -R 0777 HTMLPurifier/DefinitionCache/Serializer
+
+You can also chmod files via your FTP client; this option
+is usually accessible by right clicking the corresponding directory and
+then selecting "chmod" or "file permissions".
+
+Starting with 2.0.1, HTML Purifier will generate friendly error messages
+that will tell you exactly what you have to chmod the directory to, if in doubt,
+follow its advice.
+
+If you are unable or unwilling to give write permissions to the cache
+directory, you can either disable the cache (and suffer a performance
+hit):
+
+    $config->set('Core.DefinitionCache', null);
+
+Or move the cache directory somewhere else (no trailing slash):
+
+    $config->set('Cache.SerializerPath', '/home/user/absolute/path');
+
+
+---------------------------------------------------------------------------
+6.   Using the code
+
+The interface is mind-numbingly simple:
+
+    $purifier = new HTMLPurifier($config);
+    $clean_html = $purifier->purify( $dirty_html );
+
+That's it!  For more examples, check out docs/examples/ (they aren't very
+different though).  Also, docs/enduser-slow.html gives advice on what to
+do if HTML Purifier is slowing down your application.
+
+
+---------------------------------------------------------------------------
+7.   Quick install
+
+First, make sure library/HTMLPurifier/DefinitionCache/Serializer is
+writable by the webserver (see Section 5: Caching above for details).
+If your website is in UTF-8 and XHTML Transitional, use this code:
+
+<?php
+    require_once '/path/to/htmlpurifier/library/HTMLPurifier.auto.php';
+
+    $config = HTMLPurifier_Config::createDefault();
+    $purifier = new HTMLPurifier($config);
+    $clean_html = $purifier->purify($dirty_html);
+?>
+
+If your website is in a different encoding or doctype, use this code:
+
+<?php
+    require_once '/path/to/htmlpurifier/library/HTMLPurifier.auto.php';
+
+    $config = HTMLPurifier_Config::createDefault();
+    $config->set('Core.Encoding', 'ISO-8859-1'); // replace with your encoding
+    $config->set('HTML.Doctype', 'HTML 4.01 Transitional'); // replace with your doctype
+    $purifier = new HTMLPurifier($config);
+
+    $clean_html = $purifier->purify($dirty_html);
+?>
+
+    vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/INSTALL.fr.utf8 b/library/ezyang/htmlpurifier/INSTALL.fr.utf8
new file mode 100644 (file)
index 0000000..06e628c
--- /dev/null
@@ -0,0 +1,60 @@
+
+Installation
+    Comment installer HTML Purifier
+
+Attention : Ce document est encodé en UTF-8, si les lettres avec des accents
+ne s'affichent pas, prenez un meilleur éditeur de texte.
+
+L'installation de HTML Purifier est très simple, parce qu'il n'a pas besoin
+de configuration. Pour les utilisateurs impatients, le code se trouve dans le
+pied de page, mais je recommande de lire le document.
+
+1.  Compatibilité
+
+HTML Purifier fonctionne avec PHP 5. PHP 5.0.5 est la dernière version testée.
+Il ne dépend pas d'autres librairies.
+
+Les extensions optionnelles sont iconv (généralement déjà installée) et tidy
+(répendue aussi). Si vous utilisez UTF-8 et que vous ne voulez pas l'indentation,
+vous pouvez utiliser HTML Purifier sans ces extensions.
+
+
+2.  Inclure la librairie
+
+Quand vous devez l'utilisez, incluez le :
+
+    require_once('/path/to/library/HTMLPurifier.auto.php');
+
+Ne pas l'inclure si ce n'est pas nécessaire, car HTML Purifier est lourd.
+
+HTML Purifier utilise "autoload". Si vous avez défini la fonction __autoload,
+vous devez ajouter cette fonction :
+
+    spl_autoload_register('__autoload')
+
+Plus d'informations dans le document "INSTALL".
+
+3.  Installation rapide
+
+Si votre site Web est en UTF-8 et XHTML Transitional, utilisez :
+
+<?php
+    require_once('/path/to/htmlpurifier/library/HTMLPurifier.auto.php');
+    $purificateur = new HTMLPurifier();
+    $html_propre = $purificateur->purify($html_a_purifier);
+?>
+
+Sinon, utilisez :
+
+<?php
+    require_once('/path/to/html/purifier/library/HTMLPurifier.auto.load');
+    $config = $HTMLPurifier_Config::createDefault();
+    $config->set('Core', 'Encoding', 'ISO-8859-1'); //Remplacez par votre
+    encodage
+    $config->set('Core', 'XHTML', true); //Remplacer par false si HTML 4.01
+    $purificateur = new HTMLPurifier($config);
+    $html_propre = $purificateur->purify($html_a_purifier);
+?>
+
+
+    vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/LICENSE b/library/ezyang/htmlpurifier/LICENSE
new file mode 100644 (file)
index 0000000..8c88a20
--- /dev/null
@@ -0,0 +1,504 @@
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                 GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+    vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/NEWS b/library/ezyang/htmlpurifier/NEWS
new file mode 100644 (file)
index 0000000..a9124af
--- /dev/null
@@ -0,0 +1,1094 @@
+NEWS ( CHANGELOG and HISTORY )                                     HTMLPurifier
+|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+
+= KEY ====================
+    # Breaks back-compat
+    ! Feature
+    - Bugfix
+      + Sub-comment
+    . Internal change
+==========================
+
+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 </div> 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 <li>
+! 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 <li> 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 <code>file</code> 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 <ol><ol> properly, by inserting the necessary <li> 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 &nbsp; 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 <a><span>http://foo</span></a>
+- Make %URI.Munge not apply to links that have the same host as your host.
+- Prevent stray </body> tag from truncating output, if a second </body>
+  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 <a> 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[<body></body>]]> 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, <img src="">)
+  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. <span><span><div> 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 <style> from
+  documents and cleaning their contents up. Requires the CSSTidy library
+  <http://csstidy.sourceforge.net/>. You can access the blocks with the
+  'StyleBlocks' Context variable ($purifier->context->get('StyleBlocks')).
+  The output CSS can also be "scoped" for a specific element, use:
+  %Filter.ExtractStyleBlocksScope
+! Experimental support for some proprietary CSS attributes allowed:
+  opacity (and all of the browser-specific equivalents) and scrollbar colors.
+  Enable by setting %CSS.Proprietary to true.
+- Colors missing # but in hex form will be corrected
+- CSS Number algorithm improved
+- Unit testing and multi-testing now on steroids: command lines,
+  XML output, and other goodies now added.
+. Unit tests for Injector improved
+. New classes:
+  + HTMLPurifier_AttrDef_CSS_AlphaValue
+  + HTMLPurifier_AttrDef_CSS_Filter
+. Multitest now has a file docblock
+
+2.1.3, released 2007-11-05
+! tests/multitest.php allows you to test multiple versions by running
+  tests/index.php through multiple interpreters using `phpv` shell
+  script (you must provide this script!)
+- Fixed poor include ordering for Email URI AttrDefs, causes fatal errors
+  on some systems.
+- Injector algorithm further refined: off-by-one error regarding skip
+  counts for dormant injectors fixed
+- Corrective blockquote definition now enabled for HTML 4.01 Strict
+- Fatal error when <img> tag (or any other element with required attributes)
+  has 'id' attribute fixed, thanks NykO18 for reporting
+- Fix warning emitted when a non-supported URI scheme is passed to the
+  MakeAbsolute URIFilter, thanks NykO18 (again)
+- Further refine AutoParagraph injector. Behavior inside of elements
+  allowing paragraph tags clarified: only inline content delimeted by
+  double newlines (not block elements) are paragraphed.
+- Buggy treatment of end tags of elements that have required attributes
+  fixed (does not manifest on default tag-set)
+- Spurious internal content reorganization error suppressed
+- HTMLDefinition->addElement now returns a reference to the created
+  element object, as implied by the documentation
+- Phorum mod's HTML Purifier help message expanded (unreleased elsewhere)
+- Fix a theoretical class of infinite loops from DirectLex reported
+  by Nate Abele
+- Work around unnecessary DOMElement type-cast in PH5P that caused errors
+  in PHP 5.1
+- Work around PHP 4 SimpleTest lack-of-error complaining for one-time-only
+  HTMLDefinition errors, this may indicate problems with error-collecting
+  facilities in PHP 5
+- Make ErrorCollectorEMock work in both PHP 4 and PHP 5
+- Make PH5P work with PHP 5.0 by removing unnecessary array parameter typedef
+. %Core.AcceptFullDocuments renamed to %Core.ConvertDocumentToFragment
+  to better communicate its purpose
+. Error unit tests can now specify the expectation of no errors. Future
+  iterations of the harness will be extremely strict about what errors
+  are allowed
+. Extend Injector hooks to allow for more powerful injector routines
+. HTMLDefinition->addBlankElement created, as according to the HTMLModule
+  method
+. Doxygen configuration file updated, with minor improvements
+. Test runner now checks for similarly named files in conf/ directory too.
+. Minor cosmetic change to flush-definition-cache.php: trailing newline is
+  outputted
+. Maintenance script for generating PH5P patch added, original PH5P source
+  file also added under version control
+. Full unit test runner script title made more descriptive with PHP version
+. Updated INSTALL file to state that 4.3.7 is the earliest version we
+  are actively testing
+
+2.1.2, released 2007-09-03
+! Implemented Object module for trusted users
+! Implemented experimental HTML5 parsing mode using PH5P. To use, add
+  this to your code:
+        require_once 'HTMLPurifier/Lexer/PH5P.php';
+        $config->set('Core', 'LexerImpl', 'PH5P');
+  Note that this Lexer introduces some classes not in the HTMLPurifier
+  namespace.  Also, this is PHP5 only.
+! CSS property border-spacing implemented
+- Fix non-visible parsing error in DirectLex with empty tags that have
+  slashes inside attribute values.
+- Fix typo in CSS definition: border-collapse:seperate; was incorrectly
+  accepted as valid CSS. Usually non-visible, because this styling is the
+  default for tables in most browsers. Thanks Brett Zamir for pointing
+  this out.
+- Fix validation errors in configuration form
+- Hammer out a bunch of edge-case bugs in the standalone distribution
+- Inclusion reflection removed from URISchemeRegistry; you must manually
+  include any new schema files you wish to use
+- Numerous typo fixes in documentation thanks to Brett Zamir
+. Unit test refactoring for one logical test per test function
+. Config and context parameters in ComplexHarness deprecated: instead, edit
+  the $config and $context member variables
+. HTML wrapper in DOMLex now takes DTD identifiers into account; doesn't
+  really make a difference, but is good for completeness sake
+. merge-library.php script refactored for greater code reusability and
+  PHP4 compatibility
+
+2.1.1, released 2007-08-04
+- Fix show-stopper bug in %URI.MakeAbsolute functionality
+- Fix PHP4 syntax error in standalone version
+. Add prefix directory to include path for standalone, this prevents
+  other installations from clobbering the standalone's URI schemes
+. Single test methods can be invoked by prefixing with __only
+
+2.1.0, released 2007-08-02
+# flush-htmldefinition-cache.php superseded in favor of a generic
+  flush-definition-cache.php script, you can clear a specific cache
+  by passing its name as a parameter to the script
+! Phorum mod implemented for HTML Purifier
+! With %Core.AggressivelyFixLt, <3 and similar emoticons no longer
+  trigger HTML removal in PHP5 (DOMLex). This directive is not necessary
+  for PHP4 (DirectLex).
+! Standalone file now available, which greatly reduces the amount of
+  includes (although there are still a few files that reside in the
+  standalone folder)
+! Relative URIs can now be transformed into their absolute equivalents
+  using %URI.Base and %URI.MakeAbsolute
+! Ruby implemented for XHTML 1.1
+! You can now define custom URI filtering behavior, see enduser-uri-filter.html
+  for more details
+! UTF-8 font names now supported in CSS
+- AutoFormatters emit friendly error messages if tags or attributes they
+  need are not allowed
+- ConfigForm's compactification of directive names is now configurable
+- AutoParagraph autoformatter algorithm refined after field-testing
+- XHTML 1.1 now applies XHTML 1.0 Strict cleanup routines, namely
+  blockquote wrapping
+- Contents of <style> tags removed by default when tags are removed
+. HTMLPurifier_Config->getSerial() implemented, this is extremely useful
+  for output cache invalidation
+. ConfigForm printer now can retrieve CSS and JS files as strings, in
+  case HTML Purifier's directory is not publically accessible
+. Introduce new text/itext configuration directive values: these represent
+  longer strings that would be more appropriately edited with a textarea
+. Allow newlines to act as separators for lists, hashes, lookups and
+  %HTML.Allowed
+. ConfigForm generates textareas instead of text inputs for lists, hashes,
+  lookups, text and itext fields
+. Hidden element content removal genericized: %Core.HiddenElements can
+  be used to customize this behavior, by default <script> and <style> are
+  hidden
+. Added HTMLPURIFIER_PREFIX constant, should be used instead of dirname(__FILE__)
+. Custom ChildDef added to default include list
+. URIScheme reflection improved: will not attempt to include file if class
+  already exists. May clobber autoload, so I need to keep an eye on it
+. ConfigSchema heavily optimized, will only collect information and validate
+  definitions when HTMLPURIFIER_SCHEMA_STRICT is true.
+. AttrDef_URI unit tests and implementation refactored
+. benchmarks/ directory now protected from public view with .htaccess file;
+  run the tests via command line
+. URI scheme is munged off if there is no authority and the scheme is the
+  default one
+. All unit tests inherit from HTMLPurifier_Harness, not UnitTestCase
+. Interface for URIScheme changed
+. Generic URI object to hold components of URI added, most systems involved
+  in URI validation have been migrated to use it
+. Custom filtering for URIs factored out to URIDefinition interface for
+  maximum extensibility
+
+2.0.1, released 2007-06-27
+! Tag auto-closing now based on a ChildDef heuristic rather than a
+  manually set auto_close array; some behavior may change
+! Experimental AutoFormat functionality added: auto-paragraph and
+  linkify your HTML input by setting %AutoFormat.AutoParagraph and
+  %AutoFormat.Linkify to true
+! Newlines normalized internally, and then converted back to the
+  value of PHP_EOL. If this is not desired, set your newline format
+  using %Output.Newline.
+! Beta error collection, messages are implemented for the most generic
+  cases involving Lexing or Strategies
+- Clean up special case code for <script> tags
+- Reorder includes for DefinitionCache decorators, fixes a possible
+  missing class error
+- Fixed bug where manually modified definitions were not saved via cache
+  (mostly harmless, except for the fact that it would be a little slower)
+- Configuration objects with different serials do not clobber each
+  others when revision numbers are unequal
+- Improve Serializer DefinitionCache directory permissions checks
+- DefinitionCache no longer throws errors when it encounters old
+  serial files that do not conform to the current style
+- Stray xmlns attributes removed from configuration documentation
+- configForm.php smoketest no longer has XSS vulnerability due to
+  unescaped print_r output
+- Printer adheres to configuration's directives on output format
+- Fix improperly named form field in ConfigForm printer
+. Rewire some test-cases to swallow errors rather than expect them
+. HTMLDefinition printer updated with some of the new attributes
+. DefinitionCache keys reordered to reflect precedence: version number,
+  hash, then revision number
+. %Core.DefinitionCache renamed to %Cache.DefinitionImpl
+. Interlinking in configuration documentation added using
+  Injector_PurifierLinkify
+. Directives now keep track of aliases to themselves
+. Error collector now requires a severity to be passed, use PHP's internal
+  error constants for this
+. HTMLPurifier_Config::getAllowedDirectivesForForm implemented, allows
+  much easier selective embedding of configuration values
+. Doctype objects now accept public and system DTD identifiers
+. %HTML.Doctype is now constrained by specific values, to specify a custom
+  doctype use new %HTML.CustomDoctype
+. ConfigForm truncates long directives to keep the form small, and does
+  not re-output namespaces
+
+2.0.0, released 2007-06-20
+# Completely refactored HTMLModuleManager, decentralizing safety
+  information
+# Transform modules changed to Tidy modules, which offer more flexibility
+  and better modularization
+# Configuration object now finalizes itself when a read operation is
+  performed on it, ensuring that its internal state stays consistent.
+  To revert this behavior, you can set the $autoFinalize member variable
+  off, but it's not recommended.
+# New compact syntax for AttrDef objects that can be used to instantiate
+  new objects via make()
+# Definitions (esp. HTMLDefinition) are now cached for a significant
+  performance boost. You can disable caching by setting %Core.DefinitionCache
+  to null. You CANNOT edit raw definitions without setting the corresponding
+  DefinitionID directive (%HTML.DefinitionID for HTMLDefinition).
+# Contents between <script> tags are now completely removed if <script>
+  is not allowed
+# Prototype-declarations for Lexer removed in favor of configuration
+  determination of Lexer implementations.
+! HTML Purifier now works in PHP 4.3.2.
+! Configuration form-editing API makes tweaking HTMLPurifier_Config a
+  breeze!
+! Configuration directives that accept hashes now allow new string
+  format: key1:value1,key2:value2
+! ConfigDoc now factored into OOP design
+! All deprecated elements now natively supported
+! Implement TinyMCE styled whitelist specification format in
+  %HTML.Allowed
+! Config object gives more friendly error messages when things go wrong
+! Advanced API implemented: easy functions for creating elements (addElement)
+  and attributes (addAttribute) on HTMLDefinition
+! Add native support for required attributes
+- Deprecated and removed EnableRedundantUTF8Cleaning. It didn't even work!
+- DOMLex will not emit errors when a custom error handler that does not
+  honor error_reporting is used
+- StrictBlockquote child definition refrains from wrapping whitespace
+  in tags now.
+- Bug resulting from tag transforms to non-allowed elements fixed
+- ChildDef_Custom's regex generation has been improved, removing several
+  false positives
+. Unit test for ElementDef created, ElementDef behavior modified to
+  be more flexible
+. Added convenience functions for HTMLModule constructors
+. AttrTypes now has accessor functions that should be used instead
+  of directly manipulating info
+. TagTransform_Center deprecated in favor of generic TagTransform_Simple
+. Add extra protection in AttrDef_URI against phantom Schemes
+. Doctype object added to HTMLDefinition which describes certain aspects
+  of the operational document type
+. Lexer is now pre-emptively included, with a conditional include for the
+  PHP5 only version.
+. HTMLDefinition and CSSDefinition have a common parent class: Definition.
+. DirectLex can now track line-numbers
+. Preliminary error collector is in place, although no code actually reports
+  errors yet
+. Factor out most of ValidateAttributes to new AttrValidator class
+
+1.6.1, released 2007-05-05
+! Support for more deprecated attributes via transformations:
+  + hspace and vspace in img
+  + size and noshade in hr
+  + nowrap in td
+  + clear in br
+  + align in caption, table, img and hr
+  + type in ul, ol and li
+! DirectLex now preserves text in which a < bracket is followed by
+  a non-alphanumeric character. This means that certain emoticons
+  are now preserved.
+! %Core.RemoveInvalidImg is now operational, when set to false invalid
+  images will hang around with an empty src
+! target attribute in a tag supported, use %Attr.AllowedFrameTargets
+  to enable
+! CSS property white-space now allows nowrap (supported in all modern
+  browsers) but not others (which have spotty browser implementations)
+! XHTML 1.1 mode now sort-of works without any fatal errors, and
+  lang is now moved over to xml:lang.
+! Attribute transformation smoketest available at smoketests/attrTransform.php
+! Transformation of font's size attribute now handles super-large numbers
+- Possibly fatal bug with __autoload() fixed in module manager
+- Invert HTMLModuleManager->addModule() processing order to check
+  prefixes first and then the literal module
+- Empty strings get converted to empty arrays instead of arrays with
+  an empty string in them.
+- Merging in attribute lists now works.
+. Demo script removed: it has been added to the website's repository
+. Basic.php script modified to work out of the box
+. Refactor AttrTransform classes to reduce duplication
+. AttrTransform_TextAlign axed in favor of a more general
+  AttrTransform_EnumToCSS, refer to HTMLModule/TransformToStrict.php to
+  see how the new equivalent is implemented
+. Unit tests now use exclusively assertIdentical
+
+1.6.0, released 2007-04-01
+! Support for most common deprecated attributes via transformations:
+  + bgcolor in td, th, tr and table
+  + border in img
+  + name in a and img
+  + width in td, th and hr
+  + height in td, th
+! Support for CSS attribute 'height' added
+! Support for rel and rev attributes in a tags added, use %Attr.AllowedRel
+  and %Attr.AllowedRev to activate
+- You can define ID blacklists using regular expressions via
+  %Attr.IDBlacklistRegexp
+- Error messages are emitted when you attempt to "allow" elements or
+  attributes that HTML Purifier does not support
+- Fix segfault in unit test. The problem is not very reproduceable and
+  I don't know what causes it, but a six line patch fixed it.
+
+1.5.0, released 2007-03-23
+! Added a rudimentary I18N and L10N system modeled off MediaWiki. It
+  doesn't actually do anything yet, but keep your eyes peeled.
+! docs/enduser-utf8.html explains how to use UTF-8 and HTML Purifier
+! Newly structured HTMLDefinition modeled off of XHTML 1.1 modules.
+  I am loathe to release beta quality APIs, but this is exactly that;
+  don't use the internal interfaces if you're not willing to do migration
+  later on.
+- Allow 'x' subtag in language codes
+- Fixed buggy chameleon-support for ins and del
+. Added support for IDREF attributes (i.e. for)
+. Renamed HTMLPurifier_AttrDef_Class to HTMLPurifier_AttrDef_Nmtokens
+. Removed context variable ParentType, replaced with IsInline, which
+  is false when you're not inline and an integer of the parent that
+  caused you to become inline when you are (so possibly zero)
+. Removed ElementDef->type in favor of ElementDef->descendants_are_inline
+  and HTMLDefinition->content_sets
+. StrictBlockquote now reports what elements its supposed to allow,
+  rather than what it does allow
+. Removed HTMLDefinition->info_flow_elements in favor of
+  HTMLDefinition->content_sets['Flow']
+. Removed redundant "exclusionary" definitions from DTD roster
+. StrictBlockquote now requires a construction parameter as if it
+  were an Required ChildDef, this is the "real" set of allowed elements
+. AttrDef partitioned into HTML, CSS and URI segments
+. Modify Youtube filter regexp to be multiline
+. Require both PHP5 and DOM extension in order to use DOMLex, fixes
+  some edge cases where a DOMDocument class exists in a PHP4 environment
+  due to DOM XML extension.
+
+1.4.1, released 2007-01-21
+! docs/enduser-youtube.html updated according to new functionality
+- YouTube IDs can have underscores and dashes
+
+1.4.0, released 2007-01-21
+! Implemented list-style-image, URIs now allowed in list-style
+! Implemented background-image, background-repeat, background-attachment
+  and background-position CSS properties. Shorthand property background
+  supports all of these properties.
+! Configuration documentation looks nicer
+! Added %Core.EscapeNonASCIICharacters to workaround loss of Unicode
+  characters while %Core.Encoding is set to a non-UTF-8 encoding.
+! Support for configuration directive aliases added
+! Config object can now be instantiated from ini files
+! YouTube preservation code added to the core, with two lines of code
+  you can add it as a filter to your code. See smoketests/preserveYouTube.php
+  for sample code.
+! Moved SLOW to docs/enduser-slow.html and added code examples
+- Replaced version check with functionality check for DOM (thanks Stephen
+  Khoo)
+. Added smoketest 'all.php', which loads all other smoketests via frames
+. Implemented AttrDef_CSSURI for url(http://google.com) style declarations
+. Added convenient single test selector form on test runner
+
+1.3.2, released 2006-12-25
+! HTMLPurifier object now accepts configuration arrays, no need to manually
+  instantiate a configuration object
+! Context object now accessible to outside
+! Added enduser-youtube.html, explains how to embed YouTube videos. See
+  also corresponding smoketest preserveYouTube.php.
+! Added purifyArray(), which takes a list of HTML and purifies it all
+! Added static member variable $version to HTML Purifier with PHP-compatible
+  version number string.
+- Fixed fatal error thrown by upper-cased language attributes
+- printDefinition.php: added labels, added better clarification
+. HTMLPurifier_Config::create() added, takes mixed variable and converts into
+  a HTMLPurifier_Config object.
+
+1.3.1, released 2006-12-06
+! Added HTMLPurifier.func.php stub for a convenient function to call the library
+- Fixed bug in RemoveInvalidImg code that caused all images to be dropped
+  (thanks to .mario for reporting this)
+. Standardized all attribute handling variables to attr, made it plural
+
+1.3.0, released 2006-11-26
+# Invalid images are now removed, rather than replaced with a dud
+  <img src="" alt="Invalid image" />. Previous behavior can be restored
+  with new directive %Core.RemoveInvalidImg set to false.
+! (X)HTML Strict now supported
+  + Transparently handles inline elements in block context (blockquote)
+! Added GET method to demo for easier validation, added 50kb max input size
+! New directive %HTML.BlockWrapper, for block-ifying inline elements
+! New directive %HTML.Parent, allows you to only allow inline content
+! New directives %HTML.AllowedElements and %HTML.AllowedAttributes to let
+  users narrow the set of allowed tags
+! <li value="4"> and <ul start="2"> now allowed in loose mode
+! New directives %URI.DisableExternalResources and %URI.DisableResources
+! New directive %Attr.DisableURI, which eliminates all hyperlinking
+! New directive %URI.Munge, munges URI so you can use some sort of redirector
+  service to avoid PageRank leaks or warn users that they are exiting your site.
+! Added spiffy new smoketest printDefinition.php, which lets you twiddle with
+  the configuration settings and see how the internal rules are affected.
+! New directive %URI.HostBlacklist for blocking links to bad hosts.
+  xssAttacks.php smoketest updated accordingly.
+- Added missing type to ChildDef_Chameleon
+- Remove Tidy option from demo if there is not Tidy available
+. ChildDef_Required guards against empty tags
+. Lookup table HTMLDefinition->info_flow_elements added
+. Added peace-of-mind variable initialization to Strategy_FixNesting
+. Added HTMLPurifier->info_parent_def, parent child processing made special
+. Added internal documents briefly summarizing future progression of HTML
+. HTMLPurifier_Config->getBatch($namespace) added
+. More lenient casting to bool from string in HTMLPurifier_ConfigSchema
+. Refactored ChildDef classes into their own files
+
+1.2.0, released 2006-11-19
+# ID attributes now disabled by default. New directives:
+  + %HTML.EnableAttrID - restores old behavior by allowing IDs
+  + %Attr.IDPrefix - %Attr.IDBlacklist alternative that munges all user IDs
+    so that they don't collide with your IDs
+  + %Attr.IDPrefixLocal - Same as above, but for when there are multiple
+    instances of user content on the page
+  + Profuse documentation on how to use these available in docs/enduser-id.txt
+! Added MODx plugin <http://modxcms.com/forums/index.php/topic,6604.0.html>
+! Added percent encoding normalization
+! XSS attacks smoketest given facelift
+! Configuration documentation now has table of contents
+! Added %URI.DisableExternal, which prevents links to external websites.  You
+  can also use %URI.Host to permit absolute linking to subdomains
+! Non-accessible resources (ex. mailto) blocked from embedded URIs (img src)
+- Type variable in HTMLDefinition was not being set properly, fixed
+- Documentation updated
+  + TODO added request Phalanger
+  + TODO added request Native compression
+  + TODO added request Remove redundant tags
+  + TODO added possible plaintext formatter for HTML Purifier documentation
+  + Updated ConfigDoc TODO
+  + Improved inline comments in AttrDef/Class.php, AttrDef/CSS.php
+    and AttrDef/Host.php
+  + Revamped documentation into HTML, along with misc updates
+- HTMLPurifier_Context doesn't throw a variable reference error if you attempt
+  to retrieve a non-existent variable
+. Switched to purify()-wide Context object registry
+. Refactored unit tests to minimize duplication
+. XSS attack sheet updated
+. configdoc.xml now has xml:space attached to default value nodes
+. Allow configuration directives to permit null values
+. Cleaned up test-cases to remove unnecessary swallowErrors()
+
+1.1.2, released 2006-09-30
+! Add HTMLPurifier.auto.php stub file that configures include_path
+- Documentation updated
+  + INSTALL document rewritten
+  + TODO added semi-lossy conversion
+  + API Doxygen docs' file exclusions updated
+  + Added notes on HTML versus XML attribute whitespace handling
+  + Noted that HTMLPurifier_ChildDef_Custom isn't being used
+  + Noted that config object's definitions are cached versions
+- Fixed lack of attribute parsing in HTMLPurifier_Lexer_PEARSax3
+- ftp:// URIs now have their typecodes checked
+- Hooked up HTMLPurifier_ChildDef_Custom's unit tests (they weren't being run)
+. Line endings standardized throughout project (svn:eol-style standardized)
+. Refactored parseData() to general Lexer class
+. Tester named "HTML Purifier" not "HTMLPurifier"
+
+1.1.1, released 2006-09-24
+! Configuration option to optionally Tidy up output for indentation to make up
+  for dropped whitespace by DOMLex (pretty-printing for the entire application
+  should be done by a page-wide Tidy)
+- Various documentation updates
+- Fixed parse error in configuration documentation script
+- Fixed fatal error in benchmark scripts, slightly augmented
+- As far as possible, whitespace is preserved in-between table children
+- Sample test-settings.php file included
+
+1.1.0, released 2006-09-16
+! Directive documentation generation using XSLT
+! XHTML can now be turned off, output becomes <br>
+- Made URI validator more forgiving: will ignore leading and trailing
+  quotes, apostrophes and less than or greater than signs.
+- Enforce alphanumeric namespace and directive names for configuration.
+- Table child definition made more flexible, will fix up poorly ordered elements
+. Renamed ConfigDef to ConfigSchema
+
+1.0.1, released 2006-09-04
+- Fixed slight bug in DOMLex attribute parsing
+- Fixed rejection of case-insensitive configuration values when there is a
+  set of allowed values.  This manifested in %Core.Encoding.
+- Fixed rejection of inline style declarations that had lots of extra
+  space in them.  This manifested in TinyMCE.
+
+1.0.0, released 2006-09-01
+! Shorthand CSS properties implemented: font, border, background, list-style
+! Basic color keywords translated into hexadecimal values
+! Table CSS properties implemented
+! Support for charsets other than UTF-8 (defined by iconv)
+! Malformed UTF-8 and non-SGML character detection and cleaning implemented
+- Fixed broken numeric entity conversion
+- API documentation completed
+. (HTML|CSS)Definition de-singleton-ized
+
+1.0.0beta, released 2006-08-16
+! First public release, most functionality implemented. Notable omissions are:
+  + Shorthand CSS properties
+  + Table CSS properties
+  + Deprecated attribute transformations
+
+    vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/README b/library/ezyang/htmlpurifier/README
new file mode 100644 (file)
index 0000000..53f26f1
--- /dev/null
@@ -0,0 +1,24 @@
+
+README
+    All about HTML Purifier
+
+HTML Purifier is an HTML filtering solution that uses a unique combination
+of robust whitelists and agressive 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/
+
+    vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/TODO b/library/ezyang/htmlpurifier/TODO
new file mode 100644 (file)
index 0000000..a92abf2
--- /dev/null
@@ -0,0 +1,150 @@
+
+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.8 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 -> <br>)
+ - 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 <pre> 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 <style> tags; may be troublesome
+   because regular CSS has no way of uniquely identifying nodes, so we'd
+   have to generate IDs
+ - Explain how to use HTML Purifier in non-PHP languages / create
+   a simple command line stub (or complicated?)
+ - Fixes for Firefox's inability to handle COL alignment props (Bug 915)
+ - Automatically add non-breaking spaces to empty table cells when
+   empty-cells:show is applied to have compatibility with Internet Explorer
+ - Table of Contents generation (XHTML Compiler might be reusable). May also
+   be out-of-band information.
+ - Full set of color keywords. Also, a way to add onto them without
+   finalizing the configuration object.
+ - Write a var_export and memcached DefinitionCache - Denis
+ - Built-in support for target="_blank" on all external links
+ - Convert RTL/LTR override characters to <bdo> tags, or vice versa on demand.
+   Also, enable disabling of directionality
+ ? Externalize inline CSS to promote clean HTML, proposed by Sander Tekelenburg
+ ? Remove redundant tags, ex. <u><u>Underlined</u></u>. Implementation notes:
+    1. Analyzing which tags to remove duplicants
+    2. Ensure attributes are merged into the parent tag
+    3. Extend the tag exclusion system to specify whether or not the
+    contents should be dropped or not (currently, there's code that could do
+    something like this if it didn't drop the inner text too.)
+ ? Make AutoParagraph also support paragraph-izing double <br> tags, and not
+   just double newlines.  This is kind of tough to do in the current framework,
+   though, and might be reasonably approximated by search replacing double <br>s
+   with newlines before running it through HTML Purifier.
+
+Maintenance related (slightly boring)
+ # CHMOD install script for PEAR installs
+ ! Factor out command line parser into its own class, and unit test it
+ - Reduce size of internal data-structures (esp. HTMLDefinition)
+ - Allow merging configurations.  Thus,
+        a -> b -> default
+        c -> d -> default
+   becomes
+        a -> b -> c -> d -> default
+   Maybe allow more fine-grained tuning of this behavior. Alternatively,
+   encourage people to use short plist depths before building them up.
+ - Time PHPT tests
+
+ChildDef related (very boring)
+ - Abstract ChildDef_BlockQuote to work with all elements that only
+   allow blocks in them, required or optional
+ - Implement lenient <ruby> child validation
+
+Wontfix
+ - Non-lossy smart alternate character encoding transformations (unless
+   patch provided)
+ - Pretty-printing HTML: users can use Tidy on the output on entire page
+ - Native content compression, whitespace stripping: use gzip if this is
+   really important
+
+    vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/VERSION b/library/ezyang/htmlpurifier/VERSION
new file mode 100644 (file)
index 0000000..1163055
--- /dev/null
@@ -0,0 +1 @@
+4.7.0
\ No newline at end of file
diff --git a/library/ezyang/htmlpurifier/WHATSNEW b/library/ezyang/htmlpurifier/WHATSNEW
new file mode 100644 (file)
index 0000000..4e5eb2b
--- /dev/null
@@ -0,0 +1,4 @@
+HTML Purifier 4.7.0 is a bugfix release, collecting two years
+worth of accumulated bug fixes.  Highlighted bugfixes are updated
+YouTube filter code, corrected rgb() CSS parsing, and one new
+configuration option, %AutoFormat.RemoveEmpty.Predicate.
diff --git a/library/ezyang/htmlpurifier/WYSIWYG b/library/ezyang/htmlpurifier/WYSIWYG
new file mode 100644 (file)
index 0000000..c518aac
--- /dev/null
@@ -0,0 +1,20 @@
+
+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
diff --git a/library/ezyang/htmlpurifier/composer.json b/library/ezyang/htmlpurifier/composer.json
new file mode 100644 (file)
index 0000000..2f59d0f
--- /dev/null
@@ -0,0 +1,22 @@
+{
+    "name": "ezyang/htmlpurifier",
+    "description": "Standards compliant HTML filter written in PHP",
+    "type": "library",
+    "keywords": ["html"],
+    "homepage": "http://htmlpurifier.org/",
+    "license": "LGPL",
+    "authors": [
+        {
+            "name": "Edward Z. Yang",
+            "email": "admin@htmlpurifier.org",
+            "homepage": "http://ezyang.com"
+        }
+    ],
+    "require": {
+        "php": ">=5.2"
+    },
+    "autoload": {
+        "psr-0": { "HTMLPurifier": "library/" },
+        "files": ["library/HTMLPurifier.composer.php"]
+    }
+}
diff --git a/library/ezyang/htmlpurifier/extras/ConfigDoc/HTMLXSLTProcessor.php b/library/ezyang/htmlpurifier/extras/ConfigDoc/HTMLXSLTProcessor.php
new file mode 100644 (file)
index 0000000..1cfec5d
--- /dev/null
@@ -0,0 +1,91 @@
+<?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); // <br /> not <br/>
+        $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
diff --git a/library/ezyang/htmlpurifier/extras/FSTools.php b/library/ezyang/htmlpurifier/extras/FSTools.php
new file mode 100644 (file)
index 0000000..ce00763
--- /dev/null
@@ -0,0 +1,164 @@
+<?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
diff --git a/library/ezyang/htmlpurifier/extras/FSTools/File.php b/library/ezyang/htmlpurifier/extras/FSTools/File.php
new file mode 100644 (file)
index 0000000..6453a7a
--- /dev/null
@@ -0,0 +1,141 @@
+<?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
diff --git a/library/ezyang/htmlpurifier/extras/HTMLPurifierExtras.auto.php b/library/ezyang/htmlpurifier/extras/HTMLPurifierExtras.auto.php
new file mode 100644 (file)
index 0000000..4016d8a
--- /dev/null
@@ -0,0 +1,11 @@
+<?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
diff --git a/library/ezyang/htmlpurifier/extras/HTMLPurifierExtras.autoload.php b/library/ezyang/htmlpurifier/extras/HTMLPurifierExtras.autoload.php
new file mode 100644 (file)
index 0000000..de4a8aa
--- /dev/null
@@ -0,0 +1,26 @@
+<?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')) {
+    function __autoload($class)
+    {
+        return HTMLPurifierExtras::autoload($class);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/extras/HTMLPurifierExtras.php b/library/ezyang/htmlpurifier/extras/HTMLPurifierExtras.php
new file mode 100644 (file)
index 0000000..35c2ca7
--- /dev/null
@@ -0,0 +1,31 @@
+<?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
diff --git a/library/ezyang/htmlpurifier/extras/README b/library/ezyang/htmlpurifier/extras/README
new file mode 100644 (file)
index 0000000..4bfece7
--- /dev/null
@@ -0,0 +1,32 @@
+
+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
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier.auto.php b/library/ezyang/htmlpurifier/library/HTMLPurifier.auto.php
new file mode 100644 (file)
index 0000000..1960c39
--- /dev/null
@@ -0,0 +1,11 @@
+<?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
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier.autoload.php b/library/ezyang/htmlpurifier/library/HTMLPurifier.autoload.php
new file mode 100644 (file)
index 0000000..c3ea67e
--- /dev/null
@@ -0,0 +1,27 @@
+<?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')) {
+    function __autoload($class)
+    {
+        return HTMLPurifier_Bootstrap::autoload($class);
+    }
+}
+
+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
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier.composer.php b/library/ezyang/htmlpurifier/library/HTMLPurifier.composer.php
new file mode 100644 (file)
index 0000000..52acc56
--- /dev/null
@@ -0,0 +1,4 @@
+<?php
+if (!defined('HTMLPURIFIER_PREFIX')) {
+    define('HTMLPURIFIER_PREFIX', dirname(__FILE__));
+}
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier.func.php b/library/ezyang/htmlpurifier/library/HTMLPurifier.func.php
new file mode 100644 (file)
index 0000000..64b140b
--- /dev/null
@@ -0,0 +1,25 @@
+<?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
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier.includes.php b/library/ezyang/htmlpurifier/library/HTMLPurifier.includes.php
new file mode 100644 (file)
index 0000000..fdb58c2
--- /dev/null
@@ -0,0 +1,229 @@
+<?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.7.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/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/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/VarParser/Flexible.php';
+require 'HTMLPurifier/VarParser/Native.php';
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier.kses.php b/library/ezyang/htmlpurifier/library/HTMLPurifier.kses.php
new file mode 100644 (file)
index 0000000..7522900
--- /dev/null
@@ -0,0 +1,30 @@
+<?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
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier.path.php b/library/ezyang/htmlpurifier/library/HTMLPurifier.path.php
new file mode 100644 (file)
index 0000000..39b1b65
--- /dev/null
@@ -0,0 +1,11 @@
+<?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
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier.php b/library/ezyang/htmlpurifier/library/HTMLPurifier.php
new file mode 100644 (file)
index 0000000..c6041bc
--- /dev/null
@@ -0,0 +1,292 @@
+<?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.7.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.7.0';
+
+    /**
+     * Constant with version of HTML Purifier.
+     */
+    const VERSION = '4.7.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 $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
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier.safe-includes.php b/library/ezyang/htmlpurifier/library/HTMLPurifier.safe-includes.php
new file mode 100644 (file)
index 0000000..9dea6d1
--- /dev/null
@@ -0,0 +1,223 @@
+<?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/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/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/VarParser/Flexible.php';
+require_once $__dir . '/HTMLPurifier/VarParser/Native.php';
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Arborize.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Arborize.php
new file mode 100644 (file)
index 0000000..9e6617b
--- /dev/null
@@ -0,0 +1,71 @@
+<?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;
+    }
+}
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrCollections.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrCollections.php
new file mode 100644 (file)
index 0000000..4f6c2e3
--- /dev/null
@@ -0,0 +1,143 @@
+<?php
+
+/**
+ * Defines common attribute collections that modules reference
+ */
+
+class HTMLPurifier_AttrCollections
+{
+
+    /**
+     * Associative array of attribute collections, indexed by name.
+     * @type array
+     */
+    public $info = array();
+
+    /**
+     * Performs all expansions on internal data for use by other inclusions
+     * It also collects all attribute collection extensions from
+     * modules
+     * @param HTMLPurifier_AttrTypes $attr_types HTMLPurifier_AttrTypes instance
+     * @param HTMLPurifier_HTMLModule[] $modules Hash array of HTMLPurifier_HTMLModule members
+     */
+    public function __construct($attr_types, $modules)
+    {
+        // load extensions from the modules
+        foreach ($modules as $module) {
+            foreach ($module->attr_collections as $coll_i => $coll) {
+                if (!isset($this->info[$coll_i])) {
+                    $this->info[$coll_i] = array();
+                }
+                foreach ($coll as $attr_i => $attr) {
+                    if ($attr_i === 0 && isset($this->info[$coll_i][$attr_i])) {
+                        // merge in includes
+                        $this->info[$coll_i][$attr_i] = array_merge(
+                            $this->info[$coll_i][$attr_i],
+                            $attr
+                        );
+                        continue;
+                    }
+                    $this->info[$coll_i][$attr_i] = $attr;
+                }
+            }
+        }
+        // perform internal expansions and inclusions
+        foreach ($this->info as $name => $attr) {
+            // merge attribute collections that include others
+            $this->performInclusions($this->info[$name]);
+            // replace string identifiers with actual attribute objects
+            $this->expandIdentifiers($this->info[$name], $attr_types);
+        }
+    }
+
+    /**
+     * Takes a reference to an attribute associative array and performs
+     * all inclusions specified by the zero index.
+     * @param array &$attr Reference to attribute array
+     */
+    public function performInclusions(&$attr)
+    {
+        if (!isset($attr[0])) {
+            return;
+        }
+        $merge = $attr[0];
+        $seen  = array(); // recursion guard
+        // loop through all the inclusions
+        for ($i = 0; isset($merge[$i]); $i++) {
+            if (isset($seen[$merge[$i]])) {
+                continue;
+            }
+            $seen[$merge[$i]] = true;
+            // foreach attribute of the inclusion, copy it over
+            if (!isset($this->info[$merge[$i]])) {
+                continue;
+            }
+            foreach ($this->info[$merge[$i]] as $key => $value) {
+                if (isset($attr[$key])) {
+                    continue;
+                } // also catches more inclusions
+                $attr[$key] = $value;
+            }
+            if (isset($this->info[$merge[$i]][0])) {
+                // recursion
+                $merge = array_merge($merge, $this->info[$merge[$i]][0]);
+            }
+        }
+        unset($attr[0]);
+    }
+
+    /**
+     * Expands all string identifiers in an attribute array by replacing
+     * them with the appropriate values inside HTMLPurifier_AttrTypes
+     * @param array &$attr Reference to attribute array
+     * @param HTMLPurifier_AttrTypes $attr_types HTMLPurifier_AttrTypes instance
+     */
+    public function expandIdentifiers(&$attr, $attr_types)
+    {
+        // because foreach will process new elements we add, make sure we
+        // skip duplicates
+        $processed = array();
+
+        foreach ($attr as $def_i => $def) {
+            // skip inclusions
+            if ($def_i === 0) {
+                continue;
+            }
+
+            if (isset($processed[$def_i])) {
+                continue;
+            }
+
+            // determine whether or not attribute is required
+            if ($required = (strpos($def_i, '*') !== false)) {
+                // rename the definition
+                unset($attr[$def_i]);
+                $def_i = trim($def_i, '*');
+                $attr[$def_i] = $def;
+            }
+
+            $processed[$def_i] = true;
+
+            // if we've already got a literal object, move on
+            if (is_object($def)) {
+                // preserve previous required
+                $attr[$def_i]->required = ($required || $attr[$def_i]->required);
+                continue;
+            }
+
+            if ($def === false) {
+                unset($attr[$def_i]);
+                continue;
+            }
+
+            if ($t = $attr_types->get($def)) {
+                $attr[$def_i] = $t;
+                $attr[$def_i]->required = $required;
+            } else {
+                unset($attr[$def_i]);
+            }
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef.php
new file mode 100644 (file)
index 0000000..5ac0652
--- /dev/null
@@ -0,0 +1,138 @@
+<?php
+
+/**
+ * Base class for all validating attribute definitions.
+ *
+ * This family of classes forms the core for not only HTML attribute validation,
+ * but also any sort of string that needs to be validated or cleaned (which
+ * means CSS properties and composite definitions are defined here too).
+ * Besides defining (through code) what precisely makes the string valid,
+ * subclasses are also responsible for cleaning the code if possible.
+ */
+
+abstract class HTMLPurifier_AttrDef
+{
+
+    /**
+     * Tells us whether or not an HTML attribute is minimized.
+     * Has no meaning in other contexts.
+     * @type bool
+     */
+    public $minimized = false;
+
+    /**
+     * Tells us whether or not an HTML attribute is required.
+     * Has no meaning in other contexts
+     * @type bool
+     */
+    public $required = false;
+
+    /**
+     * Validates and cleans passed string according to a definition.
+     *
+     * @param string $string String to be validated and cleaned.
+     * @param HTMLPurifier_Config $config Mandatory HTMLPurifier_Config object.
+     * @param HTMLPurifier_Context $context Mandatory HTMLPurifier_Context object.
+     */
+    abstract public function validate($string, $config, $context);
+
+    /**
+     * Convenience method that parses a string as if it were CDATA.
+     *
+     * This method process a string in the manner specified at
+     * <http://www.w3.org/TR/html4/types.html#h-6.2> by removing
+     * leading and trailing whitespace, ignoring line feeds, and replacing
+     * carriage returns and tabs with spaces.  While most useful for HTML
+     * attributes specified as CDATA, it can also be applied to most CSS
+     * values.
+     *
+     * @note This method is not entirely standards compliant, as trim() removes
+     *       more types of whitespace than specified in the spec. In practice,
+     *       this is rarely a problem, as those extra characters usually have
+     *       already been removed by HTMLPurifier_Encoder.
+     *
+     * @warning This processing is inconsistent with XML's whitespace handling
+     *          as specified by section 3.3.3 and referenced XHTML 1.0 section
+     *          4.7.  However, note that we are NOT necessarily
+     *          parsing XML, thus, this behavior may still be correct. We
+     *          assume that newlines have been normalized.
+     */
+    public function parseCDATA($string)
+    {
+        $string = trim($string);
+        $string = str_replace(array("\n", "\t", "\r"), ' ', $string);
+        return $string;
+    }
+
+    /**
+     * Factory method for creating this class from a string.
+     * @param string $string String construction info
+     * @return HTMLPurifier_AttrDef Created AttrDef object corresponding to $string
+     */
+    public function make($string)
+    {
+        // default implementation, return a flyweight of this object.
+        // If $string has an effect on the returned object (i.e. you
+        // need to overload this method), it is best
+        // to clone or instantiate new copies. (Instantiation is safer.)
+        return $this;
+    }
+
+    /**
+     * Removes spaces from rgb(0, 0, 0) so that shorthand CSS properties work
+     * properly. THIS IS A HACK!
+     * @param string $string a CSS colour definition
+     * @return string
+     */
+    protected function mungeRgb($string)
+    {
+        return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string);
+    }
+
+    /**
+     * Parses a possibly escaped CSS string and returns the "pure"
+     * version of it.
+     */
+    protected function expandCSSEscape($string)
+    {
+        // flexibly parse it
+        $ret = '';
+        for ($i = 0, $c = strlen($string); $i < $c; $i++) {
+            if ($string[$i] === '\\') {
+                $i++;
+                if ($i >= $c) {
+                    $ret .= '\\';
+                    break;
+                }
+                if (ctype_xdigit($string[$i])) {
+                    $code = $string[$i];
+                    for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) {
+                        if (!ctype_xdigit($string[$i])) {
+                            break;
+                        }
+                        $code .= $string[$i];
+                    }
+                    // We have to be extremely careful when adding
+                    // new characters, to make sure we're not breaking
+                    // the encoding.
+                    $char = HTMLPurifier_Encoder::unichr(hexdec($code));
+                    if (HTMLPurifier_Encoder::cleanUTF8($char) === '') {
+                        continue;
+                    }
+                    $ret .= $char;
+                    if ($i < $c && trim($string[$i]) !== '') {
+                        $i--;
+                    }
+                    continue;
+                }
+                if ($string[$i] === "\n") {
+                    continue;
+                }
+            }
+            $ret .= $string[$i];
+        }
+        return $ret;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS.php
new file mode 100644 (file)
index 0000000..02c1641
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+
+/**
+ * Validates the HTML attribute style, otherwise known as CSS.
+ * @note We don't implement the whole CSS specification, so it might be
+ *       difficult to reuse this component in the context of validating
+ *       actual stylesheet declarations.
+ * @note If we were really serious about validating the CSS, we would
+ *       tokenize the styles and then parse the tokens. Obviously, we
+ *       are not doing that. Doing that could seriously harm performance,
+ *       but would make these components a lot more viable for a CSS
+ *       filtering solution.
+ */
+class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * @param string $css
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($css, $config, $context)
+    {
+        $css = $this->parseCDATA($css);
+
+        $definition = $config->getCSSDefinition();
+
+        // we're going to break the spec and explode by semicolons.
+        // This is because semicolon rarely appears in escaped form
+        // Doing this is generally flaky but fast
+        // IT MIGHT APPEAR IN URIs, see HTMLPurifier_AttrDef_CSSURI
+        // for details
+
+        $declarations = explode(';', $css);
+        $propvalues = array();
+
+        /**
+         * Name of the current CSS property being validated.
+         */
+        $property = false;
+        $context->register('CurrentCSSProperty', $property);
+
+        foreach ($declarations as $declaration) {
+            if (!$declaration) {
+                continue;
+            }
+            if (!strpos($declaration, ':')) {
+                continue;
+            }
+            list($property, $value) = explode(':', $declaration, 2);
+            $property = trim($property);
+            $value = trim($value);
+            $ok = false;
+            do {
+                if (isset($definition->info[$property])) {
+                    $ok = true;
+                    break;
+                }
+                if (ctype_lower($property)) {
+                    break;
+                }
+                $property = strtolower($property);
+                if (isset($definition->info[$property])) {
+                    $ok = true;
+                    break;
+                }
+            } while (0);
+            if (!$ok) {
+                continue;
+            }
+            // inefficient call, since the validator will do this again
+            if (strtolower(trim($value)) !== 'inherit') {
+                // inherit works for everything (but only on the base property)
+                $result = $definition->info[$property]->validate(
+                    $value,
+                    $config,
+                    $context
+                );
+            } else {
+                $result = 'inherit';
+            }
+            if ($result === false) {
+                continue;
+            }
+            $propvalues[$property] = $result;
+        }
+
+        $context->destroy('CurrentCSSProperty');
+
+        // procedure does not write the new CSS simultaneously, so it's
+        // slightly inefficient, but it's the only way of getting rid of
+        // duplicates. Perhaps config to optimize it, but not now.
+
+        $new_declarations = '';
+        foreach ($propvalues as $prop => $value) {
+            $new_declarations .= "$prop:$value;";
+        }
+
+        return $new_declarations ? $new_declarations : false;
+
+    }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php
new file mode 100644 (file)
index 0000000..af2b83d
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+class HTMLPurifier_AttrDef_CSS_AlphaValue extends HTMLPurifier_AttrDef_CSS_Number
+{
+
+    public function __construct()
+    {
+        parent::__construct(false); // opacity is non-negative, but we will clamp it
+    }
+
+    /**
+     * @param string $number
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return string
+     */
+    public function validate($number, $config, $context)
+    {
+        $result = parent::validate($number, $config, $context);
+        if ($result === false) {
+            return $result;
+        }
+        $float = (float)$result;
+        if ($float < 0.0) {
+            $result = '0';
+        }
+        if ($float > 1.0) {
+            $result = '1';
+        }
+        return $result;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Background.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Background.php
new file mode 100644 (file)
index 0000000..7f1ea3b
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * Validates shorthand CSS property background.
+ * @warning Does not support url tokens that have internal spaces.
+ */
+class HTMLPurifier_AttrDef_CSS_Background extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * Local copy of component validators.
+     * @type HTMLPurifier_AttrDef[]
+     * @note See HTMLPurifier_AttrDef_Font::$info for a similar impl.
+     */
+    protected $info;
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function __construct($config)
+    {
+        $def = $config->getCSSDefinition();
+        $this->info['background-color'] = $def->info['background-color'];
+        $this->info['background-image'] = $def->info['background-image'];
+        $this->info['background-repeat'] = $def->info['background-repeat'];
+        $this->info['background-attachment'] = $def->info['background-attachment'];
+        $this->info['background-position'] = $def->info['background-position'];
+    }
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        // regular pre-processing
+        $string = $this->parseCDATA($string);
+        if ($string === '') {
+            return false;
+        }
+
+        // munge rgb() decl if necessary
+        $string = $this->mungeRgb($string);
+
+        // assumes URI doesn't have spaces in it
+        $bits = explode(' ', $string); // bits to process
+
+        $caught = array();
+        $caught['color'] = false;
+        $caught['image'] = false;
+        $caught['repeat'] = false;
+        $caught['attachment'] = false;
+        $caught['position'] = false;
+
+        $i = 0; // number of catches
+
+        foreach ($bits as $bit) {
+            if ($bit === '') {
+                continue;
+            }
+            foreach ($caught as $key => $status) {
+                if ($key != 'position') {
+                    if ($status !== false) {
+                        continue;
+                    }
+                    $r = $this->info['background-' . $key]->validate($bit, $config, $context);
+                } else {
+                    $r = $bit;
+                }
+                if ($r === false) {
+                    continue;
+                }
+                if ($key == 'position') {
+                    if ($caught[$key] === false) {
+                        $caught[$key] = '';
+                    }
+                    $caught[$key] .= $r . ' ';
+                } else {
+                    $caught[$key] = $r;
+                }
+                $i++;
+                break;
+            }
+        }
+
+        if (!$i) {
+            return false;
+        }
+        if ($caught['position'] !== false) {
+            $caught['position'] = $this->info['background-position']->
+                validate($caught['position'], $config, $context);
+        }
+
+        $ret = array();
+        foreach ($caught as $value) {
+            if ($value === false) {
+                continue;
+            }
+            $ret[] = $value;
+        }
+
+        if (empty($ret)) {
+            return false;
+        }
+        return implode(' ', $ret);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php
new file mode 100644 (file)
index 0000000..4580ef5
--- /dev/null
@@ -0,0 +1,157 @@
+<?php
+
+/* W3C says:
+    [ // adjective and number must be in correct order, even if
+      // you could switch them without introducing ambiguity.
+      // some browsers support that syntax
+        [
+            <percentage> | <length> | left | center | right
+        ]
+        [
+            <percentage> | <length> | top | center | bottom
+        ]?
+    ] |
+    [ // this signifies that the vertical and horizontal adjectives
+      // can be arbitrarily ordered, however, there can only be two,
+      // one of each, or none at all
+        [
+            left | center | right
+        ] ||
+        [
+            top | center | bottom
+        ]
+    ]
+    top, left = 0%
+    center, (none) = 50%
+    bottom, right = 100%
+*/
+
+/* QuirksMode says:
+    keyword + length/percentage must be ordered correctly, as per W3C
+
+    Internet Explorer and Opera, however, support arbitrary ordering. We
+    should fix it up.
+
+    Minor issue though, not strictly necessary.
+*/
+
+// control freaks may appreciate the ability to convert these to
+// percentages or something, but it's not necessary
+
+/**
+ * Validates the value of background-position.
+ */
+class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * @type HTMLPurifier_AttrDef_CSS_Length
+     */
+    protected $length;
+
+    /**
+     * @type HTMLPurifier_AttrDef_CSS_Percentage
+     */
+    protected $percentage;
+
+    public function __construct()
+    {
+        $this->length = new HTMLPurifier_AttrDef_CSS_Length();
+        $this->percentage = new HTMLPurifier_AttrDef_CSS_Percentage();
+    }
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        $string = $this->parseCDATA($string);
+        $bits = explode(' ', $string);
+
+        $keywords = array();
+        $keywords['h'] = false; // left, right
+        $keywords['v'] = false; // top, bottom
+        $keywords['ch'] = false; // center (first word)
+        $keywords['cv'] = false; // center (second word)
+        $measures = array();
+
+        $i = 0;
+
+        $lookup = array(
+            'top' => 'v',
+            'bottom' => 'v',
+            'left' => 'h',
+            'right' => 'h',
+            'center' => 'c'
+        );
+
+        foreach ($bits as $bit) {
+            if ($bit === '') {
+                continue;
+            }
+
+            // test for keyword
+            $lbit = ctype_lower($bit) ? $bit : strtolower($bit);
+            if (isset($lookup[$lbit])) {
+                $status = $lookup[$lbit];
+                if ($status == 'c') {
+                    if ($i == 0) {
+                        $status = 'ch';
+                    } else {
+                        $status = 'cv';
+                    }
+                }
+                $keywords[$status] = $lbit;
+                $i++;
+            }
+
+            // test for length
+            $r = $this->length->validate($bit, $config, $context);
+            if ($r !== false) {
+                $measures[] = $r;
+                $i++;
+            }
+
+            // test for percentage
+            $r = $this->percentage->validate($bit, $config, $context);
+            if ($r !== false) {
+                $measures[] = $r;
+                $i++;
+            }
+        }
+
+        if (!$i) {
+            return false;
+        } // no valid values were caught
+
+        $ret = array();
+
+        // first keyword
+        if ($keywords['h']) {
+            $ret[] = $keywords['h'];
+        } elseif ($keywords['ch']) {
+            $ret[] = $keywords['ch'];
+            $keywords['cv'] = false; // prevent re-use: center = center center
+        } elseif (count($measures)) {
+            $ret[] = array_shift($measures);
+        }
+
+        if ($keywords['v']) {
+            $ret[] = $keywords['v'];
+        } elseif ($keywords['cv']) {
+            $ret[] = $keywords['cv'];
+        } elseif (count($measures)) {
+            $ret[] = array_shift($measures);
+        }
+
+        if (empty($ret)) {
+            return false;
+        }
+        return implode(' ', $ret);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Border.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Border.php
new file mode 100644 (file)
index 0000000..16243ba
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * Validates the border property as defined by CSS.
+ */
+class HTMLPurifier_AttrDef_CSS_Border extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * Local copy of properties this property is shorthand for.
+     * @type HTMLPurifier_AttrDef[]
+     */
+    protected $info = array();
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function __construct($config)
+    {
+        $def = $config->getCSSDefinition();
+        $this->info['border-width'] = $def->info['border-width'];
+        $this->info['border-style'] = $def->info['border-style'];
+        $this->info['border-top-color'] = $def->info['border-top-color'];
+    }
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        $string = $this->parseCDATA($string);
+        $string = $this->mungeRgb($string);
+        $bits = explode(' ', $string);
+        $done = array(); // segments we've finished
+        $ret = ''; // return value
+        foreach ($bits as $bit) {
+            foreach ($this->info as $propname => $validator) {
+                if (isset($done[$propname])) {
+                    continue;
+                }
+                $r = $validator->validate($bit, $config, $context);
+                if ($r !== false) {
+                    $ret .= $r . ' ';
+                    $done[$propname] = true;
+                    break;
+                }
+            }
+        }
+        return rtrim($ret);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Color.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Color.php
new file mode 100644 (file)
index 0000000..16d2a6b
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+
+/**
+ * Validates Color as defined by CSS.
+ */
+class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * @param string $color
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($color, $config, $context)
+    {
+        static $colors = null;
+        if ($colors === null) {
+            $colors = $config->get('Core.ColorKeywords');
+        }
+
+        $color = trim($color);
+        if ($color === '') {
+            return false;
+        }
+
+        $lower = strtolower($color);
+        if (isset($colors[$lower])) {
+            return $colors[$lower];
+        }
+
+        if (strpos($color, 'rgb(') !== false) {
+            // rgb literal handling
+            $length = strlen($color);
+            if (strpos($color, ')') !== $length - 1) {
+                return false;
+            }
+            $triad = substr($color, 4, $length - 4 - 1);
+            $parts = explode(',', $triad);
+            if (count($parts) !== 3) {
+                return false;
+            }
+            $type = false; // to ensure that they're all the same type
+            $new_parts = array();
+            foreach ($parts as $part) {
+                $part = trim($part);
+                if ($part === '') {
+                    return false;
+                }
+                $length = strlen($part);
+                if ($part[$length - 1] === '%') {
+                    // handle percents
+                    if (!$type) {
+                        $type = 'percentage';
+                    } elseif ($type !== 'percentage') {
+                        return false;
+                    }
+                    $num = (float)substr($part, 0, $length - 1);
+                    if ($num < 0) {
+                        $num = 0;
+                    }
+                    if ($num > 100) {
+                        $num = 100;
+                    }
+                    $new_parts[] = "$num%";
+                } else {
+                    // handle integers
+                    if (!$type) {
+                        $type = 'integer';
+                    } elseif ($type !== 'integer') {
+                        return false;
+                    }
+                    $num = (int)$part;
+                    if ($num < 0) {
+                        $num = 0;
+                    }
+                    if ($num > 255) {
+                        $num = 255;
+                    }
+                    $new_parts[] = (string)$num;
+                }
+            }
+            $new_triad = implode(',', $new_parts);
+            $color = "rgb($new_triad)";
+        } else {
+            // hexadecimal handling
+            if ($color[0] === '#') {
+                $hex = substr($color, 1);
+            } else {
+                $hex = $color;
+                $color = '#' . $color;
+            }
+            $length = strlen($hex);
+            if ($length !== 3 && $length !== 6) {
+                return false;
+            }
+            if (!ctype_xdigit($hex)) {
+                return false;
+            }
+        }
+        return $color;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Composite.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Composite.php
new file mode 100644 (file)
index 0000000..9c17505
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * Allows multiple validators to attempt to validate attribute.
+ *
+ * Composite is just what it sounds like: a composite of many validators.
+ * This means that multiple HTMLPurifier_AttrDef objects will have a whack
+ * at the string.  If one of them passes, that's what is returned.  This is
+ * especially useful for CSS values, which often are a choice between
+ * an enumerated set of predefined values or a flexible data type.
+ */
+class HTMLPurifier_AttrDef_CSS_Composite extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * List of objects that may process strings.
+     * @type HTMLPurifier_AttrDef[]
+     * @todo Make protected
+     */
+    public $defs;
+
+    /**
+     * @param HTMLPurifier_AttrDef[] $defs List of HTMLPurifier_AttrDef objects
+     */
+    public function __construct($defs)
+    {
+        $this->defs = $defs;
+    }
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        foreach ($this->defs as $i => $def) {
+            $result = $this->defs[$i]->validate($string, $config, $context);
+            if ($result !== false) {
+                return $result;
+            }
+        }
+        return false;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php
new file mode 100644 (file)
index 0000000..9d77cc9
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * Decorator which enables CSS properties to be disabled for specific elements.
+ */
+class HTMLPurifier_AttrDef_CSS_DenyElementDecorator extends HTMLPurifier_AttrDef
+{
+    /**
+     * @type HTMLPurifier_AttrDef
+     */
+    public $def;
+    /**
+     * @type string
+     */
+    public $element;
+
+    /**
+     * @param HTMLPurifier_AttrDef $def Definition to wrap
+     * @param string $element Element to deny
+     */
+    public function __construct($def, $element)
+    {
+        $this->def = $def;
+        $this->element = $element;
+    }
+
+    /**
+     * Checks if CurrentToken is set and equal to $this->element
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        $token = $context->get('CurrentToken', true);
+        if ($token && $token->name == $this->element) {
+            return false;
+        }
+        return $this->def->validate($string, $config, $context);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Filter.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Filter.php
new file mode 100644 (file)
index 0000000..bde4c33
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * Microsoft's proprietary filter: CSS property
+ * @note Currently supports the alpha filter. In the future, this will
+ *       probably need an extensible framework
+ */
+class HTMLPurifier_AttrDef_CSS_Filter extends HTMLPurifier_AttrDef
+{
+    /**
+     * @type HTMLPurifier_AttrDef_Integer
+     */
+    protected $intValidator;
+
+    public function __construct()
+    {
+        $this->intValidator = new HTMLPurifier_AttrDef_Integer();
+    }
+
+    /**
+     * @param string $value
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($value, $config, $context)
+    {
+        $value = $this->parseCDATA($value);
+        if ($value === 'none') {
+            return $value;
+        }
+        // if we looped this we could support multiple filters
+        $function_length = strcspn($value, '(');
+        $function = trim(substr($value, 0, $function_length));
+        if ($function !== 'alpha' &&
+            $function !== 'Alpha' &&
+            $function !== 'progid:DXImageTransform.Microsoft.Alpha'
+        ) {
+            return false;
+        }
+        $cursor = $function_length + 1;
+        $parameters_length = strcspn($value, ')', $cursor);
+        $parameters = substr($value, $cursor, $parameters_length);
+        $params = explode(',', $parameters);
+        $ret_params = array();
+        $lookup = array();
+        foreach ($params as $param) {
+            list($key, $value) = explode('=', $param);
+            $key = trim($key);
+            $value = trim($value);
+            if (isset($lookup[$key])) {
+                continue;
+            }
+            if ($key !== 'opacity') {
+                continue;
+            }
+            $value = $this->intValidator->validate($value, $config, $context);
+            if ($value === false) {
+                continue;
+            }
+            $int = (int)$value;
+            if ($int > 100) {
+                $value = '100';
+            }
+            if ($int < 0) {
+                $value = '0';
+            }
+            $ret_params[] = "$key=$value";
+            $lookup[$key] = true;
+        }
+        $ret_parameters = implode(',', $ret_params);
+        $ret_function = "$function($ret_parameters)";
+        return $ret_function;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Font.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Font.php
new file mode 100644 (file)
index 0000000..579b97e
--- /dev/null
@@ -0,0 +1,176 @@
+<?php
+
+/**
+ * Validates shorthand CSS property font.
+ */
+class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * Local copy of validators
+     * @type HTMLPurifier_AttrDef[]
+     * @note If we moved specific CSS property definitions to their own
+     *       classes instead of having them be assembled at run time by
+     *       CSSDefinition, this wouldn't be necessary.  We'd instantiate
+     *       our own copies.
+     */
+    protected $info = array();
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function __construct($config)
+    {
+        $def = $config->getCSSDefinition();
+        $this->info['font-style'] = $def->info['font-style'];
+        $this->info['font-variant'] = $def->info['font-variant'];
+        $this->info['font-weight'] = $def->info['font-weight'];
+        $this->info['font-size'] = $def->info['font-size'];
+        $this->info['line-height'] = $def->info['line-height'];
+        $this->info['font-family'] = $def->info['font-family'];
+    }
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        static $system_fonts = array(
+            'caption' => true,
+            'icon' => true,
+            'menu' => true,
+            'message-box' => true,
+            'small-caption' => true,
+            'status-bar' => true
+        );
+
+        // regular pre-processing
+        $string = $this->parseCDATA($string);
+        if ($string === '') {
+            return false;
+        }
+
+        // check if it's one of the keywords
+        $lowercase_string = strtolower($string);
+        if (isset($system_fonts[$lowercase_string])) {
+            return $lowercase_string;
+        }
+
+        $bits = explode(' ', $string); // bits to process
+        $stage = 0; // this indicates what we're looking for
+        $caught = array(); // which stage 0 properties have we caught?
+        $stage_1 = array('font-style', 'font-variant', 'font-weight');
+        $final = ''; // output
+
+        for ($i = 0, $size = count($bits); $i < $size; $i++) {
+            if ($bits[$i] === '') {
+                continue;
+            }
+            switch ($stage) {
+                case 0: // attempting to catch font-style, font-variant or font-weight
+                    foreach ($stage_1 as $validator_name) {
+                        if (isset($caught[$validator_name])) {
+                            continue;
+                        }
+                        $r = $this->info[$validator_name]->validate(
+                            $bits[$i],
+                            $config,
+                            $context
+                        );
+                        if ($r !== false) {
+                            $final .= $r . ' ';
+                            $caught[$validator_name] = true;
+                            break;
+                        }
+                    }
+                    // all three caught, continue on
+                    if (count($caught) >= 3) {
+                        $stage = 1;
+                    }
+                    if ($r !== false) {
+                        break;
+                    }
+                case 1: // attempting to catch font-size and perhaps line-height
+                    $found_slash = false;
+                    if (strpos($bits[$i], '/') !== false) {
+                        list($font_size, $line_height) =
+                            explode('/', $bits[$i]);
+                        if ($line_height === '') {
+                            // ooh, there's a space after the slash!
+                            $line_height = false;
+                            $found_slash = true;
+                        }
+                    } else {
+                        $font_size = $bits[$i];
+                        $line_height = false;
+                    }
+                    $r = $this->info['font-size']->validate(
+                        $font_size,
+                        $config,
+                        $context
+                    );
+                    if ($r !== false) {
+                        $final .= $r;
+                        // attempt to catch line-height
+                        if ($line_height === false) {
+                            // we need to scroll forward
+                            for ($j = $i + 1; $j < $size; $j++) {
+                                if ($bits[$j] === '') {
+                                    continue;
+                                }
+                                if ($bits[$j] === '/') {
+                                    if ($found_slash) {
+                                        return false;
+                                    } else {
+                                        $found_slash = true;
+                                        continue;
+                                    }
+                                }
+                                $line_height = $bits[$j];
+                                break;
+                            }
+                        } else {
+                            // slash already found
+                            $found_slash = true;
+                            $j = $i;
+                        }
+                        if ($found_slash) {
+                            $i = $j;
+                            $r = $this->info['line-height']->validate(
+                                $line_height,
+                                $config,
+                                $context
+                            );
+                            if ($r !== false) {
+                                $final .= '/' . $r;
+                            }
+                        }
+                        $final .= ' ';
+                        $stage = 2;
+                        break;
+                    }
+                    return false;
+                case 2: // attempting to catch font-family
+                    $font_family =
+                        implode(' ', array_slice($bits, $i, $size - $i));
+                    $r = $this->info['font-family']->validate(
+                        $font_family,
+                        $config,
+                        $context
+                    );
+                    if ($r !== false) {
+                        $final .= $r . ' ';
+                        // processing completed successfully
+                        return rtrim($final);
+                    }
+                    return false;
+            }
+        }
+        return false;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php
new file mode 100644 (file)
index 0000000..74e24c8
--- /dev/null
@@ -0,0 +1,219 @@
+<?php
+
+/**
+ * Validates a font family list according to CSS spec
+ */
+class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef
+{
+
+    protected $mask = null;
+
+    public function __construct()
+    {
+        $this->mask = '_- ';
+        for ($c = 'a'; $c <= 'z'; $c++) {
+            $this->mask .= $c;
+        }
+        for ($c = 'A'; $c <= 'Z'; $c++) {
+            $this->mask .= $c;
+        }
+        for ($c = '0'; $c <= '9'; $c++) {
+            $this->mask .= $c;
+        } // cast-y, but should be fine
+        // special bytes used by UTF-8
+        for ($i = 0x80; $i <= 0xFF; $i++) {
+            // We don't bother excluding invalid bytes in this range,
+            // because the our restriction of well-formed UTF-8 will
+            // prevent these from ever occurring.
+            $this->mask .= chr($i);
+        }
+
+        /*
+            PHP's internal strcspn implementation is
+            O(length of string * length of mask), making it inefficient
+            for large masks.  However, it's still faster than
+            preg_match 8)
+          for (p = s1;;) {
+            spanp = s2;
+            do {
+              if (*spanp == c || p == s1_end) {
+                return p - s1;
+              }
+            } while (spanp++ < (s2_end - 1));
+            c = *++p;
+          }
+         */
+        // possible optimization: invert the mask.
+    }
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        static $generic_names = array(
+            'serif' => true,
+            'sans-serif' => true,
+            'monospace' => true,
+            'fantasy' => true,
+            'cursive' => true
+        );
+        $allowed_fonts = $config->get('CSS.AllowedFonts');
+
+        // assume that no font names contain commas in them
+        $fonts = explode(',', $string);
+        $final = '';
+        foreach ($fonts as $font) {
+            $font = trim($font);
+            if ($font === '') {
+                continue;
+            }
+            // match a generic name
+            if (isset($generic_names[$font])) {
+                if ($allowed_fonts === null || isset($allowed_fonts[$font])) {
+                    $final .= $font . ', ';
+                }
+                continue;
+            }
+            // match a quoted name
+            if ($font[0] === '"' || $font[0] === "'") {
+                $length = strlen($font);
+                if ($length <= 2) {
+                    continue;
+                }
+                $quote = $font[0];
+                if ($font[$length - 1] !== $quote) {
+                    continue;
+                }
+                $font = substr($font, 1, $length - 2);
+            }
+
+            $font = $this->expandCSSEscape($font);
+
+            // $font is a pure representation of the font name
+
+            if ($allowed_fonts !== null && !isset($allowed_fonts[$font])) {
+                continue;
+            }
+
+            if (ctype_alnum($font) && $font !== '') {
+                // very simple font, allow it in unharmed
+                $final .= $font . ', ';
+                continue;
+            }
+
+            // bugger out on whitespace.  form feed (0C) really
+            // shouldn't show up regardless
+            $font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font);
+
+            // Here, there are various classes of characters which need
+            // to be treated differently:
+            //  - Alphanumeric characters are essentially safe.  We
+            //    handled these above.
+            //  - Spaces require quoting, though most parsers will do
+            //    the right thing if there aren't any characters that
+            //    can be misinterpreted
+            //  - Dashes rarely occur, but they fairly unproblematic
+            //    for parsing/rendering purposes.
+            //  The above characters cover the majority of Western font
+            //  names.
+            //  - Arbitrary Unicode characters not in ASCII.  Because
+            //    most parsers give little thought to Unicode, treatment
+            //    of these codepoints is basically uniform, even for
+            //    punctuation-like codepoints.  These characters can
+            //    show up in non-Western pages and are supported by most
+            //    major browsers, for example: "MS 明朝" is a
+            //    legitimate font-name
+            //    <http://ja.wikipedia.org/wiki/MS_明朝>.  See
+            //    the CSS3 spec for more examples:
+            //    <http://www.w3.org/TR/2011/WD-css3-fonts-20110324/localizedfamilynames.png>
+            //    You can see live samples of these on the Internet:
+            //    <http://www.google.co.jp/search?q=font-family+MS+明朝|ゴシック>
+            //    However, most of these fonts have ASCII equivalents:
+            //    for example, 'MS Mincho', and it's considered
+            //    professional to use ASCII font names instead of
+            //    Unicode font names.  Thanks Takeshi Terada for
+            //    providing this information.
+            //  The following characters, to my knowledge, have not been
+            //  used to name font names.
+            //  - Single quote.  While theoretically you might find a
+            //    font name that has a single quote in its name (serving
+            //    as an apostrophe, e.g. Dave's Scribble), I haven't
+            //    been able to find any actual examples of this.
+            //    Internet Explorer's cssText translation (which I
+            //    believe is invoked by innerHTML) normalizes any
+            //    quoting to single quotes, and fails to escape single
+            //    quotes.  (Note that this is not IE's behavior for all
+            //    CSS properties, just some sort of special casing for
+            //    font-family).  So a single quote *cannot* be used
+            //    safely in the font-family context if there will be an
+            //    innerHTML/cssText translation.  Note that Firefox 3.x
+            //    does this too.
+            //  - Double quote.  In IE, these get normalized to
+            //    single-quotes, no matter what the encoding.  (Fun
+            //    fact, in IE8, the 'content' CSS property gained
+            //    support, where they special cased to preserve encoded
+            //    double quotes, but still translate unadorned double
+            //    quotes into single quotes.)  So, because their
+            //    fixpoint behavior is identical to single quotes, they
+            //    cannot be allowed either.  Firefox 3.x displays
+            //    single-quote style behavior.
+            //  - Backslashes are reduced by one (so \\ -> \) every
+            //    iteration, so they cannot be used safely.  This shows
+            //    up in IE7, IE8 and FF3
+            //  - Semicolons, commas and backticks are handled properly.
+            //  - The rest of the ASCII punctuation is handled properly.
+            // We haven't checked what browsers do to unadorned
+            // versions, but this is not important as long as the
+            // browser doesn't /remove/ surrounding quotes (as IE does
+            // for HTML).
+            //
+            // With these results in hand, we conclude that there are
+            // various levels of safety:
+            //  - Paranoid: alphanumeric, spaces and dashes(?)
+            //  - International: Paranoid + non-ASCII Unicode
+            //  - Edgy: Everything except quotes, backslashes
+            //  - NoJS: Standards compliance, e.g. sod IE. Note that
+            //    with some judicious character escaping (since certain
+            //    types of escaping doesn't work) this is theoretically
+            //    OK as long as innerHTML/cssText is not called.
+            // We believe that international is a reasonable default
+            // (that we will implement now), and once we do more
+            // extensive research, we may feel comfortable with dropping
+            // it down to edgy.
+
+            // Edgy: alphanumeric, spaces, dashes, underscores and Unicode.  Use of
+            // str(c)spn assumes that the string was already well formed
+            // Unicode (which of course it is).
+            if (strspn($font, $this->mask) !== strlen($font)) {
+                continue;
+            }
+
+            // Historical:
+            // In the absence of innerHTML/cssText, these ugly
+            // transforms don't pose a security risk (as \\ and \"
+            // might--these escapes are not supported by most browsers).
+            // We could try to be clever and use single-quote wrapping
+            // when there is a double quote present, but I have choosen
+            // not to implement that.  (NOTE: you can reduce the amount
+            // of escapes by one depending on what quoting style you use)
+            // $font = str_replace('\\', '\\5C ', $font);
+            // $font = str_replace('"',  '\\22 ', $font);
+            // $font = str_replace("'",  '\\27 ', $font);
+
+            // font possibly with spaces, requires quoting
+            $final .= "'$font', ";
+        }
+        $final = rtrim($final, ', ');
+        if ($final === '') {
+            return false;
+        }
+        return $final;
+    }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Ident.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Ident.php
new file mode 100644 (file)
index 0000000..973002c
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * Validates based on {ident} CSS grammar production
+ */
+class HTMLPurifier_AttrDef_CSS_Ident extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        $string = trim($string);
+
+        // early abort: '' and '0' (strings that convert to false) are invalid
+        if (!$string) {
+            return false;
+        }
+
+        $pattern = '/^(-?[A-Za-z_][A-Za-z_\-0-9]*)$/';
+        if (!preg_match($pattern, $string)) {
+            return false;
+        }
+        return $string;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php
new file mode 100644 (file)
index 0000000..ffc989f
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * Decorator which enables !important to be used in CSS values.
+ */
+class HTMLPurifier_AttrDef_CSS_ImportantDecorator extends HTMLPurifier_AttrDef
+{
+    /**
+     * @type HTMLPurifier_AttrDef
+     */
+    public $def;
+    /**
+     * @type bool
+     */
+    public $allow;
+
+    /**
+     * @param HTMLPurifier_AttrDef $def Definition to wrap
+     * @param bool $allow Whether or not to allow !important
+     */
+    public function __construct($def, $allow = false)
+    {
+        $this->def = $def;
+        $this->allow = $allow;
+    }
+
+    /**
+     * Intercepts and removes !important if necessary
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        // test for ! and important tokens
+        $string = trim($string);
+        $is_important = false;
+        // :TODO: optimization: test directly for !important and ! important
+        if (strlen($string) >= 9 && substr($string, -9) === 'important') {
+            $temp = rtrim(substr($string, 0, -9));
+            // use a temp, because we might want to restore important
+            if (strlen($temp) >= 1 && substr($temp, -1) === '!') {
+                $string = rtrim(substr($temp, 0, -1));
+                $is_important = true;
+            }
+        }
+        $string = $this->def->validate($string, $config, $context);
+        if ($this->allow && $is_important) {
+            $string .= ' !important';
+        }
+        return $string;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Length.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Length.php
new file mode 100644 (file)
index 0000000..f12453a
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * Represents a Length as defined by CSS.
+ */
+class HTMLPurifier_AttrDef_CSS_Length extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * @type HTMLPurifier_Length|string
+     */
+    protected $min;
+
+    /**
+     * @type HTMLPurifier_Length|string
+     */
+    protected $max;
+
+    /**
+     * @param HTMLPurifier_Length|string $min Minimum length, or null for no bound. String is also acceptable.
+     * @param HTMLPurifier_Length|string $max Maximum length, or null for no bound. String is also acceptable.
+     */
+    public function __construct($min = null, $max = null)
+    {
+        $this->min = $min !== null ? HTMLPurifier_Length::make($min) : null;
+        $this->max = $max !== null ? HTMLPurifier_Length::make($max) : null;
+    }
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        $string = $this->parseCDATA($string);
+
+        // Optimizations
+        if ($string === '') {
+            return false;
+        }
+        if ($string === '0') {
+            return '0';
+        }
+        if (strlen($string) === 1) {
+            return false;
+        }
+
+        $length = HTMLPurifier_Length::make($string);
+        if (!$length->isValid()) {
+            return false;
+        }
+
+        if ($this->min) {
+            $c = $length->compareTo($this->min);
+            if ($c === false) {
+                return false;
+            }
+            if ($c < 0) {
+                return false;
+            }
+        }
+        if ($this->max) {
+            $c = $length->compareTo($this->max);
+            if ($c === false) {
+                return false;
+            }
+            if ($c > 0) {
+                return false;
+            }
+        }
+        return $length->toString();
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ListStyle.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ListStyle.php
new file mode 100644 (file)
index 0000000..e74d426
--- /dev/null
@@ -0,0 +1,112 @@
+<?php
+
+/**
+ * Validates shorthand CSS property list-style.
+ * @warning Does not support url tokens that have internal spaces.
+ */
+class HTMLPurifier_AttrDef_CSS_ListStyle extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * Local copy of validators.
+     * @type HTMLPurifier_AttrDef[]
+     * @note See HTMLPurifier_AttrDef_CSS_Font::$info for a similar impl.
+     */
+    protected $info;
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function __construct($config)
+    {
+        $def = $config->getCSSDefinition();
+        $this->info['list-style-type'] = $def->info['list-style-type'];
+        $this->info['list-style-position'] = $def->info['list-style-position'];
+        $this->info['list-style-image'] = $def->info['list-style-image'];
+    }
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        // regular pre-processing
+        $string = $this->parseCDATA($string);
+        if ($string === '') {
+            return false;
+        }
+
+        // assumes URI doesn't have spaces in it
+        $bits = explode(' ', strtolower($string)); // bits to process
+
+        $caught = array();
+        $caught['type'] = false;
+        $caught['position'] = false;
+        $caught['image'] = false;
+
+        $i = 0; // number of catches
+        $none = false;
+
+        foreach ($bits as $bit) {
+            if ($i >= 3) {
+                return;
+            } // optimization bit
+            if ($bit === '') {
+                continue;
+            }
+            foreach ($caught as $key => $status) {
+                if ($status !== false) {
+                    continue;
+                }
+                $r = $this->info['list-style-' . $key]->validate($bit, $config, $context);
+                if ($r === false) {
+                    continue;
+                }
+                if ($r === 'none') {
+                    if ($none) {
+                        continue;
+                    } else {
+                        $none = true;
+                    }
+                    if ($key == 'image') {
+                        continue;
+                    }
+                }
+                $caught[$key] = $r;
+                $i++;
+                break;
+            }
+        }
+
+        if (!$i) {
+            return false;
+        }
+
+        $ret = array();
+
+        // construct type
+        if ($caught['type']) {
+            $ret[] = $caught['type'];
+        }
+
+        // construct image
+        if ($caught['image']) {
+            $ret[] = $caught['image'];
+        }
+
+        // construct position
+        if ($caught['position']) {
+            $ret[] = $caught['position'];
+        }
+
+        if (empty($ret)) {
+            return false;
+        }
+        return implode(' ', $ret);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Multiple.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Multiple.php
new file mode 100644 (file)
index 0000000..e707f87
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * Framework class for strings that involve multiple values.
+ *
+ * Certain CSS properties such as border-width and margin allow multiple
+ * lengths to be specified.  This class can take a vanilla border-width
+ * definition and multiply it, usually into a max of four.
+ *
+ * @note Even though the CSS specification isn't clear about it, inherit
+ *       can only be used alone: it will never manifest as part of a multi
+ *       shorthand declaration.  Thus, this class does not allow inherit.
+ */
+class HTMLPurifier_AttrDef_CSS_Multiple extends HTMLPurifier_AttrDef
+{
+    /**
+     * Instance of component definition to defer validation to.
+     * @type HTMLPurifier_AttrDef
+     * @todo Make protected
+     */
+    public $single;
+
+    /**
+     * Max number of values allowed.
+     * @todo Make protected
+     */
+    public $max;
+
+    /**
+     * @param HTMLPurifier_AttrDef $single HTMLPurifier_AttrDef to multiply
+     * @param int $max Max number of values allowed (usually four)
+     */
+    public function __construct($single, $max = 4)
+    {
+        $this->single = $single;
+        $this->max = $max;
+    }
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        $string = $this->mungeRgb($this->parseCDATA($string));
+        if ($string === '') {
+            return false;
+        }
+        $parts = explode(' ', $string); // parseCDATA replaced \r, \t and \n
+        $length = count($parts);
+        $final = '';
+        for ($i = 0, $num = 0; $i < $length && $num < $this->max; $i++) {
+            if (ctype_space($parts[$i])) {
+                continue;
+            }
+            $result = $this->single->validate($parts[$i], $config, $context);
+            if ($result !== false) {
+                $final .= $result . ' ';
+                $num++;
+            }
+        }
+        if ($final === '') {
+            return false;
+        }
+        return rtrim($final);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Number.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Number.php
new file mode 100644 (file)
index 0000000..8edc159
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * Validates a number as defined by the CSS spec.
+ */
+class HTMLPurifier_AttrDef_CSS_Number extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * Indicates whether or not only positive values are allowed.
+     * @type bool
+     */
+    protected $non_negative = false;
+
+    /**
+     * @param bool $non_negative indicates whether negatives are forbidden
+     */
+    public function __construct($non_negative = false)
+    {
+        $this->non_negative = $non_negative;
+    }
+
+    /**
+     * @param string $number
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return string|bool
+     * @warning Some contexts do not pass $config, $context. These
+     *          variables should not be used without checking HTMLPurifier_Length
+     */
+    public function validate($number, $config, $context)
+    {
+        $number = $this->parseCDATA($number);
+
+        if ($number === '') {
+            return false;
+        }
+        if ($number === '0') {
+            return '0';
+        }
+
+        $sign = '';
+        switch ($number[0]) {
+            case '-':
+                if ($this->non_negative) {
+                    return false;
+                }
+                $sign = '-';
+            case '+':
+                $number = substr($number, 1);
+        }
+
+        if (ctype_digit($number)) {
+            $number = ltrim($number, '0');
+            return $number ? $sign . $number : '0';
+        }
+
+        // Period is the only non-numeric character allowed
+        if (strpos($number, '.') === false) {
+            return false;
+        }
+
+        list($left, $right) = explode('.', $number, 2);
+
+        if ($left === '' && $right === '') {
+            return false;
+        }
+        if ($left !== '' && !ctype_digit($left)) {
+            return false;
+        }
+
+        $left = ltrim($left, '0');
+        $right = rtrim($right, '0');
+
+        if ($right === '') {
+            return $left ? $sign . $left : '0';
+        } elseif (!ctype_digit($right)) {
+            return false;
+        }
+        return $sign . $left . '.' . $right;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Percentage.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Percentage.php
new file mode 100644 (file)
index 0000000..f0f25c5
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * Validates a Percentage as defined by the CSS spec.
+ */
+class HTMLPurifier_AttrDef_CSS_Percentage extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * Instance to defer number validation to.
+     * @type HTMLPurifier_AttrDef_CSS_Number
+     */
+    protected $number_def;
+
+    /**
+     * @param bool $non_negative Whether to forbid negative values
+     */
+    public function __construct($non_negative = false)
+    {
+        $this->number_def = new HTMLPurifier_AttrDef_CSS_Number($non_negative);
+    }
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        $string = $this->parseCDATA($string);
+
+        if ($string === '') {
+            return false;
+        }
+        $length = strlen($string);
+        if ($length === 1) {
+            return false;
+        }
+        if ($string[$length - 1] !== '%') {
+            return false;
+        }
+
+        $number = substr($string, 0, $length - 1);
+        $number = $this->number_def->validate($number, $config, $context);
+
+        if ($number === false) {
+            return false;
+        }
+        return "$number%";
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/TextDecoration.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/TextDecoration.php
new file mode 100644 (file)
index 0000000..5fd4b7f
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * Validates the value for the CSS property text-decoration
+ * @note This class could be generalized into a version that acts sort of
+ *       like Enum except you can compound the allowed values.
+ */
+class HTMLPurifier_AttrDef_CSS_TextDecoration extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        static $allowed_values = array(
+            'line-through' => true,
+            'overline' => true,
+            'underline' => true,
+        );
+
+        $string = strtolower($this->parseCDATA($string));
+
+        if ($string === 'none') {
+            return $string;
+        }
+
+        $parts = explode(' ', $string);
+        $final = '';
+        foreach ($parts as $part) {
+            if (isset($allowed_values[$part])) {
+                $final .= $part . ' ';
+            }
+        }
+        $final = rtrim($final);
+        if ($final === '') {
+            return false;
+        }
+        return $final;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/URI.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/URI.php
new file mode 100644 (file)
index 0000000..f943423
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * Validates a URI in CSS syntax, which uses url('http://example.com')
+ * @note While theoretically speaking a URI in a CSS document could
+ *       be non-embedded, as of CSS2 there is no such usage so we're
+ *       generalizing it. This may need to be changed in the future.
+ * @warning Since HTMLPurifier_AttrDef_CSS blindly uses semicolons as
+ *          the separator, you cannot put a literal semicolon in
+ *          in the URI. Try percent encoding it, in that case.
+ */
+class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI
+{
+
+    public function __construct()
+    {
+        parent::__construct(true); // always embedded
+    }
+
+    /**
+     * @param string $uri_string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($uri_string, $config, $context)
+    {
+        // parse the URI out of the string and then pass it onto
+        // the parent object
+
+        $uri_string = $this->parseCDATA($uri_string);
+        if (strpos($uri_string, 'url(') !== 0) {
+            return false;
+        }
+        $uri_string = substr($uri_string, 4);
+        $new_length = strlen($uri_string) - 1;
+        if ($uri_string[$new_length] != ')') {
+            return false;
+        }
+        $uri = trim(substr($uri_string, 0, $new_length));
+
+        if (!empty($uri) && ($uri[0] == "'" || $uri[0] == '"')) {
+            $quote = $uri[0];
+            $new_length = strlen($uri) - 1;
+            if ($uri[$new_length] !== $quote) {
+                return false;
+            }
+            $uri = substr($uri, 1, $new_length - 1);
+        }
+
+        $uri = $this->expandCSSEscape($uri);
+
+        $result = parent::validate($uri, $config, $context);
+
+        if ($result === false) {
+            return false;
+        }
+
+        // extra sanity check; should have been done by URI
+        $result = str_replace(array('"', "\\", "\n", "\x0c", "\r"), "", $result);
+
+        // suspicious characters are ()'; we're going to percent encode
+        // them for safety.
+        $result = str_replace(array('(', ')', "'"), array('%28', '%29', '%27'), $result);
+
+        // there's an extra bug where ampersands lose their escaping on
+        // an innerHTML cycle, so a very unlucky query parameter could
+        // then change the meaning of the URL.  Unfortunately, there's
+        // not much we can do about that...
+        return "url(\"$result\")";
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Clone.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Clone.php
new file mode 100644 (file)
index 0000000..6698a00
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * Dummy AttrDef that mimics another AttrDef, BUT it generates clones
+ * with make.
+ */
+class HTMLPurifier_AttrDef_Clone extends HTMLPurifier_AttrDef
+{
+    /**
+     * What we're cloning.
+     * @type HTMLPurifier_AttrDef
+     */
+    protected $clone;
+
+    /**
+     * @param HTMLPurifier_AttrDef $clone
+     */
+    public function __construct($clone)
+    {
+        $this->clone = $clone;
+    }
+
+    /**
+     * @param string $v
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($v, $config, $context)
+    {
+        return $this->clone->validate($v, $config, $context);
+    }
+
+    /**
+     * @param string $string
+     * @return HTMLPurifier_AttrDef
+     */
+    public function make($string)
+    {
+        return clone $this->clone;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Enum.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Enum.php
new file mode 100644 (file)
index 0000000..8abda7f
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+
+// Enum = Enumerated
+/**
+ * Validates a keyword against a list of valid values.
+ * @warning The case-insensitive compare of this function uses PHP's
+ *          built-in strtolower and ctype_lower functions, which may
+ *          cause problems with international comparisons
+ */
+class HTMLPurifier_AttrDef_Enum extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * Lookup table of valid values.
+     * @type array
+     * @todo Make protected
+     */
+    public $valid_values = array();
+
+    /**
+     * Bool indicating whether or not enumeration is case sensitive.
+     * @note In general this is always case insensitive.
+     */
+    protected $case_sensitive = false; // values according to W3C spec
+
+    /**
+     * @param array $valid_values List of valid values
+     * @param bool $case_sensitive Whether or not case sensitive
+     */
+    public function __construct($valid_values = array(), $case_sensitive = false)
+    {
+        $this->valid_values = array_flip($valid_values);
+        $this->case_sensitive = $case_sensitive;
+    }
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        $string = trim($string);
+        if (!$this->case_sensitive) {
+            // we may want to do full case-insensitive libraries
+            $string = ctype_lower($string) ? $string : strtolower($string);
+        }
+        $result = isset($this->valid_values[$string]);
+
+        return $result ? $string : false;
+    }
+
+    /**
+     * @param string $string In form of comma-delimited list of case-insensitive
+     *      valid values. Example: "foo,bar,baz". Prepend "s:" to make
+     *      case sensitive
+     * @return HTMLPurifier_AttrDef_Enum
+     */
+    public function make($string)
+    {
+        if (strlen($string) > 2 && $string[0] == 's' && $string[1] == ':') {
+            $string = substr($string, 2);
+            $sensitive = true;
+        } else {
+            $sensitive = false;
+        }
+        $values = explode(',', $string);
+        return new HTMLPurifier_AttrDef_Enum($values, $sensitive);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Bool.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Bool.php
new file mode 100644 (file)
index 0000000..dea15d2
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * Validates a boolean attribute
+ */
+class HTMLPurifier_AttrDef_HTML_Bool extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * @type bool
+     */
+    protected $name;
+
+    /**
+     * @type bool
+     */
+    public $minimized = true;
+
+    /**
+     * @param bool $name
+     */
+    public function __construct($name = false)
+    {
+        $this->name = $name;
+    }
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        return $this->name;
+    }
+
+    /**
+     * @param string $string Name of attribute
+     * @return HTMLPurifier_AttrDef_HTML_Bool
+     */
+    public function make($string)
+    {
+        return new HTMLPurifier_AttrDef_HTML_Bool($string);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Class.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Class.php
new file mode 100644 (file)
index 0000000..d501348
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * Implements special behavior for class attribute (normally NMTOKENS)
+ */
+class HTMLPurifier_AttrDef_HTML_Class extends HTMLPurifier_AttrDef_HTML_Nmtokens
+{
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    protected function split($string, $config, $context)
+    {
+        // really, this twiddle should be lazy loaded
+        $name = $config->getDefinition('HTML')->doctype->name;
+        if ($name == "XHTML 1.1" || $name == "XHTML 2.0") {
+            return parent::split($string, $config, $context);
+        } else {
+            return preg_split('/\s+/', $string);
+        }
+    }
+
+    /**
+     * @param array $tokens
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    protected function filter($tokens, $config, $context)
+    {
+        $allowed = $config->get('Attr.AllowedClasses');
+        $forbidden = $config->get('Attr.ForbiddenClasses');
+        $ret = array();
+        foreach ($tokens as $token) {
+            if (($allowed === null || isset($allowed[$token])) &&
+                !isset($forbidden[$token]) &&
+                // We need this O(n) check because of PHP's array
+                // implementation that casts -0 to 0.
+                !in_array($token, $ret, true)
+            ) {
+                $ret[] = $token;
+            }
+        }
+        return $ret;
+    }
+}
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Color.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Color.php
new file mode 100644 (file)
index 0000000..946ebb7
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * Validates a color according to the HTML spec.
+ */
+class HTMLPurifier_AttrDef_HTML_Color extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        static $colors = null;
+        if ($colors === null) {
+            $colors = $config->get('Core.ColorKeywords');
+        }
+
+        $string = trim($string);
+
+        if (empty($string)) {
+            return false;
+        }
+        $lower = strtolower($string);
+        if (isset($colors[$lower])) {
+            return $colors[$lower];
+        }
+        if ($string[0] === '#') {
+            $hex = substr($string, 1);
+        } else {
+            $hex = $string;
+        }
+
+        $length = strlen($hex);
+        if ($length !== 3 && $length !== 6) {
+            return false;
+        }
+        if (!ctype_xdigit($hex)) {
+            return false;
+        }
+        if ($length === 3) {
+            $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
+        }
+        return "#$hex";
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php
new file mode 100644 (file)
index 0000000..d79ba12
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * Special-case enum attribute definition that lazy loads allowed frame targets
+ */
+class HTMLPurifier_AttrDef_HTML_FrameTarget extends HTMLPurifier_AttrDef_Enum
+{
+
+    /**
+     * @type array
+     */
+    public $valid_values = false; // uninitialized value
+
+    /**
+     * @type bool
+     */
+    protected $case_sensitive = false;
+
+    public function __construct()
+    {
+    }
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        if ($this->valid_values === false) {
+            $this->valid_values = $config->get('Attr.AllowedFrameTargets');
+        }
+        return parent::validate($string, $config, $context);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/ID.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/ID.php
new file mode 100644 (file)
index 0000000..3d86efb
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+
+/**
+ * Validates the HTML attribute ID.
+ * @warning Even though this is the id processor, it
+ *          will ignore the directive Attr:IDBlacklist, since it will only
+ *          go according to the ID accumulator. Since the accumulator is
+ *          automatically generated, it will have already absorbed the
+ *          blacklist. If you're hacking around, make sure you use load()!
+ */
+
+class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
+{
+
+    // selector is NOT a valid thing to use for IDREFs, because IDREFs
+    // *must* target IDs that exist, whereas selector #ids do not.
+
+    /**
+     * Determines whether or not we're validating an ID in a CSS
+     * selector context.
+     * @type bool
+     */
+    protected $selector;
+
+    /**
+     * @param bool $selector
+     */
+    public function __construct($selector = false)
+    {
+        $this->selector = $selector;
+    }
+
+    /**
+     * @param string $id
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($id, $config, $context)
+    {
+        if (!$this->selector && !$config->get('Attr.EnableID')) {
+            return false;
+        }
+
+        $id = trim($id); // trim it first
+
+        if ($id === '') {
+            return false;
+        }
+
+        $prefix = $config->get('Attr.IDPrefix');
+        if ($prefix !== '') {
+            $prefix .= $config->get('Attr.IDPrefixLocal');
+            // prevent re-appending the prefix
+            if (strpos($id, $prefix) !== 0) {
+                $id = $prefix . $id;
+            }
+        } elseif ($config->get('Attr.IDPrefixLocal') !== '') {
+            trigger_error(
+                '%Attr.IDPrefixLocal cannot be used unless ' .
+                '%Attr.IDPrefix is set',
+                E_USER_WARNING
+            );
+        }
+
+        if (!$this->selector) {
+            $id_accumulator =& $context->get('IDAccumulator');
+            if (isset($id_accumulator->ids[$id])) {
+                return false;
+            }
+        }
+
+        // we purposely avoid using regex, hopefully this is faster
+
+        if (ctype_alpha($id)) {
+            $result = true;
+        } else {
+            if (!ctype_alpha(@$id[0])) {
+                return false;
+            }
+            // primitive style of regexps, I suppose
+            $trim = trim(
+                $id,
+                'A..Za..z0..9:-._'
+            );
+            $result = ($trim === '');
+        }
+
+        $regexp = $config->get('Attr.IDBlacklistRegexp');
+        if ($regexp && preg_match($regexp, $id)) {
+            return false;
+        }
+
+        if (!$this->selector && $result) {
+            $id_accumulator->add($id);
+        }
+
+        // if no change was made to the ID, return the result
+        // else, return the new id if stripping whitespace made it
+        //     valid, or return false.
+        return $result ? $id : false;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Length.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Length.php
new file mode 100644 (file)
index 0000000..1c4006f
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * Validates the HTML type length (not to be confused with CSS's length).
+ *
+ * This accepts integer pixels or percentages as lengths for certain
+ * HTML attributes.
+ */
+
+class HTMLPurifier_AttrDef_HTML_Length extends HTMLPurifier_AttrDef_HTML_Pixels
+{
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        $string = trim($string);
+        if ($string === '') {
+            return false;
+        }
+
+        $parent_result = parent::validate($string, $config, $context);
+        if ($parent_result !== false) {
+            return $parent_result;
+        }
+
+        $length = strlen($string);
+        $last_char = $string[$length - 1];
+
+        if ($last_char !== '%') {
+            return false;
+        }
+
+        $points = substr($string, 0, $length - 1);
+
+        if (!is_numeric($points)) {
+            return false;
+        }
+
+        $points = (int)$points;
+
+        if ($points < 0) {
+            return '0%';
+        }
+        if ($points > 100) {
+            return '100%';
+        }
+        return ((string)$points) . '%';
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/LinkTypes.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/LinkTypes.php
new file mode 100644 (file)
index 0000000..63fa04c
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * Validates a rel/rev link attribute against a directive of allowed values
+ * @note We cannot use Enum because link types allow multiple
+ *       values.
+ * @note Assumes link types are ASCII text
+ */
+class HTMLPurifier_AttrDef_HTML_LinkTypes extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * Name config attribute to pull.
+     * @type string
+     */
+    protected $name;
+
+    /**
+     * @param string $name
+     */
+    public function __construct($name)
+    {
+        $configLookup = array(
+            'rel' => 'AllowedRel',
+            'rev' => 'AllowedRev'
+        );
+        if (!isset($configLookup[$name])) {
+            trigger_error(
+                'Unrecognized attribute name for link ' .
+                'relationship.',
+                E_USER_ERROR
+            );
+            return;
+        }
+        $this->name = $configLookup[$name];
+    }
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        $allowed = $config->get('Attr.' . $this->name);
+        if (empty($allowed)) {
+            return false;
+        }
+
+        $string = $this->parseCDATA($string);
+        $parts = explode(' ', $string);
+
+        // lookup to prevent duplicates
+        $ret_lookup = array();
+        foreach ($parts as $part) {
+            $part = strtolower(trim($part));
+            if (!isset($allowed[$part])) {
+                continue;
+            }
+            $ret_lookup[$part] = true;
+        }
+
+        if (empty($ret_lookup)) {
+            return false;
+        }
+        $string = implode(' ', array_keys($ret_lookup));
+        return $string;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/MultiLength.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/MultiLength.php
new file mode 100644 (file)
index 0000000..bbb20f2
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * Validates a MultiLength as defined by the HTML spec.
+ *
+ * A multilength is either a integer (pixel count), a percentage, or
+ * a relative number.
+ */
+class HTMLPurifier_AttrDef_HTML_MultiLength extends HTMLPurifier_AttrDef_HTML_Length
+{
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        $string = trim($string);
+        if ($string === '') {
+            return false;
+        }
+
+        $parent_result = parent::validate($string, $config, $context);
+        if ($parent_result !== false) {
+            return $parent_result;
+        }
+
+        $length = strlen($string);
+        $last_char = $string[$length - 1];
+
+        if ($last_char !== '*') {
+            return false;
+        }
+
+        $int = substr($string, 0, $length - 1);
+
+        if ($int == '') {
+            return '*';
+        }
+        if (!is_numeric($int)) {
+            return false;
+        }
+
+        $int = (int)$int;
+        if ($int < 0) {
+            return false;
+        }
+        if ($int == 0) {
+            return '0';
+        }
+        if ($int == 1) {
+            return '*';
+        }
+        return ((string)$int) . '*';
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Nmtokens.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Nmtokens.php
new file mode 100644 (file)
index 0000000..f79683b
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * Validates contents based on NMTOKENS attribute type.
+ */
+class HTMLPurifier_AttrDef_HTML_Nmtokens extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        $string = trim($string);
+
+        // early abort: '' and '0' (strings that convert to false) are invalid
+        if (!$string) {
+            return false;
+        }
+
+        $tokens = $this->split($string, $config, $context);
+        $tokens = $this->filter($tokens, $config, $context);
+        if (empty($tokens)) {
+            return false;
+        }
+        return implode(' ', $tokens);
+    }
+
+    /**
+     * Splits a space separated list of tokens into its constituent parts.
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    protected function split($string, $config, $context)
+    {
+        // OPTIMIZABLE!
+        // do the preg_match, capture all subpatterns for reformulation
+
+        // we don't support U+00A1 and up codepoints or
+        // escaping because I don't know how to do that with regexps
+        // and plus it would complicate optimization efforts (you never
+        // see that anyway).
+        $pattern = '/(?:(?<=\s)|\A)' . // look behind for space or string start
+            '((?:--|-?[A-Za-z_])[A-Za-z_\-0-9]*)' .
+            '(?:(?=\s)|\z)/'; // look ahead for space or string end
+        preg_match_all($pattern, $string, $matches);
+        return $matches[1];
+    }
+
+    /**
+     * Template method for removing certain tokens based on arbitrary criteria.
+     * @note If we wanted to be really functional, we'd do an array_filter
+     *       with a callback. But... we're not.
+     * @param array $tokens
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    protected function filter($tokens, $config, $context)
+    {
+        return $tokens;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Pixels.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Pixels.php
new file mode 100644 (file)
index 0000000..a1d019e
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * Validates an integer representation of pixels according to the HTML spec.
+ */
+class HTMLPurifier_AttrDef_HTML_Pixels extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * @type int
+     */
+    protected $max;
+
+    /**
+     * @param int $max
+     */
+    public function __construct($max = null)
+    {
+        $this->max = $max;
+    }
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        $string = trim($string);
+        if ($string === '0') {
+            return $string;
+        }
+        if ($string === '') {
+            return false;
+        }
+        $length = strlen($string);
+        if (substr($string, $length - 2) == 'px') {
+            $string = substr($string, 0, $length - 2);
+        }
+        if (!is_numeric($string)) {
+            return false;
+        }
+        $int = (int)$string;
+
+        if ($int < 0) {
+            return '0';
+        }
+
+        // upper-bound value, extremely high values can
+        // crash operating systems, see <http://ha.ckers.org/imagecrash.html>
+        // WARNING, above link WILL crash you if you're using Windows
+
+        if ($this->max !== null && $int > $this->max) {
+            return (string)$this->max;
+        }
+        return (string)$int;
+    }
+
+    /**
+     * @param string $string
+     * @return HTMLPurifier_AttrDef
+     */
+    public function make($string)
+    {
+        if ($string === '') {
+            $max = null;
+        } else {
+            $max = (int)$string;
+        }
+        $class = get_class($this);
+        return new $class($max);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Integer.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Integer.php
new file mode 100644 (file)
index 0000000..400e707
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+
+/**
+ * Validates an integer.
+ * @note While this class was modeled off the CSS definition, no currently
+ *       allowed CSS uses this type.  The properties that do are: widows,
+ *       orphans, z-index, counter-increment, counter-reset.  Some of the
+ *       HTML attributes, however, find use for a non-negative version of this.
+ */
+class HTMLPurifier_AttrDef_Integer extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * Whether or not negative values are allowed.
+     * @type bool
+     */
+    protected $negative = true;
+
+    /**
+     * Whether or not zero is allowed.
+     * @type bool
+     */
+    protected $zero = true;
+
+    /**
+     * Whether or not positive values are allowed.
+     * @type bool
+     */
+    protected $positive = true;
+
+    /**
+     * @param $negative Bool indicating whether or not negative values are allowed
+     * @param $zero Bool indicating whether or not zero is allowed
+     * @param $positive Bool indicating whether or not positive values are allowed
+     */
+    public function __construct($negative = true, $zero = true, $positive = true)
+    {
+        $this->negative = $negative;
+        $this->zero = $zero;
+        $this->positive = $positive;
+    }
+
+    /**
+     * @param string $integer
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($integer, $config, $context)
+    {
+        $integer = $this->parseCDATA($integer);
+        if ($integer === '') {
+            return false;
+        }
+
+        // we could possibly simply typecast it to integer, but there are
+        // certain fringe cases that must not return an integer.
+
+        // clip leading sign
+        if ($this->negative && $integer[0] === '-') {
+            $digits = substr($integer, 1);
+            if ($digits === '0') {
+                $integer = '0';
+            } // rm minus sign for zero
+        } elseif ($this->positive && $integer[0] === '+') {
+            $digits = $integer = substr($integer, 1); // rm unnecessary plus
+        } else {
+            $digits = $integer;
+        }
+
+        // test if it's numeric
+        if (!ctype_digit($digits)) {
+            return false;
+        }
+
+        // perform scope tests
+        if (!$this->zero && $integer == 0) {
+            return false;
+        }
+        if (!$this->positive && $integer > 0) {
+            return false;
+        }
+        if (!$this->negative && $integer < 0) {
+            return false;
+        }
+
+        return $integer;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Lang.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Lang.php
new file mode 100644 (file)
index 0000000..2a55cea
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+
+/**
+ * Validates the HTML attribute lang, effectively a language code.
+ * @note Built according to RFC 3066, which obsoleted RFC 1766
+ */
+class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        $string = trim($string);
+        if (!$string) {
+            return false;
+        }
+
+        $subtags = explode('-', $string);
+        $num_subtags = count($subtags);
+
+        if ($num_subtags == 0) { // sanity check
+            return false;
+        }
+
+        // process primary subtag : $subtags[0]
+        $length = strlen($subtags[0]);
+        switch ($length) {
+            case 0:
+                return false;
+            case 1:
+                if (!($subtags[0] == 'x' || $subtags[0] == 'i')) {
+                    return false;
+                }
+                break;
+            case 2:
+            case 3:
+                if (!ctype_alpha($subtags[0])) {
+                    return false;
+                } elseif (!ctype_lower($subtags[0])) {
+                    $subtags[0] = strtolower($subtags[0]);
+                }
+                break;
+            default:
+                return false;
+        }
+
+        $new_string = $subtags[0];
+        if ($num_subtags == 1) {
+            return $new_string;
+        }
+
+        // process second subtag : $subtags[1]
+        $length = strlen($subtags[1]);
+        if ($length == 0 || ($length == 1 && $subtags[1] != 'x') || $length > 8 || !ctype_alnum($subtags[1])) {
+            return $new_string;
+        }
+        if (!ctype_lower($subtags[1])) {
+            $subtags[1] = strtolower($subtags[1]);
+        }
+
+        $new_string .= '-' . $subtags[1];
+        if ($num_subtags == 2) {
+            return $new_string;
+        }
+
+        // process all other subtags, index 2 and up
+        for ($i = 2; $i < $num_subtags; $i++) {
+            $length = strlen($subtags[$i]);
+            if ($length == 0 || $length > 8 || !ctype_alnum($subtags[$i])) {
+                return $new_string;
+            }
+            if (!ctype_lower($subtags[$i])) {
+                $subtags[$i] = strtolower($subtags[$i]);
+            }
+            $new_string .= '-' . $subtags[$i];
+        }
+        return $new_string;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Switch.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Switch.php
new file mode 100644 (file)
index 0000000..c7eb319
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * Decorator that, depending on a token, switches between two definitions.
+ */
+class HTMLPurifier_AttrDef_Switch
+{
+
+    /**
+     * @type string
+     */
+    protected $tag;
+
+    /**
+     * @type HTMLPurifier_AttrDef
+     */
+    protected $withTag;
+
+    /**
+     * @type HTMLPurifier_AttrDef
+     */
+    protected $withoutTag;
+
+    /**
+     * @param string $tag Tag name to switch upon
+     * @param HTMLPurifier_AttrDef $with_tag Call if token matches tag
+     * @param HTMLPurifier_AttrDef $without_tag Call if token doesn't match, or there is no token
+     */
+    public function __construct($tag, $with_tag, $without_tag)
+    {
+        $this->tag = $tag;
+        $this->withTag = $with_tag;
+        $this->withoutTag = $without_tag;
+    }
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        $token = $context->get('CurrentToken', true);
+        if (!$token || $token->name !== $this->tag) {
+            return $this->withoutTag->validate($string, $config, $context);
+        } else {
+            return $this->withTag->validate($string, $config, $context);
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Text.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Text.php
new file mode 100644 (file)
index 0000000..4553a4e
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * Validates arbitrary text according to the HTML spec.
+ */
+class HTMLPurifier_AttrDef_Text extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        return $this->parseCDATA($string);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI.php
new file mode 100644 (file)
index 0000000..c1cd897
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * Validates a URI as defined by RFC 3986.
+ * @note Scheme-specific mechanics deferred to HTMLPurifier_URIScheme
+ */
+class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * @type HTMLPurifier_URIParser
+     */
+    protected $parser;
+
+    /**
+     * @type bool
+     */
+    protected $embedsResource;
+
+    /**
+     * @param bool $embeds_resource Does the URI here result in an extra HTTP request?
+     */
+    public function __construct($embeds_resource = false)
+    {
+        $this->parser = new HTMLPurifier_URIParser();
+        $this->embedsResource = (bool)$embeds_resource;
+    }
+
+    /**
+     * @param string $string
+     * @return HTMLPurifier_AttrDef_URI
+     */
+    public function make($string)
+    {
+        $embeds = ($string === 'embedded');
+        return new HTMLPurifier_AttrDef_URI($embeds);
+    }
+
+    /**
+     * @param string $uri
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($uri, $config, $context)
+    {
+        if ($config->get('URI.Disable')) {
+            return false;
+        }
+
+        $uri = $this->parseCDATA($uri);
+
+        // parse the URI
+        $uri = $this->parser->parse($uri);
+        if ($uri === false) {
+            return false;
+        }
+
+        // add embedded flag to context for validators
+        $context->register('EmbeddedURI', $this->embedsResource);
+
+        $ok = false;
+        do {
+
+            // generic validation
+            $result = $uri->validate($config, $context);
+            if (!$result) {
+                break;
+            }
+
+            // chained filtering
+            $uri_def = $config->getDefinition('URI');
+            $result = $uri_def->filter($uri, $config, $context);
+            if (!$result) {
+                break;
+            }
+
+            // scheme-specific validation
+            $scheme_obj = $uri->getSchemeObj($config, $context);
+            if (!$scheme_obj) {
+                break;
+            }
+            if ($this->embedsResource && !$scheme_obj->browsable) {
+                break;
+            }
+            $result = $scheme_obj->validate($uri, $config, $context);
+            if (!$result) {
+                break;
+            }
+
+            // Post chained filtering
+            $result = $uri_def->postFilter($uri, $config, $context);
+            if (!$result) {
+                break;
+            }
+
+            // survived gauntlet
+            $ok = true;
+
+        } while (false);
+
+        $context->destroy('EmbeddedURI');
+        if (!$ok) {
+            return false;
+        }
+        // back to string
+        return $uri->toString();
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email.php
new file mode 100644 (file)
index 0000000..daf32b7
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+
+abstract class HTMLPurifier_AttrDef_URI_Email extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * Unpacks a mailbox into its display-name and address
+     * @param string $string
+     * @return mixed
+     */
+    public function unpack($string)
+    {
+        // needs to be implemented
+    }
+
+}
+
+// sub-implementations
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php
new file mode 100644 (file)
index 0000000..52c0d59
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * Primitive email validation class based on the regexp found at
+ * http://www.regular-expressions.info/email.html
+ */
+class HTMLPurifier_AttrDef_URI_Email_SimpleCheck extends HTMLPurifier_AttrDef_URI_Email
+{
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        // no support for named mailboxes i.e. "Bob <bob@example.com>"
+        // that needs more percent encoding to be done
+        if ($string == '') {
+            return false;
+        }
+        $string = trim($string);
+        $result = preg_match('/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i', $string);
+        return $result ? $string : false;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php
new file mode 100644 (file)
index 0000000..e7df800
--- /dev/null
@@ -0,0 +1,128 @@
+<?php
+
+/**
+ * Validates a host according to the IPv4, IPv6 and DNS (future) specifications.
+ */
+class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * IPv4 sub-validator.
+     * @type HTMLPurifier_AttrDef_URI_IPv4
+     */
+    protected $ipv4;
+
+    /**
+     * IPv6 sub-validator.
+     * @type HTMLPurifier_AttrDef_URI_IPv6
+     */
+    protected $ipv6;
+
+    public function __construct()
+    {
+        $this->ipv4 = new HTMLPurifier_AttrDef_URI_IPv4();
+        $this->ipv6 = new HTMLPurifier_AttrDef_URI_IPv6();
+    }
+
+    /**
+     * @param string $string
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($string, $config, $context)
+    {
+        $length = strlen($string);
+        // empty hostname is OK; it's usually semantically equivalent:
+        // the default host as defined by a URI scheme is used:
+        //
+        //      If the URI scheme defines a default for host, then that
+        //      default applies when the host subcomponent is undefined
+        //      or when the registered name is empty (zero length).
+        if ($string === '') {
+            return '';
+        }
+        if ($length > 1 && $string[0] === '[' && $string[$length - 1] === ']') {
+            //IPv6
+            $ip = substr($string, 1, $length - 2);
+            $valid = $this->ipv6->validate($ip, $config, $context);
+            if ($valid === false) {
+                return false;
+            }
+            return '[' . $valid . ']';
+        }
+
+        // need to do checks on unusual encodings too
+        $ipv4 = $this->ipv4->validate($string, $config, $context);
+        if ($ipv4 !== false) {
+            return $ipv4;
+        }
+
+        // A regular domain name.
+
+        // This doesn't match I18N domain names, but we don't have proper IRI support,
+        // so force users to insert Punycode.
+
+        // There is not a good sense in which underscores should be
+        // allowed, since it's technically not! (And if you go as
+        // far to allow everything as specified by the DNS spec...
+        // well, that's literally everything, modulo some space limits
+        // for the components and the overall name (which, by the way,
+        // we are NOT checking!).  So we (arbitrarily) decide this:
+        // let's allow underscores wherever we would have allowed
+        // hyphens, if they are enabled.  This is a pretty good match
+        // for browser behavior, for example, a large number of browsers
+        // cannot handle foo_.example.com, but foo_bar.example.com is
+        // fairly well supported.
+        $underscore = $config->get('Core.AllowHostnameUnderscore') ? '_' : '';
+
+        // The productions describing this are:
+        $a   = '[a-z]';     // alpha
+        $an  = '[a-z0-9]';  // alphanum
+        $and = "[a-z0-9-$underscore]"; // alphanum | "-"
+        // domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
+        $domainlabel = "$an($and*$an)?";
+        // toplabel    = alpha | alpha *( alphanum | "-" ) alphanum
+        $toplabel = "$a($and*$an)?";
+        // hostname    = *( domainlabel "." ) toplabel [ "." ]
+        if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
+            return $string;
+        }
+
+        // If we have Net_IDNA2 support, we can support IRIs by
+        // punycoding them. (This is the most portable thing to do,
+        // since otherwise we have to assume browsers support
+
+        if ($config->get('Core.EnableIDNA')) {
+            $idna = new Net_IDNA2(array('encoding' => 'utf8', 'overlong' => false, 'strict' => true));
+            // we need to encode each period separately
+            $parts = explode('.', $string);
+            try {
+                $new_parts = array();
+                foreach ($parts as $part) {
+                    $encodable = false;
+                    for ($i = 0, $c = strlen($part); $i < $c; $i++) {
+                        if (ord($part[$i]) > 0x7a) {
+                            $encodable = true;
+                            break;
+                        }
+                    }
+                    if (!$encodable) {
+                        $new_parts[] = $part;
+                    } else {
+                        $new_parts[] = $idna->encode($part);
+                    }
+                }
+                $string = implode('.', $new_parts);
+                if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
+                    return $string;
+                }
+            } catch (Exception $e) {
+                // XXX error reporting
+            }
+        }
+        return false;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv4.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv4.php
new file mode 100644 (file)
index 0000000..30ac16c
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * Validates an IPv4 address
+ * @author Feyd @ forums.devnetwork.net (public domain)
+ */
+class HTMLPurifier_AttrDef_URI_IPv4 extends HTMLPurifier_AttrDef
+{
+
+    /**
+     * IPv4 regex, protected so that IPv6 can reuse it.
+     * @type string
+     */
+    protected $ip4;
+
+    /**
+     * @param string $aIP
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($aIP, $config, $context)
+    {
+        if (!$this->ip4) {
+            $this->_loadRegex();
+        }
+
+        if (preg_match('#^' . $this->ip4 . '$#s', $aIP)) {
+            return $aIP;
+        }
+        return false;
+    }
+
+    /**
+     * Lazy load function to prevent regex from being stuffed in
+     * cache.
+     */
+    protected function _loadRegex()
+    {
+        $oct = '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])'; // 0-255
+        $this->ip4 = "(?:{$oct}\\.{$oct}\\.{$oct}\\.{$oct})";
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv6.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv6.php
new file mode 100644 (file)
index 0000000..f243793
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * Validates an IPv6 address.
+ * @author Feyd @ forums.devnetwork.net (public domain)
+ * @note This function requires brackets to have been removed from address
+ *       in URI.
+ */
+class HTMLPurifier_AttrDef_URI_IPv6 extends HTMLPurifier_AttrDef_URI_IPv4
+{
+
+    /**
+     * @param string $aIP
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string
+     */
+    public function validate($aIP, $config, $context)
+    {
+        if (!$this->ip4) {
+            $this->_loadRegex();
+        }
+
+        $original = $aIP;
+
+        $hex = '[0-9a-fA-F]';
+        $blk = '(?:' . $hex . '{1,4})';
+        $pre = '(?:/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))'; // /0 - /128
+
+        //      prefix check
+        if (strpos($aIP, '/') !== false) {
+            if (preg_match('#' . $pre . '$#s', $aIP, $find)) {
+                $aIP = substr($aIP, 0, 0 - strlen($find[0]));
+                unset($find);
+            } else {
+                return false;
+            }
+        }
+
+        //      IPv4-compatiblity check
+        if (preg_match('#(?<=:' . ')' . $this->ip4 . '$#s', $aIP, $find)) {
+            $aIP = substr($aIP, 0, 0 - strlen($find[0]));
+            $ip = explode('.', $find[0]);
+            $ip = array_map('dechex', $ip);
+            $aIP .= $ip[0] . $ip[1] . ':' . $ip[2] . $ip[3];
+            unset($find, $ip);
+        }
+
+        //      compression check
+        $aIP = explode('::', $aIP);
+        $c = count($aIP);
+        if ($c > 2) {
+            return false;
+        } elseif ($c == 2) {
+            list($first, $second) = $aIP;
+            $first = explode(':', $first);
+            $second = explode(':', $second);
+
+            if (count($first) + count($second) > 8) {
+                return false;
+            }
+
+            while (count($first) < 8) {
+                array_push($first, '0');
+            }
+
+            array_splice($first, 8 - count($second), 8, $second);
+            $aIP = $first;
+            unset($first, $second);
+        } else {
+            $aIP = explode(':', $aIP[0]);
+        }
+        $c = count($aIP);
+
+        if ($c != 8) {
+            return false;
+        }
+
+        //      All the pieces should be 16-bit hex strings. Are they?
+        foreach ($aIP as $piece) {
+            if (!preg_match('#^[0-9a-fA-F]{4}$#s', sprintf('%04s', $piece))) {
+                return false;
+            }
+        }
+        return $original;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform.php
new file mode 100644 (file)
index 0000000..b428331
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * Processes an entire attribute array for corrections needing multiple values.
+ *
+ * Occasionally, a certain attribute will need to be removed and popped onto
+ * another value.  Instead of creating a complex return syntax for
+ * HTMLPurifier_AttrDef, we just pass the whole attribute array to a
+ * specialized object and have that do the special work.  That is the
+ * family of HTMLPurifier_AttrTransform.
+ *
+ * An attribute transformation can be assigned to run before or after
+ * HTMLPurifier_AttrDef validation.  See HTMLPurifier_HTMLDefinition for
+ * more details.
+ */
+
+abstract class HTMLPurifier_AttrTransform
+{
+
+    /**
+     * Abstract: makes changes to the attributes dependent on multiple values.
+     *
+     * @param array $attr Assoc array of attributes, usually from
+     *              HTMLPurifier_Token_Tag::$attr
+     * @param HTMLPurifier_Config $config Mandatory HTMLPurifier_Config object.
+     * @param HTMLPurifier_Context $context Mandatory HTMLPurifier_Context object
+     * @return array Processed attribute array.
+     */
+    abstract public function transform($attr, $config, $context);
+
+    /**
+     * Prepends CSS properties to the style attribute, creating the
+     * attribute if it doesn't exist.
+     * @param array &$attr Attribute array to process (passed by reference)
+     * @param string $css CSS to prepend
+     */
+    public function prependCSS(&$attr, $css)
+    {
+        $attr['style'] = isset($attr['style']) ? $attr['style'] : '';
+        $attr['style'] = $css . $attr['style'];
+    }
+
+    /**
+     * Retrieves and removes an attribute
+     * @param array &$attr Attribute array to process (passed by reference)
+     * @param mixed $key Key of attribute to confiscate
+     * @return mixed
+     */
+    public function confiscateAttr(&$attr, $key)
+    {
+        if (!isset($attr[$key])) {
+            return null;
+        }
+        $value = $attr[$key];
+        unset($attr[$key]);
+        return $value;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Background.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Background.php
new file mode 100644 (file)
index 0000000..2f72869
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * Pre-transform that changes proprietary background attribute to CSS.
+ */
+class HTMLPurifier_AttrTransform_Background extends HTMLPurifier_AttrTransform
+{
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        if (!isset($attr['background'])) {
+            return $attr;
+        }
+
+        $background = $this->confiscateAttr($attr, 'background');
+        // some validation should happen here
+
+        $this->prependCSS($attr, "background-image:url($background);");
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BdoDir.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BdoDir.php
new file mode 100644 (file)
index 0000000..d66c04a
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+// this MUST be placed in post, as it assumes that any value in dir is valid
+
+/**
+ * Post-trasnform that ensures that bdo tags have the dir attribute set.
+ */
+class HTMLPurifier_AttrTransform_BdoDir extends HTMLPurifier_AttrTransform
+{
+
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        if (isset($attr['dir'])) {
+            return $attr;
+        }
+        $attr['dir'] = $config->get('Attr.DefaultTextDir');
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BgColor.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BgColor.php
new file mode 100644 (file)
index 0000000..0f51fd2
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * Pre-transform that changes deprecated bgcolor attribute to CSS.
+ */
+class HTMLPurifier_AttrTransform_BgColor extends HTMLPurifier_AttrTransform
+{
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        if (!isset($attr['bgcolor'])) {
+            return $attr;
+        }
+
+        $bgcolor = $this->confiscateAttr($attr, 'bgcolor');
+        // some validation should happen here
+
+        $this->prependCSS($attr, "background-color:$bgcolor;");
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BoolToCSS.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BoolToCSS.php
new file mode 100644 (file)
index 0000000..f25cd01
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * Pre-transform that changes converts a boolean attribute to fixed CSS
+ */
+class HTMLPurifier_AttrTransform_BoolToCSS extends HTMLPurifier_AttrTransform
+{
+    /**
+     * Name of boolean attribute that is trigger.
+     * @type string
+     */
+    protected $attr;
+
+    /**
+     * CSS declarations to add to style, needs trailing semicolon.
+     * @type string
+     */
+    protected $css;
+
+    /**
+     * @param string $attr attribute name to convert from
+     * @param string $css CSS declarations to add to style (needs semicolon)
+     */
+    public function __construct($attr, $css)
+    {
+        $this->attr = $attr;
+        $this->css = $css;
+    }
+
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        if (!isset($attr[$this->attr])) {
+            return $attr;
+        }
+        unset($attr[$this->attr]);
+        $this->prependCSS($attr, $this->css);
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Border.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Border.php
new file mode 100644 (file)
index 0000000..057dc01
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * Pre-transform that changes deprecated border attribute to CSS.
+ */
+class HTMLPurifier_AttrTransform_Border extends HTMLPurifier_AttrTransform
+{
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        if (!isset($attr['border'])) {
+            return $attr;
+        }
+        $border_width = $this->confiscateAttr($attr, 'border');
+        // some validation should happen here
+        $this->prependCSS($attr, "border:{$border_width}px solid;");
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/EnumToCSS.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/EnumToCSS.php
new file mode 100644 (file)
index 0000000..7ccd0e3
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * Generic pre-transform that converts an attribute with a fixed number of
+ * values (enumerated) to CSS.
+ */
+class HTMLPurifier_AttrTransform_EnumToCSS extends HTMLPurifier_AttrTransform
+{
+    /**
+     * Name of attribute to transform from.
+     * @type string
+     */
+    protected $attr;
+
+    /**
+     * Lookup array of attribute values to CSS.
+     * @type array
+     */
+    protected $enumToCSS = array();
+
+    /**
+     * Case sensitivity of the matching.
+     * @type bool
+     * @warning Currently can only be guaranteed to work with ASCII
+     *          values.
+     */
+    protected $caseSensitive = false;
+
+    /**
+     * @param string $attr Attribute name to transform from
+     * @param array $enum_to_css Lookup array of attribute values to CSS
+     * @param bool $case_sensitive Case sensitivity indicator, default false
+     */
+    public function __construct($attr, $enum_to_css, $case_sensitive = false)
+    {
+        $this->attr = $attr;
+        $this->enumToCSS = $enum_to_css;
+        $this->caseSensitive = (bool)$case_sensitive;
+    }
+
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        if (!isset($attr[$this->attr])) {
+            return $attr;
+        }
+
+        $value = trim($attr[$this->attr]);
+        unset($attr[$this->attr]);
+
+        if (!$this->caseSensitive) {
+            $value = strtolower($value);
+        }
+
+        if (!isset($this->enumToCSS[$value])) {
+            return $attr;
+        }
+        $this->prependCSS($attr, $this->enumToCSS[$value]);
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgRequired.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgRequired.php
new file mode 100644 (file)
index 0000000..7df6cb3
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+// must be called POST validation
+
+/**
+ * Transform that supplies default values for the src and alt attributes
+ * in img tags, as well as prevents the img tag from being removed
+ * because of a missing alt tag. This needs to be registered as both
+ * a pre and post attribute transform.
+ */
+class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform
+{
+
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        $src = true;
+        if (!isset($attr['src'])) {
+            if ($config->get('Core.RemoveInvalidImg')) {
+                return $attr;
+            }
+            $attr['src'] = $config->get('Attr.DefaultInvalidImage');
+            $src = false;
+        }
+
+        if (!isset($attr['alt'])) {
+            if ($src) {
+                $alt = $config->get('Attr.DefaultImageAlt');
+                if ($alt === null) {
+                    // truncate if the alt is too long
+                    $attr['alt'] = substr(basename($attr['src']), 0, 40);
+                } else {
+                    $attr['alt'] = $alt;
+                }
+            } else {
+                $attr['alt'] = $config->get('Attr.DefaultInvalidImageAlt');
+            }
+        }
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgSpace.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgSpace.php
new file mode 100644 (file)
index 0000000..350b335
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * Pre-transform that changes deprecated hspace and vspace attributes to CSS
+ */
+class HTMLPurifier_AttrTransform_ImgSpace extends HTMLPurifier_AttrTransform
+{
+    /**
+     * @type string
+     */
+    protected $attr;
+
+    /**
+     * @type array
+     */
+    protected $css = array(
+        'hspace' => array('left', 'right'),
+        'vspace' => array('top', 'bottom')
+    );
+
+    /**
+     * @param string $attr
+     */
+    public function __construct($attr)
+    {
+        $this->attr = $attr;
+        if (!isset($this->css[$attr])) {
+            trigger_error(htmlspecialchars($attr) . ' is not valid space attribute');
+        }
+    }
+
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        if (!isset($attr[$this->attr])) {
+            return $attr;
+        }
+
+        $width = $this->confiscateAttr($attr, $this->attr);
+        // some validation could happen here
+
+        if (!isset($this->css[$this->attr])) {
+            return $attr;
+        }
+
+        $style = '';
+        foreach ($this->css[$this->attr] as $suffix) {
+            $property = "margin-$suffix";
+            $style .= "$property:{$width}px;";
+        }
+        $this->prependCSS($attr, $style);
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Input.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Input.php
new file mode 100644 (file)
index 0000000..3ab47ed
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * Performs miscellaneous cross attribute validation and filtering for
+ * input elements. This is meant to be a post-transform.
+ */
+class HTMLPurifier_AttrTransform_Input extends HTMLPurifier_AttrTransform
+{
+    /**
+     * @type HTMLPurifier_AttrDef_HTML_Pixels
+     */
+    protected $pixels;
+
+    public function __construct()
+    {
+        $this->pixels = new HTMLPurifier_AttrDef_HTML_Pixels();
+    }
+
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        if (!isset($attr['type'])) {
+            $t = 'text';
+        } else {
+            $t = strtolower($attr['type']);
+        }
+        if (isset($attr['checked']) && $t !== 'radio' && $t !== 'checkbox') {
+            unset($attr['checked']);
+        }
+        if (isset($attr['maxlength']) && $t !== 'text' && $t !== 'password') {
+            unset($attr['maxlength']);
+        }
+        if (isset($attr['size']) && $t !== 'text' && $t !== 'password') {
+            $result = $this->pixels->validate($attr['size'], $config, $context);
+            if ($result === false) {
+                unset($attr['size']);
+            } else {
+                $attr['size'] = $result;
+            }
+        }
+        if (isset($attr['src']) && $t !== 'image') {
+            unset($attr['src']);
+        }
+        if (!isset($attr['value']) && ($t === 'radio' || $t === 'checkbox')) {
+            $attr['value'] = '';
+        }
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Lang.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Lang.php
new file mode 100644 (file)
index 0000000..5b0aff0
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * Post-transform that copies lang's value to xml:lang (and vice-versa)
+ * @note Theoretically speaking, this could be a pre-transform, but putting
+ *       post is more efficient.
+ */
+class HTMLPurifier_AttrTransform_Lang extends HTMLPurifier_AttrTransform
+{
+
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        $lang = isset($attr['lang']) ? $attr['lang'] : false;
+        $xml_lang = isset($attr['xml:lang']) ? $attr['xml:lang'] : false;
+
+        if ($lang !== false && $xml_lang === false) {
+            $attr['xml:lang'] = $lang;
+        } elseif ($xml_lang !== false) {
+            $attr['lang'] = $xml_lang;
+        }
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Length.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Length.php
new file mode 100644 (file)
index 0000000..853f335
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * Class for handling width/height length attribute transformations to CSS
+ */
+class HTMLPurifier_AttrTransform_Length extends HTMLPurifier_AttrTransform
+{
+
+    /**
+     * @type string
+     */
+    protected $name;
+
+    /**
+     * @type string
+     */
+    protected $cssName;
+
+    public function __construct($name, $css_name = null)
+    {
+        $this->name = $name;
+        $this->cssName = $css_name ? $css_name : $name;
+    }
+
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        if (!isset($attr[$this->name])) {
+            return $attr;
+        }
+        $length = $this->confiscateAttr($attr, $this->name);
+        if (ctype_digit($length)) {
+            $length .= 'px';
+        }
+        $this->prependCSS($attr, $this->cssName . ":$length;");
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Name.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Name.php
new file mode 100644 (file)
index 0000000..63cce68
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * Pre-transform that changes deprecated name attribute to ID if necessary
+ */
+class HTMLPurifier_AttrTransform_Name extends HTMLPurifier_AttrTransform
+{
+
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        // Abort early if we're using relaxed definition of name
+        if ($config->get('HTML.Attr.Name.UseCDATA')) {
+            return $attr;
+        }
+        if (!isset($attr['name'])) {
+            return $attr;
+        }
+        $id = $this->confiscateAttr($attr, 'name');
+        if (isset($attr['id'])) {
+            return $attr;
+        }
+        $attr['id'] = $id;
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/NameSync.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/NameSync.php
new file mode 100644 (file)
index 0000000..36079b7
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * Post-transform that performs validation to the name attribute; if
+ * it is present with an equivalent id attribute, it is passed through;
+ * otherwise validation is performed.
+ */
+class HTMLPurifier_AttrTransform_NameSync extends HTMLPurifier_AttrTransform
+{
+
+    public function __construct()
+    {
+        $this->idDef = new HTMLPurifier_AttrDef_HTML_ID();
+    }
+
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        if (!isset($attr['name'])) {
+            return $attr;
+        }
+        $name = $attr['name'];
+        if (isset($attr['id']) && $attr['id'] === $name) {
+            return $attr;
+        }
+        $result = $this->idDef->validate($name, $config, $context);
+        if ($result === false) {
+            unset($attr['name']);
+        } else {
+            $attr['name'] = $result;
+        }
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Nofollow.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Nofollow.php
new file mode 100644 (file)
index 0000000..1057ebe
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+
+// must be called POST validation
+
+/**
+ * Adds rel="nofollow" to all outbound links.  This transform is
+ * only attached if Attr.Nofollow is TRUE.
+ */
+class HTMLPurifier_AttrTransform_Nofollow extends HTMLPurifier_AttrTransform
+{
+    /**
+     * @type HTMLPurifier_URIParser
+     */
+    private $parser;
+
+    public function __construct()
+    {
+        $this->parser = new HTMLPurifier_URIParser();
+    }
+
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        if (!isset($attr['href'])) {
+            return $attr;
+        }
+
+        // XXX Kind of inefficient
+        $url = $this->parser->parse($attr['href']);
+        $scheme = $url->getSchemeObj($config, $context);
+
+        if ($scheme->browsable && !$url->isLocal($config, $context)) {
+            if (isset($attr['rel'])) {
+                $rels = explode(' ', $attr['rel']);
+                if (!in_array('nofollow', $rels)) {
+                    $rels[] = 'nofollow';
+                }
+                $attr['rel'] = implode(' ', $rels);
+            } else {
+                $attr['rel'] = 'nofollow';
+            }
+        }
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeEmbed.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeEmbed.php
new file mode 100644 (file)
index 0000000..231c81a
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+
+class HTMLPurifier_AttrTransform_SafeEmbed extends HTMLPurifier_AttrTransform
+{
+    /**
+     * @type string
+     */
+    public $name = "SafeEmbed";
+
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        $attr['allowscriptaccess'] = 'never';
+        $attr['allownetworking'] = 'internal';
+        $attr['type'] = 'application/x-shockwave-flash';
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeObject.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeObject.php
new file mode 100644 (file)
index 0000000..d1f3a4d
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * Writes default type for all objects. Currently only supports flash.
+ */
+class HTMLPurifier_AttrTransform_SafeObject extends HTMLPurifier_AttrTransform
+{
+    /**
+     * @type string
+     */
+    public $name = "SafeObject";
+
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        if (!isset($attr['type'])) {
+            $attr['type'] = 'application/x-shockwave-flash';
+        }
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeParam.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeParam.php
new file mode 100644 (file)
index 0000000..1143b4b
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * Validates name/value pairs in param tags to be used in safe objects. This
+ * will only allow name values it recognizes, and pre-fill certain attributes
+ * with required values.
+ *
+ * @note
+ *      This class only supports Flash. In the future, Quicktime support
+ *      may be added.
+ *
+ * @warning
+ *      This class expects an injector to add the necessary parameters tags.
+ */
+class HTMLPurifier_AttrTransform_SafeParam extends HTMLPurifier_AttrTransform
+{
+    /**
+     * @type string
+     */
+    public $name = "SafeParam";
+
+    /**
+     * @type HTMLPurifier_AttrDef_URI
+     */
+    private $uri;
+
+    public function __construct()
+    {
+        $this->uri = new HTMLPurifier_AttrDef_URI(true); // embedded
+        $this->wmode = new HTMLPurifier_AttrDef_Enum(array('window', 'opaque', 'transparent'));
+    }
+
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        // If we add support for other objects, we'll need to alter the
+        // transforms.
+        switch ($attr['name']) {
+            // application/x-shockwave-flash
+            // Keep this synchronized with Injector/SafeObject.php
+            case 'allowScriptAccess':
+                $attr['value'] = 'never';
+                break;
+            case 'allowNetworking':
+                $attr['value'] = 'internal';
+                break;
+            case 'allowFullScreen':
+                if ($config->get('HTML.FlashAllowFullScreen')) {
+                    $attr['value'] = ($attr['value'] == 'true') ? 'true' : 'false';
+                } else {
+                    $attr['value'] = 'false';
+                }
+                break;
+            case 'wmode':
+                $attr['value'] = $this->wmode->validate($attr['value'], $config, $context);
+                break;
+            case 'movie':
+            case 'src':
+                $attr['name'] = "movie";
+                $attr['value'] = $this->uri->validate($attr['value'], $config, $context);
+                break;
+            case 'flashvars':
+                // we're going to allow arbitrary inputs to the SWF, on
+                // the reasoning that it could only hack the SWF, not us.
+                break;
+            // add other cases to support other param name/value pairs
+            default:
+                $attr['name'] = $attr['value'] = null;
+        }
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ScriptRequired.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ScriptRequired.php
new file mode 100644 (file)
index 0000000..b7057bb
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * Implements required attribute stipulation for <script>
+ */
+class HTMLPurifier_AttrTransform_ScriptRequired extends HTMLPurifier_AttrTransform
+{
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        if (!isset($attr['type'])) {
+            $attr['type'] = 'text/javascript';
+        }
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetBlank.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetBlank.php
new file mode 100644 (file)
index 0000000..dd63ea8
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+// must be called POST validation
+
+/**
+ * Adds target="blank" to all outbound links.  This transform is
+ * only attached if Attr.TargetBlank is TRUE.  This works regardless
+ * of whether or not Attr.AllowedFrameTargets
+ */
+class HTMLPurifier_AttrTransform_TargetBlank extends HTMLPurifier_AttrTransform
+{
+    /**
+     * @type HTMLPurifier_URIParser
+     */
+    private $parser;
+
+    public function __construct()
+    {
+        $this->parser = new HTMLPurifier_URIParser();
+    }
+
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        if (!isset($attr['href'])) {
+            return $attr;
+        }
+
+        // XXX Kind of inefficient
+        $url = $this->parser->parse($attr['href']);
+        $scheme = $url->getSchemeObj($config, $context);
+
+        if ($scheme->browsable && !$url->isBenign($config, $context)) {
+            $attr['target'] = '_blank';
+        }
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Textarea.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Textarea.php
new file mode 100644 (file)
index 0000000..6a9f33a
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * Sets height/width defaults for <textarea>
+ */
+class HTMLPurifier_AttrTransform_Textarea extends HTMLPurifier_AttrTransform
+{
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        // Calculated from Firefox
+        if (!isset($attr['cols'])) {
+            $attr['cols'] = '22';
+        }
+        if (!isset($attr['rows'])) {
+            $attr['rows'] = '3';
+        }
+        return $attr;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTypes.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrTypes.php
new file mode 100644 (file)
index 0000000..3b70520
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+
+/**
+ * Provides lookup array of attribute types to HTMLPurifier_AttrDef objects
+ */
+class HTMLPurifier_AttrTypes
+{
+    /**
+     * Lookup array of attribute string identifiers to concrete implementations.
+     * @type HTMLPurifier_AttrDef[]
+     */
+    protected $info = array();
+
+    /**
+     * Constructs the info array, supplying default implementations for attribute
+     * types.
+     */
+    public function __construct()
+    {
+        // XXX This is kind of poor, since we don't actually /clone/
+        // instances; instead, we use the supplied make() attribute. So,
+        // the underlying class must know how to deal with arguments.
+        // With the old implementation of Enum, that ignored its
+        // arguments when handling a make dispatch, the IAlign
+        // definition wouldn't work.
+
+        // pseudo-types, must be instantiated via shorthand
+        $this->info['Enum']    = new HTMLPurifier_AttrDef_Enum();
+        $this->info['Bool']    = new HTMLPurifier_AttrDef_HTML_Bool();
+
+        $this->info['CDATA']    = new HTMLPurifier_AttrDef_Text();
+        $this->info['ID']       = new HTMLPurifier_AttrDef_HTML_ID();
+        $this->info['Length']   = new HTMLPurifier_AttrDef_HTML_Length();
+        $this->info['MultiLength'] = new HTMLPurifier_AttrDef_HTML_MultiLength();
+        $this->info['NMTOKENS'] = new HTMLPurifier_AttrDef_HTML_Nmtokens();
+        $this->info['Pixels']   = new HTMLPurifier_AttrDef_HTML_Pixels();
+        $this->info['Text']     = new HTMLPurifier_AttrDef_Text();
+        $this->info['URI']      = new HTMLPurifier_AttrDef_URI();
+        $this->info['LanguageCode'] = new HTMLPurifier_AttrDef_Lang();
+        $this->info['Color']    = new HTMLPurifier_AttrDef_HTML_Color();
+        $this->info['IAlign']   = self::makeEnum('top,middle,bottom,left,right');
+        $this->info['LAlign']   = self::makeEnum('top,bottom,left,right');
+        $this->info['FrameTarget'] = new HTMLPurifier_AttrDef_HTML_FrameTarget();
+
+        // unimplemented aliases
+        $this->info['ContentType'] = new HTMLPurifier_AttrDef_Text();
+        $this->info['ContentTypes'] = new HTMLPurifier_AttrDef_Text();
+        $this->info['Charsets'] = new HTMLPurifier_AttrDef_Text();
+        $this->info['Character'] = new HTMLPurifier_AttrDef_Text();
+
+        // "proprietary" types
+        $this->info['Class'] = new HTMLPurifier_AttrDef_HTML_Class();
+
+        // number is really a positive integer (one or more digits)
+        // FIXME: ^^ not always, see start and value of list items
+        $this->info['Number']   = new HTMLPurifier_AttrDef_Integer(false, false, true);
+    }
+
+    private static function makeEnum($in)
+    {
+        return new HTMLPurifier_AttrDef_Clone(new HTMLPurifier_AttrDef_Enum(explode(',', $in)));
+    }
+
+    /**
+     * Retrieves a type
+     * @param string $type String type name
+     * @return HTMLPurifier_AttrDef Object AttrDef for type
+     */
+    public function get($type)
+    {
+        // determine if there is any extra info tacked on
+        if (strpos($type, '#') !== false) {
+            list($type, $string) = explode('#', $type, 2);
+        } else {
+            $string = '';
+        }
+
+        if (!isset($this->info[$type])) {
+            trigger_error('Cannot retrieve undefined attribute type ' . $type, E_USER_ERROR);
+            return;
+        }
+        return $this->info[$type]->make($string);
+    }
+
+    /**
+     * Sets a new implementation for a type
+     * @param string $type String type name
+     * @param HTMLPurifier_AttrDef $impl Object AttrDef for type
+     */
+    public function set($type, $impl)
+    {
+        $this->info[$type] = $impl;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrValidator.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/AttrValidator.php
new file mode 100644 (file)
index 0000000..f97dc93
--- /dev/null
@@ -0,0 +1,178 @@
+<?php
+
+/**
+ * Validates the attributes of a token. Doesn't manage required attributes
+ * very well. The only reason we factored this out was because RemoveForeignElements
+ * also needed it besides ValidateAttributes.
+ */
+class HTMLPurifier_AttrValidator
+{
+
+    /**
+     * Validates the attributes of a token, mutating it as necessary.
+     * that has valid tokens
+     * @param HTMLPurifier_Token $token Token to validate.
+     * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config
+     * @param HTMLPurifier_Context $context Instance of HTMLPurifier_Context
+     */
+    public function validateToken($token, $config, $context)
+    {
+        $definition = $config->getHTMLDefinition();
+        $e =& $context->get('ErrorCollector', true);
+
+        // initialize IDAccumulator if necessary
+        $ok =& $context->get('IDAccumulator', true);
+        if (!$ok) {
+            $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
+            $context->register('IDAccumulator', $id_accumulator);
+        }
+
+        // initialize CurrentToken if necessary
+        $current_token =& $context->get('CurrentToken', true);
+        if (!$current_token) {
+            $context->register('CurrentToken', $token);
+        }
+
+        if (!$token instanceof HTMLPurifier_Token_Start &&
+            !$token instanceof HTMLPurifier_Token_Empty
+        ) {
+            return;
+        }
+
+        // create alias to global definition array, see also $defs
+        // DEFINITION CALL
+        $d_defs = $definition->info_global_attr;
+
+        // don't update token until the very end, to ensure an atomic update
+        $attr = $token->attr;
+
+        // do global transformations (pre)
+        // nothing currently utilizes this
+        foreach ($definition->info_attr_transform_pre as $transform) {
+            $attr = $transform->transform($o = $attr, $config, $context);
+            if ($e) {
+                if ($attr != $o) {
+                    $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
+                }
+            }
+        }
+
+        // do local transformations only applicable to this element (pre)
+        // ex. <p align="right"> to <p style="text-align:right;">
+        foreach ($definition->info[$token->name]->attr_transform_pre as $transform) {
+            $attr = $transform->transform($o = $attr, $config, $context);
+            if ($e) {
+                if ($attr != $o) {
+                    $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
+                }
+            }
+        }
+
+        // create alias to this element's attribute definition array, see
+        // also $d_defs (global attribute definition array)
+        // DEFINITION CALL
+        $defs = $definition->info[$token->name]->attr;
+
+        $attr_key = false;
+        $context->register('CurrentAttr', $attr_key);
+
+        // iterate through all the attribute keypairs
+        // Watch out for name collisions: $key has previously been used
+        foreach ($attr as $attr_key => $value) {
+
+            // call the definition
+            if (isset($defs[$attr_key])) {
+                // there is a local definition defined
+                if ($defs[$attr_key] === false) {
+                    // We've explicitly been told not to allow this element.
+                    // This is usually when there's a global definition
+                    // that must be overridden.
+                    // Theoretically speaking, we could have a
+                    // AttrDef_DenyAll, but this is faster!
+                    $result = false;
+                } else {
+                    // validate according to the element's definition
+                    $result = $defs[$attr_key]->validate(
+                        $value,
+                        $config,
+                        $context
+                    );
+                }
+            } elseif (isset($d_defs[$attr_key])) {
+                // there is a global definition defined, validate according
+                // to the global definition
+                $result = $d_defs[$attr_key]->validate(
+                    $value,
+                    $config,
+                    $context
+                );
+            } else {
+                // system never heard of the attribute? DELETE!
+                $result = false;
+            }
+
+            // put the results into effect
+            if ($result === false || $result === null) {
+                // this is a generic error message that should replaced
+                // with more specific ones when possible
+                if ($e) {
+                    $e->send(E_ERROR, 'AttrValidator: Attribute removed');
+                }
+
+                // remove the attribute
+                unset($attr[$attr_key]);
+            } elseif (is_string($result)) {
+                // generally, if a substitution is happening, there
+                // was some sort of implicit correction going on. We'll
+                // delegate it to the attribute classes to say exactly what.
+
+                // simple substitution
+                $attr[$attr_key] = $result;
+            } else {
+                // nothing happens
+            }
+
+            // we'd also want slightly more complicated substitution
+            // involving an array as the return value,
+            // although we're not sure how colliding attributes would
+            // resolve (certain ones would be completely overriden,
+            // others would prepend themselves).
+        }
+
+        $context->destroy('CurrentAttr');
+
+        // post transforms
+
+        // global (error reporting untested)
+        foreach ($definition->info_attr_transform_post as $transform) {
+            $attr = $transform->transform($o = $attr, $config, $context);
+            if ($e) {
+                if ($attr != $o) {
+                    $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
+                }
+            }
+        }
+
+        // local (error reporting untested)
+        foreach ($definition->info[$token->name]->attr_transform_post as $transform) {
+            $attr = $transform->transform($o = $attr, $config, $context);
+            if ($e) {
+                if ($attr != $o) {
+                    $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
+                }
+            }
+        }
+
+        $token->attr = $attr;
+
+        // destroy CurrentToken if we made it ourselves
+        if (!$current_token) {
+            $context->destroy('CurrentToken');
+        }
+
+    }
+
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Bootstrap.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Bootstrap.php
new file mode 100644 (file)
index 0000000..707122b
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+
+// constants are slow, so we use as few as possible
+if (!defined('HTMLPURIFIER_PREFIX')) {
+    define('HTMLPURIFIER_PREFIX', realpath(dirname(__FILE__) . '/..'));
+}
+
+// accomodations for versions earlier than 5.0.2
+// borrowed from PHP_Compat, LGPL licensed, by Aidan Lister <aidan@php.net>
+if (!defined('PHP_EOL')) {
+    switch (strtoupper(substr(PHP_OS, 0, 3))) {
+        case 'WIN':
+            define('PHP_EOL', "\r\n");
+            break;
+        case 'DAR':
+            define('PHP_EOL', "\r");
+            break;
+        default:
+            define('PHP_EOL', "\n");
+    }
+}
+
+/**
+ * Bootstrap class that contains meta-functionality for HTML Purifier such as
+ * the autoload function.
+ *
+ * @note
+ *      This class may be used without any other files from HTML Purifier.
+ */
+class HTMLPurifier_Bootstrap
+{
+
+    /**
+     * Autoload function for HTML Purifier
+     * @param string $class Class to load
+     * @return bool
+     */
+    public static function autoload($class)
+    {
+        $file = HTMLPurifier_Bootstrap::getPath($class);
+        if (!$file) {
+            return false;
+        }
+        // Technically speaking, it should be ok and more efficient to
+        // just do 'require', but Antonio Parraga reports that with
+        // Zend extensions such as Zend debugger and APC, this invariant
+        // may be broken.  Since we have efficient alternatives, pay
+        // the cost here and avoid the bug.
+        require_once HTMLPURIFIER_PREFIX . '/' . $file;
+        return true;
+    }
+
+    /**
+     * Returns the path for a specific class.
+     * @param string $class Class path to get
+     * @return string
+     */
+    public static function getPath($class)
+    {
+        if (strncmp('HTMLPurifier', $class, 12) !== 0) {
+            return false;
+        }
+        // Custom implementations
+        if (strncmp('HTMLPurifier_Language_', $class, 22) === 0) {
+            $code = str_replace('_', '-', substr($class, 22));
+            $file = 'HTMLPurifier/Language/classes/' . $code . '.php';
+        } else {
+            $file = str_replace('_', '/', $class) . '.php';
+        }
+        if (!file_exists(HTMLPURIFIER_PREFIX . '/' . $file)) {
+            return false;
+        }
+        return $file;
+    }
+
+    /**
+     * "Pre-registers" our autoloader on the SPL stack.
+     */
+    public static function registerAutoload()
+    {
+        $autoload = array('HTMLPurifier_Bootstrap', 'autoload');
+        if (($funcs = spl_autoload_functions()) === false) {
+            spl_autoload_register($autoload);
+        } elseif (function_exists('spl_autoload_unregister')) {
+            if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
+                // prepend flag exists, no need for shenanigans
+                spl_autoload_register($autoload, true, true);
+            } else {
+                $buggy  = version_compare(PHP_VERSION, '5.2.11', '<');
+                $compat = version_compare(PHP_VERSION, '5.1.2', '<=') &&
+                          version_compare(PHP_VERSION, '5.1.0', '>=');
+                foreach ($funcs as $func) {
+                    if ($buggy && is_array($func)) {
+                        // :TRICKY: There are some compatibility issues and some
+                        // places where we need to error out
+                        $reflector = new ReflectionMethod($func[0], $func[1]);
+                        if (!$reflector->isStatic()) {
+                            throw new Exception(
+                                'HTML Purifier autoloader registrar is not compatible
+                                with non-static object methods due to PHP Bug #44144;
+                                Please do not use HTMLPurifier.autoload.php (or any
+                                file that includes this file); instead, place the code:
+                                spl_autoload_register(array(\'HTMLPurifier_Bootstrap\', \'autoload\'))
+                                after your own autoloaders.'
+                            );
+                        }
+                        // Suprisingly, spl_autoload_register supports the
+                        // Class::staticMethod callback format, although call_user_func doesn't
+                        if ($compat) {
+                            $func = implode('::', $func);
+                        }
+                    }
+                    spl_autoload_unregister($func);
+                }
+                spl_autoload_register($autoload);
+                foreach ($funcs as $func) {
+                    spl_autoload_register($func);
+                }
+            }
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/CSSDefinition.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/CSSDefinition.php
new file mode 100644 (file)
index 0000000..07cc941
--- /dev/null
@@ -0,0 +1,474 @@
+<?php
+
+/**
+ * Defines allowed CSS attributes and what their values are.
+ * @see HTMLPurifier_HTMLDefinition
+ */
+class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
+{
+
+    public $type = 'CSS';
+
+    /**
+     * Assoc array of attribute name to definition object.
+     * @type HTMLPurifier_AttrDef[]
+     */
+    public $info = array();
+
+    /**
+     * Constructs the info array.  The meat of this class.
+     * @param HTMLPurifier_Config $config
+     */
+    protected function doSetup($config)
+    {
+        $this->info['text-align'] = new HTMLPurifier_AttrDef_Enum(
+            array('left', 'right', 'center', 'justify'),
+            false
+        );
+
+        $border_style =
+            $this->info['border-bottom-style'] =
+            $this->info['border-right-style'] =
+            $this->info['border-left-style'] =
+            $this->info['border-top-style'] = new HTMLPurifier_AttrDef_Enum(
+                array(
+                    'none',
+                    'hidden',
+                    'dotted',
+                    'dashed',
+                    'solid',
+                    'double',
+                    'groove',
+                    'ridge',
+                    'inset',
+                    'outset'
+                ),
+                false
+            );
+
+        $this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style);
+
+        $this->info['clear'] = new HTMLPurifier_AttrDef_Enum(
+            array('none', 'left', 'right', 'both'),
+            false
+        );
+        $this->info['float'] = new HTMLPurifier_AttrDef_Enum(
+            array('none', 'left', 'right'),
+            false
+        );
+        $this->info['font-style'] = new HTMLPurifier_AttrDef_Enum(
+            array('normal', 'italic', 'oblique'),
+            false
+        );
+        $this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum(
+            array('normal', 'small-caps'),
+            false
+        );
+
+        $uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite(
+            array(
+                new HTMLPurifier_AttrDef_Enum(array('none')),
+                new HTMLPurifier_AttrDef_CSS_URI()
+            )
+        );
+
+        $this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum(
+            array('inside', 'outside'),
+            false
+        );
+        $this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum(
+            array(
+                'disc',
+                'circle',
+                'square',
+                'decimal',
+                'lower-roman',
+                'upper-roman',
+                'lower-alpha',
+                'upper-alpha',
+                'none'
+            ),
+            false
+        );
+        $this->info['list-style-image'] = $uri_or_none;
+
+        $this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config);
+
+        $this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum(
+            array('capitalize', 'uppercase', 'lowercase', 'none'),
+            false
+        );
+        $this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color();
+
+        $this->info['background-image'] = $uri_or_none;
+        $this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum(
+            array('repeat', 'repeat-x', 'repeat-y', 'no-repeat')
+        );
+        $this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum(
+            array('scroll', 'fixed')
+        );
+        $this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition();
+
+        $border_color =
+            $this->info['border-top-color'] =
+            $this->info['border-bottom-color'] =
+            $this->info['border-left-color'] =
+            $this->info['border-right-color'] =
+            $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(
+                array(
+                    new HTMLPurifier_AttrDef_Enum(array('transparent')),
+                    new HTMLPurifier_AttrDef_CSS_Color()
+                )
+            );
+
+        $this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config);
+
+        $this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color);
+
+        $border_width =
+            $this->info['border-top-width'] =
+            $this->info['border-bottom-width'] =
+            $this->info['border-left-width'] =
+            $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(
+                array(
+                    new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')),
+                    new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative
+                )
+            );
+
+        $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width);
+
+        $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(
+            array(
+                new HTMLPurifier_AttrDef_Enum(array('normal')),
+                new HTMLPurifier_AttrDef_CSS_Length()
+            )
+        );
+
+        $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(
+            array(
+                new HTMLPurifier_AttrDef_Enum(array('normal')),
+                new HTMLPurifier_AttrDef_CSS_Length()
+            )
+        );
+
+        $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(
+            array(
+                new HTMLPurifier_AttrDef_Enum(
+                    array(
+                        'xx-small',
+                        'x-small',
+                        'small',
+                        'medium',
+                        'large',
+                        'x-large',
+                        'xx-large',
+                        'larger',
+                        'smaller'
+                    )
+                ),
+                new HTMLPurifier_AttrDef_CSS_Percentage(),
+                new HTMLPurifier_AttrDef_CSS_Length()
+            )
+        );
+
+        $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(
+            array(
+                new HTMLPurifier_AttrDef_Enum(array('normal')),
+                new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives
+                new HTMLPurifier_AttrDef_CSS_Length('0'),
+                new HTMLPurifier_AttrDef_CSS_Percentage(true)
+            )
+        );
+
+        $margin =
+            $this->info['margin-top'] =
+            $this->info['margin-bottom'] =
+            $this->info['margin-left'] =
+            $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(
+                array(
+                    new HTMLPurifier_AttrDef_CSS_Length(),
+                    new HTMLPurifier_AttrDef_CSS_Percentage(),
+                    new HTMLPurifier_AttrDef_Enum(array('auto'))
+                )
+            );
+
+        $this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin);
+
+        // non-negative
+        $padding =
+            $this->info['padding-top'] =
+            $this->info['padding-bottom'] =
+            $this->info['padding-left'] =
+            $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(
+                array(
+                    new HTMLPurifier_AttrDef_CSS_Length('0'),
+                    new HTMLPurifier_AttrDef_CSS_Percentage(true)
+                )
+            );
+
+        $this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding);
+
+        $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(
+            array(
+                new HTMLPurifier_AttrDef_CSS_Length(),
+                new HTMLPurifier_AttrDef_CSS_Percentage()
+            )
+        );
+
+        $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(
+            array(
+                new HTMLPurifier_AttrDef_CSS_Length('0'),
+                new HTMLPurifier_AttrDef_CSS_Percentage(true),
+                new HTMLPurifier_AttrDef_Enum(array('auto'))
+            )
+        );
+        $max = $config->get('CSS.MaxImgLength');
+
+        $this->info['width'] =
+        $this->info['height'] =
+            $max === null ?
+                $trusted_wh :
+                new HTMLPurifier_AttrDef_Switch(
+                    'img',
+                    // For img tags:
+                    new HTMLPurifier_AttrDef_CSS_Composite(
+                        array(
+                            new HTMLPurifier_AttrDef_CSS_Length('0', $max),
+                            new HTMLPurifier_AttrDef_Enum(array('auto'))
+                        )
+                    ),
+                    // For everyone else:
+                    $trusted_wh
+                );
+
+        $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration();
+
+        $this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily();
+
+        // this could use specialized code
+        $this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum(
+            array(
+                'normal',
+                'bold',
+                'bolder',
+                'lighter',
+                '100',
+                '200',
+                '300',
+                '400',
+                '500',
+                '600',
+                '700',
+                '800',
+                '900'
+            ),
+            false
+        );
+
+        // MUST be called after other font properties, as it references
+        // a CSSDefinition object
+        $this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config);
+
+        // same here
+        $this->info['border'] =
+        $this->info['border-bottom'] =
+        $this->info['border-top'] =
+        $this->info['border-left'] =
+        $this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config);
+
+        $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(
+            array('collapse', 'separate')
+        );
+
+        $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(
+            array('top', 'bottom')
+        );
+
+        $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(
+            array('auto', 'fixed')
+        );
+
+        $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(
+            array(
+                new HTMLPurifier_AttrDef_Enum(
+                    array(
+                        'baseline',
+                        'sub',
+                        'super',
+                        'top',
+                        'text-top',
+                        'middle',
+                        'bottom',
+                        'text-bottom'
+                    )
+                ),
+                new HTMLPurifier_AttrDef_CSS_Length(),
+                new HTMLPurifier_AttrDef_CSS_Percentage()
+            )
+        );
+
+        $this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2);
+
+        // These CSS properties don't work on many browsers, but we live
+        // in THE FUTURE!
+        $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(
+            array('nowrap', 'normal', 'pre', 'pre-wrap', 'pre-line')
+        );
+
+        if ($config->get('CSS.Proprietary')) {
+            $this->doSetupProprietary($config);
+        }
+
+        if ($config->get('CSS.AllowTricky')) {
+            $this->doSetupTricky($config);
+        }
+
+        if ($config->get('CSS.Trusted')) {
+            $this->doSetupTrusted($config);
+        }
+
+        $allow_important = $config->get('CSS.AllowImportant');
+        // wrap all attr-defs with decorator that handles !important
+        foreach ($this->info as $k => $v) {
+            $this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important);
+        }
+
+        $this->setupConfigStuff($config);
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    protected function doSetupProprietary($config)
+    {
+        // Internet Explorer only scrollbar colors
+        $this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
+        $this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color();
+        $this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
+        $this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color();
+        $this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color();
+        $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
+
+        // vendor specific prefixes of opacity
+        $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
+        $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
+
+        // only opacity, for now
+        $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter();
+
+        // more CSS3
+        $this->info['page-break-after'] =
+        $this->info['page-break-before'] = new HTMLPurifier_AttrDef_Enum(
+            array(
+                'auto',
+                'always',
+                'avoid',
+                'left',
+                'right'
+            )
+        );
+        $this->info['page-break-inside'] = new HTMLPurifier_AttrDef_Enum(array('auto', 'avoid'));
+
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    protected function doSetupTricky($config)
+    {
+        $this->info['display'] = new HTMLPurifier_AttrDef_Enum(
+            array(
+                'inline',
+                'block',
+                'list-item',
+                'run-in',
+                'compact',
+                'marker',
+                'table',
+                'inline-block',
+                'inline-table',
+                'table-row-group',
+                'table-header-group',
+                'table-footer-group',
+                'table-row',
+                'table-column-group',
+                'table-column',
+                'table-cell',
+                'table-caption',
+                'none'
+            )
+        );
+        $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(
+            array('visible', 'hidden', 'collapse')
+        );
+        $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll'));
+        $this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    protected function doSetupTrusted($config)
+    {
+        $this->info['position'] = new HTMLPurifier_AttrDef_Enum(
+            array('static', 'relative', 'absolute', 'fixed')
+        );
+        $this->info['top'] =
+        $this->info['left'] =
+        $this->info['right'] =
+        $this->info['bottom'] = new HTMLPurifier_AttrDef_CSS_Composite(
+            array(
+                new HTMLPurifier_AttrDef_CSS_Length(),
+                new HTMLPurifier_AttrDef_CSS_Percentage(),
+                new HTMLPurifier_AttrDef_Enum(array('auto')),
+            )
+        );
+        $this->info['z-index'] = new HTMLPurifier_AttrDef_CSS_Composite(
+            array(
+                new HTMLPurifier_AttrDef_Integer(),
+                new HTMLPurifier_AttrDef_Enum(array('auto')),
+            )
+        );
+    }
+
+    /**
+     * Performs extra config-based processing. Based off of
+     * HTMLPurifier_HTMLDefinition.
+     * @param HTMLPurifier_Config $config
+     * @todo Refactor duplicate elements into common class (probably using
+     *       composition, not inheritance).
+     */
+    protected function setupConfigStuff($config)
+    {
+        // setup allowed elements
+        $support = "(for information on implementing this, see the " .
+            "support forums) ";
+        $allowed_properties = $config->get('CSS.AllowedProperties');
+        if ($allowed_properties !== null) {
+            foreach ($this->info as $name => $d) {
+                if (!isset($allowed_properties[$name])) {
+                    unset($this->info[$name]);
+                }
+                unset($allowed_properties[$name]);
+            }
+            // emit errors
+            foreach ($allowed_properties as $name => $d) {
+                // :TODO: Is this htmlspecialchars() call really necessary?
+                $name = htmlspecialchars($name);
+                trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING);
+            }
+        }
+
+        $forbidden_properties = $config->get('CSS.ForbiddenProperties');
+        if ($forbidden_properties !== null) {
+            foreach ($this->info as $name => $d) {
+                if (isset($forbidden_properties[$name])) {
+                    unset($this->info[$name]);
+                }
+            }
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef.php
new file mode 100644 (file)
index 0000000..8eb17b8
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * Defines allowed child nodes and validates nodes against it.
+ */
+abstract class HTMLPurifier_ChildDef
+{
+    /**
+     * Type of child definition, usually right-most part of class name lowercase.
+     * Used occasionally in terms of context.
+     * @type string
+     */
+    public $type;
+
+    /**
+     * Indicates whether or not an empty array of children is okay.
+     *
+     * This is necessary for redundant checking when changes affecting
+     * a child node may cause a parent node to now be disallowed.
+     * @type bool
+     */
+    public $allow_empty;
+
+    /**
+     * Lookup array of all elements that this definition could possibly allow.
+     * @type array
+     */
+    public $elements = array();
+
+    /**
+     * Get lookup of tag names that should not close this element automatically.
+     * All other elements will do so.
+     * @param HTMLPurifier_Config $config HTMLPurifier_Config object
+     * @return array
+     */
+    public function getAllowedElements($config)
+    {
+        return $this->elements;
+    }
+
+    /**
+     * Validates nodes according to definition and returns modification.
+     *
+     * @param HTMLPurifier_Node[] $children Array of HTMLPurifier_Node
+     * @param HTMLPurifier_Config $config HTMLPurifier_Config object
+     * @param HTMLPurifier_Context $context HTMLPurifier_Context object
+     * @return bool|array true to leave nodes as is, false to remove parent node, array of replacement children
+     */
+    abstract public function validateChildren($children, $config, $context);
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Chameleon.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Chameleon.php
new file mode 100644 (file)
index 0000000..7439be2
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * Definition that uses different definitions depending on context.
+ *
+ * The del and ins tags are notable because they allow different types of
+ * elements depending on whether or not they're in a block or inline context.
+ * Chameleon allows this behavior to happen by using two different
+ * definitions depending on context.  While this somewhat generalized,
+ * it is specifically intended for those two tags.
+ */
+class HTMLPurifier_ChildDef_Chameleon extends HTMLPurifier_ChildDef
+{
+
+    /**
+     * Instance of the definition object to use when inline. Usually stricter.
+     * @type HTMLPurifier_ChildDef_Optional
+     */
+    public $inline;
+
+    /**
+     * Instance of the definition object to use when block.
+     * @type HTMLPurifier_ChildDef_Optional
+     */
+    public $block;
+
+    /**
+     * @type string
+     */
+    public $type = 'chameleon';
+
+    /**
+     * @param array $inline List of elements to allow when inline.
+     * @param array $block List of elements to allow when block.
+     */
+    public function __construct($inline, $block)
+    {
+        $this->inline = new HTMLPurifier_ChildDef_Optional($inline);
+        $this->block = new HTMLPurifier_ChildDef_Optional($block);
+        $this->elements = $this->block->elements;
+    }
+
+    /**
+     * @param HTMLPurifier_Node[] $children
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function validateChildren($children, $config, $context)
+    {
+        if ($context->get('IsInline') === false) {
+            return $this->block->validateChildren(
+                $children,
+                $config,
+                $context
+            );
+        } else {
+            return $this->inline->validateChildren(
+                $children,
+                $config,
+                $context
+            );
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Custom.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Custom.php
new file mode 100644 (file)
index 0000000..128132e
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * Custom validation class, accepts DTD child definitions
+ *
+ * @warning Currently this class is an all or nothing proposition, that is,
+ *          it will only give a bool return value.
+ */
+class HTMLPurifier_ChildDef_Custom extends HTMLPurifier_ChildDef
+{
+    /**
+     * @type string
+     */
+    public $type = 'custom';
+
+    /**
+     * @type bool
+     */
+    public $allow_empty = false;
+
+    /**
+     * Allowed child pattern as defined by the DTD.
+     * @type string
+     */
+    public $dtd_regex;
+
+    /**
+     * PCRE regex derived from $dtd_regex.
+     * @type string
+     */
+    private $_pcre_regex;
+
+    /**
+     * @param $dtd_regex Allowed child pattern from the DTD
+     */
+    public function __construct($dtd_regex)
+    {
+        $this->dtd_regex = $dtd_regex;
+        $this->_compileRegex();
+    }
+
+    /**
+     * Compiles the PCRE regex from a DTD regex ($dtd_regex to $_pcre_regex)
+     */
+    protected function _compileRegex()
+    {
+        $raw = str_replace(' ', '', $this->dtd_regex);
+        if ($raw{0} != '(') {
+            $raw = "($raw)";
+        }
+        $el = '[#a-zA-Z0-9_.-]+';
+        $reg = $raw;
+
+        // COMPLICATED! AND MIGHT BE BUGGY! I HAVE NO CLUE WHAT I'M
+        // DOING! Seriously: if there's problems, please report them.
+
+        // collect all elements into the $elements array
+        preg_match_all("/$el/", $reg, $matches);
+        foreach ($matches[0] as $match) {
+            $this->elements[$match] = true;
+        }
+
+        // setup all elements as parentheticals with leading commas
+        $reg = preg_replace("/$el/", '(,\\0)', $reg);
+
+        // remove commas when they were not solicited
+        $reg = preg_replace("/([^,(|]\(+),/", '\\1', $reg);
+
+        // remove all non-paranthetical commas: they are handled by first regex
+        $reg = preg_replace("/,\(/", '(', $reg);
+
+        $this->_pcre_regex = $reg;
+    }
+
+    /**
+     * @param HTMLPurifier_Node[] $children
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function validateChildren($children, $config, $context)
+    {
+        $list_of_children = '';
+        $nesting = 0; // depth into the nest
+        foreach ($children as $node) {
+            if (!empty($node->is_whitespace)) {
+                continue;
+            }
+            $list_of_children .= $node->name . ',';
+        }
+        // add leading comma to deal with stray comma declarations
+        $list_of_children = ',' . rtrim($list_of_children, ',');
+        $okay =
+            preg_match(
+                '/^,?' . $this->_pcre_regex . '$/',
+                $list_of_children
+            );
+        return (bool)$okay;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Empty.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Empty.php
new file mode 100644 (file)
index 0000000..a8a6cbd
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * Definition that disallows all elements.
+ * @warning validateChildren() in this class is actually never called, because
+ *          empty elements are corrected in HTMLPurifier_Strategy_MakeWellFormed
+ *          before child definitions are parsed in earnest by
+ *          HTMLPurifier_Strategy_FixNesting.
+ */
+class HTMLPurifier_ChildDef_Empty extends HTMLPurifier_ChildDef
+{
+    /**
+     * @type bool
+     */
+    public $allow_empty = true;
+
+    /**
+     * @type string
+     */
+    public $type = 'empty';
+
+    public function __construct()
+    {
+    }
+
+    /**
+     * @param HTMLPurifier_Node[] $children
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function validateChildren($children, $config, $context)
+    {
+        return array();
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/List.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/List.php
new file mode 100644 (file)
index 0000000..891b9f6
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+
+/**
+ * Definition for list containers ul and ol.
+ *
+ * What does this do?  The big thing is to handle ol/ul at the top
+ * level of list nodes, which should be handled specially by /folding/
+ * them into the previous list node.  We generally shouldn't ever
+ * see other disallowed elements, because the autoclose behavior
+ * in MakeWellFormed handles it.
+ */
+class HTMLPurifier_ChildDef_List extends HTMLPurifier_ChildDef
+{
+    /**
+     * @type string
+     */
+    public $type = 'list';
+    /**
+     * @type array
+     */
+    // lying a little bit, so that we can handle ul and ol ourselves
+    // XXX: This whole business with 'wrap' is all a bit unsatisfactory
+    public $elements = array('li' => true, 'ul' => true, 'ol' => true);
+
+    /**
+     * @param array $children
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function validateChildren($children, $config, $context)
+    {
+        // Flag for subclasses
+        $this->whitespace = false;
+
+        // if there are no tokens, delete parent node
+        if (empty($children)) {
+            return false;
+        }
+
+        // the new set of children
+        $result = array();
+
+        // a little sanity check to make sure it's not ALL whitespace
+        $all_whitespace = true;
+
+        $current_li = false;
+
+        foreach ($children as $node) {
+            if (!empty($node->is_whitespace)) {
+                $result[] = $node;
+                continue;
+            }
+            $all_whitespace = false; // phew, we're not talking about whitespace
+
+            if ($node->name === 'li') {
+                // good
+                $current_li = $node;
+                $result[] = $node;
+            } else {
+                // we want to tuck this into the previous li
+                // Invariant: we expect the node to be ol/ul
+                // ToDo: Make this more robust in the case of not ol/ul
+                // by distinguishing between existing li and li created
+                // to handle non-list elements; non-list elements should
+                // not be appended to an existing li; only li created
+                // for non-list. This distinction is not currently made.
+                if ($current_li === false) {
+                    $current_li = new HTMLPurifier_Node_Element('li');
+                    $result[] = $current_li;
+                }
+                $current_li->children[] = $node;
+                $current_li->empty = false; // XXX fascinating! Check for this error elsewhere ToDo
+            }
+        }
+        if (empty($result)) {
+            return false;
+        }
+        if ($all_whitespace) {
+            return false;
+        }
+        return $result;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Optional.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Optional.php
new file mode 100644 (file)
index 0000000..b946806
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * Definition that allows a set of elements, and allows no children.
+ * @note This is a hack to reuse code from HTMLPurifier_ChildDef_Required,
+ *       really, one shouldn't inherit from the other.  Only altered behavior
+ *       is to overload a returned false with an array.  Thus, it will never
+ *       return false.
+ */
+class HTMLPurifier_ChildDef_Optional extends HTMLPurifier_ChildDef_Required
+{
+    /**
+     * @type bool
+     */
+    public $allow_empty = true;
+
+    /**
+     * @type string
+     */
+    public $type = 'optional';
+
+    /**
+     * @param array $children
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function validateChildren($children, $config, $context)
+    {
+        $result = parent::validateChildren($children, $config, $context);
+        // we assume that $children is not modified
+        if ($result === false) {
+            if (empty($children)) {
+                return true;
+            } elseif ($this->whitespace) {
+                return $children;
+            } else {
+                return array();
+            }
+        }
+        return $result;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Required.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Required.php
new file mode 100644 (file)
index 0000000..0d1c8f5
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+
+/**
+ * Definition that allows a set of elements, but disallows empty children.
+ */
+class HTMLPurifier_ChildDef_Required extends HTMLPurifier_ChildDef
+{
+    /**
+     * Lookup table of allowed elements.
+     * @type array
+     */
+    public $elements = array();
+
+    /**
+     * Whether or not the last passed node was all whitespace.
+     * @type bool
+     */
+    protected $whitespace = false;
+
+    /**
+     * @param array|string $elements List of allowed element names (lowercase).
+     */
+    public function __construct($elements)
+    {
+        if (is_string($elements)) {
+            $elements = str_replace(' ', '', $elements);
+            $elements = explode('|', $elements);
+        }
+        $keys = array_keys($elements);
+        if ($keys == array_keys($keys)) {
+            $elements = array_flip($elements);
+            foreach ($elements as $i => $x) {
+                $elements[$i] = true;
+                if (empty($i)) {
+                    unset($elements[$i]);
+                } // remove blank
+            }
+        }
+        $this->elements = $elements;
+    }
+
+    /**
+     * @type bool
+     */
+    public $allow_empty = false;
+
+    /**
+     * @type string
+     */
+    public $type = 'required';
+
+    /**
+     * @param array $children
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function validateChildren($children, $config, $context)
+    {
+        // Flag for subclasses
+        $this->whitespace = false;
+
+        // if there are no tokens, delete parent node
+        if (empty($children)) {
+            return false;
+        }
+
+        // the new set of children
+        $result = array();
+
+        // whether or not parsed character data is allowed
+        // this controls whether or not we silently drop a tag
+        // or generate escaped HTML from it
+        $pcdata_allowed = isset($this->elements['#PCDATA']);
+
+        // a little sanity check to make sure it's not ALL whitespace
+        $all_whitespace = true;
+
+        $stack = array_reverse($children);
+        while (!empty($stack)) {
+            $node = array_pop($stack);
+            if (!empty($node->is_whitespace)) {
+                $result[] = $node;
+                continue;
+            }
+            $all_whitespace = false; // phew, we're not talking about whitespace
+
+            if (!isset($this->elements[$node->name])) {
+                // special case text
+                // XXX One of these ought to be redundant or something
+                if ($pcdata_allowed && $node instanceof HTMLPurifier_Node_Text) {
+                    $result[] = $node;
+                    continue;
+                }
+                // spill the child contents in
+                // ToDo: Make configurable
+                if ($node instanceof HTMLPurifier_Node_Element) {
+                    for ($i = count($node->children) - 1; $i >= 0; $i--) {
+                        $stack[] = $node->children[$i];
+                    }
+                    continue;
+                }
+                continue;
+            }
+            $result[] = $node;
+        }
+        if (empty($result)) {
+            return false;
+        }
+        if ($all_whitespace) {
+            $this->whitespace = true;
+            return false;
+        }
+        return $result;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/StrictBlockquote.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/StrictBlockquote.php
new file mode 100644 (file)
index 0000000..3270a46
--- /dev/null
@@ -0,0 +1,110 @@
+<?php
+
+/**
+ * Takes the contents of blockquote when in strict and reformats for validation.
+ */
+class HTMLPurifier_ChildDef_StrictBlockquote extends HTMLPurifier_ChildDef_Required
+{
+    /**
+     * @type array
+     */
+    protected $real_elements;
+
+    /**
+     * @type array
+     */
+    protected $fake_elements;
+
+    /**
+     * @type bool
+     */
+    public $allow_empty = true;
+
+    /**
+     * @type string
+     */
+    public $type = 'strictblockquote';
+
+    /**
+     * @type bool
+     */
+    protected $init = false;
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return array
+     * @note We don't want MakeWellFormed to auto-close inline elements since
+     *       they might be allowed.
+     */
+    public function getAllowedElements($config)
+    {
+        $this->init($config);
+        return $this->fake_elements;
+    }
+
+    /**
+     * @param array $children
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function validateChildren($children, $config, $context)
+    {
+        $this->init($config);
+
+        // trick the parent class into thinking it allows more
+        $this->elements = $this->fake_elements;
+        $result = parent::validateChildren($children, $config, $context);
+        $this->elements = $this->real_elements;
+
+        if ($result === false) {
+            return array();
+        }
+        if ($result === true) {
+            $result = $children;
+        }
+
+        $def = $config->getHTMLDefinition();
+        $block_wrap_name = $def->info_block_wrapper;
+        $block_wrap = false;
+        $ret = array();
+
+        foreach ($result as $node) {
+            if ($block_wrap === false) {
+                if (($node instanceof HTMLPurifier_Node_Text && !$node->is_whitespace) ||
+                    ($node instanceof HTMLPurifier_Node_Element && !isset($this->elements[$node->name]))) {
+                        $block_wrap = new HTMLPurifier_Node_Element($def->info_block_wrapper);
+                        $ret[] = $block_wrap;
+                }
+            } else {
+                if ($node instanceof HTMLPurifier_Node_Element && isset($this->elements[$node->name])) {
+                    $block_wrap = false;
+
+                }
+            }
+            if ($block_wrap) {
+                $block_wrap->children[] = $node;
+            } else {
+                $ret[] = $node;
+            }
+        }
+        return $ret;
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    private function init($config)
+    {
+        if (!$this->init) {
+            $def = $config->getHTMLDefinition();
+            // allow all inline elements
+            $this->real_elements = $this->elements;
+            $this->fake_elements = $def->info_content_sets['Flow'];
+            $this->fake_elements['#PCDATA'] = true;
+            $this->init = true;
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php
new file mode 100644 (file)
index 0000000..3e4a0f2
--- /dev/null
@@ -0,0 +1,224 @@
+<?php
+
+/**
+ * Definition for tables.  The general idea is to extract out all of the
+ * essential bits, and then reconstruct it later.
+ *
+ * This is a bit confusing, because the DTDs and the W3C
+ * validators seem to disagree on the appropriate definition. The
+ * DTD claims:
+ *
+ *      (CAPTION?, (COL*|COLGROUP*), THEAD?, TFOOT?, TBODY+)
+ *
+ * But actually, the HTML4 spec then has this to say:
+ *
+ *      The TBODY start tag is always required except when the table
+ *      contains only one table body and no table head or foot sections.
+ *      The TBODY end tag may always be safely omitted.
+ *
+ * So the DTD is kind of wrong.  The validator is, unfortunately, kind
+ * of on crack.
+ *
+ * The definition changed again in XHTML1.1; and in my opinion, this
+ * formulation makes the most sense.
+ *
+ *      caption?, ( col* | colgroup* ), (( thead?, tfoot?, tbody+ ) | ( tr+ ))
+ *
+ * Essentially, we have two modes: thead/tfoot/tbody mode, and tr mode.
+ * If we encounter a thead, tfoot or tbody, we are placed in the former
+ * mode, and we *must* wrap any stray tr segments with a tbody. But if
+ * we don't run into any of them, just have tr tags is OK.
+ */
+class HTMLPurifier_ChildDef_Table extends HTMLPurifier_ChildDef
+{
+    /**
+     * @type bool
+     */
+    public $allow_empty = false;
+
+    /**
+     * @type string
+     */
+    public $type = 'table';
+
+    /**
+     * @type array
+     */
+    public $elements = array(
+        'tr' => true,
+        'tbody' => true,
+        'thead' => true,
+        'tfoot' => true,
+        'caption' => true,
+        'colgroup' => true,
+        'col' => true
+    );
+
+    public function __construct()
+    {
+    }
+
+    /**
+     * @param array $children
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function validateChildren($children, $config, $context)
+    {
+        if (empty($children)) {
+            return false;
+        }
+
+        // only one of these elements is allowed in a table
+        $caption = false;
+        $thead = false;
+        $tfoot = false;
+
+        // whitespace
+        $initial_ws = array();
+        $after_caption_ws = array();
+        $after_thead_ws = array();
+        $after_tfoot_ws = array();
+
+        // as many of these as you want
+        $cols = array();
+        $content = array();
+
+        $tbody_mode = false; // if true, then we need to wrap any stray
+                             // <tr>s with a <tbody>.
+
+        $ws_accum =& $initial_ws;
+
+        foreach ($children as $node) {
+            if ($node instanceof HTMLPurifier_Node_Comment) {
+                $ws_accum[] = $node;
+                continue;
+            }
+            switch ($node->name) {
+            case 'tbody':
+                $tbody_mode = true;
+                // fall through
+            case 'tr':
+                $content[] = $node;
+                $ws_accum =& $content;
+                break;
+            case 'caption':
+                // there can only be one caption!
+                if ($caption !== false)  break;
+                $caption = $node;
+                $ws_accum =& $after_caption_ws;
+                break;
+            case 'thead':
+                $tbody_mode = true;
+                // XXX This breaks rendering properties with
+                // Firefox, which never floats a <thead> to
+                // the top. Ever. (Our scheme will float the
+                // first <thead> to the top.)  So maybe
+                // <thead>s that are not first should be
+                // turned into <tbody>? Very tricky, indeed.
+                if ($thead === false) {
+                    $thead = $node;
+                    $ws_accum =& $after_thead_ws;
+                } else {
+                    // Oops, there's a second one! What
+                    // should we do?  Current behavior is to
+                    // transmutate the first and last entries into
+                    // tbody tags, and then put into content.
+                    // Maybe a better idea is to *attach
+                    // it* to the existing thead or tfoot?
+                    // We don't do this, because Firefox
+                    // doesn't float an extra tfoot to the
+                    // bottom like it does for the first one.
+                    $node->name = 'tbody';
+                    $content[] = $node;
+                    $ws_accum =& $content;
+                }
+                break;
+            case 'tfoot':
+                // see above for some aveats
+                $tbody_mode = true;
+                if ($tfoot === false) {
+                    $tfoot = $node;
+                    $ws_accum =& $after_tfoot_ws;
+                } else {
+                    $node->name = 'tbody';
+                    $content[] = $node;
+                    $ws_accum =& $content;
+                }
+                break;
+            case 'colgroup':
+            case 'col':
+                $cols[] = $node;
+                $ws_accum =& $cols;
+                break;
+            case '#PCDATA':
+                // How is whitespace handled? We treat is as sticky to
+                // the *end* of the previous element. So all of the
+                // nonsense we have worked on is to keep things
+                // together.
+                if (!empty($node->is_whitespace)) {
+                    $ws_accum[] = $node;
+                }
+                break;
+            }
+        }
+
+        if (empty($content)) {
+            return false;
+        }
+
+        $ret = $initial_ws;
+        if ($caption !== false) {
+            $ret[] = $caption;
+            $ret = array_merge($ret, $after_caption_ws);
+        }
+        if ($cols !== false) {
+            $ret = array_merge($ret, $cols);
+        }
+        if ($thead !== false) {
+            $ret[] = $thead;
+            $ret = array_merge($ret, $after_thead_ws);
+        }
+        if ($tfoot !== false) {
+            $ret[] = $tfoot;
+            $ret = array_merge($ret, $after_tfoot_ws);
+        }
+
+        if ($tbody_mode) {
+            // we have to shuffle tr into tbody
+            $current_tr_tbody = null;
+
+            foreach($content as $node) {
+                switch ($node->name) {
+                case 'tbody':
+                    $current_tr_tbody = null;
+                    $ret[] = $node;
+                    break;
+                case 'tr':
+                    if ($current_tr_tbody === null) {
+                        $current_tr_tbody = new HTMLPurifier_Node_Element('tbody');
+                        $ret[] = $current_tr_tbody;
+                    }
+                    $current_tr_tbody->children[] = $node;
+                    break;
+                case '#PCDATA':
+                    assert($node->is_whitespace);
+                    if ($current_tr_tbody === null) {
+                        $ret[] = $node;
+                    } else {
+                        $current_tr_tbody->children[] = $node;
+                    }
+                    break;
+                }
+            }
+        } else {
+            $ret = array_merge($ret, $content);
+        }
+
+        return $ret;
+
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Config.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Config.php
new file mode 100644 (file)
index 0000000..2b2db0c
--- /dev/null
@@ -0,0 +1,920 @@
+<?php
+
+/**
+ * Configuration object that triggers customizable behavior.
+ *
+ * @warning This class is strongly defined: that means that the class
+ *          will fail if an undefined directive is retrieved or set.
+ *
+ * @note Many classes that could (although many times don't) use the
+ *       configuration object make it a mandatory parameter.  This is
+ *       because a configuration object should always be forwarded,
+ *       otherwise, you run the risk of missing a parameter and then
+ *       being stumped when a configuration directive doesn't work.
+ *
+ * @todo Reconsider some of the public member variables
+ */
+class HTMLPurifier_Config
+{
+
+    /**
+     * HTML Purifier's version
+     * @type string
+     */
+    public $version = '4.7.0';
+
+    /**
+     * Whether or not to automatically finalize
+     * the object if a read operation is done.
+     * @type bool
+     */
+    public $autoFinalize = true;
+
+    // protected member variables
+
+    /**
+     * Namespace indexed array of serials for specific namespaces.
+     * @see getSerial() for more info.
+     * @type string[]
+     */
+    protected $serials = array();
+
+    /**
+     * Serial for entire configuration object.
+     * @type string
+     */
+    protected $serial;
+
+    /**
+     * Parser for variables.
+     * @type HTMLPurifier_VarParser_Flexible
+     */
+    protected $parser = null;
+
+    /**
+     * Reference HTMLPurifier_ConfigSchema for value checking.
+     * @type HTMLPurifier_ConfigSchema
+     * @note This is public for introspective purposes. Please don't
+     *       abuse!
+     */
+    public $def;
+
+    /**
+     * Indexed array of definitions.
+     * @type HTMLPurifier_Definition[]
+     */
+    protected $definitions;
+
+    /**
+     * Whether or not config is finalized.
+     * @type bool
+     */
+    protected $finalized = false;
+
+    /**
+     * Property list containing configuration directives.
+     * @type array
+     */
+    protected $plist;
+
+    /**
+     * Whether or not a set is taking place due to an alias lookup.
+     * @type bool
+     */
+    private $aliasMode;
+
+    /**
+     * Set to false if you do not want line and file numbers in errors.
+     * (useful when unit testing).  This will also compress some errors
+     * and exceptions.
+     * @type bool
+     */
+    public $chatty = true;
+
+    /**
+     * Current lock; only gets to this namespace are allowed.
+     * @type string
+     */
+    private $lock;
+
+    /**
+     * Constructor
+     * @param HTMLPurifier_ConfigSchema $definition ConfigSchema that defines
+     * what directives are allowed.
+     * @param HTMLPurifier_PropertyList $parent
+     */
+    public function __construct($definition, $parent = null)
+    {
+        $parent = $parent ? $parent : $definition->defaultPlist;
+        $this->plist = new HTMLPurifier_PropertyList($parent);
+        $this->def = $definition; // keep a copy around for checking
+        $this->parser = new HTMLPurifier_VarParser_Flexible();
+    }
+
+    /**
+     * Convenience constructor that creates a config object based on a mixed var
+     * @param mixed $config Variable that defines the state of the config
+     *                      object. Can be: a HTMLPurifier_Config() object,
+     *                      an array of directives based on loadArray(),
+     *                      or a string filename of an ini file.
+     * @param HTMLPurifier_ConfigSchema $schema Schema object
+     * @return HTMLPurifier_Config Configured object
+     */
+    public static function create($config, $schema = null)
+    {
+        if ($config instanceof HTMLPurifier_Config) {
+            // pass-through
+            return $config;
+        }
+        if (!$schema) {
+            $ret = HTMLPurifier_Config::createDefault();
+        } else {
+            $ret = new HTMLPurifier_Config($schema);
+        }
+        if (is_string($config)) {
+            $ret->loadIni($config);
+        } elseif (is_array($config)) $ret->loadArray($config);
+        return $ret;
+    }
+
+    /**
+     * Creates a new config object that inherits from a previous one.
+     * @param HTMLPurifier_Config $config Configuration object to inherit from.
+     * @return HTMLPurifier_Config object with $config as its parent.
+     */
+    public static function inherit(HTMLPurifier_Config $config)
+    {
+        return new HTMLPurifier_Config($config->def, $config->plist);
+    }
+
+    /**
+     * Convenience constructor that creates a default configuration object.
+     * @return HTMLPurifier_Config default object.
+     */
+    public static function createDefault()
+    {
+        $definition = HTMLPurifier_ConfigSchema::instance();
+        $config = new HTMLPurifier_Config($definition);
+        return $config;
+    }
+
+    /**
+     * Retrieves a value from the configuration.
+     *
+     * @param string $key String key
+     * @param mixed $a
+     *
+     * @return mixed
+     */
+    public function get($key, $a = null)
+    {
+        if ($a !== null) {
+            $this->triggerError(
+                "Using deprecated API: use \$config->get('$key.$a') instead",
+                E_USER_WARNING
+            );
+            $key = "$key.$a";
+        }
+        if (!$this->finalized) {
+            $this->autoFinalize();
+        }
+        if (!isset($this->def->info[$key])) {
+            // can't add % due to SimpleTest bug
+            $this->triggerError(
+                'Cannot retrieve value of undefined directive ' . htmlspecialchars($key),
+                E_USER_WARNING
+            );
+            return;
+        }
+        if (isset($this->def->info[$key]->isAlias)) {
+            $d = $this->def->info[$key];
+            $this->triggerError(
+                'Cannot get value from aliased directive, use real name ' . $d->key,
+                E_USER_ERROR
+            );
+            return;
+        }
+        if ($this->lock) {
+            list($ns) = explode('.', $key);
+            if ($ns !== $this->lock) {
+                $this->triggerError(
+                    'Cannot get value of namespace ' . $ns . ' when lock for ' .
+                    $this->lock .
+                    ' is active, this probably indicates a Definition setup method ' .
+                    'is accessing directives that are not within its namespace',
+                    E_USER_ERROR
+                );
+                return;
+            }
+        }
+        return $this->plist->get($key);
+    }
+
+    /**
+     * Retrieves an array of directives to values from a given namespace
+     *
+     * @param string $namespace String namespace
+     *
+     * @return array
+     */
+    public function getBatch($namespace)
+    {
+        if (!$this->finalized) {
+            $this->autoFinalize();
+        }
+        $full = $this->getAll();
+        if (!isset($full[$namespace])) {
+            $this->triggerError(
+                'Cannot retrieve undefined namespace ' .
+                htmlspecialchars($namespace),
+                E_USER_WARNING
+            );
+            return;
+        }
+        return $full[$namespace];
+    }
+
+    /**
+     * Returns a SHA-1 signature of a segment of the configuration object
+     * that uniquely identifies that particular configuration
+     *
+     * @param string $namespace Namespace to get serial for
+     *
+     * @return string
+     * @note Revision is handled specially and is removed from the batch
+     *       before processing!
+     */
+    public function getBatchSerial($namespace)
+    {
+        if (empty($this->serials[$namespace])) {
+            $batch = $this->getBatch($namespace);
+            unset($batch['DefinitionRev']);
+            $this->serials[$namespace] = sha1(serialize($batch));
+        }
+        return $this->serials[$namespace];
+    }
+
+    /**
+     * Returns a SHA-1 signature for the entire configuration object
+     * that uniquely identifies that particular configuration
+     *
+     * @return string
+     */
+    public function getSerial()
+    {
+        if (empty($this->serial)) {
+            $this->serial = sha1(serialize($this->getAll()));
+        }
+        return $this->serial;
+    }
+
+    /**
+     * Retrieves all directives, organized by namespace
+     *
+     * @warning This is a pretty inefficient function, avoid if you can
+     */
+    public function getAll()
+    {
+        if (!$this->finalized) {
+            $this->autoFinalize();
+        }
+        $ret = array();
+        foreach ($this->plist->squash() as $name => $value) {
+            list($ns, $key) = explode('.', $name, 2);
+            $ret[$ns][$key] = $value;
+        }
+        return $ret;
+    }
+
+    /**
+     * Sets a value to configuration.
+     *
+     * @param string $key key
+     * @param mixed $value value
+     * @param mixed $a
+     */
+    public function set($key, $value, $a = null)
+    {
+        if (strpos($key, '.') === false) {
+            $namespace = $key;
+            $directive = $value;
+            $value = $a;
+            $key = "$key.$directive";
+            $this->triggerError("Using deprecated API: use \$config->set('$key', ...) instead", E_USER_NOTICE);
+        } else {
+            list($namespace) = explode('.', $key);
+        }
+        if ($this->isFinalized('Cannot set directive after finalization')) {
+            return;
+        }
+        if (!isset($this->def->info[$key])) {
+            $this->triggerError(
+                'Cannot set undefined directive ' . htmlspecialchars($key) . ' to value',
+                E_USER_WARNING
+            );
+            return;
+        }
+        $def = $this->def->info[$key];
+
+        if (isset($def->isAlias)) {
+            if ($this->aliasMode) {
+                $this->triggerError(
+                    'Double-aliases not allowed, please fix '.
+                    'ConfigSchema bug with' . $key,
+                    E_USER_ERROR
+                );
+                return;
+            }
+            $this->aliasMode = true;
+            $this->set($def->key, $value);
+            $this->aliasMode = false;
+            $this->triggerError("$key is an alias, preferred directive name is {$def->key}", E_USER_NOTICE);
+            return;
+        }
+
+        // Raw type might be negative when using the fully optimized form
+        // of stdclass, which indicates allow_null == true
+        $rtype = is_int($def) ? $def : $def->type;
+        if ($rtype < 0) {
+            $type = -$rtype;
+            $allow_null = true;
+        } else {
+            $type = $rtype;
+            $allow_null = isset($def->allow_null);
+        }
+
+        try {
+            $value = $this->parser->parse($value, $type, $allow_null);
+        } catch (HTMLPurifier_VarParserException $e) {
+            $this->triggerError(
+                'Value for ' . $key . ' is of invalid type, should be ' .
+                HTMLPurifier_VarParser::getTypeName($type),
+                E_USER_WARNING
+            );
+            return;
+        }
+        if (is_string($value) && is_object($def)) {
+            // resolve value alias if defined
+            if (isset($def->aliases[$value])) {
+                $value = $def->aliases[$value];
+            }
+            // check to see if the value is allowed
+            if (isset($def->allowed) && !isset($def->allowed[$value])) {
+                $this->triggerError(
+                    'Value not supported, valid values are: ' .
+                    $this->_listify($def->allowed),
+                    E_USER_WARNING
+                );
+                return;
+            }
+        }
+        $this->plist->set($key, $value);
+
+        // reset definitions if the directives they depend on changed
+        // this is a very costly process, so it's discouraged
+        // with finalization
+        if ($namespace == 'HTML' || $namespace == 'CSS' || $namespace == 'URI') {
+            $this->definitions[$namespace] = null;
+        }
+
+        $this->serials[$namespace] = false;
+    }
+
+    /**
+     * Convenience function for error reporting
+     *
+     * @param array $lookup
+     *
+     * @return string
+     */
+    private function _listify($lookup)
+    {
+        $list = array();
+        foreach ($lookup as $name => $b) {
+            $list[] = $name;
+        }
+        return implode(', ', $list);
+    }
+
+    /**
+     * Retrieves object reference to the HTML definition.
+     *
+     * @param bool $raw Return a copy that has not been setup yet. Must be
+     *             called before it's been setup, otherwise won't work.
+     * @param bool $optimized If true, this method may return null, to
+     *             indicate that a cached version of the modified
+     *             definition object is available and no further edits
+     *             are necessary.  Consider using
+     *             maybeGetRawHTMLDefinition, which is more explicitly
+     *             named, instead.
+     *
+     * @return HTMLPurifier_HTMLDefinition
+     */
+    public function getHTMLDefinition($raw = false, $optimized = false)
+    {
+        return $this->getDefinition('HTML', $raw, $optimized);
+    }
+
+    /**
+     * Retrieves object reference to the CSS definition
+     *
+     * @param bool $raw Return a copy that has not been setup yet. Must be
+     *             called before it's been setup, otherwise won't work.
+     * @param bool $optimized If true, this method may return null, to
+     *             indicate that a cached version of the modified
+     *             definition object is available and no further edits
+     *             are necessary.  Consider using
+     *             maybeGetRawCSSDefinition, which is more explicitly
+     *             named, instead.
+     *
+     * @return HTMLPurifier_CSSDefinition
+     */
+    public function getCSSDefinition($raw = false, $optimized = false)
+    {
+        return $this->getDefinition('CSS', $raw, $optimized);
+    }
+
+    /**
+     * Retrieves object reference to the URI definition
+     *
+     * @param bool $raw Return a copy that has not been setup yet. Must be
+     *             called before it's been setup, otherwise won't work.
+     * @param bool $optimized If true, this method may return null, to
+     *             indicate that a cached version of the modified
+     *             definition object is available and no further edits
+     *             are necessary.  Consider using
+     *             maybeGetRawURIDefinition, which is more explicitly
+     *             named, instead.
+     *
+     * @return HTMLPurifier_URIDefinition
+     */
+    public function getURIDefinition($raw = false, $optimized = false)
+    {
+        return $this->getDefinition('URI', $raw, $optimized);
+    }
+
+    /**
+     * Retrieves a definition
+     *
+     * @param string $type Type of definition: HTML, CSS, etc
+     * @param bool $raw Whether or not definition should be returned raw
+     * @param bool $optimized Only has an effect when $raw is true.  Whether
+     *        or not to return null if the result is already present in
+     *        the cache.  This is off by default for backwards
+     *        compatibility reasons, but you need to do things this
+     *        way in order to ensure that caching is done properly.
+     *        Check out enduser-customize.html for more details.
+     *        We probably won't ever change this default, as much as the
+     *        maybe semantics is the "right thing to do."
+     *
+     * @throws HTMLPurifier_Exception
+     * @return HTMLPurifier_Definition
+     */
+    public function getDefinition($type, $raw = false, $optimized = false)
+    {
+        if ($optimized && !$raw) {
+            throw new HTMLPurifier_Exception("Cannot set optimized = true when raw = false");
+        }
+        if (!$this->finalized) {
+            $this->autoFinalize();
+        }
+        // temporarily suspend locks, so we can handle recursive definition calls
+        $lock = $this->lock;
+        $this->lock = null;
+        $factory = HTMLPurifier_DefinitionCacheFactory::instance();
+        $cache = $factory->create($type, $this);
+        $this->lock = $lock;
+        if (!$raw) {
+            // full definition
+            // ---------------
+            // check if definition is in memory
+            if (!empty($this->definitions[$type])) {
+                $def = $this->definitions[$type];
+                // check if the definition is setup
+                if ($def->setup) {
+                    return $def;
+                } else {
+                    $def->setup($this);
+                    if ($def->optimized) {
+                        $cache->add($def, $this);
+                    }
+                    return $def;
+                }
+            }
+            // check if definition is in cache
+            $def = $cache->get($this);
+            if ($def) {
+                // definition in cache, save to memory and return it
+                $this->definitions[$type] = $def;
+                return $def;
+            }
+            // initialize it
+            $def = $this->initDefinition($type);
+            // set it up
+            $this->lock = $type;
+            $def->setup($this);
+            $this->lock = null;
+            // save in cache
+            $cache->add($def, $this);
+            // return it
+            return $def;
+        } else {
+            // raw definition
+            // --------------
+            // check preconditions
+            $def = null;
+            if ($optimized) {
+                if (is_null($this->get($type . '.DefinitionID'))) {
+                    // fatally error out if definition ID not set
+                    throw new HTMLPurifier_Exception(
+                        "Cannot retrieve raw version without specifying %$type.DefinitionID"
+                    );
+                }
+            }
+            if (!empty($this->definitions[$type])) {
+                $def = $this->definitions[$type];
+                if ($def->setup && !$optimized) {
+                    $extra = $this->chatty ?
+                        " (try moving this code block earlier in your initialization)" :
+                        "";
+                    throw new HTMLPurifier_Exception(
+                        "Cannot retrieve raw definition after it has already been setup" .
+                        $extra
+                    );
+                }
+                if ($def->optimized === null) {
+                    $extra = $this->chatty ? " (try flushing your cache)" : "";
+                    throw new HTMLPurifier_Exception(
+                        "Optimization status of definition is unknown" . $extra
+                    );
+                }
+                if ($def->optimized !== $optimized) {
+                    $msg = $optimized ? "optimized" : "unoptimized";
+                    $extra = $this->chatty ?
+                        " (this backtrace is for the first inconsistent call, which was for a $msg raw definition)"
+                        : "";
+                    throw new HTMLPurifier_Exception(
+                        "Inconsistent use of optimized and unoptimized raw definition retrievals" . $extra
+                    );
+                }
+            }
+            // check if definition was in memory
+            if ($def) {
+                if ($def->setup) {
+                    // invariant: $optimized === true (checked above)
+                    return null;
+                } else {
+                    return $def;
+                }
+            }
+            // if optimized, check if definition was in cache
+            // (because we do the memory check first, this formulation
+            // is prone to cache slamming, but I think
+            // guaranteeing that either /all/ of the raw
+            // setup code or /none/ of it is run is more important.)
+            if ($optimized) {
+                // This code path only gets run once; once we put
+                // something in $definitions (which is guaranteed by the
+                // trailing code), we always short-circuit above.
+                $def = $cache->get($this);
+                if ($def) {
+                    // save the full definition for later, but don't
+                    // return it yet
+                    $this->definitions[$type] = $def;
+                    return null;
+                }
+            }
+            // check invariants for creation
+            if (!$optimized) {
+                if (!is_null($this->get($type . '.DefinitionID'))) {
+                    if ($this->chatty) {
+                        $this->triggerError(
+                            'Due to a documentation error in previous version of HTML Purifier, your ' .
+                            'definitions are not being cached.  If this is OK, you can remove the ' .
+                            '%$type.DefinitionRev and %$type.DefinitionID declaration.  Otherwise, ' .
+                            'modify your code to use maybeGetRawDefinition, and test if the returned ' .
+                            'value is null before making any edits (if it is null, that means that a ' .
+                            'cached version is available, and no raw operations are necessary).  See ' .
+                            '<a href="http://htmlpurifier.org/docs/enduser-customize.html#optimized">' .
+                            'Customize</a> for more details',
+                            E_USER_WARNING
+                        );
+                    } else {
+                        $this->triggerError(
+                            "Useless DefinitionID declaration",
+                            E_USER_WARNING
+                        );
+                    }
+                }
+            }
+            // initialize it
+            $def = $this->initDefinition($type);
+            $def->optimized = $optimized;
+            return $def;
+        }
+        throw new HTMLPurifier_Exception("The impossible happened!");
+    }
+
+    /**
+     * Initialise definition
+     *
+     * @param string $type What type of definition to create
+     *
+     * @return HTMLPurifier_CSSDefinition|HTMLPurifier_HTMLDefinition|HTMLPurifier_URIDefinition
+     * @throws HTMLPurifier_Exception
+     */
+    private function initDefinition($type)
+    {
+        // quick checks failed, let's create the object
+        if ($type == 'HTML') {
+            $def = new HTMLPurifier_HTMLDefinition();
+        } elseif ($type == 'CSS') {
+            $def = new HTMLPurifier_CSSDefinition();
+        } elseif ($type == 'URI') {
+            $def = new HTMLPurifier_URIDefinition();
+        } else {
+            throw new HTMLPurifier_Exception(
+                "Definition of $type type not supported"
+            );
+        }
+        $this->definitions[$type] = $def;
+        return $def;
+    }
+
+    public function maybeGetRawDefinition($name)
+    {
+        return $this->getDefinition($name, true, true);
+    }
+
+    /**
+     * @return HTMLPurifier_HTMLDefinition
+     */
+    public function maybeGetRawHTMLDefinition()
+    {
+        return $this->getDefinition('HTML', true, true);
+    }
+    
+    /**
+     * @return HTMLPurifier_CSSDefinition
+     */
+    public function maybeGetRawCSSDefinition()
+    {
+        return $this->getDefinition('CSS', true, true);
+    }
+    
+    /**
+     * @return HTMLPurifier_URIDefinition
+     */
+    public function maybeGetRawURIDefinition()
+    {
+        return $this->getDefinition('URI', true, true);
+    }
+
+    /**
+     * Loads configuration values from an array with the following structure:
+     * Namespace.Directive => Value
+     *
+     * @param array $config_array Configuration associative array
+     */
+    public function loadArray($config_array)
+    {
+        if ($this->isFinalized('Cannot load directives after finalization')) {
+            return;
+        }
+        foreach ($config_array as $key => $value) {
+            $key = str_replace('_', '.', $key);
+            if (strpos($key, '.') !== false) {
+                $this->set($key, $value);
+            } else {
+                $namespace = $key;
+                $namespace_values = $value;
+                foreach ($namespace_values as $directive => $value2) {
+                    $this->set($namespace .'.'. $directive, $value2);
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns a list of array(namespace, directive) for all directives
+     * that are allowed in a web-form context as per an allowed
+     * namespaces/directives list.
+     *
+     * @param array $allowed List of allowed namespaces/directives
+     * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy
+     *
+     * @return array
+     */
+    public static function getAllowedDirectivesForForm($allowed, $schema = null)
+    {
+        if (!$schema) {
+            $schema = HTMLPurifier_ConfigSchema::instance();
+        }
+        if ($allowed !== true) {
+            if (is_string($allowed)) {
+                $allowed = array($allowed);
+            }
+            $allowed_ns = array();
+            $allowed_directives = array();
+            $blacklisted_directives = array();
+            foreach ($allowed as $ns_or_directive) {
+                if (strpos($ns_or_directive, '.') !== false) {
+                    // directive
+                    if ($ns_or_directive[0] == '-') {
+                        $blacklisted_directives[substr($ns_or_directive, 1)] = true;
+                    } else {
+                        $allowed_directives[$ns_or_directive] = true;
+                    }
+                } else {
+                    // namespace
+                    $allowed_ns[$ns_or_directive] = true;
+                }
+            }
+        }
+        $ret = array();
+        foreach ($schema->info as $key => $def) {
+            list($ns, $directive) = explode('.', $key, 2);
+            if ($allowed !== true) {
+                if (isset($blacklisted_directives["$ns.$directive"])) {
+                    continue;
+                }
+                if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) {
+                    continue;
+                }
+            }
+            if (isset($def->isAlias)) {
+                continue;
+            }
+            if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') {
+                continue;
+            }
+            $ret[] = array($ns, $directive);
+        }
+        return $ret;
+    }
+
+    /**
+     * Loads configuration values from $_GET/$_POST that were posted
+     * via ConfigForm
+     *
+     * @param array $array $_GET or $_POST array to import
+     * @param string|bool $index Index/name that the config variables are in
+     * @param array|bool $allowed List of allowed namespaces/directives
+     * @param bool $mq_fix Boolean whether or not to enable magic quotes fix
+     * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy
+     *
+     * @return mixed
+     */
+    public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null)
+    {
+        $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $schema);
+        $config = HTMLPurifier_Config::create($ret, $schema);
+        return $config;
+    }
+
+    /**
+     * Merges in configuration values from $_GET/$_POST to object. NOT STATIC.
+     *
+     * @param array $array $_GET or $_POST array to import
+     * @param string|bool $index Index/name that the config variables are in
+     * @param array|bool $allowed List of allowed namespaces/directives
+     * @param bool $mq_fix Boolean whether or not to enable magic quotes fix
+     */
+    public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true)
+    {
+         $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $this->def);
+         $this->loadArray($ret);
+    }
+
+    /**
+     * Prepares an array from a form into something usable for the more
+     * strict parts of HTMLPurifier_Config
+     *
+     * @param array $array $_GET or $_POST array to import
+     * @param string|bool $index Index/name that the config variables are in
+     * @param array|bool $allowed List of allowed namespaces/directives
+     * @param bool $mq_fix Boolean whether or not to enable magic quotes fix
+     * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy
+     *
+     * @return array
+     */
+    public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null)
+    {
+        if ($index !== false) {
+            $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array();
+        }
+        $mq = $mq_fix && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc();
+
+        $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $schema);
+        $ret = array();
+        foreach ($allowed as $key) {
+            list($ns, $directive) = $key;
+            $skey = "$ns.$directive";
+            if (!empty($array["Null_$skey"])) {
+                $ret[$ns][$directive] = null;
+                continue;
+            }
+            if (!isset($array[$skey])) {
+                continue;
+            }
+            $value = $mq ? stripslashes($array[$skey]) : $array[$skey];
+            $ret[$ns][$directive] = $value;
+        }
+        return $ret;
+    }
+
+    /**
+     * Loads configuration values from an ini file
+     *
+     * @param string $filename Name of ini file
+     */
+    public function loadIni($filename)
+    {
+        if ($this->isFinalized('Cannot load directives after finalization')) {
+            return;
+        }
+        $array = parse_ini_file($filename, true);
+        $this->loadArray($array);
+    }
+
+    /**
+     * Checks whether or not the configuration object is finalized.
+     *
+     * @param string|bool $error String error message, or false for no error
+     *
+     * @return bool
+     */
+    public function isFinalized($error = false)
+    {
+        if ($this->finalized && $error) {
+            $this->triggerError($error, E_USER_ERROR);
+        }
+        return $this->finalized;
+    }
+
+    /**
+     * Finalizes configuration only if auto finalize is on and not
+     * already finalized
+     */
+    public function autoFinalize()
+    {
+        if ($this->autoFinalize) {
+            $this->finalize();
+        } else {
+            $this->plist->squash(true);
+        }
+    }
+
+    /**
+     * Finalizes a configuration object, prohibiting further change
+     */
+    public function finalize()
+    {
+        $this->finalized = true;
+        $this->parser = null;
+    }
+
+    /**
+     * Produces a nicely formatted error message by supplying the
+     * stack frame information OUTSIDE of HTMLPurifier_Config.
+     *
+     * @param string $msg An error message
+     * @param int $no An error number
+     */
+    protected function triggerError($msg, $no)
+    {
+        // determine previous stack frame
+        $extra = '';
+        if ($this->chatty) {
+            $trace = debug_backtrace();
+            // zip(tail(trace), trace) -- but PHP is not Haskell har har
+            for ($i = 0, $c = count($trace); $i < $c - 1; $i++) {
+                // XXX this is not correct on some versions of HTML Purifier
+                if ($trace[$i + 1]['class'] === 'HTMLPurifier_Config') {
+                    continue;
+                }
+                $frame = $trace[$i];
+                $extra = " invoked on line {$frame['line']} in file {$frame['file']}";
+                break;
+            }
+        }
+        trigger_error($msg . $extra, $no);
+    }
+
+    /**
+     * Returns a serialized form of the configuration object that can
+     * be reconstituted.
+     *
+     * @return string
+     */
+    public function serialize()
+    {
+        $this->getDefinition('HTML');
+        $this->getDefinition('CSS');
+        $this->getDefinition('URI');
+        return serialize($this);
+    }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema.php
new file mode 100644 (file)
index 0000000..bfbb0f9
--- /dev/null
@@ -0,0 +1,176 @@
+<?php
+
+/**
+ * Configuration definition, defines directives and their defaults.
+ */
+class HTMLPurifier_ConfigSchema
+{
+    /**
+     * Defaults of the directives and namespaces.
+     * @type array
+     * @note This shares the exact same structure as HTMLPurifier_Config::$conf
+     */
+    public $defaults = array();
+
+    /**
+     * The default property list. Do not edit this property list.
+     * @type array
+     */
+    public $defaultPlist;
+
+    /**
+     * Definition of the directives.
+     * The structure of this is:
+     *
+     *  array(
+     *      'Namespace' => array(
+     *          'Directive' => new stdclass(),
+     *      )
+     *  )
+     *
+     * The stdclass may have the following properties:
+     *
+     *  - If isAlias isn't set:
+     *      - type: Integer type of directive, see HTMLPurifier_VarParser for definitions
+     *      - allow_null: If set, this directive allows null values
+     *      - aliases: If set, an associative array of value aliases to real values
+     *      - allowed: If set, a lookup array of allowed (string) values
+     *  - If isAlias is set:
+     *      - namespace: Namespace this directive aliases to
+     *      - name: Directive name this directive aliases to
+     *
+     * In certain degenerate cases, stdclass will actually be an integer. In
+     * that case, the value is equivalent to an stdclass with the type
+     * property set to the integer. If the integer is negative, type is
+     * equal to the absolute value of integer, and allow_null is true.
+     *
+     * This class is friendly with HTMLPurifier_Config. If you need introspection
+     * about the schema, you're better of using the ConfigSchema_Interchange,
+     * which uses more memory but has much richer information.
+     * @type array
+     */
+    public $info = array();
+
+    /**
+     * Application-wide singleton
+     * @type HTMLPurifier_ConfigSchema
+     */
+    protected static $singleton;
+
+    public function __construct()
+    {
+        $this->defaultPlist = new HTMLPurifier_PropertyList();
+    }
+
+    /**
+     * Unserializes the default ConfigSchema.
+     * @return HTMLPurifier_ConfigSchema
+     */
+    public static function makeFromSerial()
+    {
+        $contents = file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema.ser');
+        $r = unserialize($contents);
+        if (!$r) {
+            $hash = sha1($contents);
+            trigger_error("Unserialization of configuration schema failed, sha1 of file was $hash", E_USER_ERROR);
+        }
+        return $r;
+    }
+
+    /**
+     * Retrieves an instance of the application-wide configuration definition.
+     * @param HTMLPurifier_ConfigSchema $prototype
+     * @return HTMLPurifier_ConfigSchema
+     */
+    public static function instance($prototype = null)
+    {
+        if ($prototype !== null) {
+            HTMLPurifier_ConfigSchema::$singleton = $prototype;
+        } elseif (HTMLPurifier_ConfigSchema::$singleton === null || $prototype === true) {
+            HTMLPurifier_ConfigSchema::$singleton = HTMLPurifier_ConfigSchema::makeFromSerial();
+        }
+        return HTMLPurifier_ConfigSchema::$singleton;
+    }
+
+    /**
+     * Defines a directive for configuration
+     * @warning Will fail of directive's namespace is defined.
+     * @warning This method's signature is slightly different from the legacy
+     *          define() static method! Beware!
+     * @param string $key Name of directive
+     * @param mixed $default Default value of directive
+     * @param string $type Allowed type of the directive. See
+     *      HTMLPurifier_DirectiveDef::$type for allowed values
+     * @param bool $allow_null Whether or not to allow null values
+     */
+    public function add($key, $default, $type, $allow_null)
+    {
+        $obj = new stdclass();
+        $obj->type = is_int($type) ? $type : HTMLPurifier_VarParser::$types[$type];
+        if ($allow_null) {
+            $obj->allow_null = true;
+        }
+        $this->info[$key] = $obj;
+        $this->defaults[$key] = $default;
+        $this->defaultPlist->set($key, $default);
+    }
+
+    /**
+     * Defines a directive value alias.
+     *
+     * Directive value aliases are convenient for developers because it lets
+     * them set a directive to several values and get the same result.
+     * @param string $key Name of Directive
+     * @param array $aliases Hash of aliased values to the real alias
+     */
+    public function addValueAliases($key, $aliases)
+    {
+        if (!isset($this->info[$key]->aliases)) {
+            $this->info[$key]->aliases = array();
+        }
+        foreach ($aliases as $alias => $real) {
+            $this->info[$key]->aliases[$alias] = $real;
+        }
+    }
+
+    /**
+     * Defines a set of allowed values for a directive.
+     * @warning This is slightly different from the corresponding static
+     *          method definition.
+     * @param string $key Name of directive
+     * @param array $allowed Lookup array of allowed values
+     */
+    public function addAllowedValues($key, $allowed)
+    {
+        $this->info[$key]->allowed = $allowed;
+    }
+
+    /**
+     * Defines a directive alias for backwards compatibility
+     * @param string $key Directive that will be aliased
+     * @param string $new_key Directive that the alias will be to
+     */
+    public function addAlias($key, $new_key)
+    {
+        $obj = new stdclass;
+        $obj->key = $new_key;
+        $obj->isAlias = true;
+        $this->info[$key] = $obj;
+    }
+
+    /**
+     * Replaces any stdclass that only has the type property with type integer.
+     */
+    public function postProcess()
+    {
+        foreach ($this->info as $key => $v) {
+            if (count((array) $v) == 1) {
+                $this->info[$key] = $v->type;
+            } elseif (count((array) $v) == 2 && isset($v->allow_null)) {
+                $this->info[$key] = -$v->type;
+            }
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php
new file mode 100644 (file)
index 0000000..d5906cd
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * Converts HTMLPurifier_ConfigSchema_Interchange to our runtime
+ * representation used to perform checks on user configuration.
+ */
+class HTMLPurifier_ConfigSchema_Builder_ConfigSchema
+{
+
+    /**
+     * @param HTMLPurifier_ConfigSchema_Interchange $interchange
+     * @return HTMLPurifier_ConfigSchema
+     */
+    public function build($interchange)
+    {
+        $schema = new HTMLPurifier_ConfigSchema();
+        foreach ($interchange->directives as $d) {
+            $schema->add(
+                $d->id->key,
+                $d->default,
+                $d->type,
+                $d->typeAllowsNull
+            );
+            if ($d->allowed !== null) {
+                $schema->addAllowedValues(
+                    $d->id->key,
+                    $d->allowed
+                );
+            }
+            foreach ($d->aliases as $alias) {
+                $schema->addAlias(
+                    $alias->key,
+                    $d->id->key
+                );
+            }
+            if ($d->valueAliases !== null) {
+                $schema->addValueAliases(
+                    $d->id->key,
+                    $d->valueAliases
+                );
+            }
+        }
+        $schema->postProcess();
+        return $schema;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/Xml.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/Xml.php
new file mode 100644 (file)
index 0000000..5fa56f7
--- /dev/null
@@ -0,0 +1,144 @@
+<?php
+
+/**
+ * Converts HTMLPurifier_ConfigSchema_Interchange to an XML format,
+ * which can be further processed to generate documentation.
+ */
+class HTMLPurifier_ConfigSchema_Builder_Xml extends XMLWriter
+{
+
+    /**
+     * @type HTMLPurifier_ConfigSchema_Interchange
+     */
+    protected $interchange;
+
+    /**
+     * @type string
+     */
+    private $namespace;
+
+    /**
+     * @param string $html
+     */
+    protected function writeHTMLDiv($html)
+    {
+        $this->startElement('div');
+
+        $purifier = HTMLPurifier::getInstance();
+        $html = $purifier->purify($html);
+        $this->writeAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
+        $this->writeRaw($html);
+
+        $this->endElement(); // div
+    }
+
+    /**
+     * @param mixed $var
+     * @return string
+     */
+    protected function export($var)
+    {
+        if ($var === array()) {
+            return 'array()';
+        }
+        return var_export($var, true);
+    }
+
+    /**
+     * @param HTMLPurifier_ConfigSchema_Interchange $interchange
+     */
+    public function build($interchange)
+    {
+        // global access, only use as last resort
+        $this->interchange = $interchange;
+
+        $this->setIndent(true);
+        $this->startDocument('1.0', 'UTF-8');
+        $this->startElement('configdoc');
+        $this->writeElement('title', $interchange->name);
+
+        foreach ($interchange->directives as $directive) {
+            $this->buildDirective($directive);
+        }
+
+        if ($this->namespace) {
+            $this->endElement();
+        } // namespace
+
+        $this->endElement(); // configdoc
+        $this->flush();
+    }
+
+    /**
+     * @param HTMLPurifier_ConfigSchema_Interchange_Directive $directive
+     */
+    public function buildDirective($directive)
+    {
+        // Kludge, although I suppose having a notion of a "root namespace"
+        // certainly makes things look nicer when documentation is built.
+        // Depends on things being sorted.
+        if (!$this->namespace || $this->namespace !== $directive->id->getRootNamespace()) {
+            if ($this->namespace) {
+                $this->endElement();
+            } // namespace
+            $this->namespace = $directive->id->getRootNamespace();
+            $this->startElement('namespace');
+            $this->writeAttribute('id', $this->namespace);
+            $this->writeElement('name', $this->namespace);
+        }
+
+        $this->startElement('directive');
+        $this->writeAttribute('id', $directive->id->toString());
+
+        $this->writeElement('name', $directive->id->getDirective());
+
+        $this->startElement('aliases');
+        foreach ($directive->aliases as $alias) {
+            $this->writeElement('alias', $alias->toString());
+        }
+        $this->endElement(); // aliases
+
+        $this->startElement('constraints');
+        if ($directive->version) {
+            $this->writeElement('version', $directive->version);
+        }
+        $this->startElement('type');
+        if ($directive->typeAllowsNull) {
+            $this->writeAttribute('allow-null', 'yes');
+        }
+        $this->text($directive->type);
+        $this->endElement(); // type
+        if ($directive->allowed) {
+            $this->startElement('allowed');
+            foreach ($directive->allowed as $value => $x) {
+                $this->writeElement('value', $value);
+            }
+            $this->endElement(); // allowed
+        }
+        $this->writeElement('default', $this->export($directive->default));
+        $this->writeAttribute('xml:space', 'preserve');
+        if ($directive->external) {
+            $this->startElement('external');
+            foreach ($directive->external as $project) {
+                $this->writeElement('project', $project);
+            }
+            $this->endElement();
+        }
+        $this->endElement(); // constraints
+
+        if ($directive->deprecatedVersion) {
+            $this->startElement('deprecated');
+            $this->writeElement('version', $directive->deprecatedVersion);
+            $this->writeElement('use', $directive->deprecatedUse->toString());
+            $this->endElement(); // deprecated
+        }
+
+        $this->startElement('description');
+        $this->writeHTMLDiv($directive->description);
+        $this->endElement(); // description
+
+        $this->endElement(); // directive
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Exception.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Exception.php
new file mode 100644 (file)
index 0000000..2671516
--- /dev/null
@@ -0,0 +1,11 @@
+<?php
+
+/**
+ * Exceptions related to configuration schema
+ */
+class HTMLPurifier_ConfigSchema_Exception extends HTMLPurifier_Exception
+{
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange.php
new file mode 100644 (file)
index 0000000..0e08ae8
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * Generic schema interchange format that can be converted to a runtime
+ * representation (HTMLPurifier_ConfigSchema) or HTML documentation. Members
+ * are completely validated.
+ */
+class HTMLPurifier_ConfigSchema_Interchange
+{
+
+    /**
+     * Name of the application this schema is describing.
+     * @type string
+     */
+    public $name;
+
+    /**
+     * Array of Directive ID => array(directive info)
+     * @type HTMLPurifier_ConfigSchema_Interchange_Directive[]
+     */
+    public $directives = array();
+
+    /**
+     * Adds a directive array to $directives
+     * @param HTMLPurifier_ConfigSchema_Interchange_Directive $directive
+     * @throws HTMLPurifier_ConfigSchema_Exception
+     */
+    public function addDirective($directive)
+    {
+        if (isset($this->directives[$i = $directive->id->toString()])) {
+            throw new HTMLPurifier_ConfigSchema_Exception("Cannot redefine directive '$i'");
+        }
+        $this->directives[$i] = $directive;
+    }
+
+    /**
+     * Convenience function to perform standard validation. Throws exception
+     * on failed validation.
+     */
+    public function validate()
+    {
+        $validator = new HTMLPurifier_ConfigSchema_Validator();
+        return $validator->validate($this);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Directive.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Directive.php
new file mode 100644 (file)
index 0000000..127a39a
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * Interchange component class describing configuration directives.
+ */
+class HTMLPurifier_ConfigSchema_Interchange_Directive
+{
+
+    /**
+     * ID of directive.
+     * @type HTMLPurifier_ConfigSchema_Interchange_Id
+     */
+    public $id;
+
+    /**
+     * Type, e.g. 'integer' or 'istring'.
+     * @type string
+     */
+    public $type;
+
+    /**
+     * Default value, e.g. 3 or 'DefaultVal'.
+     * @type mixed
+     */
+    public $default;
+
+    /**
+     * HTML description.
+     * @type string
+     */
+    public $description;
+
+    /**
+     * Whether or not null is allowed as a value.
+     * @type bool
+     */
+    public $typeAllowsNull = false;
+
+    /**
+     * Lookup table of allowed scalar values.
+     * e.g. array('allowed' => true).
+     * Null if all values are allowed.
+     * @type array
+     */
+    public $allowed;
+
+    /**
+     * List of aliases for the directive.
+     * e.g. array(new HTMLPurifier_ConfigSchema_Interchange_Id('Ns', 'Dir'))).
+     * @type HTMLPurifier_ConfigSchema_Interchange_Id[]
+     */
+    public $aliases = array();
+
+    /**
+     * Hash of value aliases, e.g. array('alt' => 'real'). Null if value
+     * aliasing is disabled (necessary for non-scalar types).
+     * @type array
+     */
+    public $valueAliases;
+
+    /**
+     * Version of HTML Purifier the directive was introduced, e.g. '1.3.1'.
+     * Null if the directive has always existed.
+     * @type string
+     */
+    public $version;
+
+    /**
+     * ID of directive that supercedes this old directive.
+     * Null if not deprecated.
+     * @type HTMLPurifier_ConfigSchema_Interchange_Id
+     */
+    public $deprecatedUse;
+
+    /**
+     * Version of HTML Purifier this directive was deprecated. Null if not
+     * deprecated.
+     * @type string
+     */
+    public $deprecatedVersion;
+
+    /**
+     * List of external projects this directive depends on, e.g. array('CSSTidy').
+     * @type array
+     */
+    public $external = array();
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Id.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Id.php
new file mode 100644 (file)
index 0000000..126f09d
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * Represents a directive ID in the interchange format.
+ */
+class HTMLPurifier_ConfigSchema_Interchange_Id
+{
+
+    /**
+     * @type string
+     */
+    public $key;
+
+    /**
+     * @param string $key
+     */
+    public function __construct($key)
+    {
+        $this->key = $key;
+    }
+
+    /**
+     * @return string
+     * @warning This is NOT magic, to ensure that people don't abuse SPL and
+     *          cause problems for PHP 5.0 support.
+     */
+    public function toString()
+    {
+        return $this->key;
+    }
+
+    /**
+     * @return string
+     */
+    public function getRootNamespace()
+    {
+        return substr($this->key, 0, strpos($this->key, "."));
+    }
+
+    /**
+     * @return string
+     */
+    public function getDirective()
+    {
+        return substr($this->key, strpos($this->key, ".") + 1);
+    }
+
+    /**
+     * @param string $id
+     * @return HTMLPurifier_ConfigSchema_Interchange_Id
+     */
+    public static function make($id)
+    {
+        return new HTMLPurifier_ConfigSchema_Interchange_Id($id);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php
new file mode 100644 (file)
index 0000000..655e6dd
--- /dev/null
@@ -0,0 +1,226 @@
+<?php
+
+class HTMLPurifier_ConfigSchema_InterchangeBuilder
+{
+
+    /**
+     * Used for processing DEFAULT, nothing else.
+     * @type HTMLPurifier_VarParser
+     */
+    protected $varParser;
+
+    /**
+     * @param HTMLPurifier_VarParser $varParser
+     */
+    public function __construct($varParser = null)
+    {
+        $this->varParser = $varParser ? $varParser : new HTMLPurifier_VarParser_Native();
+    }
+
+    /**
+     * @param string $dir
+     * @return HTMLPurifier_ConfigSchema_Interchange
+     */
+    public static function buildFromDirectory($dir = null)
+    {
+        $builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder();
+        $interchange = new HTMLPurifier_ConfigSchema_Interchange();
+        return $builder->buildDir($interchange, $dir);
+    }
+
+    /**
+     * @param HTMLPurifier_ConfigSchema_Interchange $interchange
+     * @param string $dir
+     * @return HTMLPurifier_ConfigSchema_Interchange
+     */
+    public function buildDir($interchange, $dir = null)
+    {
+        if (!$dir) {
+            $dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema';
+        }
+        if (file_exists($dir . '/info.ini')) {
+            $info = parse_ini_file($dir . '/info.ini');
+            $interchange->name = $info['name'];
+        }
+
+        $files = array();
+        $dh = opendir($dir);
+        while (false !== ($file = readdir($dh))) {
+            if (!$file || $file[0] == '.' || strrchr($file, '.') !== '.txt') {
+                continue;
+            }
+            $files[] = $file;
+        }
+        closedir($dh);
+
+        sort($files);
+        foreach ($files as $file) {
+            $this->buildFile($interchange, $dir . '/' . $file);
+        }
+        return $interchange;
+    }
+
+    /**
+     * @param HTMLPurifier_ConfigSchema_Interchange $interchange
+     * @param string $file
+     */
+    public function buildFile($interchange, $file)
+    {
+        $parser = new HTMLPurifier_StringHashParser();
+        $this->build(
+            $interchange,
+            new HTMLPurifier_StringHash($parser->parseFile($file))
+        );
+    }
+
+    /**
+     * Builds an interchange object based on a hash.
+     * @param HTMLPurifier_ConfigSchema_Interchange $interchange HTMLPurifier_ConfigSchema_Interchange object to build
+     * @param HTMLPurifier_StringHash $hash source data
+     * @throws HTMLPurifier_ConfigSchema_Exception
+     */
+    public function build($interchange, $hash)
+    {
+        if (!$hash instanceof HTMLPurifier_StringHash) {
+            $hash = new HTMLPurifier_StringHash($hash);
+        }
+        if (!isset($hash['ID'])) {
+            throw new HTMLPurifier_ConfigSchema_Exception('Hash does not have any ID');
+        }
+        if (strpos($hash['ID'], '.') === false) {
+            if (count($hash) == 2 && isset($hash['DESCRIPTION'])) {
+                $hash->offsetGet('DESCRIPTION'); // prevent complaining
+            } else {
+                throw new HTMLPurifier_ConfigSchema_Exception('All directives must have a namespace');
+            }
+        } else {
+            $this->buildDirective($interchange, $hash);
+        }
+        $this->_findUnused($hash);
+    }
+
+    /**
+     * @param HTMLPurifier_ConfigSchema_Interchange $interchange
+     * @param HTMLPurifier_StringHash $hash
+     * @throws HTMLPurifier_ConfigSchema_Exception
+     */
+    public function buildDirective($interchange, $hash)
+    {
+        $directive = new HTMLPurifier_ConfigSchema_Interchange_Directive();
+
+        // These are required elements:
+        $directive->id = $this->id($hash->offsetGet('ID'));
+        $id = $directive->id->toString(); // convenience
+
+        if (isset($hash['TYPE'])) {
+            $type = explode('/', $hash->offsetGet('TYPE'));
+            if (isset($type[1])) {
+                $directive->typeAllowsNull = true;
+            }
+            $directive->type = $type[0];
+        } else {
+            throw new HTMLPurifier_ConfigSchema_Exception("TYPE in directive hash '$id' not defined");
+        }
+
+        if (isset($hash['DEFAULT'])) {
+            try {
+                $directive->default = $this->varParser->parse(
+                    $hash->offsetGet('DEFAULT'),
+                    $directive->type,
+                    $directive->typeAllowsNull
+                );
+            } catch (HTMLPurifier_VarParserException $e) {
+                throw new HTMLPurifier_ConfigSchema_Exception($e->getMessage() . " in DEFAULT in directive hash '$id'");
+            }
+        }
+
+        if (isset($hash['DESCRIPTION'])) {
+            $directive->description = $hash->offsetGet('DESCRIPTION');
+        }
+
+        if (isset($hash['ALLOWED'])) {
+            $directive->allowed = $this->lookup($this->evalArray($hash->offsetGet('ALLOWED')));
+        }
+
+        if (isset($hash['VALUE-ALIASES'])) {
+            $directive->valueAliases = $this->evalArray($hash->offsetGet('VALUE-ALIASES'));
+        }
+
+        if (isset($hash['ALIASES'])) {
+            $raw_aliases = trim($hash->offsetGet('ALIASES'));
+            $aliases = preg_split('/\s*,\s*/', $raw_aliases);
+            foreach ($aliases as $alias) {
+                $directive->aliases[] = $this->id($alias);
+            }
+        }
+
+        if (isset($hash['VERSION'])) {
+            $directive->version = $hash->offsetGet('VERSION');
+        }
+
+        if (isset($hash['DEPRECATED-USE'])) {
+            $directive->deprecatedUse = $this->id($hash->offsetGet('DEPRECATED-USE'));
+        }
+
+        if (isset($hash['DEPRECATED-VERSION'])) {
+            $directive->deprecatedVersion = $hash->offsetGet('DEPRECATED-VERSION');
+        }
+
+        if (isset($hash['EXTERNAL'])) {
+            $directive->external = preg_split('/\s*,\s*/', trim($hash->offsetGet('EXTERNAL')));
+        }
+
+        $interchange->addDirective($directive);
+    }
+
+    /**
+     * Evaluates an array PHP code string without array() wrapper
+     * @param string $contents
+     */
+    protected function evalArray($contents)
+    {
+        return eval('return array(' . $contents . ');');
+    }
+
+    /**
+     * Converts an array list into a lookup array.
+     * @param array $array
+     * @return array
+     */
+    protected function lookup($array)
+    {
+        $ret = array();
+        foreach ($array as $val) {
+            $ret[$val] = true;
+        }
+        return $ret;
+    }
+
+    /**
+     * Convenience function that creates an HTMLPurifier_ConfigSchema_Interchange_Id
+     * object based on a string Id.
+     * @param string $id
+     * @return HTMLPurifier_ConfigSchema_Interchange_Id
+     */
+    protected function id($id)
+    {
+        return HTMLPurifier_ConfigSchema_Interchange_Id::make($id);
+    }
+
+    /**
+     * Triggers errors for any unused keys passed in the hash; such keys
+     * may indicate typos, missing values, etc.
+     * @param HTMLPurifier_StringHash $hash Hash to check.
+     */
+    protected function _findUnused($hash)
+    {
+        $accessed = $hash->getAccessed();
+        foreach ($hash as $k => $v) {
+            if (!isset($accessed[$k])) {
+                trigger_error("String hash key '$k' not used by builder", E_USER_NOTICE);
+            }
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Validator.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Validator.php
new file mode 100644 (file)
index 0000000..fb31277
--- /dev/null
@@ -0,0 +1,248 @@
+<?php
+
+/**
+ * Performs validations on HTMLPurifier_ConfigSchema_Interchange
+ *
+ * @note If you see '// handled by InterchangeBuilder', that means a
+ *       design decision in that class would prevent this validation from
+ *       ever being necessary. We have them anyway, however, for
+ *       redundancy.
+ */
+class HTMLPurifier_ConfigSchema_Validator
+{
+
+    /**
+     * @type HTMLPurifier_ConfigSchema_Interchange
+     */
+    protected $interchange;
+
+    /**
+     * @type array
+     */
+    protected $aliases;
+
+    /**
+     * Context-stack to provide easy to read error messages.
+     * @type array
+     */
+    protected $context = array();
+
+    /**
+     * to test default's type.
+     * @type HTMLPurifier_VarParser
+     */
+    protected $parser;
+
+    public function __construct()
+    {
+        $this->parser = new HTMLPurifier_VarParser();
+    }
+
+    /**
+     * Validates a fully-formed interchange object.
+     * @param HTMLPurifier_ConfigSchema_Interchange $interchange
+     * @return bool
+     */
+    public function validate($interchange)
+    {
+        $this->interchange = $interchange;
+        $this->aliases = array();
+        // PHP is a bit lax with integer <=> string conversions in
+        // arrays, so we don't use the identical !== comparison
+        foreach ($interchange->directives as $i => $directive) {
+            $id = $directive->id->toString();
+            if ($i != $id) {
+                $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'");
+            }
+            $this->validateDirective($directive);
+        }
+        return true;
+    }
+
+    /**
+     * Validates a HTMLPurifier_ConfigSchema_Interchange_Id object.
+     * @param HTMLPurifier_ConfigSchema_Interchange_Id $id
+     */
+    public function validateId($id)
+    {
+        $id_string = $id->toString();
+        $this->context[] = "id '$id_string'";
+        if (!$id instanceof HTMLPurifier_ConfigSchema_Interchange_Id) {
+            // handled by InterchangeBuilder
+            $this->error(false, 'is not an instance of HTMLPurifier_ConfigSchema_Interchange_Id');
+        }
+        // keys are now unconstrained (we might want to narrow down to A-Za-z0-9.)
+        // we probably should check that it has at least one namespace
+        $this->with($id, 'key')
+            ->assertNotEmpty()
+            ->assertIsString(); // implicit assertIsString handled by InterchangeBuilder
+        array_pop($this->context);
+    }
+
+    /**
+     * Validates a HTMLPurifier_ConfigSchema_Interchange_Directive object.
+     * @param HTMLPurifier_ConfigSchema_Interchange_Directive $d
+     */
+    public function validateDirective($d)
+    {
+        $id = $d->id->toString();
+        $this->context[] = "directive '$id'";
+        $this->validateId($d->id);
+
+        $this->with($d, 'description')
+            ->assertNotEmpty();
+
+        // BEGIN - handled by InterchangeBuilder
+        $this->with($d, 'type')
+            ->assertNotEmpty();
+        $this->with($d, 'typeAllowsNull')
+            ->assertIsBool();
+        try {
+            // This also tests validity of $d->type
+            $this->parser->parse($d->default, $d->type, $d->typeAllowsNull);
+        } catch (HTMLPurifier_VarParserException $e) {
+            $this->error('default', 'had error: ' . $e->getMessage());
+        }
+        // END - handled by InterchangeBuilder
+
+        if (!is_null($d->allowed) || !empty($d->valueAliases)) {
+            // allowed and valueAliases require that we be dealing with
+            // strings, so check for that early.
+            $d_int = HTMLPurifier_VarParser::$types[$d->type];
+            if (!isset(HTMLPurifier_VarParser::$stringTypes[$d_int])) {
+                $this->error('type', 'must be a string type when used with allowed or value aliases');
+            }
+        }
+
+        $this->validateDirectiveAllowed($d);
+        $this->validateDirectiveValueAliases($d);
+        $this->validateDirectiveAliases($d);
+
+        array_pop($this->context);
+    }
+
+    /**
+     * Extra validation if $allowed member variable of
+     * HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
+     * @param HTMLPurifier_ConfigSchema_Interchange_Directive $d
+     */
+    public function validateDirectiveAllowed($d)
+    {
+        if (is_null($d->allowed)) {
+            return;
+        }
+        $this->with($d, 'allowed')
+            ->assertNotEmpty()
+            ->assertIsLookup(); // handled by InterchangeBuilder
+        if (is_string($d->default) && !isset($d->allowed[$d->default])) {
+            $this->error('default', 'must be an allowed value');
+        }
+        $this->context[] = 'allowed';
+        foreach ($d->allowed as $val => $x) {
+            if (!is_string($val)) {
+                $this->error("value $val", 'must be a string');
+            }
+        }
+        array_pop($this->context);
+    }
+
+    /**
+     * Extra validation if $valueAliases member variable of
+     * HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
+     * @param HTMLPurifier_ConfigSchema_Interchange_Directive $d
+     */
+    public function validateDirectiveValueAliases($d)
+    {
+        if (is_null($d->valueAliases)) {
+            return;
+        }
+        $this->with($d, 'valueAliases')
+            ->assertIsArray(); // handled by InterchangeBuilder
+        $this->context[] = 'valueAliases';
+        foreach ($d->valueAliases as $alias => $real) {
+            if (!is_string($alias)) {
+                $this->error("alias $alias", 'must be a string');
+            }
+            if (!is_string($real)) {
+                $this->error("alias target $real from alias '$alias'", 'must be a string');
+            }
+            if ($alias === $real) {
+                $this->error("alias '$alias'", "must not be an alias to itself");
+            }
+        }
+        if (!is_null($d->allowed)) {
+            foreach ($d->valueAliases as $alias => $real) {
+                if (isset($d->allowed[$alias])) {
+                    $this->error("alias '$alias'", 'must not be an allowed value');
+                } elseif (!isset($d->allowed[$real])) {
+                    $this->error("alias '$alias'", 'must be an alias to an allowed value');
+                }
+            }
+        }
+        array_pop($this->context);
+    }
+
+    /**
+     * Extra validation if $aliases member variable of
+     * HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
+     * @param HTMLPurifier_ConfigSchema_Interchange_Directive $d
+     */
+    public function validateDirectiveAliases($d)
+    {
+        $this->with($d, 'aliases')
+            ->assertIsArray(); // handled by InterchangeBuilder
+        $this->context[] = 'aliases';
+        foreach ($d->aliases as $alias) {
+            $this->validateId($alias);
+            $s = $alias->toString();
+            if (isset($this->interchange->directives[$s])) {
+                $this->error("alias '$s'", 'collides with another directive');
+            }
+            if (isset($this->aliases[$s])) {
+                $other_directive = $this->aliases[$s];
+                $this->error("alias '$s'", "collides with alias for directive '$other_directive'");
+            }
+            $this->aliases[$s] = $d->id->toString();
+        }
+        array_pop($this->context);
+    }
+
+    // protected helper functions
+
+    /**
+     * Convenience function for generating HTMLPurifier_ConfigSchema_ValidatorAtom
+     * for validating simple member variables of objects.
+     * @param $obj
+     * @param $member
+     * @return HTMLPurifier_ConfigSchema_ValidatorAtom
+     */
+    protected function with($obj, $member)
+    {
+        return new HTMLPurifier_ConfigSchema_ValidatorAtom($this->getFormattedContext(), $obj, $member);
+    }
+
+    /**
+     * Emits an error, providing helpful context.
+     * @throws HTMLPurifier_ConfigSchema_Exception
+     */
+    protected function error($target, $msg)
+    {
+        if ($target !== false) {
+            $prefix = ucfirst($target) . ' in ' . $this->getFormattedContext();
+        } else {
+            $prefix = ucfirst($this->getFormattedContext());
+        }
+        throw new HTMLPurifier_ConfigSchema_Exception(trim($prefix . ' ' . $msg));
+    }
+
+    /**
+     * Returns a formatted context string.
+     * @return string
+     */
+    protected function getFormattedContext()
+    {
+        return implode(' in ', array_reverse($this->context));
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php
new file mode 100644 (file)
index 0000000..c9aa364
--- /dev/null
@@ -0,0 +1,130 @@
+<?php
+
+/**
+ * Fluent interface for validating the contents of member variables.
+ * This should be immutable. See HTMLPurifier_ConfigSchema_Validator for
+ * use-cases. We name this an 'atom' because it's ONLY for validations that
+ * are independent and usually scalar.
+ */
+class HTMLPurifier_ConfigSchema_ValidatorAtom
+{
+    /**
+     * @type string
+     */
+    protected $context;
+
+    /**
+     * @type object
+     */
+    protected $obj;
+
+    /**
+     * @type string
+     */
+    protected $member;
+
+    /**
+     * @type mixed
+     */
+    protected $contents;
+
+    public function __construct($context, $obj, $member)
+    {
+        $this->context = $context;
+        $this->obj = $obj;
+        $this->member = $member;
+        $this->contents =& $obj->$member;
+    }
+
+    /**
+     * @return HTMLPurifier_ConfigSchema_ValidatorAtom
+     */
+    public function assertIsString()
+    {
+        if (!is_string($this->contents)) {
+            $this->error('must be a string');
+        }
+        return $this;
+    }
+
+    /**
+     * @return HTMLPurifier_ConfigSchema_ValidatorAtom
+     */
+    public function assertIsBool()
+    {
+        if (!is_bool($this->contents)) {
+            $this->error('must be a boolean');
+        }
+        return $this;
+    }
+
+    /**
+     * @return HTMLPurifier_ConfigSchema_ValidatorAtom
+     */
+    public function assertIsArray()
+    {
+        if (!is_array($this->contents)) {
+            $this->error('must be an array');
+        }
+        return $this;
+    }
+
+    /**
+     * @return HTMLPurifier_ConfigSchema_ValidatorAtom
+     */
+    public function assertNotNull()
+    {
+        if ($this->contents === null) {
+            $this->error('must not be null');
+        }
+        return $this;
+    }
+
+    /**
+     * @return HTMLPurifier_ConfigSchema_ValidatorAtom
+     */
+    public function assertAlnum()
+    {
+        $this->assertIsString();
+        if (!ctype_alnum($this->contents)) {
+            $this->error('must be alphanumeric');
+        }
+        return $this;
+    }
+
+    /**
+     * @return HTMLPurifier_ConfigSchema_ValidatorAtom
+     */
+    public function assertNotEmpty()
+    {
+        if (empty($this->contents)) {
+            $this->error('must not be empty');
+        }
+        return $this;
+    }
+
+    /**
+     * @return HTMLPurifier_ConfigSchema_ValidatorAtom
+     */
+    public function assertIsLookup()
+    {
+        $this->assertIsArray();
+        foreach ($this->contents as $v) {
+            if ($v !== true) {
+                $this->error('must be a lookup array');
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * @param string $msg
+     * @throws HTMLPurifier_ConfigSchema_Exception
+     */
+    protected function error($msg)
+    {
+        throw new HTMLPurifier_ConfigSchema_Exception(ucfirst($this->member) . ' in ' . $this->context . ' ' . $msg);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema.ser b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema.ser
new file mode 100644 (file)
index 0000000..1e6ccd2
Binary files /dev/null and b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema.ser differ
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt
new file mode 100644 (file)
index 0000000..0517fed
--- /dev/null
@@ -0,0 +1,8 @@
+Attr.AllowedClasses
+TYPE: lookup/null
+VERSION: 4.0.0
+DEFAULT: null
+--DESCRIPTION--
+List of allowed class values in the class attribute. By default, this is null,
+which means all classes are allowed.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt
new file mode 100644 (file)
index 0000000..249edd6
--- /dev/null
@@ -0,0 +1,12 @@
+Attr.AllowedFrameTargets
+TYPE: lookup
+DEFAULT: array()
+--DESCRIPTION--
+Lookup table of all allowed link frame targets.  Some commonly used link
+targets include _blank, _self, _parent and _top. Values should be
+lowercase, as validation will be done in a case-sensitive manner despite
+W3C's recommendation. XHTML 1.0 Strict does not permit the target attribute
+so this directive will have no effect in that doctype. XHTML 1.1 does not
+enable the Target module by default, you will have to manually enable it
+(see the module documentation for more details.)
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt
new file mode 100644 (file)
index 0000000..9a8fa6a
--- /dev/null
@@ -0,0 +1,9 @@
+Attr.AllowedRel
+TYPE: lookup
+VERSION: 1.6.0
+DEFAULT: array()
+--DESCRIPTION--
+List of allowed forward document relationships in the rel attribute. Common
+values may be nofollow or print. By default, this is empty, meaning that no
+document relationships are allowed.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt
new file mode 100644 (file)
index 0000000..b017883
--- /dev/null
@@ -0,0 +1,9 @@
+Attr.AllowedRev
+TYPE: lookup
+VERSION: 1.6.0
+DEFAULT: array()
+--DESCRIPTION--
+List of allowed reverse document relationships in the rev attribute. This
+attribute is a bit of an edge-case; if you don't know what it is for, stay
+away.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt
new file mode 100644 (file)
index 0000000..e774b82
--- /dev/null
@@ -0,0 +1,19 @@
+Attr.ClassUseCDATA
+TYPE: bool/null
+DEFAULT: null
+VERSION: 4.0.0
+--DESCRIPTION--
+If null, class will auto-detect the doctype and, if matching XHTML 1.1 or
+XHTML 2.0, will use the restrictive NMTOKENS specification of class. Otherwise,
+it will use a relaxed CDATA definition.  If true, the relaxed CDATA definition
+is forced; if false, the NMTOKENS definition is forced.  To get behavior
+of HTML Purifier prior to 4.0.0, set this directive to false.
+
+Some rational behind the auto-detection:
+in previous versions of HTML Purifier, it was assumed that the form of
+class was NMTOKENS, as specified by the XHTML Modularization (representing
+XHTML 1.1 and XHTML 2.0).  The DTDs for HTML 4.01 and XHTML 1.0, however
+specify class as CDATA.  HTML 5 effectively defines it as CDATA, but
+with the additional constraint that each name should be unique (this is not
+explicitly outlined in previous specifications).
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt
new file mode 100644 (file)
index 0000000..533165e
--- /dev/null
@@ -0,0 +1,11 @@
+Attr.DefaultImageAlt
+TYPE: string/null
+DEFAULT: null
+VERSION: 3.2.0
+--DESCRIPTION--
+This is the content of the alt tag of an image if the user had not
+previously specified an alt attribute.  This applies to all images without
+a valid alt attribute, as opposed to %Attr.DefaultInvalidImageAlt, which
+only applies to invalid images, and overrides in the case of an invalid image.
+Default behavior with null is to use the basename of the src tag for the alt.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt
new file mode 100644 (file)
index 0000000..9eb7e38
--- /dev/null
@@ -0,0 +1,9 @@
+Attr.DefaultInvalidImage
+TYPE: string
+DEFAULT: ''
+--DESCRIPTION--
+This is the default image an img tag will be pointed to if it does not have
+a valid src attribute.  In future versions, we may allow the image tag to
+be removed completely, but due to design issues, this is not possible right
+now.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt
new file mode 100644 (file)
index 0000000..2f17bf4
--- /dev/null
@@ -0,0 +1,8 @@
+Attr.DefaultInvalidImageAlt
+TYPE: string
+DEFAULT: 'Invalid image'
+--DESCRIPTION--
+This is the content of the alt tag of an invalid image if the user had not
+previously specified an alt attribute.  It has no effect when the image is
+valid but there was no alt attribute present.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt
new file mode 100644 (file)
index 0000000..52654b5
--- /dev/null
@@ -0,0 +1,10 @@
+Attr.DefaultTextDir
+TYPE: string
+DEFAULT: 'ltr'
+--DESCRIPTION--
+Defines the default text direction (ltr or rtl) of the document being
+parsed.  This generally is the same as the value of the dir attribute in
+HTML, or ltr if that is not specified.
+--ALLOWED--
+'ltr', 'rtl'
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt
new file mode 100644 (file)
index 0000000..6440d21
--- /dev/null
@@ -0,0 +1,16 @@
+Attr.EnableID
+TYPE: bool
+DEFAULT: false
+VERSION: 1.2.0
+--DESCRIPTION--
+Allows the ID attribute in HTML.  This is disabled by default due to the
+fact that without proper configuration user input can easily break the
+validation of a webpage by specifying an ID that is already on the
+surrounding HTML.  If you don't mind throwing caution to the wind, enable
+this directive, but I strongly recommend you also consider blacklisting IDs
+you use (%Attr.IDBlacklist) or prefixing all user supplied IDs
+(%Attr.IDPrefix).  When set to true HTML Purifier reverts to the behavior of
+pre-1.2.0 versions.
+--ALIASES--
+HTML.EnableAttrID
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt
new file mode 100644 (file)
index 0000000..f31d226
--- /dev/null
@@ -0,0 +1,8 @@
+Attr.ForbiddenClasses
+TYPE: lookup
+VERSION: 4.0.0
+DEFAULT: array()
+--DESCRIPTION--
+List of forbidden class values in the class attribute. By default, this is
+empty, which means that no classes are forbidden. See also %Attr.AllowedClasses.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt
new file mode 100644 (file)
index 0000000..5f2b5e3
--- /dev/null
@@ -0,0 +1,5 @@
+Attr.IDBlacklist
+TYPE: list
+DEFAULT: array()
+DESCRIPTION: Array of IDs not allowed in the document.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt
new file mode 100644 (file)
index 0000000..6f58245
--- /dev/null
@@ -0,0 +1,9 @@
+Attr.IDBlacklistRegexp
+TYPE: string/null
+VERSION: 1.6.0
+DEFAULT: NULL
+--DESCRIPTION--
+PCRE regular expression to be matched against all IDs. If the expression is
+matches, the ID is rejected. Use this with care: may cause significant
+degradation. ID matching is done after all other validation.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt
new file mode 100644 (file)
index 0000000..cc49d43
--- /dev/null
@@ -0,0 +1,12 @@
+Attr.IDPrefix
+TYPE: string
+VERSION: 1.2.0
+DEFAULT: ''
+--DESCRIPTION--
+String to prefix to IDs.  If you have no idea what IDs your pages may use,
+you may opt to simply add a prefix to all user-submitted ID attributes so
+that they are still usable, but will not conflict with core page IDs.
+Example: setting the directive to 'user_' will result in a user submitted
+'foo' to become 'user_foo'  Be sure to set %HTML.EnableAttrID to true
+before using this.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt
new file mode 100644 (file)
index 0000000..2c5924a
--- /dev/null
@@ -0,0 +1,14 @@
+Attr.IDPrefixLocal
+TYPE: string
+VERSION: 1.2.0
+DEFAULT: ''
+--DESCRIPTION--
+Temporary prefix for IDs used in conjunction with %Attr.IDPrefix.  If you
+need to allow multiple sets of user content on web page, you may need to
+have a seperate prefix that changes with each iteration.  This way,
+seperately submitted user content displayed on the same page doesn't
+clobber each other. Ideal values are unique identifiers for the content it
+represents (i.e. the id of the row in the database). Be sure to add a
+seperator (like an underscore) at the end.  Warning: this directive will
+not work unless %Attr.IDPrefix is set to a non-empty value!
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt
new file mode 100644 (file)
index 0000000..d5caa1b
--- /dev/null
@@ -0,0 +1,31 @@
+AutoFormat.AutoParagraph
+TYPE: bool
+VERSION: 2.0.1
+DEFAULT: false
+--DESCRIPTION--
+
+<p>
+  This directive turns on auto-paragraphing, where double newlines are
+  converted in to paragraphs whenever possible. Auto-paragraphing:
+</p>
+<ul>
+  <li>Always applies to inline elements or text in the root node,</li>
+  <li>Applies to inline elements or text with double newlines in nodes
+      that allow paragraph tags,</li>
+  <li>Applies to double newlines in paragraph tags</li>
+</ul>
+<p>
+  <code>p</code> tags must be allowed for this directive to take effect.
+  We do not use <code>br</code> tags for paragraphing, as that is
+  semantically incorrect.
+</p>
+<p>
+  To prevent auto-paragraphing as a content-producer, refrain from using
+  double-newlines except to specify a new paragraph or in contexts where
+  it has special meaning (whitespace usually has no meaning except in
+  tags like <code>pre</code>, so this should not be difficult.) To prevent
+  the paragraphing of inline text adjacent to block elements, wrap them
+  in <code>div</code> tags (the behavior is slightly different outside of
+  the root node.)
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt
new file mode 100644 (file)
index 0000000..2a47648
--- /dev/null
@@ -0,0 +1,12 @@
+AutoFormat.Custom
+TYPE: list
+VERSION: 2.0.1
+DEFAULT: array()
+--DESCRIPTION--
+
+<p>
+  This directive can be used to add custom auto-format injectors.
+  Specify an array of injector names (class name minus the prefix)
+  or concrete implementations. Injector class must exist.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt
new file mode 100644 (file)
index 0000000..663064a
--- /dev/null
@@ -0,0 +1,11 @@
+AutoFormat.DisplayLinkURI
+TYPE: bool
+VERSION: 3.2.0
+DEFAULT: false
+--DESCRIPTION--
+<p>
+  This directive turns on the in-text display of URIs in &lt;a&gt; tags, and disables
+  those links. For example, <a href="http://example.com">example</a> becomes
+  example (<a>http://example.com</a>).
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt
new file mode 100644 (file)
index 0000000..3a48ba9
--- /dev/null
@@ -0,0 +1,12 @@
+AutoFormat.Linkify
+TYPE: bool
+VERSION: 2.0.1
+DEFAULT: false
+--DESCRIPTION--
+
+<p>
+  This directive turns on linkification, auto-linking http, ftp and
+  https URLs. <code>a</code> tags with the <code>href</code> attribute
+  must be allowed.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt
new file mode 100644 (file)
index 0000000..db58b13
--- /dev/null
@@ -0,0 +1,12 @@
+AutoFormat.PurifierLinkify.DocURL
+TYPE: string
+VERSION: 2.0.1
+DEFAULT: '#%s'
+ALIASES: AutoFormatParam.PurifierLinkifyDocURL
+--DESCRIPTION--
+<p>
+  Location of configuration documentation to link to, let %s substitute
+  into the configuration's namespace and directive names sans the percent
+  sign.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt
new file mode 100644 (file)
index 0000000..7996488
--- /dev/null
@@ -0,0 +1,12 @@
+AutoFormat.PurifierLinkify
+TYPE: bool
+VERSION: 2.0.1
+DEFAULT: false
+--DESCRIPTION--
+
+<p>
+  Internal auto-formatter that converts configuration directives in
+  syntax <a>%Namespace.Directive</a> to links. <code>a</code> tags
+  with the <code>href</code> attribute must be allowed.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.Predicate.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.Predicate.txt
new file mode 100644 (file)
index 0000000..6367fe2
--- /dev/null
@@ -0,0 +1,14 @@
+AutoFormat.RemoveEmpty.Predicate
+TYPE: hash
+VERSION: 4.7.0
+DEFAULT: array('colgroup' => array(), 'th' => array(), 'td' => array(), 'iframe' => array('src'))
+--DESCRIPTION--
+<p>
+  Given that an element has no contents, it will be removed by default, unless
+  this predicate dictates otherwise.  The predicate can either be an associative
+  map from tag name to list of attributes that must be present for the element
+  to be considered preserved: thus, the default always preserves <code>colgroup</code>,
+  <code>th</code> and <code>td</code>, and also <code>iframe</code> if it
+  has a <code>src</code>.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt
new file mode 100644 (file)
index 0000000..35c393b
--- /dev/null
@@ -0,0 +1,11 @@
+AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions
+TYPE: lookup
+VERSION: 4.0.0
+DEFAULT: array('td' => true, 'th' => true)
+--DESCRIPTION--
+<p>
+  When %AutoFormat.RemoveEmpty and %AutoFormat.RemoveEmpty.RemoveNbsp
+  are enabled, this directive defines what HTML elements should not be
+  removede if they have only a non-breaking space in them.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt
new file mode 100644 (file)
index 0000000..ca17eb1
--- /dev/null
@@ -0,0 +1,15 @@
+AutoFormat.RemoveEmpty.RemoveNbsp
+TYPE: bool
+VERSION: 4.0.0
+DEFAULT: false
+--DESCRIPTION--
+<p>
+  When enabled, HTML Purifier will treat any elements that contain only
+  non-breaking spaces as well as regular whitespace as empty, and remove
+  them when %AutoForamt.RemoveEmpty is enabled.
+</p>
+<p>
+  See %AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions for a list of elements
+  that don't have this behavior applied to them.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt
new file mode 100644 (file)
index 0000000..34657ba
--- /dev/null
@@ -0,0 +1,46 @@
+AutoFormat.RemoveEmpty
+TYPE: bool
+VERSION: 3.2.0
+DEFAULT: false
+--DESCRIPTION--
+<p>
+  When enabled, HTML Purifier will attempt to remove empty elements that
+  contribute no semantic information to the document. The following types
+  of nodes will be removed:
+</p>
+<ul><li>
+    Tags with no attributes and no content, and that are not empty
+    elements (remove <code>&lt;a&gt;&lt;/a&gt;</code> but not
+    <code>&lt;br /&gt;</code>), and
+  </li>
+  <li>
+    Tags with no content, except for:<ul>
+      <li>The <code>colgroup</code> element, or</li>
+      <li>
+        Elements with the <code>id</code> or <code>name</code> attribute,
+        when those attributes are permitted on those elements.
+      </li>
+    </ul></li>
+</ul>
+<p>
+  Please be very careful when using this functionality; while it may not
+  seem that empty elements contain useful information, they can alter the
+  layout of a document given appropriate styling. This directive is most
+  useful when you are processing machine-generated HTML, please avoid using
+  it on regular user HTML.
+</p>
+<p>
+  Elements that contain only whitespace will be treated as empty. Non-breaking
+  spaces, however, do not count as whitespace. See
+  %AutoFormat.RemoveEmpty.RemoveNbsp for alternate behavior.
+</p>
+<p>
+  This algorithm is not perfect; you may still notice some empty tags,
+  particularly if a node had elements, but those elements were later removed
+  because they were not permitted in that context, or tags that, after
+  being auto-closed by another tag, where empty. This is for safety reasons
+  to prevent clever code from breaking validation. The general rule of thumb:
+  if a tag looked empty on the way in, it will get removed; if HTML Purifier
+  made it empty, it will stay.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt
new file mode 100644 (file)
index 0000000..dde990a
--- /dev/null
@@ -0,0 +1,11 @@
+AutoFormat.RemoveSpansWithoutAttributes
+TYPE: bool
+VERSION: 4.0.1
+DEFAULT: false
+--DESCRIPTION--
+<p>
+  This directive causes <code>span</code> tags without any attributes
+  to be removed. It will also remove spans that had all attributes
+  removed during processing.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt
new file mode 100644 (file)
index 0000000..b324608
--- /dev/null
@@ -0,0 +1,8 @@
+CSS.AllowImportant
+TYPE: bool
+DEFAULT: false
+VERSION: 3.1.0
+--DESCRIPTION--
+This parameter determines whether or not !important cascade modifiers should
+be allowed in user CSS. If false, !important will stripped.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt
new file mode 100644 (file)
index 0000000..748be0e
--- /dev/null
@@ -0,0 +1,11 @@
+CSS.AllowTricky
+TYPE: bool
+DEFAULT: false
+VERSION: 3.1.0
+--DESCRIPTION--
+This parameter determines whether or not to allow "tricky" CSS properties and
+values. Tricky CSS properties/values can drastically modify page layout or
+be used for deceptive practices but do not directly constitute a security risk.
+For example, <code>display:none;</code> is considered a tricky property that
+will only be allowed if this directive is set to true.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedFonts.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedFonts.txt
new file mode 100644 (file)
index 0000000..3fd4654
--- /dev/null
@@ -0,0 +1,12 @@
+CSS.AllowedFonts
+TYPE: lookup/null
+VERSION: 4.3.0
+DEFAULT: NULL
+--DESCRIPTION--
+<p>
+    Allows you to manually specify a set of allowed fonts.  If
+    <code>NULL</code>, all fonts are allowed.  This directive
+    affects generic names (serif, sans-serif, monospace, cursive,
+    fantasy) as well as specific font families.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt
new file mode 100644 (file)
index 0000000..460112e
--- /dev/null
@@ -0,0 +1,18 @@
+CSS.AllowedProperties
+TYPE: lookup/null
+VERSION: 3.1.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    If HTML Purifier's style attributes set is unsatisfactory for your needs,
+    you can overload it with your own list of tags to allow.  Note that this
+    method is subtractive: it does its job by taking away from HTML Purifier
+    usual feature set, so you cannot add an attribute that HTML Purifier never
+    supported in the first place.
+</p>
+<p>
+    <strong>Warning:</strong> If another directive conflicts with the
+    elements here, <em>that</em> directive will win and override.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt
new file mode 100644 (file)
index 0000000..5cb7dda
--- /dev/null
@@ -0,0 +1,11 @@
+CSS.DefinitionRev
+TYPE: int
+VERSION: 2.0.0
+DEFAULT: 1
+--DESCRIPTION--
+
+<p>
+    Revision identifier for your custom definition. See
+    %HTML.DefinitionRev for details.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt
new file mode 100644 (file)
index 0000000..f1f5c5f
--- /dev/null
@@ -0,0 +1,13 @@
+CSS.ForbiddenProperties
+TYPE: lookup
+VERSION: 4.2.0
+DEFAULT: array()
+--DESCRIPTION--
+<p>
+    This is the logical inverse of %CSS.AllowedProperties, and it will
+    override that directive or any other directive.  If possible,
+    %CSS.AllowedProperties is recommended over this directive,
+    because it can sometimes be difficult to tell whether or not you've
+    forbidden all of the CSS properties you truly would like to disallow.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt
new file mode 100644 (file)
index 0000000..7a32914
--- /dev/null
@@ -0,0 +1,16 @@
+CSS.MaxImgLength
+TYPE: string/null
+DEFAULT: '1200px'
+VERSION: 3.1.1
+--DESCRIPTION--
+<p>
+ This parameter sets the maximum allowed length on <code>img</code> tags,
+ effectively the <code>width</code> and <code>height</code> properties.
+ Only absolute units of measurement (in, pt, pc, mm, cm) and pixels (px) are allowed. This is
+ in place to prevent imagecrash attacks, disable with null at your own risk.
+ This directive is similar to %HTML.MaxImgLength, and both should be
+ concurrently edited, although there are
+ subtle differences in the input format (the CSS max is a number with
+ a unit).
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt
new file mode 100644 (file)
index 0000000..148eedb
--- /dev/null
@@ -0,0 +1,10 @@
+CSS.Proprietary
+TYPE: bool
+VERSION: 3.0.0
+DEFAULT: false
+--DESCRIPTION--
+
+<p>
+    Whether or not to allow safe, proprietary CSS values.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Trusted.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Trusted.txt
new file mode 100644 (file)
index 0000000..e733a61
--- /dev/null
@@ -0,0 +1,9 @@
+CSS.Trusted
+TYPE: bool
+VERSION: 4.2.1
+DEFAULT: false
+--DESCRIPTION--
+Indicates whether or not the user's CSS input is trusted or not. If the
+input is trusted, a more expansive set of allowed properties.  See
+also %HTML.Trusted.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt
new file mode 100644 (file)
index 0000000..c486724
--- /dev/null
@@ -0,0 +1,14 @@
+Cache.DefinitionImpl
+TYPE: string/null
+VERSION: 2.0.0
+DEFAULT: 'Serializer'
+--DESCRIPTION--
+
+This directive defines which method to use when caching definitions,
+the complex data-type that makes HTML Purifier tick. Set to null
+to disable caching (not recommended, as you will see a definite
+performance degradation).
+
+--ALIASES--
+Core.DefinitionCache
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt
new file mode 100644 (file)
index 0000000..5403650
--- /dev/null
@@ -0,0 +1,13 @@
+Cache.SerializerPath
+TYPE: string/null
+VERSION: 2.0.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    Absolute path with no trailing slash to store serialized definitions in.
+    Default is within the
+    HTML Purifier library inside DefinitionCache/Serializer. This
+    path must be writable by the webserver.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt
new file mode 100644 (file)
index 0000000..b2b83d9
--- /dev/null
@@ -0,0 +1,11 @@
+Cache.SerializerPermissions
+TYPE: int
+VERSION: 4.3.0
+DEFAULT: 0755
+--DESCRIPTION--
+
+<p>
+    Directory permissions of the files and directories created inside
+    the DefinitionCache/Serializer or other custom serializer path.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt
new file mode 100644 (file)
index 0000000..568cbf3
--- /dev/null
@@ -0,0 +1,18 @@
+Core.AggressivelyFixLt
+TYPE: bool
+VERSION: 2.1.0
+DEFAULT: true
+--DESCRIPTION--
+<p>
+    This directive enables aggressive pre-filter fixes HTML Purifier can
+    perform in order to ensure that open angled-brackets do not get killed
+    during parsing stage. Enabling this will result in two preg_replace_callback
+    calls and at least two preg_replace calls for every HTML document parsed;
+    if your users make very well-formed HTML, you can set this directive false.
+    This has no effect when DirectLex is used.
+</p>
+<p>
+    <strong>Notice:</strong> This directive's default turned from false to true
+    in HTML Purifier 3.2.0.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AllowHostnameUnderscore.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AllowHostnameUnderscore.txt
new file mode 100644 (file)
index 0000000..2c910cc
--- /dev/null
@@ -0,0 +1,16 @@
+Core.AllowHostnameUnderscore
+TYPE: bool
+VERSION: 4.6.0
+DEFAULT: false
+--DESCRIPTION--
+<p>
+    By RFC 1123, underscores are not permitted in host names.
+    (This is in contrast to the specification for DNS, RFC
+    2181, which allows underscores.)
+    However, most browsers do the right thing when faced with
+    an underscore in the host name, and so some poorly written
+    websites are written with the expectation this should work.
+    Setting this parameter to true relaxes our allowed character
+    check so that underscores are permitted.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt
new file mode 100644 (file)
index 0000000..d731791
--- /dev/null
@@ -0,0 +1,12 @@
+Core.CollectErrors
+TYPE: bool
+VERSION: 2.0.0
+DEFAULT: false
+--DESCRIPTION--
+
+Whether or not to collect errors found while filtering the document. This
+is a useful way to give feedback to your users. <strong>Warning:</strong>
+Currently this feature is very patchy and experimental, with lots of
+possible error messages not yet implemented. It will not cause any
+problems, but it may not help your users either.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt
new file mode 100644 (file)
index 0000000..c572c14
--- /dev/null
@@ -0,0 +1,29 @@
+Core.ColorKeywords
+TYPE: hash
+VERSION: 2.0.0
+--DEFAULT--
+array (
+  'maroon' => '#800000',
+  'red' => '#FF0000',
+  'orange' => '#FFA500',
+  'yellow' => '#FFFF00',
+  'olive' => '#808000',
+  'purple' => '#800080',
+  'fuchsia' => '#FF00FF',
+  'white' => '#FFFFFF',
+  'lime' => '#00FF00',
+  'green' => '#008000',
+  'navy' => '#000080',
+  'blue' => '#0000FF',
+  'aqua' => '#00FFFF',
+  'teal' => '#008080',
+  'black' => '#000000',
+  'silver' => '#C0C0C0',
+  'gray' => '#808080',
+)
+--DESCRIPTION--
+
+Lookup array of color names to six digit hexadecimal number corresponding
+to color, with preceding hash mark. Used when parsing colors.  The lookup
+is done in a case-insensitive manner.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt
new file mode 100644 (file)
index 0000000..64b114f
--- /dev/null
@@ -0,0 +1,14 @@
+Core.ConvertDocumentToFragment
+TYPE: bool
+DEFAULT: true
+--DESCRIPTION--
+
+This parameter determines whether or not the filter should convert
+input that is a full document with html and body tags to a fragment
+of just the contents of a body tag. This parameter is simply something
+HTML Purifier can do during an edge-case: for most inputs, this
+processing is not necessary.
+
+--ALIASES--
+Core.AcceptFullDocuments
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt
new file mode 100644 (file)
index 0000000..36f16e0
--- /dev/null
@@ -0,0 +1,17 @@
+Core.DirectLexLineNumberSyncInterval
+TYPE: int
+VERSION: 2.0.0
+DEFAULT: 0
+--DESCRIPTION--
+
+<p>
+  Specifies the number of tokens the DirectLex line number tracking
+  implementations should process before attempting to resyncronize the
+  current line count by manually counting all previous new-lines. When
+  at 0, this functionality is disabled. Lower values will decrease
+  performance, and this is only strictly necessary if the counting
+  algorithm is buggy (in which case you should report it as a bug).
+  This has no effect when %Core.MaintainLineNumbers is disabled or DirectLex is
+  not being used.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DisableExcludes.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DisableExcludes.txt
new file mode 100644 (file)
index 0000000..1cd4c2c
--- /dev/null
@@ -0,0 +1,14 @@
+Core.DisableExcludes
+TYPE: bool
+DEFAULT: false
+VERSION: 4.5.0
+--DESCRIPTION--
+<p>
+  This directive disables SGML-style exclusions, e.g. the exclusion of
+  <code>&lt;object&gt;</code> in any descendant of a
+  <code>&lt;pre&gt;</code> tag.  Disabling excludes will allow some
+  invalid documents to pass through HTML Purifier, but HTML Purifier
+  will also be less likely to accidentally remove large documents during
+  processing.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EnableIDNA.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EnableIDNA.txt
new file mode 100644 (file)
index 0000000..ce243c3
--- /dev/null
@@ -0,0 +1,9 @@
+Core.EnableIDNA
+TYPE: bool
+DEFAULT: false
+VERSION: 4.4.0
+--DESCRIPTION--
+Allows international domain names in URLs.  This configuration option
+requires the PEAR Net_IDNA2 module to be installed.  It operates by
+punycoding any internationalized host names for maximum portability.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt
new file mode 100644 (file)
index 0000000..8bfb47c
--- /dev/null
@@ -0,0 +1,15 @@
+Core.Encoding
+TYPE: istring
+DEFAULT: 'utf-8'
+--DESCRIPTION--
+If for some reason you are unable to convert all webpages to UTF-8, you can
+use this directive as a stop-gap compatibility change to let HTML Purifier
+deal with non UTF-8 input.  This technique has notable deficiencies:
+absolutely no characters outside of the selected character encoding will be
+preserved, not even the ones that have been ampersand escaped (this is due
+to a UTF-8 specific <em>feature</em> that automatically resolves all
+entities), making it pretty useless for anything except the most I18N-blind
+applications, although %Core.EscapeNonASCIICharacters offers fixes this
+trouble with another tradeoff. This directive only accepts ISO-8859-1 if
+iconv is not enabled.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt
new file mode 100644 (file)
index 0000000..a3881be
--- /dev/null
@@ -0,0 +1,12 @@
+Core.EscapeInvalidChildren
+TYPE: bool
+DEFAULT: false
+--DESCRIPTION--
+<p><strong>Warning:</strong> this configuration option is no longer does anything as of 4.6.0.</p>
+
+<p>When true, a child is found that is not allowed in the context of the
+parent element will be transformed into text as if it were ASCII. When
+false, that element and all internal tags will be dropped, though text will
+be preserved.  There is no option for dropping the element but preserving
+child nodes.</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt
new file mode 100644 (file)
index 0000000..a7a5b24
--- /dev/null
@@ -0,0 +1,7 @@
+Core.EscapeInvalidTags
+TYPE: bool
+DEFAULT: false
+--DESCRIPTION--
+When true, invalid tags will be written back to the document as plain text.
+Otherwise, they are silently dropped.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt
new file mode 100644 (file)
index 0000000..abb4999
--- /dev/null
@@ -0,0 +1,13 @@
+Core.EscapeNonASCIICharacters
+TYPE: bool
+VERSION: 1.4.0
+DEFAULT: false
+--DESCRIPTION--
+This directive overcomes a deficiency in %Core.Encoding by blindly
+converting all non-ASCII characters into decimal numeric entities before
+converting it to its native encoding. This means that even characters that
+can be expressed in the non-UTF-8 encoding will be entity-ized, which can
+be a real downer for encodings like Big5. It also assumes that the ASCII
+repetoire is available, although this is the case for almost all encodings.
+Anyway, use UTF-8!
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt
new file mode 100644 (file)
index 0000000..915391e
--- /dev/null
@@ -0,0 +1,19 @@
+Core.HiddenElements
+TYPE: lookup
+--DEFAULT--
+array (
+  'script' => true,
+  'style' => true,
+)
+--DESCRIPTION--
+
+<p>
+  This directive is a lookup array of elements which should have their
+  contents removed when they are not allowed by the HTML definition.
+  For example, the contents of a <code>script</code> tag are not
+  normally shown in a document, so if script tags are to be removed,
+  their contents should be removed to. This is opposed to a <code>b</code>
+  tag, which defines some presentational changes but does not hide its
+  contents.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Language.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Language.txt
new file mode 100644 (file)
index 0000000..233fca1
--- /dev/null
@@ -0,0 +1,10 @@
+Core.Language
+TYPE: string
+VERSION: 2.0.0
+DEFAULT: 'en'
+--DESCRIPTION--
+
+ISO 639 language code for localizable things in HTML Purifier to use,
+which is mainly error reporting. There is currently only an English (en)
+translation, so this directive is currently useless.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt
new file mode 100644 (file)
index 0000000..8983e2c
--- /dev/null
@@ -0,0 +1,34 @@
+Core.LexerImpl
+TYPE: mixed/null
+VERSION: 2.0.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+  This parameter determines what lexer implementation can be used. The
+  valid values are:
+</p>
+<dl>
+  <dt><em>null</em></dt>
+  <dd>
+    Recommended, the lexer implementation will be auto-detected based on
+    your PHP-version and configuration.
+  </dd>
+  <dt><em>string</em> lexer identifier</dt>
+  <dd>
+    This is a slim way of manually overridding the implementation.
+    Currently recognized values are: DOMLex (the default PHP5
+implementation)
+    and DirectLex (the default PHP4 implementation). Only use this if
+    you know what you are doing: usually, the auto-detection will
+    manage things for cases you aren't even aware of.
+  </dd>
+  <dt><em>object</em> lexer instance</dt>
+  <dd>
+    Super-advanced: you can specify your own, custom, implementation that
+    implements the interface defined by <code>HTMLPurifier_Lexer</code>.
+    I may remove this option simply because I don't expect anyone
+    to use it.
+  </dd>
+</dl>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt
new file mode 100644 (file)
index 0000000..eb841a7
--- /dev/null
@@ -0,0 +1,16 @@
+Core.MaintainLineNumbers
+TYPE: bool/null
+VERSION: 2.0.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+  If true, HTML Purifier will add line number information to all tokens.
+  This is useful when error reporting is turned on, but can result in
+  significant performance degradation and should not be used when
+  unnecessary. This directive must be used with the DirectLex lexer,
+  as the DOMLex lexer does not (yet) support this functionality.
+  If the value is null, an appropriate value will be selected based
+  on other configuration.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt
new file mode 100644 (file)
index 0000000..d77f536
--- /dev/null
@@ -0,0 +1,11 @@
+Core.NormalizeNewlines
+TYPE: bool
+VERSION: 4.2.0
+DEFAULT: true
+--DESCRIPTION--
+<p>
+    Whether or not to normalize newlines to the operating
+    system default.  When <code>false</code>, HTML Purifier
+    will attempt to preserve mixed newline files.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt
new file mode 100644 (file)
index 0000000..4070c2a
--- /dev/null
@@ -0,0 +1,12 @@
+Core.RemoveInvalidImg
+TYPE: bool
+DEFAULT: true
+VERSION: 1.3.0
+--DESCRIPTION--
+
+<p>
+  This directive enables pre-emptive URI checking in <code>img</code>
+  tags, as the attribute validation strategy is not authorized to
+  remove elements from the document. Revert to pre-1.3.0 behavior by setting to false.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt
new file mode 100644 (file)
index 0000000..3397d9f
--- /dev/null
@@ -0,0 +1,11 @@
+Core.RemoveProcessingInstructions
+TYPE: bool
+VERSION: 4.2.0
+DEFAULT: false
+--DESCRIPTION--
+Instead of escaping processing instructions in the form <code>&lt;? ...
+?&gt;</code>, remove it out-right.  This may be useful if the HTML
+you are validating contains XML processing instruction gunk, however,
+it can also be user-unfriendly for people attempting to post PHP
+snippets.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt
new file mode 100644 (file)
index 0000000..a4cd966
--- /dev/null
@@ -0,0 +1,12 @@
+Core.RemoveScriptContents
+TYPE: bool/null
+DEFAULT: NULL
+VERSION: 2.0.0
+DEPRECATED-VERSION: 2.1.0
+DEPRECATED-USE: Core.HiddenElements
+--DESCRIPTION--
+<p>
+  This directive enables HTML Purifier to remove not only script tags
+  but all of their contents.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt
new file mode 100644 (file)
index 0000000..3db50ef
--- /dev/null
@@ -0,0 +1,11 @@
+Filter.Custom
+TYPE: list
+VERSION: 3.1.0
+DEFAULT: array()
+--DESCRIPTION--
+<p>
+  This directive can be used to add custom filters; it is nearly the
+  equivalent of the now deprecated <code>HTMLPurifier-&gt;addFilter()</code>
+  method. Specify an array of concrete implementations.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt
new file mode 100644 (file)
index 0000000..16829bc
--- /dev/null
@@ -0,0 +1,14 @@
+Filter.ExtractStyleBlocks.Escaping
+TYPE: bool
+VERSION: 3.0.0
+DEFAULT: true
+ALIASES: Filter.ExtractStyleBlocksEscaping, FilterParam.ExtractStyleBlocksEscaping
+--DESCRIPTION--
+
+<p>
+  Whether or not to escape the dangerous characters &lt;, &gt; and &amp;
+  as \3C, \3E and \26, respectively. This is can be safely set to false
+  if the contents of StyleBlocks will be placed in an external stylesheet,
+  where there is no risk of it being interpreted as HTML.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt
new file mode 100644 (file)
index 0000000..7f95f54
--- /dev/null
@@ -0,0 +1,29 @@
+Filter.ExtractStyleBlocks.Scope
+TYPE: string/null
+VERSION: 3.0.0
+DEFAULT: NULL
+ALIASES: Filter.ExtractStyleBlocksScope, FilterParam.ExtractStyleBlocksScope
+--DESCRIPTION--
+
+<p>
+  If you would like users to be able to define external stylesheets, but
+  only allow them to specify CSS declarations for a specific node and
+  prevent them from fiddling with other elements, use this directive.
+  It accepts any valid CSS selector, and will prepend this to any
+  CSS declaration extracted from the document. For example, if this
+  directive is set to <code>#user-content</code> and a user uses the
+  selector <code>a:hover</code>, the final selector will be
+  <code>#user-content a:hover</code>.
+</p>
+<p>
+  The comma shorthand may be used; consider the above example, with
+  <code>#user-content, #user-content2</code>, the final selector will
+  be <code>#user-content a:hover, #user-content2 a:hover</code>.
+</p>
+<p>
+  <strong>Warning:</strong> It is possible for users to bypass this measure
+  using a naughty + selector. This is a bug in CSS Tidy 1.3, not HTML
+  Purifier, and I am working to get it fixed. Until then, HTML Purifier
+  performs a basic check to prevent this.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt
new file mode 100644 (file)
index 0000000..6c231b2
--- /dev/null
@@ -0,0 +1,16 @@
+Filter.ExtractStyleBlocks.TidyImpl
+TYPE: mixed/null
+VERSION: 3.1.0
+DEFAULT: NULL
+ALIASES: FilterParam.ExtractStyleBlocksTidyImpl
+--DESCRIPTION--
+<p>
+  If left NULL, HTML Purifier will attempt to instantiate a <code>csstidy</code>
+  class to use for internal cleaning. This will usually be good enough.
+</p>
+<p>
+  However, for trusted user input, you can set this to <code>false</code> to
+  disable cleaning. In addition, you can supply your own concrete implementation
+  of Tidy's interface to use, although I don't know why you'd want to do that.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt
new file mode 100644 (file)
index 0000000..078d087
--- /dev/null
@@ -0,0 +1,74 @@
+Filter.ExtractStyleBlocks
+TYPE: bool
+VERSION: 3.1.0
+DEFAULT: false
+EXTERNAL: CSSTidy
+--DESCRIPTION--
+<p>
+  This directive turns on the style block extraction filter, which removes
+  <code>style</code> blocks from input HTML, cleans them up with CSSTidy,
+  and places them in the <code>StyleBlocks</code> context variable, for further
+  use by you, usually to be placed in an external stylesheet, or a
+  <code>style</code> block in the <code>head</code> of your document.
+</p>
+<p>
+  Sample usage:
+</p>
+<pre><![CDATA[
+<?php
+    header('Content-type: text/html; charset=utf-8');
+    echo '<?xml version="1.0" encoding="UTF-8"?>';
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+  <title>Filter.ExtractStyleBlocks</title>
+<?php
+    require_once '/path/to/library/HTMLPurifier.auto.php';
+    require_once '/path/to/csstidy.class.php';
+
+    $dirty = '<style>body {color:#F00;}</style> Some text';
+
+    $config = HTMLPurifier_Config::createDefault();
+    $config->set('Filter', 'ExtractStyleBlocks', true);
+    $purifier = new HTMLPurifier($config);
+
+    $html = $purifier->purify($dirty);
+
+    // This implementation writes the stylesheets to the styles/ directory.
+    // You can also echo the styles inside the document, but it's a bit
+    // more difficult to make sure they get interpreted properly by
+    // browsers; try the usual CSS armoring techniques.
+    $styles = $purifier->context->get('StyleBlocks');
+    $dir = 'styles/';
+    if (!is_dir($dir)) mkdir($dir);
+    $hash = sha1($_GET['html']);
+    foreach ($styles as $i => $style) {
+        file_put_contents($name = $dir . $hash . "_$i");
+        echo '<link rel="stylesheet" type="text/css" href="'.$name.'" />';
+    }
+?>
+</head>
+<body>
+  <div>
+    <?php echo $html; ?>
+  </div>
+</b]]><![CDATA[ody>
+</html>
+]]></pre>
+<p>
+  <strong>Warning:</strong> It is possible for a user to mount an
+  imagecrash attack using this CSS. Counter-measures are difficult;
+  it is not simply enough to limit the range of CSS lengths (using
+  relative lengths with many nesting levels allows for large values
+  to be attained without actually specifying them in the stylesheet),
+  and the flexible nature of selectors makes it difficult to selectively
+  disable lengths on image tags (HTML Purifier, however, does disable
+  CSS width and height in inline styling). There are probably two effective
+  counter measures: an explicit width and height set to auto in all
+  images in your document (unlikely) or the disabling of width and
+  height (somewhat reasonable). Whether or not these measures should be
+  used is left to the reader.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt
new file mode 100644 (file)
index 0000000..321eaa2
--- /dev/null
@@ -0,0 +1,16 @@
+Filter.YouTube
+TYPE: bool
+VERSION: 3.1.0
+DEFAULT: false
+--DESCRIPTION--
+<p>
+  <strong>Warning:</strong> Deprecated in favor of %HTML.SafeObject and
+  %Output.FlashCompat (turn both on to allow YouTube videos and other
+  Flash content).
+</p>
+<p>
+  This directive enables YouTube video embedding in HTML Purifier. Check
+  <a href="http://htmlpurifier.org/docs/enduser-youtube.html">this document
+  on embedding videos</a> for more information on what this filter does.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt
new file mode 100644 (file)
index 0000000..0b2c106
--- /dev/null
@@ -0,0 +1,25 @@
+HTML.Allowed
+TYPE: itext/null
+VERSION: 2.0.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    This is a preferred convenience directive that combines
+    %HTML.AllowedElements and %HTML.AllowedAttributes.
+    Specify elements and attributes that are allowed using:
+    <code>element1[attr1|attr2],element2...</code>.  For example,
+    if you would like to only allow paragraphs and links, specify
+    <code>a[href],p</code>.  You can specify attributes that apply
+    to all elements using an asterisk, e.g. <code>*[lang]</code>.
+    You can also use newlines instead of commas to separate elements.
+</p>
+<p>
+    <strong>Warning</strong>:
+    All of the constraints on the component directives are still enforced.
+    The syntax is a <em>subset</em> of TinyMCE's <code>valid_elements</code>
+    whitelist: directly copy-pasting it here will probably result in
+    broken whitelists. If %HTML.AllowedElements or %HTML.AllowedAttributes
+    are set, this directive has no effect.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt
new file mode 100644 (file)
index 0000000..fcf093f
--- /dev/null
@@ -0,0 +1,19 @@
+HTML.AllowedAttributes
+TYPE: lookup/null
+VERSION: 1.3.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    If HTML Purifier's attribute set is unsatisfactory, overload it!
+    The syntax is "tag.attr" or "*.attr" for the global attributes
+    (style, id, class, dir, lang, xml:lang).
+</p>
+<p>
+    <strong>Warning:</strong> If another directive conflicts with the
+    elements here, <em>that</em> directive will win and override. For
+    example, %HTML.EnableAttrID will take precedence over *.id in this
+    directive.  You must set that directive to true before you can use
+    IDs at all.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedComments.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedComments.txt
new file mode 100644 (file)
index 0000000..140e214
--- /dev/null
@@ -0,0 +1,10 @@
+HTML.AllowedComments
+TYPE: lookup
+VERSION: 4.4.0
+DEFAULT: array()
+--DESCRIPTION--
+A whitelist which indicates what explicit comment bodies should be
+allowed, modulo leading and trailing whitespace.  See also %HTML.AllowedCommentsRegexp
+(these directives are union'ed together, so a comment is considered
+valid if any directive deems it valid.)
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedCommentsRegexp.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedCommentsRegexp.txt
new file mode 100644 (file)
index 0000000..f22e977
--- /dev/null
@@ -0,0 +1,15 @@
+HTML.AllowedCommentsRegexp
+TYPE: string/null
+VERSION: 4.4.0
+DEFAULT: NULL
+--DESCRIPTION--
+A regexp, which if it matches the body of a comment, indicates that
+it should be allowed. Trailing and leading spaces are removed prior
+to running this regular expression.
+<strong>Warning:</strong> Make sure you specify
+correct anchor metacharacters <code>^regex$</code>, otherwise you may accept
+comments that you did not mean to! In particular, the regex <code>/foo|bar/</code>
+is probably not sufficiently strict, since it also allows <code>foobar</code>.
+See also %HTML.AllowedComments (these directives are union'ed together,
+so a comment is considered valid if any directive deems it valid.)
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt
new file mode 100644 (file)
index 0000000..1d3fa79
--- /dev/null
@@ -0,0 +1,23 @@
+HTML.AllowedElements
+TYPE: lookup/null
+VERSION: 1.3.0
+DEFAULT: NULL
+--DESCRIPTION--
+<p>
+    If HTML Purifier's tag set is unsatisfactory for your needs, you can
+    overload it with your own list of tags to allow.  If you change
+    this, you probably also want to change %HTML.AllowedAttributes; see
+    also %HTML.Allowed which lets you set allowed elements and
+    attributes at the same time.
+</p>
+<p>
+    If you attempt to allow an element that HTML Purifier does not know
+    about, HTML Purifier will raise an error.  You will need to manually
+    tell HTML Purifier about this element by using the
+    <a href="http://htmlpurifier.org/docs/enduser-customize.html">advanced customization features.</a>
+</p>
+<p>
+    <strong>Warning:</strong> If another directive conflicts with the
+    elements here, <em>that</em> directive will win and override.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt
new file mode 100644 (file)
index 0000000..5a59a55
--- /dev/null
@@ -0,0 +1,20 @@
+HTML.AllowedModules
+TYPE: lookup/null
+VERSION: 2.0.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    A doctype comes with a set of usual modules to use. Without having
+    to mucking about with the doctypes, you can quickly activate or
+    disable these modules by specifying which modules you wish to allow
+    with this directive. This is most useful for unit testing specific
+    modules, although end users may find it useful for their own ends.
+</p>
+<p>
+    If you specify a module that does not exist, the manager will silently
+    fail to use it, so be careful! User-defined modules are not affected
+    by this directive. Modules defined in %HTML.CoreModules are not
+    affected by this directive.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt
new file mode 100644 (file)
index 0000000..151fb7b
--- /dev/null
@@ -0,0 +1,11 @@
+HTML.Attr.Name.UseCDATA
+TYPE: bool
+DEFAULT: false
+VERSION: 4.0.0
+--DESCRIPTION--
+The W3C specification DTD defines the name attribute to be CDATA, not ID, due
+to limitations of DTD.  In certain documents, this relaxed behavior is desired,
+whether it is to specify duplicate names, or to specify names that would be
+illegal IDs (for example, names that begin with a digit.) Set this configuration
+directive to true to use the relaxed parsing rules.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt
new file mode 100644 (file)
index 0000000..45ae469
--- /dev/null
@@ -0,0 +1,18 @@
+HTML.BlockWrapper
+TYPE: string
+VERSION: 1.3.0
+DEFAULT: 'p'
+--DESCRIPTION--
+
+<p>
+    String name of element to wrap inline elements that are inside a block
+    context.  This only occurs in the children of blockquote in strict mode.
+</p>
+<p>
+    Example: by default value,
+    <code>&lt;blockquote&gt;Foo&lt;/blockquote&gt;</code> would become
+    <code>&lt;blockquote&gt;&lt;p&gt;Foo&lt;/p&gt;&lt;/blockquote&gt;</code>.
+    The <code>&lt;p&gt;</code> tags can be replaced with whatever you desire,
+    as long as it is a block level element.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt
new file mode 100644 (file)
index 0000000..5246188
--- /dev/null
@@ -0,0 +1,23 @@
+HTML.CoreModules
+TYPE: lookup
+VERSION: 2.0.0
+--DEFAULT--
+array (
+  'Structure' => true,
+  'Text' => true,
+  'Hypertext' => true,
+  'List' => true,
+  'NonXMLCommonAttributes' => true,
+  'XMLCommonAttributes' => true,
+  'CommonAttributes' => true,
+)
+--DESCRIPTION--
+
+<p>
+    Certain modularized doctypes (XHTML, namely), have certain modules
+    that must be included for the doctype to be an conforming document
+    type: put those modules here. By default, XHTML's core modules
+    are used. You can set this to a blank array to disable core module
+    protection, but this is not recommended.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt
new file mode 100644 (file)
index 0000000..6ed70b5
--- /dev/null
@@ -0,0 +1,9 @@
+HTML.CustomDoctype
+TYPE: string/null
+VERSION: 2.0.1
+DEFAULT: NULL
+--DESCRIPTION--
+
+A custom doctype for power-users who defined their own document
+type. This directive only applies when %HTML.Doctype is blank.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt
new file mode 100644 (file)
index 0000000..103db75
--- /dev/null
@@ -0,0 +1,33 @@
+HTML.DefinitionID
+TYPE: string/null
+DEFAULT: NULL
+VERSION: 2.0.0
+--DESCRIPTION--
+
+<p>
+    Unique identifier for a custom-built HTML definition. If you edit
+    the raw version of the HTMLDefinition, introducing changes that the
+    configuration object does not reflect, you must specify this variable.
+    If you change your custom edits, you should change this directive, or
+    clear your cache. Example:
+</p>
+<pre>
+$config = HTMLPurifier_Config::createDefault();
+$config->set('HTML', 'DefinitionID', '1');
+$def = $config->getHTMLDefinition();
+$def->addAttribute('a', 'tabindex', 'Number');
+</pre>
+<p>
+    In the above example, the configuration is still at the defaults, but
+    using the advanced API, an extra attribute has been added. The
+    configuration object normally has no way of knowing that this change
+    has taken place, so it needs an extra directive: %HTML.DefinitionID.
+    If someone else attempts to use the default configuration, these two
+    pieces of code will not clobber each other in the cache, since one has
+    an extra directive attached to it.
+</p>
+<p>
+    You <em>must</em> specify a value to this directive to use the
+    advanced API features.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt
new file mode 100644 (file)
index 0000000..229ae02
--- /dev/null
@@ -0,0 +1,16 @@
+HTML.DefinitionRev
+TYPE: int
+VERSION: 2.0.0
+DEFAULT: 1
+--DESCRIPTION--
+
+<p>
+    Revision identifier for your custom definition specified in
+    %HTML.DefinitionID.  This serves the same purpose: uniquely identifying
+    your custom definition, but this one does so in a chronological
+    context: revision 3 is more up-to-date then revision 2.  Thus, when
+    this gets incremented, the cache handling is smart enough to clean
+    up any older revisions of your definition as well as flush the
+    cache.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt
new file mode 100644 (file)
index 0000000..9dab497
--- /dev/null
@@ -0,0 +1,11 @@
+HTML.Doctype
+TYPE: string/null
+DEFAULT: NULL
+--DESCRIPTION--
+Doctype to use during filtering. Technically speaking this is not actually
+a doctype (as it does not identify a corresponding DTD), but we are using
+this name for sake of simplicity. When non-blank, this will override any
+older directives like %HTML.XHTML or %HTML.Strict.
+--ALLOWED--
+'HTML 4.01 Transitional', 'HTML 4.01 Strict', 'XHTML 1.0 Transitional', 'XHTML 1.0 Strict', 'XHTML 1.1'
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt
new file mode 100644 (file)
index 0000000..7878dc0
--- /dev/null
@@ -0,0 +1,11 @@
+HTML.FlashAllowFullScreen
+TYPE: bool
+VERSION: 4.2.0
+DEFAULT: false
+--DESCRIPTION--
+<p>
+    Whether or not to permit embedded Flash content from
+    %HTML.SafeObject to expand to the full screen.  Corresponds to
+    the <code>allowFullScreen</code> parameter.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt
new file mode 100644 (file)
index 0000000..57358f9
--- /dev/null
@@ -0,0 +1,21 @@
+HTML.ForbiddenAttributes
+TYPE: lookup
+VERSION: 3.1.0
+DEFAULT: array()
+--DESCRIPTION--
+<p>
+    While this directive is similar to %HTML.AllowedAttributes, for
+    forwards-compatibility with XML, this attribute has a different syntax. Instead of
+    <code>tag.attr</code>, use <code>tag@attr</code>. To disallow <code>href</code>
+    attributes in <code>a</code> tags, set this directive to
+    <code>a@href</code>. You can also disallow an attribute globally with
+    <code>attr</code> or <code>*@attr</code> (either syntax is fine; the latter
+    is provided for consistency with %HTML.AllowedAttributes).
+</p>
+<p>
+    <strong>Warning:</strong> This directive complements %HTML.ForbiddenElements,
+    accordingly, check
+    out that directive for a discussion of why you
+    should think twice before using this directive.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt
new file mode 100644 (file)
index 0000000..93a53e1
--- /dev/null
@@ -0,0 +1,20 @@
+HTML.ForbiddenElements
+TYPE: lookup
+VERSION: 3.1.0
+DEFAULT: array()
+--DESCRIPTION--
+<p>
+    This was, perhaps, the most requested feature ever in HTML
+    Purifier. Please don't abuse it! This is the logical inverse of
+    %HTML.AllowedElements, and it will override that directive, or any
+    other directive.
+</p>
+<p>
+    If possible, %HTML.Allowed is recommended over this directive, because it
+    can sometimes be difficult to tell whether or not you've forbidden all of
+    the behavior you would like to disallow. If you forbid <code>img</code>
+    with the expectation of preventing images on your site, you'll be in for
+    a nasty surprise when people start using the <code>background-image</code>
+    CSS property.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt
new file mode 100644 (file)
index 0000000..e424c38
--- /dev/null
@@ -0,0 +1,14 @@
+HTML.MaxImgLength
+TYPE: int/null
+DEFAULT: 1200
+VERSION: 3.1.1
+--DESCRIPTION--
+<p>
+ This directive controls the maximum number of pixels in the width and
+ height attributes in <code>img</code> tags. This is
+ in place to prevent imagecrash attacks, disable with null at your own risk.
+ This directive is similar to %CSS.MaxImgLength, and both should be
+ concurrently edited, although there are
+ subtle differences in the input format (the HTML max is an integer).
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Nofollow.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Nofollow.txt
new file mode 100644 (file)
index 0000000..700b309
--- /dev/null
@@ -0,0 +1,7 @@
+HTML.Nofollow
+TYPE: bool
+VERSION: 4.3.0
+DEFAULT: FALSE
+--DESCRIPTION--
+If enabled, nofollow rel attributes are added to all outgoing links.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt
new file mode 100644 (file)
index 0000000..62e8e16
--- /dev/null
@@ -0,0 +1,12 @@
+HTML.Parent
+TYPE: string
+VERSION: 1.3.0
+DEFAULT: 'div'
+--DESCRIPTION--
+
+<p>
+    String name of element that HTML fragment passed to library will be
+    inserted in.  An interesting variation would be using span as the
+    parent element, meaning that only inline tags would be allowed.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt
new file mode 100644 (file)
index 0000000..dfb7204
--- /dev/null
@@ -0,0 +1,12 @@
+HTML.Proprietary
+TYPE: bool
+VERSION: 3.1.0
+DEFAULT: false
+--DESCRIPTION--
+<p>
+    Whether or not to allow proprietary elements and attributes in your
+    documents, as per <code>HTMLPurifier_HTMLModule_Proprietary</code>.
+    <strong>Warning:</strong> This can cause your documents to stop
+    validating!
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt
new file mode 100644 (file)
index 0000000..cdda09a
--- /dev/null
@@ -0,0 +1,13 @@
+HTML.SafeEmbed
+TYPE: bool
+VERSION: 3.1.1
+DEFAULT: false
+--DESCRIPTION--
+<p>
+    Whether or not to permit embed tags in documents, with a number of extra
+    security features added to prevent script execution. This is similar to
+    what websites like MySpace do to embed tags. Embed is a proprietary
+    element and will cause your website to stop validating; you should
+    see if you can use %Output.FlashCompat with %HTML.SafeObject instead
+    first.</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeIframe.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeIframe.txt
new file mode 100644 (file)
index 0000000..5eb6ec2
--- /dev/null
@@ -0,0 +1,13 @@
+HTML.SafeIframe
+TYPE: bool
+VERSION: 4.4.0
+DEFAULT: false
+--DESCRIPTION--
+<p>
+    Whether or not to permit iframe tags in untrusted documents.  This
+    directive must be accompanied by a whitelist of permitted iframes,
+    such as %URI.SafeIframeRegexp, otherwise it will fatally error.
+    This directive has no effect on strict doctypes, as iframes are not
+    valid.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt
new file mode 100644 (file)
index 0000000..ceb342e
--- /dev/null
@@ -0,0 +1,13 @@
+HTML.SafeObject
+TYPE: bool
+VERSION: 3.1.1
+DEFAULT: false
+--DESCRIPTION--
+<p>
+    Whether or not to permit object tags in documents, with a number of extra
+    security features added to prevent script execution. This is similar to
+    what websites like MySpace do to object tags.  You should also enable
+    %Output.FlashCompat in order to generate Internet Explorer
+    compatibility code for your object tags.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeScripting.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeScripting.txt
new file mode 100644 (file)
index 0000000..5ebc7a1
--- /dev/null
@@ -0,0 +1,10 @@
+HTML.SafeScripting
+TYPE: lookup
+VERSION: 4.5.0
+DEFAULT: array()
+--DESCRIPTION--
+<p>
+    Whether or not to permit script tags to external scripts in documents.
+    Inline scripting is not allowed, and the script must match an explicit whitelist.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt
new file mode 100644 (file)
index 0000000..a8b1de5
--- /dev/null
@@ -0,0 +1,9 @@
+HTML.Strict
+TYPE: bool
+VERSION: 1.3.0
+DEFAULT: false
+DEPRECATED-VERSION: 1.7.0
+DEPRECATED-USE: HTML.Doctype
+--DESCRIPTION--
+Determines whether or not to use Transitional (loose) or Strict rulesets.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetBlank.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetBlank.txt
new file mode 100644 (file)
index 0000000..587a167
--- /dev/null
@@ -0,0 +1,8 @@
+HTML.TargetBlank
+TYPE: bool
+VERSION: 4.4.0
+DEFAULT: FALSE
+--DESCRIPTION--
+If enabled, <code>target=blank</code> attributes are added to all outgoing links.
+(This includes links from an HTTPS version of a page to an HTTP version.)
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt
new file mode 100644 (file)
index 0000000..b4c271b
--- /dev/null
@@ -0,0 +1,8 @@
+HTML.TidyAdd
+TYPE: lookup
+VERSION: 2.0.0
+DEFAULT: array()
+--DESCRIPTION--
+
+Fixes to add to the default set of Tidy fixes as per your level.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt
new file mode 100644 (file)
index 0000000..4186ccd
--- /dev/null
@@ -0,0 +1,24 @@
+HTML.TidyLevel
+TYPE: string
+VERSION: 2.0.0
+DEFAULT: 'medium'
+--DESCRIPTION--
+
+<p>General level of cleanliness the Tidy module should enforce.
+There are four allowed values:</p>
+<dl>
+    <dt>none</dt>
+    <dd>No extra tidying should be done</dd>
+    <dt>light</dt>
+    <dd>Only fix elements that would be discarded otherwise due to
+    lack of support in doctype</dd>
+    <dt>medium</dt>
+    <dd>Enforce best practices</dd>
+    <dt>heavy</dt>
+    <dd>Transform all deprecated elements and attributes to standards
+    compliant equivalents</dd>
+</dl>
+
+--ALLOWED--
+'none', 'light', 'medium', 'heavy'
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt
new file mode 100644 (file)
index 0000000..996762b
--- /dev/null
@@ -0,0 +1,8 @@
+HTML.TidyRemove
+TYPE: lookup
+VERSION: 2.0.0
+DEFAULT: array()
+--DESCRIPTION--
+
+Fixes to remove from the default set of Tidy fixes as per your level.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt
new file mode 100644 (file)
index 0000000..1db9237
--- /dev/null
@@ -0,0 +1,9 @@
+HTML.Trusted
+TYPE: bool
+VERSION: 2.0.0
+DEFAULT: false
+--DESCRIPTION--
+Indicates whether or not the user input is trusted or not. If the input is
+trusted, a more expansive set of allowed tags and attributes will be used.
+See also %CSS.Trusted.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt
new file mode 100644 (file)
index 0000000..2a47e38
--- /dev/null
@@ -0,0 +1,11 @@
+HTML.XHTML
+TYPE: bool
+DEFAULT: true
+VERSION: 1.1.0
+DEPRECATED-VERSION: 1.7.0
+DEPRECATED-USE: HTML.Doctype
+--DESCRIPTION--
+Determines whether or not output is XHTML 1.0 or HTML 4.01 flavor.
+--ALIASES--
+Core.XHTML
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt
new file mode 100644 (file)
index 0000000..08921fd
--- /dev/null
@@ -0,0 +1,10 @@
+Output.CommentScriptContents
+TYPE: bool
+VERSION: 2.0.0
+DEFAULT: true
+--DESCRIPTION--
+Determines whether or not HTML Purifier should attempt to fix up the
+contents of script tags for legacy browsers with comments.
+--ALIASES--
+Core.CommentScriptContents
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FixInnerHTML.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FixInnerHTML.txt
new file mode 100644 (file)
index 0000000..d6f0d9f
--- /dev/null
@@ -0,0 +1,15 @@
+Output.FixInnerHTML
+TYPE: bool
+VERSION: 4.3.0
+DEFAULT: true
+--DESCRIPTION--
+<p>
+  If true, HTML Purifier will protect against Internet Explorer's
+  mishandling of the <code>innerHTML</code> attribute by appending
+  a space to any attribute that does not contain angled brackets, spaces
+  or quotes, but contains a backtick.  This slightly changes the
+  semantics of any given attribute, so if this is unacceptable and
+  you do not use <code>innerHTML</code> on any of your pages, you can
+  turn this directive off.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt
new file mode 100644 (file)
index 0000000..93398e8
--- /dev/null
@@ -0,0 +1,11 @@
+Output.FlashCompat
+TYPE: bool
+VERSION: 4.1.0
+DEFAULT: false
+--DESCRIPTION--
+<p>
+  If true, HTML Purifier will generate Internet Explorer compatibility
+  code for all object code.  This is highly recommended if you enable
+  %HTML.SafeObject.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt
new file mode 100644 (file)
index 0000000..79f8ad8
--- /dev/null
@@ -0,0 +1,13 @@
+Output.Newline
+TYPE: string/null
+VERSION: 2.0.1
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    Newline string to format final output with. If left null, HTML Purifier
+    will auto-detect the default newline type of the system and use that;
+    you can manually override it here. Remember, \r\n is Windows, \r
+    is Mac, and \n is Unix.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt
new file mode 100644 (file)
index 0000000..232b023
--- /dev/null
@@ -0,0 +1,14 @@
+Output.SortAttr
+TYPE: bool
+VERSION: 3.2.0
+DEFAULT: false
+--DESCRIPTION--
+<p>
+  If true, HTML Purifier will sort attributes by name before writing them back
+  to the document, converting a tag like: <code>&lt;el b="" a="" c="" /&gt;</code>
+  to <code>&lt;el a="" b="" c="" /&gt;</code>. This is a workaround for
+  a bug in FCKeditor which causes it to swap attributes order, adding noise
+  to text diffs. If you're not seeing this bug, chances are, you don't need
+  this directive.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt
new file mode 100644 (file)
index 0000000..06bab00
--- /dev/null
@@ -0,0 +1,25 @@
+Output.TidyFormat
+TYPE: bool
+VERSION: 1.1.1
+DEFAULT: false
+--DESCRIPTION--
+<p>
+    Determines whether or not to run Tidy on the final output for pretty
+    formatting reasons, such as indentation and wrap.
+</p>
+<p>
+    This can greatly improve readability for editors who are hand-editing
+    the HTML, but is by no means necessary as HTML Purifier has already
+    fixed all major errors the HTML may have had. Tidy is a non-default
+    extension, and this directive will silently fail if Tidy is not
+    available.
+</p>
+<p>
+    If you are looking to make the overall look of your page's source
+    better, I recommend running Tidy on the entire page rather than just
+    user-content (after all, the indentation relative to the containing
+    blocks will be incorrect).
+</p>
+--ALIASES--
+Core.TidyFormat
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt
new file mode 100644 (file)
index 0000000..071bc02
--- /dev/null
@@ -0,0 +1,7 @@
+Test.ForceNoIconv
+TYPE: bool
+DEFAULT: false
+--DESCRIPTION--
+When set to true, HTMLPurifier_Encoder will act as if iconv does not exist
+and use only pure PHP implementations.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt
new file mode 100644 (file)
index 0000000..666635a
--- /dev/null
@@ -0,0 +1,17 @@
+URI.AllowedSchemes
+TYPE: lookup
+--DEFAULT--
+array (
+  'http' => true,
+  'https' => true,
+  'mailto' => true,
+  'ftp' => true,
+  'nntp' => true,
+  'news' => true,
+)
+--DESCRIPTION--
+Whitelist that defines the schemes that a URI is allowed to have.  This
+prevents XSS attacks from using pseudo-schemes like javascript or mocha.
+There is also support for the <code>data</code> and <code>file</code>
+URI schemes, but they are not enabled by default.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt
new file mode 100644 (file)
index 0000000..876f068
--- /dev/null
@@ -0,0 +1,17 @@
+URI.Base
+TYPE: string/null
+VERSION: 2.1.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    The base URI is the URI of the document this purified HTML will be
+    inserted into.  This information is important if HTML Purifier needs
+    to calculate absolute URIs from relative URIs, such as when %URI.MakeAbsolute
+    is on.  You may use a non-absolute URI for this value, but behavior
+    may vary (%URI.MakeAbsolute deals nicely with both absolute and
+    relative paths, but forwards-compatibility is not guaranteed).
+    <strong>Warning:</strong> If set, the scheme on this URI
+    overrides the one specified by %URI.DefaultScheme.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt
new file mode 100644 (file)
index 0000000..728e378
--- /dev/null
@@ -0,0 +1,10 @@
+URI.DefaultScheme
+TYPE: string
+DEFAULT: 'http'
+--DESCRIPTION--
+
+<p>
+    Defines through what scheme the output will be served, in order to
+    select the proper object validator when no scheme information is present.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt
new file mode 100644 (file)
index 0000000..f05312b
--- /dev/null
@@ -0,0 +1,11 @@
+URI.DefinitionID
+TYPE: string/null
+VERSION: 2.1.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    Unique identifier for a custom-built URI definition. If you  want
+    to add custom URIFilters, you must specify this value.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt
new file mode 100644 (file)
index 0000000..80cfea9
--- /dev/null
@@ -0,0 +1,11 @@
+URI.DefinitionRev
+TYPE: int
+VERSION: 2.1.0
+DEFAULT: 1
+--DESCRIPTION--
+
+<p>
+    Revision identifier for your custom definition. See
+    %HTML.DefinitionRev for details.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt
new file mode 100644 (file)
index 0000000..71ce025
--- /dev/null
@@ -0,0 +1,14 @@
+URI.Disable
+TYPE: bool
+VERSION: 1.3.0
+DEFAULT: false
+--DESCRIPTION--
+
+<p>
+    Disables all URIs in all forms. Not sure why you'd want to do that
+    (after all, the Internet's founded on the notion of a hyperlink).
+</p>
+
+--ALIASES--
+Attr.DisableURI
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt
new file mode 100644 (file)
index 0000000..13c122c
--- /dev/null
@@ -0,0 +1,11 @@
+URI.DisableExternal
+TYPE: bool
+VERSION: 1.2.0
+DEFAULT: false
+--DESCRIPTION--
+Disables links to external websites.  This is a highly effective anti-spam
+and anti-pagerank-leech measure, but comes at a hefty price: nolinks or
+images outside of your domain will be allowed.  Non-linkified URIs will
+still be preserved.  If you want to be able to link to subdomains or use
+absolute URIs, specify %URI.Host for your website.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt
new file mode 100644 (file)
index 0000000..abcc1ef
--- /dev/null
@@ -0,0 +1,13 @@
+URI.DisableExternalResources
+TYPE: bool
+VERSION: 1.3.0
+DEFAULT: false
+--DESCRIPTION--
+Disables the embedding of external resources, preventing users from
+embedding things like images from other hosts. This prevents access
+tracking (good for email viewers), bandwidth leeching, cross-site request
+forging, goatse.cx posting, and other nasties, but also results in a loss
+of end-user functionality (they can't directly post a pic they posted from
+Flickr anymore). Use it if you don't have a robust user-content moderation
+team.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt
new file mode 100644 (file)
index 0000000..f891de4
--- /dev/null
@@ -0,0 +1,15 @@
+URI.DisableResources
+TYPE: bool
+VERSION: 4.2.0
+DEFAULT: false
+--DESCRIPTION--
+<p>
+    Disables embedding resources, essentially meaning no pictures. You can
+    still link to them though. See %URI.DisableExternalResources for why
+    this might be a good idea.
+</p>
+<p>
+    <em>Note:</em> While this directive has been available since 1.3.0,
+    it didn't actually start doing anything until 4.2.0.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt
new file mode 100644 (file)
index 0000000..ee83b12
--- /dev/null
@@ -0,0 +1,19 @@
+URI.Host
+TYPE: string/null
+VERSION: 1.2.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    Defines the domain name of the server, so we can determine whether or
+    an absolute URI is from your website or not.  Not strictly necessary,
+    as users should be using relative URIs to reference resources on your
+    website.  It will, however, let you use absolute URIs to link to
+    subdomains of the domain you post here: i.e. example.com will allow
+    sub.example.com.  However, higher up domains will still be excluded:
+    if you set %URI.Host to sub.example.com, example.com will be blocked.
+    <strong>Note:</strong> This directive overrides %URI.Base because
+    a given page may be on a sub-domain, but you wish HTML Purifier to be
+    more relaxed and allow some of the parent domains too.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt
new file mode 100644 (file)
index 0000000..0b6df76
--- /dev/null
@@ -0,0 +1,9 @@
+URI.HostBlacklist
+TYPE: list
+VERSION: 1.3.0
+DEFAULT: array()
+--DESCRIPTION--
+List of strings that are forbidden in the host of any URI. Use it to kill
+domain names of spam, etc. Note that it will catch anything in the domain,
+so <tt>moo.com</tt> will catch <tt>moo.com.example.com</tt>.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt
new file mode 100644 (file)
index 0000000..4214900
--- /dev/null
@@ -0,0 +1,13 @@
+URI.MakeAbsolute
+TYPE: bool
+VERSION: 2.1.0
+DEFAULT: false
+--DESCRIPTION--
+
+<p>
+    Converts all URIs into absolute forms. This is useful when the HTML
+    being filtered assumes a specific base path, but will actually be
+    viewed in a different context (and setting an alternate base URI is
+    not possible). %URI.Base must be set for this directive to work.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt
new file mode 100644 (file)
index 0000000..58c81dc
--- /dev/null
@@ -0,0 +1,83 @@
+URI.Munge
+TYPE: string/null
+VERSION: 1.3.0
+DEFAULT: NULL
+--DESCRIPTION--
+
+<p>
+    Munges all browsable (usually http, https and ftp)
+    absolute URIs into another URI, usually a URI redirection service.
+    This directive accepts a URI, formatted with a <code>%s</code> where
+    the url-encoded original URI should be inserted (sample:
+    <code>http://www.google.com/url?q=%s</code>).
+</p>
+<p>
+    Uses for this directive:
+</p>
+<ul>
+    <li>
+        Prevent PageRank leaks, while being fairly transparent
+        to users (you may also want to add some client side JavaScript to
+        override the text in the statusbar). <strong>Notice</strong>:
+        Many security experts believe that this form of protection does not deter spam-bots.
+    </li>
+    <li>
+        Redirect users to a splash page telling them they are leaving your
+        website. While this is poor usability practice, it is often mandated
+        in corporate environments.
+    </li>
+</ul>
+<p>
+    Prior to HTML Purifier 3.1.1, this directive also enabled the munging
+    of browsable external resources, which could break things if your redirection
+    script was a splash page or used <code>meta</code> tags. To revert to
+    previous behavior, please use %URI.MungeResources.
+</p>
+<p>
+    You may want to also use %URI.MungeSecretKey along with this directive
+    in order to enforce what URIs your redirector script allows. Open
+    redirector scripts can be a security risk and negatively affect the
+    reputation of your domain name.
+</p>
+<p>
+    Starting with HTML Purifier 3.1.1, there is also these substitutions:
+</p>
+<table>
+    <thead>
+        <tr>
+            <th>Key</th>
+            <th>Description</th>
+            <th>Example <code>&lt;a href=""&gt;</code></th>
+        </tr>
+    </thead>
+    <tbody>
+        <tr>
+            <td>%r</td>
+            <td>1 - The URI embeds a resource<br />(blank) - The URI is merely a link</td>
+            <td></td>
+        </tr>
+        <tr>
+            <td>%n</td>
+            <td>The name of the tag this URI came from</td>
+            <td>a</td>
+        </tr>
+        <tr>
+            <td>%m</td>
+            <td>The name of the attribute this URI came from</td>
+            <td>href</td>
+        </tr>
+        <tr>
+            <td>%p</td>
+            <td>The name of the CSS property this URI came from, or blank if irrelevant</td>
+            <td></td>
+        </tr>
+    </tbody>
+</table>
+<p>
+    Admittedly, these letters are somewhat arbitrary; the only stipulation
+    was that they couldn't be a through f. r is for resource (I would have preferred
+    e, but you take what you can get), n is for name, m
+    was picked because it came after n (and I couldn't use a), p is for
+    property.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt
new file mode 100644 (file)
index 0000000..6fce0fd
--- /dev/null
@@ -0,0 +1,17 @@
+URI.MungeResources
+TYPE: bool
+VERSION: 3.1.1
+DEFAULT: false
+--DESCRIPTION--
+<p>
+    If true, any URI munging directives like %URI.Munge
+    will also apply to embedded resources, such as <code>&lt;img src=""&gt;</code>.
+    Be careful enabling this directive if you have a redirector script
+    that does not use the <code>Location</code> HTTP header; all of your images
+    and other embedded resources will break.
+</p>
+<p>
+    <strong>Warning:</strong> It is strongly advised you use this in conjunction
+    %URI.MungeSecretKey to mitigate the security risk of an open redirector.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt
new file mode 100644 (file)
index 0000000..1e17c1d
--- /dev/null
@@ -0,0 +1,30 @@
+URI.MungeSecretKey
+TYPE: string/null
+VERSION: 3.1.1
+DEFAULT: NULL
+--DESCRIPTION--
+<p>
+    This directive enables secure checksum generation along with %URI.Munge.
+    It should be set to a secure key that is not shared with anyone else.
+    The checksum can be placed in the URI using %t. Use of this checksum
+    affords an additional level of protection by allowing a redirector
+    to check if a URI has passed through HTML Purifier with this line:
+</p>
+
+<pre>$checksum === hash_hmac("sha256", $url, $secret_key)</pre>
+
+<p>
+    If the output is TRUE, the redirector script should accept the URI.
+</p>
+
+<p>
+    Please note that it would still be possible for an attacker to procure
+    secure hashes en-mass by abusing your website's Preview feature or the
+    like, but this service affords an additional level of protection
+    that should be combined with website blacklisting.
+</p>
+
+<p>
+    Remember this has no effect if %URI.Munge is not on.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt
new file mode 100644 (file)
index 0000000..23331a4
--- /dev/null
@@ -0,0 +1,9 @@
+URI.OverrideAllowedSchemes
+TYPE: bool
+DEFAULT: true
+--DESCRIPTION--
+If this is set to true (which it is by default), you can override
+%URI.AllowedSchemes by simply registering a HTMLPurifier_URIScheme to the
+registry.  If false, you will also have to update that directive in order
+to add more schemes.
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.SafeIframeRegexp.txt b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.SafeIframeRegexp.txt
new file mode 100644 (file)
index 0000000..7908483
--- /dev/null
@@ -0,0 +1,22 @@
+URI.SafeIframeRegexp
+TYPE: string/null
+VERSION: 4.4.0
+DEFAULT: NULL
+--DESCRIPTION--
+<p>
+    A PCRE regular expression that will be matched against an iframe URI.  This is
+    a relatively inflexible scheme, but works well enough for the most common
+    use-case of iframes: embedded video.  This directive only has an effect if
+    %HTML.SafeIframe is enabled.  Here are some example values:
+</p>
+<ul>
+    <li><code>%^http://www.youtube.com/embed/%</code> - Allow YouTube videos</li>
+    <li><code>%^http://player.vimeo.com/video/%</code> - Allow Vimeo videos</li>
+    <li><code>%^http://(www.youtube.com/embed/|player.vimeo.com/video/)%</code> - Allow both</li>
+</ul>
+<p>
+    Note that this directive does not give you enough granularity to, say, disable
+    all <code>autoplay</code> videos.  Pipe up on the HTML Purifier forums if this
+    is a capability you want.
+</p>
+--# vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/info.ini b/library/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/info.ini
new file mode 100644 (file)
index 0000000..5de4505
--- /dev/null
@@ -0,0 +1,3 @@
+name = "HTML Purifier"
+
+; vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ContentSets.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ContentSets.php
new file mode 100644 (file)
index 0000000..543e3f8
--- /dev/null
@@ -0,0 +1,170 @@
+<?php
+
+/**
+ * @todo Unit test
+ */
+class HTMLPurifier_ContentSets
+{
+
+    /**
+     * List of content set strings (pipe separators) indexed by name.
+     * @type array
+     */
+    public $info = array();
+
+    /**
+     * List of content set lookups (element => true) indexed by name.
+     * @type array
+     * @note This is in HTMLPurifier_HTMLDefinition->info_content_sets
+     */
+    public $lookup = array();
+
+    /**
+     * Synchronized list of defined content sets (keys of info).
+     * @type array
+     */
+    protected $keys = array();
+    /**
+     * Synchronized list of defined content values (values of info).
+     * @type array
+     */
+    protected $values = array();
+
+    /**
+     * Merges in module's content sets, expands identifiers in the content
+     * sets and populates the keys, values and lookup member variables.
+     * @param HTMLPurifier_HTMLModule[] $modules List of HTMLPurifier_HTMLModule
+     */
+    public function __construct($modules)
+    {
+        if (!is_array($modules)) {
+            $modules = array($modules);
+        }
+        // populate content_sets based on module hints
+        // sorry, no way of overloading
+        foreach ($modules as $module) {
+            foreach ($module->content_sets as $key => $value) {
+                $temp = $this->convertToLookup($value);
+                if (isset($this->lookup[$key])) {
+                    // add it into the existing content set
+                    $this->lookup[$key] = array_merge($this->lookup[$key], $temp);
+                } else {
+                    $this->lookup[$key] = $temp;
+                }
+            }
+        }
+        $old_lookup = false;
+        while ($old_lookup !== $this->lookup) {
+            $old_lookup = $this->lookup;
+            foreach ($this->lookup as $i => $set) {
+                $add = array();
+                foreach ($set as $element => $x) {
+                    if (isset($this->lookup[$element])) {
+                        $add += $this->lookup[$element];
+                        unset($this->lookup[$i][$element]);
+                    }
+                }
+                $this->lookup[$i] += $add;
+            }
+        }
+
+        foreach ($this->lookup as $key => $lookup) {
+            $this->info[$key] = implode(' | ', array_keys($lookup));
+        }
+        $this->keys   = array_keys($this->info);
+        $this->values = array_values($this->info);
+    }
+
+    /**
+     * Accepts a definition; generates and assigns a ChildDef for it
+     * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef reference
+     * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef
+     */
+    public function generateChildDef(&$def, $module)
+    {
+        if (!empty($def->child)) { // already done!
+            return;
+        }
+        $content_model = $def->content_model;
+        if (is_string($content_model)) {
+            // Assume that $this->keys is alphanumeric
+            $def->content_model = preg_replace_callback(
+                '/\b(' . implode('|', $this->keys) . ')\b/',
+                array($this, 'generateChildDefCallback'),
+                $content_model
+            );
+            //$def->content_model = str_replace(
+            //    $this->keys, $this->values, $content_model);
+        }
+        $def->child = $this->getChildDef($def, $module);
+    }
+
+    public function generateChildDefCallback($matches)
+    {
+        return $this->info[$matches[0]];
+    }
+
+    /**
+     * Instantiates a ChildDef based on content_model and content_model_type
+     * member variables in HTMLPurifier_ElementDef
+     * @note This will also defer to modules for custom HTMLPurifier_ChildDef
+     *       subclasses that need content set expansion
+     * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef to have ChildDef extracted
+     * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef
+     * @return HTMLPurifier_ChildDef corresponding to ElementDef
+     */
+    public function getChildDef($def, $module)
+    {
+        $value = $def->content_model;
+        if (is_object($value)) {
+            trigger_error(
+                'Literal object child definitions should be stored in '.
+                'ElementDef->child not ElementDef->content_model',
+                E_USER_NOTICE
+            );
+            return $value;
+        }
+        switch ($def->content_model_type) {
+            case 'required':
+                return new HTMLPurifier_ChildDef_Required($value);
+            case 'optional':
+                return new HTMLPurifier_ChildDef_Optional($value);
+            case 'empty':
+                return new HTMLPurifier_ChildDef_Empty();
+            case 'custom':
+                return new HTMLPurifier_ChildDef_Custom($value);
+        }
+        // defer to its module
+        $return = false;
+        if ($module->defines_child_def) { // save a func call
+            $return = $module->getChildDef($def);
+        }
+        if ($return !== false) {
+            return $return;
+        }
+        // error-out
+        trigger_error(
+            'Could not determine which ChildDef class to instantiate',
+            E_USER_ERROR
+        );
+        return false;
+    }
+
+    /**
+     * Converts a string list of elements separated by pipes into
+     * a lookup array.
+     * @param string $string List of elements
+     * @return array Lookup array of elements
+     */
+    protected function convertToLookup($string)
+    {
+        $array = explode('|', str_replace(' ', '', $string));
+        $ret = array();
+        foreach ($array as $k) {
+            $ret[$k] = true;
+        }
+        return $ret;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Context.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Context.php
new file mode 100644 (file)
index 0000000..00e509c
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+
+/**
+ * Registry object that contains information about the current context.
+ * @warning Is a bit buggy when variables are set to null: it thinks
+ *          they don't exist! So use false instead, please.
+ * @note Since the variables Context deals with may not be objects,
+ *       references are very important here! Do not remove!
+ */
+class HTMLPurifier_Context
+{
+
+    /**
+     * Private array that stores the references.
+     * @type array
+     */
+    private $_storage = array();
+
+    /**
+     * Registers a variable into the context.
+     * @param string $name String name
+     * @param mixed $ref Reference to variable to be registered
+     */
+    public function register($name, &$ref)
+    {
+        if (array_key_exists($name, $this->_storage)) {
+            trigger_error(
+                "Name $name produces collision, cannot re-register",
+                E_USER_ERROR
+            );
+            return;
+        }
+        $this->_storage[$name] =& $ref;
+    }
+
+    /**
+     * Retrieves a variable reference from the context.
+     * @param string $name String name
+     * @param bool $ignore_error Boolean whether or not to ignore error
+     * @return mixed
+     */
+    public function &get($name, $ignore_error = false)
+    {
+        if (!array_key_exists($name, $this->_storage)) {
+            if (!$ignore_error) {
+                trigger_error(
+                    "Attempted to retrieve non-existent variable $name",
+                    E_USER_ERROR
+                );
+            }
+            $var = null; // so we can return by reference
+            return $var;
+        }
+        return $this->_storage[$name];
+    }
+
+    /**
+     * Destroys a variable in the context.
+     * @param string $name String name
+     */
+    public function destroy($name)
+    {
+        if (!array_key_exists($name, $this->_storage)) {
+            trigger_error(
+                "Attempted to destroy non-existent variable $name",
+                E_USER_ERROR
+            );
+            return;
+        }
+        unset($this->_storage[$name]);
+    }
+
+    /**
+     * Checks whether or not the variable exists.
+     * @param string $name String name
+     * @return bool
+     */
+    public function exists($name)
+    {
+        return array_key_exists($name, $this->_storage);
+    }
+
+    /**
+     * Loads a series of variables from an associative array
+     * @param array $context_array Assoc array of variables to load
+     */
+    public function loadArray($context_array)
+    {
+        foreach ($context_array as $key => $discard) {
+            $this->register($key, $context_array[$key]);
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Definition.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Definition.php
new file mode 100644 (file)
index 0000000..bc6d433
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * Super-class for definition datatype objects, implements serialization
+ * functions for the class.
+ */
+abstract class HTMLPurifier_Definition
+{
+
+    /**
+     * Has setup() been called yet?
+     * @type bool
+     */
+    public $setup = false;
+
+    /**
+     * If true, write out the final definition object to the cache after
+     * setup.  This will be true only if all invocations to get a raw
+     * definition object are also optimized.  This does not cause file
+     * system thrashing because on subsequent calls the cached object
+     * is used and any writes to the raw definition object are short
+     * circuited.  See enduser-customize.html for the high-level
+     * picture.
+     * @type bool
+     */
+    public $optimized = null;
+
+    /**
+     * What type of definition is it?
+     * @type string
+     */
+    public $type;
+
+    /**
+     * Sets up the definition object into the final form, something
+     * not done by the constructor
+     * @param HTMLPurifier_Config $config
+     */
+    abstract protected function doSetup($config);
+
+    /**
+     * Setup function that aborts if already setup
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        if ($this->setup) {
+            return;
+        }
+        $this->setup = true;
+        $this->doSetup($config);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache.php
new file mode 100644 (file)
index 0000000..67bb5b1
--- /dev/null
@@ -0,0 +1,129 @@
+<?php
+
+/**
+ * Abstract class representing Definition cache managers that implements
+ * useful common methods and is a factory.
+ * @todo Create a separate maintenance file advanced users can use to
+ *       cache their custom HTMLDefinition, which can be loaded
+ *       via a configuration directive
+ * @todo Implement memcached
+ */
+abstract class HTMLPurifier_DefinitionCache
+{
+    /**
+     * @type string
+     */
+    public $type;
+
+    /**
+     * @param string $type Type of definition objects this instance of the
+     *      cache will handle.
+     */
+    public function __construct($type)
+    {
+        $this->type = $type;
+    }
+
+    /**
+     * Generates a unique identifier for a particular configuration
+     * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config
+     * @return string
+     */
+    public function generateKey($config)
+    {
+        return $config->version . ',' . // possibly replace with function calls
+               $config->getBatchSerial($this->type) . ',' .
+               $config->get($this->type . '.DefinitionRev');
+    }
+
+    /**
+     * Tests whether or not a key is old with respect to the configuration's
+     * version and revision number.
+     * @param string $key Key to test
+     * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config to test against
+     * @return bool
+     */
+    public function isOld($key, $config)
+    {
+        if (substr_count($key, ',') < 2) {
+            return true;
+        }
+        list($version, $hash, $revision) = explode(',', $key, 3);
+        $compare = version_compare($version, $config->version);
+        // version mismatch, is always old
+        if ($compare != 0) {
+            return true;
+        }
+        // versions match, ids match, check revision number
+        if ($hash == $config->getBatchSerial($this->type) &&
+            $revision < $config->get($this->type . '.DefinitionRev')) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Checks if a definition's type jives with the cache's type
+     * @note Throws an error on failure
+     * @param HTMLPurifier_Definition $def Definition object to check
+     * @return bool true if good, false if not
+     */
+    public function checkDefType($def)
+    {
+        if ($def->type !== $this->type) {
+            trigger_error("Cannot use definition of type {$def->type} in cache for {$this->type}");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Adds a definition object to the cache
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     */
+    abstract public function add($def, $config);
+
+    /**
+     * Unconditionally saves a definition object to the cache
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     */
+    abstract public function set($def, $config);
+
+    /**
+     * Replace an object in the cache
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     */
+    abstract public function replace($def, $config);
+
+    /**
+     * Retrieves a definition object from the cache
+     * @param HTMLPurifier_Config $config
+     */
+    abstract public function get($config);
+
+    /**
+     * Removes a definition object to the cache
+     * @param HTMLPurifier_Config $config
+     */
+    abstract public function remove($config);
+
+    /**
+     * Clears all objects from cache
+     * @param HTMLPurifier_Config $config
+     */
+    abstract public function flush($config);
+
+    /**
+     * Clears all expired (older version or revision) objects from cache
+     * @note Be carefuly implementing this method as flush. Flush must
+     *       not interfere with other Definition types, and cleanup()
+     *       should not be repeatedly called by userland code.
+     * @param HTMLPurifier_Config $config
+     */
+    abstract public function cleanup($config);
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator.php
new file mode 100644 (file)
index 0000000..b57a51b
--- /dev/null
@@ -0,0 +1,112 @@
+<?php
+
+class HTMLPurifier_DefinitionCache_Decorator extends HTMLPurifier_DefinitionCache
+{
+
+    /**
+     * Cache object we are decorating
+     * @type HTMLPurifier_DefinitionCache
+     */
+    public $cache;
+
+    /**
+     * The name of the decorator
+     * @var string
+     */
+    public $name;
+
+    public function __construct()
+    {
+    }
+
+    /**
+     * Lazy decorator function
+     * @param HTMLPurifier_DefinitionCache $cache Reference to cache object to decorate
+     * @return HTMLPurifier_DefinitionCache_Decorator
+     */
+    public function decorate(&$cache)
+    {
+        $decorator = $this->copy();
+        // reference is necessary for mocks in PHP 4
+        $decorator->cache =& $cache;
+        $decorator->type = $cache->type;
+        return $decorator;
+    }
+
+    /**
+     * Cross-compatible clone substitute
+     * @return HTMLPurifier_DefinitionCache_Decorator
+     */
+    public function copy()
+    {
+        return new HTMLPurifier_DefinitionCache_Decorator();
+    }
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function add($def, $config)
+    {
+        return $this->cache->add($def, $config);
+    }
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function set($def, $config)
+    {
+        return $this->cache->set($def, $config);
+    }
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function replace($def, $config)
+    {
+        return $this->cache->replace($def, $config);
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function get($config)
+    {
+        return $this->cache->get($config);
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function remove($config)
+    {
+        return $this->cache->remove($config);
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function flush($config)
+    {
+        return $this->cache->flush($config);
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function cleanup($config)
+    {
+        return $this->cache->cleanup($config);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php
new file mode 100644 (file)
index 0000000..4991777
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * Definition cache decorator class that cleans up the cache
+ * whenever there is a cache miss.
+ */
+class HTMLPurifier_DefinitionCache_Decorator_Cleanup extends HTMLPurifier_DefinitionCache_Decorator
+{
+    /**
+     * @type string
+     */
+    public $name = 'Cleanup';
+
+    /**
+     * @return HTMLPurifier_DefinitionCache_Decorator_Cleanup
+     */
+    public function copy()
+    {
+        return new HTMLPurifier_DefinitionCache_Decorator_Cleanup();
+    }
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function add($def, $config)
+    {
+        $status = parent::add($def, $config);
+        if (!$status) {
+            parent::cleanup($config);
+        }
+        return $status;
+    }
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function set($def, $config)
+    {
+        $status = parent::set($def, $config);
+        if (!$status) {
+            parent::cleanup($config);
+        }
+        return $status;
+    }
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function replace($def, $config)
+    {
+        $status = parent::replace($def, $config);
+        if (!$status) {
+            parent::cleanup($config);
+        }
+        return $status;
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function get($config)
+    {
+        $ret = parent::get($config);
+        if (!$ret) {
+            parent::cleanup($config);
+        }
+        return $ret;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Memory.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Memory.php
new file mode 100644 (file)
index 0000000..d529dce
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * Definition cache decorator class that saves all cache retrievals
+ * to PHP's memory; good for unit tests or circumstances where
+ * there are lots of configuration objects floating around.
+ */
+class HTMLPurifier_DefinitionCache_Decorator_Memory extends HTMLPurifier_DefinitionCache_Decorator
+{
+    /**
+     * @type array
+     */
+    protected $definitions;
+
+    /**
+     * @type string
+     */
+    public $name = 'Memory';
+
+    /**
+     * @return HTMLPurifier_DefinitionCache_Decorator_Memory
+     */
+    public function copy()
+    {
+        return new HTMLPurifier_DefinitionCache_Decorator_Memory();
+    }
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function add($def, $config)
+    {
+        $status = parent::add($def, $config);
+        if ($status) {
+            $this->definitions[$this->generateKey($config)] = $def;
+        }
+        return $status;
+    }
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function set($def, $config)
+    {
+        $status = parent::set($def, $config);
+        if ($status) {
+            $this->definitions[$this->generateKey($config)] = $def;
+        }
+        return $status;
+    }
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function replace($def, $config)
+    {
+        $status = parent::replace($def, $config);
+        if ($status) {
+            $this->definitions[$this->generateKey($config)] = $def;
+        }
+        return $status;
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function get($config)
+    {
+        $key = $this->generateKey($config);
+        if (isset($this->definitions[$key])) {
+            return $this->definitions[$key];
+        }
+        $this->definitions[$key] = parent::get($config);
+        return $this->definitions[$key];
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in b/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in
new file mode 100644 (file)
index 0000000..b1fec8d
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+
+require_once 'HTMLPurifier/DefinitionCache/Decorator.php';
+
+/**
+ * Definition cache decorator template.
+ */
+class HTMLPurifier_DefinitionCache_Decorator_Template extends HTMLPurifier_DefinitionCache_Decorator
+{
+
+    /**
+     * @type string
+     */
+    public $name = 'Template'; // replace this
+
+    public function copy()
+    {
+        // replace class name with yours
+        return new HTMLPurifier_DefinitionCache_Decorator_Template();
+    }
+
+    // remove methods you don't need
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function add($def, $config)
+    {
+        return parent::add($def, $config);
+    }
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function set($def, $config)
+    {
+        return parent::set($def, $config);
+    }
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function replace($def, $config)
+    {
+        return parent::replace($def, $config);
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function get($config)
+    {
+        return parent::get($config);
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function flush($config)
+    {
+        return parent::flush($config);
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return mixed
+     */
+    public function cleanup($config)
+    {
+        return parent::cleanup($config);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Null.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Null.php
new file mode 100644 (file)
index 0000000..d9a75ce
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * Null cache object to use when no caching is on.
+ */
+class HTMLPurifier_DefinitionCache_Null extends HTMLPurifier_DefinitionCache
+{
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return bool
+     */
+    public function add($def, $config)
+    {
+        return false;
+    }
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return bool
+     */
+    public function set($def, $config)
+    {
+        return false;
+    }
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return bool
+     */
+    public function replace($def, $config)
+    {
+        return false;
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return bool
+     */
+    public function remove($config)
+    {
+        return false;
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return bool
+     */
+    public function get($config)
+    {
+        return false;
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return bool
+     */
+    public function flush($config)
+    {
+        return false;
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return bool
+     */
+    public function cleanup($config)
+    {
+        return false;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer.php
new file mode 100644 (file)
index 0000000..ce268d9
--- /dev/null
@@ -0,0 +1,291 @@
+<?php
+
+class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCache
+{
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return int|bool
+     */
+    public function add($def, $config)
+    {
+        if (!$this->checkDefType($def)) {
+            return;
+        }
+        $file = $this->generateFilePath($config);
+        if (file_exists($file)) {
+            return false;
+        }
+        if (!$this->_prepareDir($config)) {
+            return false;
+        }
+        return $this->_write($file, serialize($def), $config);
+    }
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return int|bool
+     */
+    public function set($def, $config)
+    {
+        if (!$this->checkDefType($def)) {
+            return;
+        }
+        $file = $this->generateFilePath($config);
+        if (!$this->_prepareDir($config)) {
+            return false;
+        }
+        return $this->_write($file, serialize($def), $config);
+    }
+
+    /**
+     * @param HTMLPurifier_Definition $def
+     * @param HTMLPurifier_Config $config
+     * @return int|bool
+     */
+    public function replace($def, $config)
+    {
+        if (!$this->checkDefType($def)) {
+            return;
+        }
+        $file = $this->generateFilePath($config);
+        if (!file_exists($file)) {
+            return false;
+        }
+        if (!$this->_prepareDir($config)) {
+            return false;
+        }
+        return $this->_write($file, serialize($def), $config);
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return bool|HTMLPurifier_Config
+     */
+    public function get($config)
+    {
+        $file = $this->generateFilePath($config);
+        if (!file_exists($file)) {
+            return false;
+        }
+        return unserialize(file_get_contents($file));
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return bool
+     */
+    public function remove($config)
+    {
+        $file = $this->generateFilePath($config);
+        if (!file_exists($file)) {
+            return false;
+        }
+        return unlink($file);
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return bool
+     */
+    public function flush($config)
+    {
+        if (!$this->_prepareDir($config)) {
+            return false;
+        }
+        $dir = $this->generateDirectoryPath($config);
+        $dh = opendir($dir);
+        while (false !== ($filename = readdir($dh))) {
+            if (empty($filename)) {
+                continue;
+            }
+            if ($filename[0] === '.') {
+                continue;
+            }
+            unlink($dir . '/' . $filename);
+        }
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return bool
+     */
+    public function cleanup($config)
+    {
+        if (!$this->_prepareDir($config)) {
+            return false;
+        }
+        $dir = $this->generateDirectoryPath($config);
+        $dh = opendir($dir);
+        while (false !== ($filename = readdir($dh))) {
+            if (empty($filename)) {
+                continue;
+            }
+            if ($filename[0] === '.') {
+                continue;
+            }
+            $key = substr($filename, 0, strlen($filename) - 4);
+            if ($this->isOld($key, $config)) {
+                unlink($dir . '/' . $filename);
+            }
+        }
+    }
+
+    /**
+     * Generates the file path to the serial file corresponding to
+     * the configuration and definition name
+     * @param HTMLPurifier_Config $config
+     * @return string
+     * @todo Make protected
+     */
+    public function generateFilePath($config)
+    {
+        $key = $this->generateKey($config);
+        return $this->generateDirectoryPath($config) . '/' . $key . '.ser';
+    }
+
+    /**
+     * Generates the path to the directory contain this cache's serial files
+     * @param HTMLPurifier_Config $config
+     * @return string
+     * @note No trailing slash
+     * @todo Make protected
+     */
+    public function generateDirectoryPath($config)
+    {
+        $base = $this->generateBaseDirectoryPath($config);
+        return $base . '/' . $this->type;
+    }
+
+    /**
+     * Generates path to base directory that contains all definition type
+     * serials
+     * @param HTMLPurifier_Config $config
+     * @return mixed|string
+     * @todo Make protected
+     */
+    public function generateBaseDirectoryPath($config)
+    {
+        $base = $config->get('Cache.SerializerPath');
+        $base = is_null($base) ? HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer' : $base;
+        return $base;
+    }
+
+    /**
+     * Convenience wrapper function for file_put_contents
+     * @param string $file File name to write to
+     * @param string $data Data to write into file
+     * @param HTMLPurifier_Config $config
+     * @return int|bool Number of bytes written if success, or false if failure.
+     */
+    private function _write($file, $data, $config)
+    {
+        $result = file_put_contents($file, $data);
+        if ($result !== false) {
+            // set permissions of the new file (no execute)
+            $chmod = $config->get('Cache.SerializerPermissions');
+            if (!$chmod) {
+                $chmod = 0644; // invalid config or simpletest
+            }
+            $chmod = $chmod & 0666;
+            chmod($file, $chmod);
+        }
+        return $result;
+    }
+
+    /**
+     * Prepares the directory that this type stores the serials in
+     * @param HTMLPurifier_Config $config
+     * @return bool True if successful
+     */
+    private function _prepareDir($config)
+    {
+        $directory = $this->generateDirectoryPath($config);
+        $chmod = $config->get('Cache.SerializerPermissions');
+        if (!$chmod) {
+            $chmod = 0755; // invalid config or simpletest
+        }
+        if (!is_dir($directory)) {
+            $base = $this->generateBaseDirectoryPath($config);
+            if (!is_dir($base)) {
+                trigger_error(
+                    'Base directory ' . $base . ' does not exist,
+                    please create or change using %Cache.SerializerPath',
+                    E_USER_WARNING
+                );
+                return false;
+            } elseif (!$this->_testPermissions($base, $chmod)) {
+                return false;
+            }
+            mkdir($directory, $chmod);
+            if (!$this->_testPermissions($directory, $chmod)) {
+                trigger_error(
+                    'Base directory ' . $base . ' does not exist,
+                    please create or change using %Cache.SerializerPath',
+                    E_USER_WARNING
+                );
+                return false;
+            }
+        } elseif (!$this->_testPermissions($directory, $chmod)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Tests permissions on a directory and throws out friendly
+     * error messages and attempts to chmod it itself if possible
+     * @param string $dir Directory path
+     * @param int $chmod Permissions
+     * @return bool True if directory is writable
+     */
+    private function _testPermissions($dir, $chmod)
+    {
+        // early abort, if it is writable, everything is hunky-dory
+        if (is_writable($dir)) {
+            return true;
+        }
+        if (!is_dir($dir)) {
+            // generally, you'll want to handle this beforehand
+            // so a more specific error message can be given
+            trigger_error(
+                'Directory ' . $dir . ' does not exist',
+                E_USER_WARNING
+            );
+            return false;
+        }
+        if (function_exists('posix_getuid')) {
+            // POSIX system, we can give more specific advice
+            if (fileowner($dir) === posix_getuid()) {
+                // we can chmod it ourselves
+                $chmod = $chmod | 0700;
+                if (chmod($dir, $chmod)) {
+                    return true;
+                }
+            } elseif (filegroup($dir) === posix_getgid()) {
+                $chmod = $chmod | 0070;
+            } else {
+                // PHP's probably running as nobody, so we'll
+                // need to give global permissions
+                $chmod = $chmod | 0777;
+            }
+            trigger_error(
+                'Directory ' . $dir . ' not writable, ' .
+                'please chmod to ' . decoct($chmod),
+                E_USER_WARNING
+            );
+        } else {
+            // generic error message
+            trigger_error(
+                'Directory ' . $dir . ' not writable, ' .
+                'please alter file permissions',
+                E_USER_WARNING
+            );
+        }
+        return false;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/README b/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/README
new file mode 100644 (file)
index 0000000..2e35c1c
--- /dev/null
@@ -0,0 +1,3 @@
+This is a dummy file to prevent Git from ignoring this empty directory.
+
+    vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCacheFactory.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCacheFactory.php
new file mode 100644 (file)
index 0000000..fd1cc9b
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+
+/**
+ * Responsible for creating definition caches.
+ */
+class HTMLPurifier_DefinitionCacheFactory
+{
+    /**
+     * @type array
+     */
+    protected $caches = array('Serializer' => array());
+
+    /**
+     * @type array
+     */
+    protected $implementations = array();
+
+    /**
+     * @type HTMLPurifier_DefinitionCache_Decorator[]
+     */
+    protected $decorators = array();
+
+    /**
+     * Initialize default decorators
+     */
+    public function setup()
+    {
+        $this->addDecorator('Cleanup');
+    }
+
+    /**
+     * Retrieves an instance of global definition cache factory.
+     * @param HTMLPurifier_DefinitionCacheFactory $prototype
+     * @return HTMLPurifier_DefinitionCacheFactory
+     */
+    public static function instance($prototype = null)
+    {
+        static $instance;
+        if ($prototype !== null) {
+            $instance = $prototype;
+        } elseif ($instance === null || $prototype === true) {
+            $instance = new HTMLPurifier_DefinitionCacheFactory();
+            $instance->setup();
+        }
+        return $instance;
+    }
+
+    /**
+     * Registers a new definition cache object
+     * @param string $short Short name of cache object, for reference
+     * @param string $long Full class name of cache object, for construction
+     */
+    public function register($short, $long)
+    {
+        $this->implementations[$short] = $long;
+    }
+
+    /**
+     * Factory method that creates a cache object based on configuration
+     * @param string $type Name of definitions handled by cache
+     * @param HTMLPurifier_Config $config Config instance
+     * @return mixed
+     */
+    public function create($type, $config)
+    {
+        $method = $config->get('Cache.DefinitionImpl');
+        if ($method === null) {
+            return new HTMLPurifier_DefinitionCache_Null($type);
+        }
+        if (!empty($this->caches[$method][$type])) {
+            return $this->caches[$method][$type];
+        }
+        if (isset($this->implementations[$method]) &&
+            class_exists($class = $this->implementations[$method], false)) {
+            $cache = new $class($type);
+        } else {
+            if ($method != 'Serializer') {
+                trigger_error("Unrecognized DefinitionCache $method, using Serializer instead", E_USER_WARNING);
+            }
+            $cache = new HTMLPurifier_DefinitionCache_Serializer($type);
+        }
+        foreach ($this->decorators as $decorator) {
+            $new_cache = $decorator->decorate($cache);
+            // prevent infinite recursion in PHP 4
+            unset($cache);
+            $cache = $new_cache;
+        }
+        $this->caches[$method][$type] = $cache;
+        return $this->caches[$method][$type];
+    }
+
+    /**
+     * Registers a decorator to add to all new cache objects
+     * @param HTMLPurifier_DefinitionCache_Decorator|string $decorator An instance or the name of a decorator
+     */
+    public function addDecorator($decorator)
+    {
+        if (is_string($decorator)) {
+            $class = "HTMLPurifier_DefinitionCache_Decorator_$decorator";
+            $decorator = new $class;
+        }
+        $this->decorators[$decorator->name] = $decorator;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Doctype.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Doctype.php
new file mode 100644 (file)
index 0000000..4acd06e
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * Represents a document type, contains information on which modules
+ * need to be loaded.
+ * @note This class is inspected by Printer_HTMLDefinition->renderDoctype.
+ *       If structure changes, please update that function.
+ */
+class HTMLPurifier_Doctype
+{
+    /**
+     * Full name of doctype
+     * @type string
+     */
+    public $name;
+
+    /**
+     * List of standard modules (string identifiers or literal objects)
+     * that this doctype uses
+     * @type array
+     */
+    public $modules = array();
+
+    /**
+     * List of modules to use for tidying up code
+     * @type array
+     */
+    public $tidyModules = array();
+
+    /**
+     * Is the language derived from XML (i.e. XHTML)?
+     * @type bool
+     */
+    public $xml = true;
+
+    /**
+     * List of aliases for this doctype
+     * @type array
+     */
+    public $aliases = array();
+
+    /**
+     * Public DTD identifier
+     * @type string
+     */
+    public $dtdPublic;
+
+    /**
+     * System DTD identifier
+     * @type string
+     */
+    public $dtdSystem;
+
+    public function __construct(
+        $name = null,
+        $xml = true,
+        $modules = array(),
+        $tidyModules = array(),
+        $aliases = array(),
+        $dtd_public = null,
+        $dtd_system = null
+    ) {
+        $this->name         = $name;
+        $this->xml          = $xml;
+        $this->modules      = $modules;
+        $this->tidyModules  = $tidyModules;
+        $this->aliases      = $aliases;
+        $this->dtdPublic    = $dtd_public;
+        $this->dtdSystem    = $dtd_system;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/DoctypeRegistry.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/DoctypeRegistry.php
new file mode 100644 (file)
index 0000000..acc1d64
--- /dev/null
@@ -0,0 +1,142 @@
+<?php
+
+class HTMLPurifier_DoctypeRegistry
+{
+
+    /**
+     * Hash of doctype names to doctype objects.
+     * @type array
+     */
+    protected $doctypes;
+
+    /**
+     * Lookup table of aliases to real doctype names.
+     * @type array
+     */
+    protected $aliases;
+
+    /**
+     * Registers a doctype to the registry
+     * @note Accepts a fully-formed doctype object, or the
+     *       parameters for constructing a doctype object
+     * @param string $doctype Name of doctype or literal doctype object
+     * @param bool $xml
+     * @param array $modules Modules doctype will load
+     * @param array $tidy_modules Modules doctype will load for certain modes
+     * @param array $aliases Alias names for doctype
+     * @param string $dtd_public
+     * @param string $dtd_system
+     * @return HTMLPurifier_Doctype Editable registered doctype
+     */
+    public function register(
+        $doctype,
+        $xml = true,
+        $modules = array(),
+        $tidy_modules = array(),
+        $aliases = array(),
+        $dtd_public = null,
+        $dtd_system = null
+    ) {
+        if (!is_array($modules)) {
+            $modules = array($modules);
+        }
+        if (!is_array($tidy_modules)) {
+            $tidy_modules = array($tidy_modules);
+        }
+        if (!is_array($aliases)) {
+            $aliases = array($aliases);
+        }
+        if (!is_object($doctype)) {
+            $doctype = new HTMLPurifier_Doctype(
+                $doctype,
+                $xml,
+                $modules,
+                $tidy_modules,
+                $aliases,
+                $dtd_public,
+                $dtd_system
+            );
+        }
+        $this->doctypes[$doctype->name] = $doctype;
+        $name = $doctype->name;
+        // hookup aliases
+        foreach ($doctype->aliases as $alias) {
+            if (isset($this->doctypes[$alias])) {
+                continue;
+            }
+            $this->aliases[$alias] = $name;
+        }
+        // remove old aliases
+        if (isset($this->aliases[$name])) {
+            unset($this->aliases[$name]);
+        }
+        return $doctype;
+    }
+
+    /**
+     * Retrieves reference to a doctype of a certain name
+     * @note This function resolves aliases
+     * @note When possible, use the more fully-featured make()
+     * @param string $doctype Name of doctype
+     * @return HTMLPurifier_Doctype Editable doctype object
+     */
+    public function get($doctype)
+    {
+        if (isset($this->aliases[$doctype])) {
+            $doctype = $this->aliases[$doctype];
+        }
+        if (!isset($this->doctypes[$doctype])) {
+            trigger_error('Doctype ' . htmlspecialchars($doctype) . ' does not exist', E_USER_ERROR);
+            $anon = new HTMLPurifier_Doctype($doctype);
+            return $anon;
+        }
+        return $this->doctypes[$doctype];
+    }
+
+    /**
+     * Creates a doctype based on a configuration object,
+     * will perform initialization on the doctype
+     * @note Use this function to get a copy of doctype that config
+     *       can hold on to (this is necessary in order to tell
+     *       Generator whether or not the current document is XML
+     *       based or not).
+     * @param HTMLPurifier_Config $config
+     * @return HTMLPurifier_Doctype
+     */
+    public function make($config)
+    {
+        return clone $this->get($this->getDoctypeFromConfig($config));
+    }
+
+    /**
+     * Retrieves the doctype from the configuration object
+     * @param HTMLPurifier_Config $config
+     * @return string
+     */
+    public function getDoctypeFromConfig($config)
+    {
+        // recommended test
+        $doctype = $config->get('HTML.Doctype');
+        if (!empty($doctype)) {
+            return $doctype;
+        }
+        $doctype = $config->get('HTML.CustomDoctype');
+        if (!empty($doctype)) {
+            return $doctype;
+        }
+        // backwards-compatibility
+        if ($config->get('HTML.XHTML')) {
+            $doctype = 'XHTML 1.0';
+        } else {
+            $doctype = 'HTML 4.01';
+        }
+        if ($config->get('HTML.Strict')) {
+            $doctype .= ' Strict';
+        } else {
+            $doctype .= ' Transitional';
+        }
+        return $doctype;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ElementDef.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ElementDef.php
new file mode 100644 (file)
index 0000000..d5311ce
--- /dev/null
@@ -0,0 +1,216 @@
+<?php
+
+/**
+ * Structure that stores an HTML element definition. Used by
+ * HTMLPurifier_HTMLDefinition and HTMLPurifier_HTMLModule.
+ * @note This class is inspected by HTMLPurifier_Printer_HTMLDefinition.
+ *       Please update that class too.
+ * @warning If you add new properties to this class, you MUST update
+ *          the mergeIn() method.
+ */
+class HTMLPurifier_ElementDef
+{
+    /**
+     * Does the definition work by itself, or is it created solely
+     * for the purpose of merging into another definition?
+     * @type bool
+     */
+    public $standalone = true;
+
+    /**
+     * Associative array of attribute name to HTMLPurifier_AttrDef.
+     * @type array
+     * @note Before being processed by HTMLPurifier_AttrCollections
+     *       when modules are finalized during
+     *       HTMLPurifier_HTMLDefinition->setup(), this array may also
+     *       contain an array at index 0 that indicates which attribute
+     *       collections to load into the full array. It may also
+     *       contain string indentifiers in lieu of HTMLPurifier_AttrDef,
+     *       see HTMLPurifier_AttrTypes on how they are expanded during
+     *       HTMLPurifier_HTMLDefinition->setup() processing.
+     */
+    public $attr = array();
+
+    // XXX: Design note: currently, it's not possible to override
+    // previously defined AttrTransforms without messing around with
+    // the final generated config. This is by design; a previous version
+    // used an associated list of attr_transform, but it was extremely
+    // easy to accidentally override other attribute transforms by
+    // forgetting to specify an index (and just using 0.)  While we
+    // could check this by checking the index number and complaining,
+    // there is a second problem which is that it is not at all easy to
+    // tell when something is getting overridden. Combine this with a
+    // codebase where this isn't really being used, and it's perfect for
+    // nuking.
+
+    /**
+     * List of tags HTMLPurifier_AttrTransform to be done before validation.
+     * @type array
+     */
+    public $attr_transform_pre = array();
+
+    /**
+     * List of tags HTMLPurifier_AttrTransform to be done after validation.
+     * @type array
+     */
+    public $attr_transform_post = array();
+
+    /**
+     * HTMLPurifier_ChildDef of this tag.
+     * @type HTMLPurifier_ChildDef
+     */
+    public $child;
+
+    /**
+     * Abstract string representation of internal ChildDef rules.
+     * @see HTMLPurifier_ContentSets for how this is parsed and then transformed
+     * into an HTMLPurifier_ChildDef.
+     * @warning This is a temporary variable that is not available after
+     *      being processed by HTMLDefinition
+     * @type string
+     */
+    public $content_model;
+
+    /**
+     * Value of $child->type, used to determine which ChildDef to use,
+     * used in combination with $content_model.
+     * @warning This must be lowercase
+     * @warning This is a temporary variable that is not available after
+     *      being processed by HTMLDefinition
+     * @type string
+     */
+    public $content_model_type;
+
+    /**
+     * Does the element have a content model (#PCDATA | Inline)*? This
+     * is important for chameleon ins and del processing in
+     * HTMLPurifier_ChildDef_Chameleon. Dynamically set: modules don't
+     * have to worry about this one.
+     * @type bool
+     */
+    public $descendants_are_inline = false;
+
+    /**
+     * List of the names of required attributes this element has.
+     * Dynamically populated by HTMLPurifier_HTMLDefinition::getElement()
+     * @type array
+     */
+    public $required_attr = array();
+
+    /**
+     * Lookup table of tags excluded from all descendants of this tag.
+     * @type array
+     * @note SGML permits exclusions for all descendants, but this is
+     *       not possible with DTDs or XML Schemas. W3C has elected to
+     *       use complicated compositions of content_models to simulate
+     *       exclusion for children, but we go the simpler, SGML-style
+     *       route of flat-out exclusions, which correctly apply to
+     *       all descendants and not just children. Note that the XHTML
+     *       Modularization Abstract Modules are blithely unaware of such
+     *       distinctions.
+     */
+    public $excludes = array();
+
+    /**
+     * This tag is explicitly auto-closed by the following tags.
+     * @type array
+     */
+    public $autoclose = array();
+
+    /**
+     * If a foreign element is found in this element, test if it is
+     * allowed by this sub-element; if it is, instead of closing the
+     * current element, place it inside this element.
+     * @type string
+     */
+    public $wrap;
+
+    /**
+     * Whether or not this is a formatting element affected by the
+     * "Active Formatting Elements" algorithm.
+     * @type bool
+     */
+    public $formatting;
+
+    /**
+     * Low-level factory constructor for creating new standalone element defs
+     */
+    public static function create($content_model, $content_model_type, $attr)
+    {
+        $def = new HTMLPurifier_ElementDef();
+        $def->content_model = $content_model;
+        $def->content_model_type = $content_model_type;
+        $def->attr = $attr;
+        return $def;
+    }
+
+    /**
+     * Merges the values of another element definition into this one.
+     * Values from the new element def take precedence if a value is
+     * not mergeable.
+     * @param HTMLPurifier_ElementDef $def
+     */
+    public function mergeIn($def)
+    {
+        // later keys takes precedence
+        foreach ($def->attr as $k => $v) {
+            if ($k === 0) {
+                // merge in the includes
+                // sorry, no way to override an include
+                foreach ($v as $v2) {
+                    $this->attr[0][] = $v2;
+                }
+                continue;
+            }
+            if ($v === false) {
+                if (isset($this->attr[$k])) {
+                    unset($this->attr[$k]);
+                }
+                continue;
+            }
+            $this->attr[$k] = $v;
+        }
+        $this->_mergeAssocArray($this->excludes, $def->excludes);
+        $this->attr_transform_pre = array_merge($this->attr_transform_pre, $def->attr_transform_pre);
+        $this->attr_transform_post = array_merge($this->attr_transform_post, $def->attr_transform_post);
+
+        if (!empty($def->content_model)) {
+            $this->content_model =
+                str_replace("#SUPER", $this->content_model, $def->content_model);
+            $this->child = false;
+        }
+        if (!empty($def->content_model_type)) {
+            $this->content_model_type = $def->content_model_type;
+            $this->child = false;
+        }
+        if (!is_null($def->child)) {
+            $this->child = $def->child;
+        }
+        if (!is_null($def->formatting)) {
+            $this->formatting = $def->formatting;
+        }
+        if ($def->descendants_are_inline) {
+            $this->descendants_are_inline = $def->descendants_are_inline;
+        }
+    }
+
+    /**
+     * Merges one array into another, removes values which equal false
+     * @param $a1 Array by reference that is merged into
+     * @param $a2 Array that merges into $a1
+     */
+    private function _mergeAssocArray(&$a1, $a2)
+    {
+        foreach ($a2 as $k => $v) {
+            if ($v === false) {
+                if (isset($a1[$k])) {
+                    unset($a1[$k]);
+                }
+                continue;
+            }
+            $a1[$k] = $v;
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Encoder.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Encoder.php
new file mode 100644 (file)
index 0000000..fef9b58
--- /dev/null
@@ -0,0 +1,611 @@
+<?php
+
+/**
+ * A UTF-8 specific character encoder that handles cleaning and transforming.
+ * @note All functions in this class should be static.
+ */
+class HTMLPurifier_Encoder
+{
+
+    /**
+     * Constructor throws fatal error if you attempt to instantiate class
+     */
+    private function __construct()
+    {
+        trigger_error('Cannot instantiate encoder, call methods statically', E_USER_ERROR);
+    }
+
+    /**
+     * Error-handler that mutes errors, alternative to shut-up operator.
+     */
+    public static function muteErrorHandler()
+    {
+    }
+
+    /**
+     * iconv wrapper which mutes errors, but doesn't work around bugs.
+     * @param string $in Input encoding
+     * @param string $out Output encoding
+     * @param string $text The text to convert
+     * @return string
+     */
+    public static function unsafeIconv($in, $out, $text)
+    {
+        set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
+        $r = iconv($in, $out, $text);
+        restore_error_handler();
+        return $r;
+    }
+
+    /**
+     * iconv wrapper which mutes errors and works around bugs.
+     * @param string $in Input encoding
+     * @param string $out Output encoding
+     * @param string $text The text to convert
+     * @param int $max_chunk_size
+     * @return string
+     */
+    public static function iconv($in, $out, $text, $max_chunk_size = 8000)
+    {
+        $code = self::testIconvTruncateBug();
+        if ($code == self::ICONV_OK) {
+            return self::unsafeIconv($in, $out, $text);
+        } elseif ($code == self::ICONV_TRUNCATES) {
+            // we can only work around this if the input character set
+            // is utf-8
+            if ($in == 'utf-8') {
+                if ($max_chunk_size < 4) {
+                    trigger_error('max_chunk_size is too small', E_USER_WARNING);
+                    return false;
+                }
+                // split into 8000 byte chunks, but be careful to handle
+                // multibyte boundaries properly
+                if (($c = strlen($text)) <= $max_chunk_size) {
+                    return self::unsafeIconv($in, $out, $text);
+                }
+                $r = '';
+                $i = 0;
+                while (true) {
+                    if ($i + $max_chunk_size >= $c) {
+                        $r .= self::unsafeIconv($in, $out, substr($text, $i));
+                        break;
+                    }
+                    // wibble the boundary
+                    if (0x80 != (0xC0 & ord($text[$i + $max_chunk_size]))) {
+                        $chunk_size = $max_chunk_size;
+                    } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 1]))) {
+                        $chunk_size = $max_chunk_size - 1;
+                    } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 2]))) {
+                        $chunk_size = $max_chunk_size - 2;
+                    } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 3]))) {
+                        $chunk_size = $max_chunk_size - 3;
+                    } else {
+                        return false; // rather confusing UTF-8...
+                    }
+                    $chunk = substr($text, $i, $chunk_size); // substr doesn't mind overlong lengths
+                    $r .= self::unsafeIconv($in, $out, $chunk);
+                    $i += $chunk_size;
+                }
+                return $r;
+            } else {
+                return false;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Cleans a UTF-8 string for well-formedness and SGML validity
+     *
+     * It will parse according to UTF-8 and return a valid UTF8 string, with
+     * non-SGML codepoints excluded.
+     *
+     * @param string $str The string to clean
+     * @param bool $force_php
+     * @return string
+     *
+     * @note Just for reference, the non-SGML code points are 0 to 31 and
+     *       127 to 159, inclusive.  However, we allow code points 9, 10
+     *       and 13, which are the tab, line feed and carriage return
+     *       respectively. 128 and above the code points map to multibyte
+     *       UTF-8 representations.
+     *
+     * @note Fallback code adapted from utf8ToUnicode by Henri Sivonen and
+     *       hsivonen@iki.fi at <http://iki.fi/hsivonen/php-utf8/> under the
+     *       LGPL license.  Notes on what changed are inside, but in general,
+     *       the original code transformed UTF-8 text into an array of integer
+     *       Unicode codepoints. Understandably, transforming that back to
+     *       a string would be somewhat expensive, so the function was modded to
+     *       directly operate on the string.  However, this discourages code
+     *       reuse, and the logic enumerated here would be useful for any
+     *       function that needs to be able to understand UTF-8 characters.
+     *       As of right now, only smart lossless character encoding converters
+     *       would need that, and I'm probably not going to implement them.
+     *       Once again, PHP 6 should solve all our problems.
+     */
+    public static function cleanUTF8($str, $force_php = false)
+    {
+        // UTF-8 validity is checked since PHP 4.3.5
+        // This is an optimization: if the string is already valid UTF-8, no
+        // need to do PHP stuff. 99% of the time, this will be the case.
+        // The regexp matches the XML char production, as well as well as excluding
+        // non-SGML codepoints U+007F to U+009F
+        if (preg_match(
+            '/^[\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]*$/Du',
+            $str
+        )) {
+            return $str;
+        }
+
+        $mState = 0; // cached expected number of octets after the current octet
+                     // until the beginning of the next UTF8 character sequence
+        $mUcs4  = 0; // cached Unicode character
+        $mBytes = 1; // cached expected number of octets in the current sequence
+
+        // original code involved an $out that was an array of Unicode
+        // codepoints.  Instead of having to convert back into UTF-8, we've
+        // decided to directly append valid UTF-8 characters onto a string
+        // $out once they're done.  $char accumulates raw bytes, while $mUcs4
+        // turns into the Unicode code point, so there's some redundancy.
+
+        $out = '';
+        $char = '';
+
+        $len = strlen($str);
+        for ($i = 0; $i < $len; $i++) {
+            $in = ord($str{$i});
+            $char .= $str[$i]; // append byte to char
+            if (0 == $mState) {
+                // When mState is zero we expect either a US-ASCII character
+                // or a multi-octet sequence.
+                if (0 == (0x80 & ($in))) {
+                    // US-ASCII, pass straight through.
+                    if (($in <= 31 || $in == 127) &&
+                        !($in == 9 || $in == 13 || $in == 10) // save \r\t\n
+                    ) {
+                        // control characters, remove
+                    } else {
+                        $out .= $char;
+                    }
+                    // reset
+                    $char = '';
+                    $mBytes = 1;
+                } elseif (0xC0 == (0xE0 & ($in))) {
+                    // First octet of 2 octet sequence
+                    $mUcs4 = ($in);
+                    $mUcs4 = ($mUcs4 & 0x1F) << 6;
+                    $mState = 1;
+                    $mBytes = 2;
+                } elseif (0xE0 == (0xF0 & ($in))) {
+                    // First octet of 3 octet sequence
+                    $mUcs4 = ($in);
+                    $mUcs4 = ($mUcs4 & 0x0F) << 12;
+                    $mState = 2;
+                    $mBytes = 3;
+                } elseif (0xF0 == (0xF8 & ($in))) {
+                    // First octet of 4 octet sequence
+                    $mUcs4 = ($in);
+                    $mUcs4 = ($mUcs4 & 0x07) << 18;
+                    $mState = 3;
+                    $mBytes = 4;
+                } elseif (0xF8 == (0xFC & ($in))) {
+                    // First octet of 5 octet sequence.
+                    //
+                    // This is illegal because the encoded codepoint must be
+                    // either:
+                    // (a) not the shortest form or
+                    // (b) outside the Unicode range of 0-0x10FFFF.
+                    // Rather than trying to resynchronize, we will carry on
+                    // until the end of the sequence and let the later error
+                    // handling code catch it.
+                    $mUcs4 = ($in);
+                    $mUcs4 = ($mUcs4 & 0x03) << 24;
+                    $mState = 4;
+                    $mBytes = 5;
+                } elseif (0xFC == (0xFE & ($in))) {
+                    // First octet of 6 octet sequence, see comments for 5
+                    // octet sequence.
+                    $mUcs4 = ($in);
+                    $mUcs4 = ($mUcs4 & 1) << 30;
+                    $mState = 5;
+                    $mBytes = 6;
+                } else {
+                    // Current octet is neither in the US-ASCII range nor a
+                    // legal first octet of a multi-octet sequence.
+                    $mState = 0;
+                    $mUcs4  = 0;
+                    $mBytes = 1;
+                    $char = '';
+                }
+            } else {
+                // When mState is non-zero, we expect a continuation of the
+                // multi-octet sequence
+                if (0x80 == (0xC0 & ($in))) {
+                    // Legal continuation.
+                    $shift = ($mState - 1) * 6;
+                    $tmp = $in;
+                    $tmp = ($tmp & 0x0000003F) << $shift;
+                    $mUcs4 |= $tmp;
+
+                    if (0 == --$mState) {
+                        // End of the multi-octet sequence. mUcs4 now contains
+                        // the final Unicode codepoint to be output
+
+                        // Check for illegal sequences and codepoints.
+
+                        // From Unicode 3.1, non-shortest form is illegal
+                        if (((2 == $mBytes) && ($mUcs4 < 0x0080)) ||
+                            ((3 == $mBytes) && ($mUcs4 < 0x0800)) ||
+                            ((4 == $mBytes) && ($mUcs4 < 0x10000)) ||
+                            (4 < $mBytes) ||
+                            // From Unicode 3.2, surrogate characters = illegal
+                            (($mUcs4 & 0xFFFFF800) == 0xD800) ||
+                            // Codepoints outside the Unicode range are illegal
+                            ($mUcs4 > 0x10FFFF)
+                        ) {
+
+                        } elseif (0xFEFF != $mUcs4 && // omit BOM
+                            // check for valid Char unicode codepoints
+                            (
+                                0x9 == $mUcs4 ||
+                                0xA == $mUcs4 ||
+                                0xD == $mUcs4 ||
+                                (0x20 <= $mUcs4 && 0x7E >= $mUcs4) ||
+                                // 7F-9F is not strictly prohibited by XML,
+                                // but it is non-SGML, and thus we don't allow it
+                                (0xA0 <= $mUcs4 && 0xD7FF >= $mUcs4) ||
+                                (0x10000 <= $mUcs4 && 0x10FFFF >= $mUcs4)
+                            )
+                        ) {
+                            $out .= $char;
+                        }
+                        // initialize UTF8 cache (reset)
+                        $mState = 0;
+                        $mUcs4  = 0;
+                        $mBytes = 1;
+                        $char = '';
+                    }
+                } else {
+                    // ((0xC0 & (*in) != 0x80) && (mState != 0))
+                    // Incomplete multi-octet sequence.
+                    // used to result in complete fail, but we'll reset
+                    $mState = 0;
+                    $mUcs4  = 0;
+                    $mBytes = 1;
+                    $char ='';
+                }
+            }
+        }
+        return $out;
+    }
+
+    /**
+     * Translates a Unicode codepoint into its corresponding UTF-8 character.
+     * @note Based on Feyd's function at
+     *       <http://forums.devnetwork.net/viewtopic.php?p=191404#191404>,
+     *       which is in public domain.
+     * @note While we're going to do code point parsing anyway, a good
+     *       optimization would be to refuse to translate code points that
+     *       are non-SGML characters.  However, this could lead to duplication.
+     * @note This is very similar to the unichr function in
+     *       maintenance/generate-entity-file.php (although this is superior,
+     *       due to its sanity checks).
+     */
+
+    // +----------+----------+----------+----------+
+    // | 33222222 | 22221111 | 111111   |          |
+    // | 10987654 | 32109876 | 54321098 | 76543210 | bit
+    // +----------+----------+----------+----------+
+    // |          |          |          | 0xxxxxxx | 1 byte 0x00000000..0x0000007F
+    // |          |          | 110yyyyy | 10xxxxxx | 2 byte 0x00000080..0x000007FF
+    // |          | 1110zzzz | 10yyyyyy | 10xxxxxx | 3 byte 0x00000800..0x0000FFFF
+    // | 11110www | 10wwzzzz | 10yyyyyy | 10xxxxxx | 4 byte 0x00010000..0x0010FFFF
+    // +----------+----------+----------+----------+
+    // | 00000000 | 00011111 | 11111111 | 11111111 | Theoretical upper limit of legal scalars: 2097151 (0x001FFFFF)
+    // | 00000000 | 00010000 | 11111111 | 11111111 | Defined upper limit of legal scalar codes
+    // +----------+----------+----------+----------+
+
+    public static function unichr($code)
+    {
+        if ($code > 1114111 or $code < 0 or
+          ($code >= 55296 and $code <= 57343) ) {
+            // bits are set outside the "valid" range as defined
+            // by UNICODE 4.1.0
+            return '';
+        }
+
+        $x = $y = $z = $w = 0;
+        if ($code < 128) {
+            // regular ASCII character
+            $x = $code;
+        } else {
+            // set up bits for UTF-8
+            $x = ($code & 63) | 128;
+            if ($code < 2048) {
+                $y = (($code & 2047) >> 6) | 192;
+            } else {
+                $y = (($code & 4032) >> 6) | 128;
+                if ($code < 65536) {
+                    $z = (($code >> 12) & 15) | 224;
+                } else {
+                    $z = (($code >> 12) & 63) | 128;
+                    $w = (($code >> 18) & 7)  | 240;
+                }
+            }
+        }
+        // set up the actual character
+        $ret = '';
+        if ($w) {
+            $ret .= chr($w);
+        }
+        if ($z) {
+            $ret .= chr($z);
+        }
+        if ($y) {
+            $ret .= chr($y);
+        }
+        $ret .= chr($x);
+
+        return $ret;
+    }
+
+    /**
+     * @return bool
+     */
+    public static function iconvAvailable()
+    {
+        static $iconv = null;
+        if ($iconv === null) {
+            $iconv = function_exists('iconv') && self::testIconvTruncateBug() != self::ICONV_UNUSABLE;
+        }
+        return $iconv;
+    }
+
+    /**
+     * Convert a string to UTF-8 based on configuration.
+     * @param string $str The string to convert
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return string
+     */
+    public static function convertToUTF8($str, $config, $context)
+    {
+        $encoding = $config->get('Core.Encoding');
+        if ($encoding === 'utf-8') {
+            return $str;
+        }
+        static $iconv = null;
+        if ($iconv === null) {
+            $iconv = self::iconvAvailable();
+        }
+        if ($iconv && !$config->get('Test.ForceNoIconv')) {
+            // unaffected by bugs, since UTF-8 support all characters
+            $str = self::unsafeIconv($encoding, 'utf-8//IGNORE', $str);
+            if ($str === false) {
+                // $encoding is not a valid encoding
+                trigger_error('Invalid encoding ' . $encoding, E_USER_ERROR);
+                return '';
+            }
+            // If the string is bjorked by Shift_JIS or a similar encoding
+            // that doesn't support all of ASCII, convert the naughty
+            // characters to their true byte-wise ASCII/UTF-8 equivalents.
+            $str = strtr($str, self::testEncodingSupportsASCII($encoding));
+            return $str;
+        } elseif ($encoding === 'iso-8859-1') {
+            $str = utf8_encode($str);
+            return $str;
+        }
+        $bug = HTMLPurifier_Encoder::testIconvTruncateBug();
+        if ($bug == self::ICONV_OK) {
+            trigger_error('Encoding not supported, please install iconv', E_USER_ERROR);
+        } else {
+            trigger_error(
+                'You have a buggy version of iconv, see https://bugs.php.net/bug.php?id=48147 ' .
+                'and http://sourceware.org/bugzilla/show_bug.cgi?id=13541',
+                E_USER_ERROR
+            );
+        }
+    }
+
+    /**
+     * Converts a string from UTF-8 based on configuration.
+     * @param string $str The string to convert
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return string
+     * @note Currently, this is a lossy conversion, with unexpressable
+     *       characters being omitted.
+     */
+    public static function convertFromUTF8($str, $config, $context)
+    {
+        $encoding = $config->get('Core.Encoding');
+        if ($escape = $config->get('Core.EscapeNonASCIICharacters')) {
+            $str = self::convertToASCIIDumbLossless($str);
+        }
+        if ($encoding === 'utf-8') {
+            return $str;
+        }
+        static $iconv = null;
+        if ($iconv === null) {
+            $iconv = self::iconvAvailable();
+        }
+        if ($iconv && !$config->get('Test.ForceNoIconv')) {
+            // Undo our previous fix in convertToUTF8, otherwise iconv will barf
+            $ascii_fix = self::testEncodingSupportsASCII($encoding);
+            if (!$escape && !empty($ascii_fix)) {
+                $clear_fix = array();
+                foreach ($ascii_fix as $utf8 => $native) {
+                    $clear_fix[$utf8] = '';
+                }
+                $str = strtr($str, $clear_fix);
+            }
+            $str = strtr($str, array_flip($ascii_fix));
+            // Normal stuff
+            $str = self::iconv('utf-8', $encoding . '//IGNORE', $str);
+            return $str;
+        } elseif ($encoding === 'iso-8859-1') {
+            $str = utf8_decode($str);
+            return $str;
+        }
+        trigger_error('Encoding not supported', E_USER_ERROR);
+        // You might be tempted to assume that the ASCII representation
+        // might be OK, however, this is *not* universally true over all
+        // encodings.  So we take the conservative route here, rather
+        // than forcibly turn on %Core.EscapeNonASCIICharacters
+    }
+
+    /**
+     * Lossless (character-wise) conversion of HTML to ASCII
+     * @param string $str UTF-8 string to be converted to ASCII
+     * @return string ASCII encoded string with non-ASCII character entity-ized
+     * @warning Adapted from MediaWiki, claiming fair use: this is a common
+     *       algorithm. If you disagree with this license fudgery,
+     *       implement it yourself.
+     * @note Uses decimal numeric entities since they are best supported.
+     * @note This is a DUMB function: it has no concept of keeping
+     *       character entities that the projected character encoding
+     *       can allow. We could possibly implement a smart version
+     *       but that would require it to also know which Unicode
+     *       codepoints the charset supported (not an easy task).
+     * @note Sort of with cleanUTF8() but it assumes that $str is
+     *       well-formed UTF-8
+     */
+    public static function convertToASCIIDumbLossless($str)
+    {
+        $bytesleft = 0;
+        $result = '';
+        $working = 0;
+        $len = strlen($str);
+        for ($i = 0; $i < $len; $i++) {
+            $bytevalue = ord($str[$i]);
+            if ($bytevalue <= 0x7F) { //0xxx xxxx
+                $result .= chr($bytevalue);
+                $bytesleft = 0;
+            } elseif ($bytevalue <= 0xBF) { //10xx xxxx
+                $working = $working << 6;
+                $working += ($bytevalue & 0x3F);
+                $bytesleft--;
+                if ($bytesleft <= 0) {
+                    $result .= "&#" . $working . ";";
+                }
+            } elseif ($bytevalue <= 0xDF) { //110x xxxx
+                $working = $bytevalue & 0x1F;
+                $bytesleft = 1;
+            } elseif ($bytevalue <= 0xEF) { //1110 xxxx
+                $working = $bytevalue & 0x0F;
+                $bytesleft = 2;
+            } else { //1111 0xxx
+                $working = $bytevalue & 0x07;
+                $bytesleft = 3;
+            }
+        }
+        return $result;
+    }
+
+    /** No bugs detected in iconv. */
+    const ICONV_OK = 0;
+
+    /** Iconv truncates output if converting from UTF-8 to another
+     *  character set with //IGNORE, and a non-encodable character is found */
+    const ICONV_TRUNCATES = 1;
+
+    /** Iconv does not support //IGNORE, making it unusable for
+     *  transcoding purposes */
+    const ICONV_UNUSABLE = 2;
+
+    /**
+     * glibc iconv has a known bug where it doesn't handle the magic
+     * //IGNORE stanza correctly.  In particular, rather than ignore
+     * characters, it will return an EILSEQ after consuming some number
+     * of characters, and expect you to restart iconv as if it were
+     * an E2BIG.  Old versions of PHP did not respect the errno, and
+     * returned the fragment, so as a result you would see iconv
+     * mysteriously truncating output. We can work around this by
+     * manually chopping our input into segments of about 8000
+     * characters, as long as PHP ignores the error code.  If PHP starts
+     * paying attention to the error code, iconv becomes unusable.
+     *
+     * @return int Error code indicating severity of bug.
+     */
+    public static function testIconvTruncateBug()
+    {
+        static $code = null;
+        if ($code === null) {
+            // better not use iconv, otherwise infinite loop!
+            $r = self::unsafeIconv('utf-8', 'ascii//IGNORE', "\xCE\xB1" . str_repeat('a', 9000));
+            if ($r === false) {
+                $code = self::ICONV_UNUSABLE;
+            } elseif (($c = strlen($r)) < 9000) {
+                $code = self::ICONV_TRUNCATES;
+            } elseif ($c > 9000) {
+                trigger_error(
+                    'Your copy of iconv is extremely buggy. Please notify HTML Purifier maintainers: ' .
+                    'include your iconv version as per phpversion()',
+                    E_USER_ERROR
+                );
+            } else {
+                $code = self::ICONV_OK;
+            }
+        }
+        return $code;
+    }
+
+    /**
+     * This expensive function tests whether or not a given character
+     * encoding supports ASCII. 7/8-bit encodings like Shift_JIS will
+     * fail this test, and require special processing. Variable width
+     * encodings shouldn't ever fail.
+     *
+     * @param string $encoding Encoding name to test, as per iconv format
+     * @param bool $bypass Whether or not to bypass the precompiled arrays.
+     * @return Array of UTF-8 characters to their corresponding ASCII,
+     *      which can be used to "undo" any overzealous iconv action.
+     */
+    public static function testEncodingSupportsASCII($encoding, $bypass = false)
+    {
+        // All calls to iconv here are unsafe, proof by case analysis:
+        // If ICONV_OK, no difference.
+        // If ICONV_TRUNCATE, all calls involve one character inputs,
+        // so bug is not triggered.
+        // If ICONV_UNUSABLE, this call is irrelevant
+        static $encodings = array();
+        if (!$bypass) {
+            if (isset($encodings[$encoding])) {
+                return $encodings[$encoding];
+            }
+            $lenc = strtolower($encoding);
+            switch ($lenc) {
+                case 'shift_jis':
+                    return array("\xC2\xA5" => '\\', "\xE2\x80\xBE" => '~');
+                case 'johab':
+                    return array("\xE2\x82\xA9" => '\\');
+            }
+            if (strpos($lenc, 'iso-8859-') === 0) {
+                return array();
+            }
+        }
+        $ret = array();
+        if (self::unsafeIconv('UTF-8', $encoding, 'a') === false) {
+            return false;
+        }
+        for ($i = 0x20; $i <= 0x7E; $i++) { // all printable ASCII chars
+            $c = chr($i); // UTF-8 char
+            $r = self::unsafeIconv('UTF-8', "$encoding//IGNORE", $c); // initial conversion
+            if ($r === '' ||
+                // This line is needed for iconv implementations that do not
+                // omit characters that do not exist in the target character set
+                ($r === $c && self::unsafeIconv($encoding, 'UTF-8//IGNORE', $r) !== $c)
+            ) {
+                // Reverse engineer: what's the UTF-8 equiv of this byte
+                // sequence? This assumes that there's no variable width
+                // encoding that doesn't support ASCII.
+                $ret[self::unsafeIconv($encoding, 'UTF-8//IGNORE', $c)] = $c;
+            }
+        }
+        $encodings[$encoding] = $ret;
+        return $ret;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup.php
new file mode 100644 (file)
index 0000000..f12ff13
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * Object that provides entity lookup table from entity name to character
+ */
+class HTMLPurifier_EntityLookup
+{
+    /**
+     * Assoc array of entity name to character represented.
+     * @type array
+     */
+    public $table;
+
+    /**
+     * Sets up the entity lookup table from the serialized file contents.
+     * @param bool $file
+     * @note The serialized contents are versioned, but were generated
+     *       using the maintenance script generate_entity_file.php
+     * @warning This is not in constructor to help enforce the Singleton
+     */
+    public function setup($file = false)
+    {
+        if (!$file) {
+            $file = HTMLPURIFIER_PREFIX . '/HTMLPurifier/EntityLookup/entities.ser';
+        }
+        $this->table = unserialize(file_get_contents($file));
+    }
+
+    /**
+     * Retrieves sole instance of the object.
+     * @param bool|HTMLPurifier_EntityLookup $prototype Optional prototype of custom lookup table to overload with.
+     * @return HTMLPurifier_EntityLookup
+     */
+    public static function instance($prototype = false)
+    {
+        // no references, since PHP doesn't copy unless modified
+        static $instance = null;
+        if ($prototype) {
+            $instance = $prototype;
+        } elseif (!$instance) {
+            $instance = new HTMLPurifier_EntityLookup();
+            $instance->setup();
+        }
+        return $instance;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser b/library/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser
new file mode 100644 (file)
index 0000000..e8b0812
--- /dev/null
@@ -0,0 +1 @@
+a:253:{s:4:"fnof";s:2:"ƒ";s:5:"Alpha";s:2:"Α";s:4:"Beta";s:2:"Β";s:5:"Gamma";s:2:"Γ";s:5:"Delta";s:2:"Δ";s:7:"Epsilon";s:2:"Ε";s:4:"Zeta";s:2:"Ζ";s:3:"Eta";s:2:"Η";s:5:"Theta";s:2:"Θ";s:4:"Iota";s:2:"Ι";s:5:"Kappa";s:2:"Κ";s:6:"Lambda";s:2:"Λ";s:2:"Mu";s:2:"Μ";s:2:"Nu";s:2:"Ν";s:2:"Xi";s:2:"Ξ";s:7:"Omicron";s:2:"Ο";s:2:"Pi";s:2:"Π";s:3:"Rho";s:2:"Ρ";s:5:"Sigma";s:2:"Σ";s:3:"Tau";s:2:"Τ";s:7:"Upsilon";s:2:"Υ";s:3:"Phi";s:2:"Φ";s:3:"Chi";s:2:"Χ";s:3:"Psi";s:2:"Ψ";s:5:"Omega";s:2:"Ω";s:5:"alpha";s:2:"α";s:4:"beta";s:2:"β";s:5:"gamma";s:2:"γ";s:5:"delta";s:2:"δ";s:7:"epsilon";s:2:"ε";s:4:"zeta";s:2:"ζ";s:3:"eta";s:2:"η";s:5:"theta";s:2:"θ";s:4:"iota";s:2:"ι";s:5:"kappa";s:2:"κ";s:6:"lambda";s:2:"λ";s:2:"mu";s:2:"μ";s:2:"nu";s:2:"ν";s:2:"xi";s:2:"ξ";s:7:"omicron";s:2:"ο";s:2:"pi";s:2:"π";s:3:"rho";s:2:"ρ";s:6:"sigmaf";s:2:"ς";s:5:"sigma";s:2:"σ";s:3:"tau";s:2:"τ";s:7:"upsilon";s:2:"υ";s:3:"phi";s:2:"φ";s:3:"chi";s:2:"χ";s:3:"psi";s:2:"ψ";s:5:"omega";s:2:"ω";s:8:"thetasym";s:2:"ϑ";s:5:"upsih";s:2:"ϒ";s:3:"piv";s:2:"ϖ";s:4:"bull";s:3:"•";s:6:"hellip";s:3:"…";s:5:"prime";s:3:"′";s:5:"Prime";s:3:"″";s:5:"oline";s:3:"‾";s:5:"frasl";s:3:"⁄";s:6:"weierp";s:3:"℘";s:5:"image";s:3:"ℑ";s:4:"real";s:3:"ℜ";s:5:"trade";s:3:"™";s:7:"alefsym";s:3:"ℵ";s:4:"larr";s:3:"←";s:4:"uarr";s:3:"↑";s:4:"rarr";s:3:"→";s:4:"darr";s:3:"↓";s:4:"harr";s:3:"↔";s:5:"crarr";s:3:"↵";s:4:"lArr";s:3:"⇐";s:4:"uArr";s:3:"⇑";s:4:"rArr";s:3:"⇒";s:4:"dArr";s:3:"⇓";s:4:"hArr";s:3:"⇔";s:6:"forall";s:3:"∀";s:4:"part";s:3:"∂";s:5:"exist";s:3:"∃";s:5:"empty";s:3:"∅";s:5:"nabla";s:3:"∇";s:4:"isin";s:3:"∈";s:5:"notin";s:3:"∉";s:2:"ni";s:3:"∋";s:4:"prod";s:3:"∏";s:3:"sum";s:3:"∑";s:5:"minus";s:3:"−";s:6:"lowast";s:3:"∗";s:5:"radic";s:3:"√";s:4:"prop";s:3:"∝";s:5:"infin";s:3:"∞";s:3:"ang";s:3:"∠";s:3:"and";s:3:"∧";s:2:"or";s:3:"∨";s:3:"cap";s:3:"∩";s:3:"cup";s:3:"∪";s:3:"int";s:3:"∫";s:6:"there4";s:3:"∴";s:3:"sim";s:3:"∼";s:4:"cong";s:3:"≅";s:5:"asymp";s:3:"≈";s:2:"ne";s:3:"≠";s:5:"equiv";s:3:"≡";s:2:"le";s:3:"≤";s:2:"ge";s:3:"≥";s:3:"sub";s:3:"⊂";s:3:"sup";s:3:"⊃";s:4:"nsub";s:3:"⊄";s:4:"sube";s:3:"⊆";s:4:"supe";s:3:"⊇";s:5:"oplus";s:3:"⊕";s:6:"otimes";s:3:"⊗";s:4:"perp";s:3:"⊥";s:4:"sdot";s:3:"⋅";s:5:"lceil";s:3:"⌈";s:5:"rceil";s:3:"⌉";s:6:"lfloor";s:3:"⌊";s:6:"rfloor";s:3:"⌋";s:4:"lang";s:3:"〈";s:4:"rang";s:3:"〉";s:3:"loz";s:3:"◊";s:6:"spades";s:3:"♠";s:5:"clubs";s:3:"♣";s:6:"hearts";s:3:"♥";s:5:"diams";s:3:"♦";s:4:"quot";s:1:""";s:3:"amp";s:1:"&";s:2:"lt";s:1:"<";s:2:"gt";s:1:">";s:4:"apos";s:1:"'";s:5:"OElig";s:2:"Œ";s:5:"oelig";s:2:"œ";s:6:"Scaron";s:2:"Š";s:6:"scaron";s:2:"š";s:4:"Yuml";s:2:"Ÿ";s:4:"circ";s:2:"ˆ";s:5:"tilde";s:2:"˜";s:4:"ensp";s:3:" ";s:4:"emsp";s:3:" ";s:6:"thinsp";s:3:" ";s:4:"zwnj";s:3:"‌";s:3:"zwj";s:3:"‍";s:3:"lrm";s:3:"‎";s:3:"rlm";s:3:"‏";s:5:"ndash";s:3:"–";s:5:"mdash";s:3:"—";s:5:"lsquo";s:3:"‘";s:5:"rsquo";s:3:"’";s:5:"sbquo";s:3:"‚";s:5:"ldquo";s:3:"“";s:5:"rdquo";s:3:"”";s:5:"bdquo";s:3:"„";s:6:"dagger";s:3:"†";s:6:"Dagger";s:3:"‡";s:6:"permil";s:3:"‰";s:6:"lsaquo";s:3:"‹";s:6:"rsaquo";s:3:"›";s:4:"euro";s:3:"€";s:4:"nbsp";s:2:" ";s:5:"iexcl";s:2:"¡";s:4:"cent";s:2:"¢";s:5:"pound";s:2:"£";s:6:"curren";s:2:"¤";s:3:"yen";s:2:"¥";s:6:"brvbar";s:2:"¦";s:4:"sect";s:2:"§";s:3:"uml";s:2:"¨";s:4:"copy";s:2:"©";s:4:"ordf";s:2:"ª";s:5:"laquo";s:2:"«";s:3:"not";s:2:"¬";s:3:"shy";s:2:"­";s:3:"reg";s:2:"®";s:4:"macr";s:2:"¯";s:3:"deg";s:2:"°";s:6:"plusmn";s:2:"±";s:4:"sup2";s:2:"²";s:4:"sup3";s:2:"³";s:5:"acute";s:2:"´";s:5:"micro";s:2:"µ";s:4:"para";s:2:"¶";s:6:"middot";s:2:"·";s:5:"cedil";s:2:"¸";s:4:"sup1";s:2:"¹";s:4:"ordm";s:2:"º";s:5:"raquo";s:2:"»";s:6:"frac14";s:2:"¼";s:6:"frac12";s:2:"½";s:6:"frac34";s:2:"¾";s:6:"iquest";s:2:"¿";s:6:"Agrave";s:2:"À";s:6:"Aacute";s:2:"Á";s:5:"Acirc";s:2:"Â";s:6:"Atilde";s:2:"Ã";s:4:"Auml";s:2:"Ä";s:5:"Aring";s:2:"Å";s:5:"AElig";s:2:"Æ";s:6:"Ccedil";s:2:"Ç";s:6:"Egrave";s:2:"È";s:6:"Eacute";s:2:"É";s:5:"Ecirc";s:2:"Ê";s:4:"Euml";s:2:"Ë";s:6:"Igrave";s:2:"Ì";s:6:"Iacute";s:2:"Í";s:5:"Icirc";s:2:"Î";s:4:"Iuml";s:2:"Ï";s:3:"ETH";s:2:"Ð";s:6:"Ntilde";s:2:"Ñ";s:6:"Ograve";s:2:"Ò";s:6:"Oacute";s:2:"Ó";s:5:"Ocirc";s:2:"Ô";s:6:"Otilde";s:2:"Õ";s:4:"Ouml";s:2:"Ö";s:5:"times";s:2:"×";s:6:"Oslash";s:2:"Ø";s:6:"Ugrave";s:2:"Ù";s:6:"Uacute";s:2:"Ú";s:5:"Ucirc";s:2:"Û";s:4:"Uuml";s:2:"Ü";s:6:"Yacute";s:2:"Ý";s:5:"THORN";s:2:"Þ";s:5:"szlig";s:2:"ß";s:6:"agrave";s:2:"à";s:6:"aacute";s:2:"á";s:5:"acirc";s:2:"â";s:6:"atilde";s:2:"ã";s:4:"auml";s:2:"ä";s:5:"aring";s:2:"å";s:5:"aelig";s:2:"æ";s:6:"ccedil";s:2:"ç";s:6:"egrave";s:2:"è";s:6:"eacute";s:2:"é";s:5:"ecirc";s:2:"ê";s:4:"euml";s:2:"ë";s:6:"igrave";s:2:"ì";s:6:"iacute";s:2:"í";s:5:"icirc";s:2:"î";s:4:"iuml";s:2:"ï";s:3:"eth";s:2:"ð";s:6:"ntilde";s:2:"ñ";s:6:"ograve";s:2:"ò";s:6:"oacute";s:2:"ó";s:5:"ocirc";s:2:"ô";s:6:"otilde";s:2:"õ";s:4:"ouml";s:2:"ö";s:6:"divide";s:2:"÷";s:6:"oslash";s:2:"ø";s:6:"ugrave";s:2:"ù";s:6:"uacute";s:2:"ú";s:5:"ucirc";s:2:"û";s:4:"uuml";s:2:"ü";s:6:"yacute";s:2:"ý";s:5:"thorn";s:2:"þ";s:4:"yuml";s:2:"ÿ";}
\ No newline at end of file
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/EntityParser.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/EntityParser.php
new file mode 100644 (file)
index 0000000..61529dc
--- /dev/null
@@ -0,0 +1,153 @@
+<?php
+
+// if want to implement error collecting here, we'll need to use some sort
+// of global data (probably trigger_error) because it's impossible to pass
+// $config or $context to the callback functions.
+
+/**
+ * Handles referencing and derefencing character entities
+ */
+class HTMLPurifier_EntityParser
+{
+
+    /**
+     * Reference to entity lookup table.
+     * @type HTMLPurifier_EntityLookup
+     */
+    protected $_entity_lookup;
+
+    /**
+     * Callback regex string for parsing entities.
+     * @type string
+     */
+    protected $_substituteEntitiesRegex =
+        '/&(?:[#]x([a-fA-F0-9]+)|[#]0*(\d+)|([A-Za-z_:][A-Za-z0-9.\-_:]*));?/';
+        //     1. hex             2. dec      3. string (XML style)
+
+    /**
+     * Decimal to parsed string conversion table for special entities.
+     * @type array
+     */
+    protected $_special_dec2str =
+            array(
+                    34 => '"',
+                    38 => '&',
+                    39 => "'",
+                    60 => '<',
+                    62 => '>'
+            );
+
+    /**
+     * Stripped entity names to decimal conversion table for special entities.
+     * @type array
+     */
+    protected $_special_ent2dec =
+            array(
+                    'quot' => 34,
+                    'amp'  => 38,
+                    'lt'   => 60,
+                    'gt'   => 62
+            );
+
+    /**
+     * Substitutes non-special entities with their parsed equivalents. Since
+     * running this whenever you have parsed character is t3h 5uck, we run
+     * it before everything else.
+     *
+     * @param string $string String to have non-special entities parsed.
+     * @return string Parsed string.
+     */
+    public function substituteNonSpecialEntities($string)
+    {
+        // it will try to detect missing semicolons, but don't rely on it
+        return preg_replace_callback(
+            $this->_substituteEntitiesRegex,
+            array($this, 'nonSpecialEntityCallback'),
+            $string
+        );
+    }
+
+    /**
+     * Callback function for substituteNonSpecialEntities() that does the work.
+     *
+     * @param array $matches  PCRE matches array, with 0 the entire match, and
+     *                  either index 1, 2 or 3 set with a hex value, dec value,
+     *                  or string (respectively).
+     * @return string Replacement string.
+     */
+
+    protected function nonSpecialEntityCallback($matches)
+    {
+        // replaces all but big five
+        $entity = $matches[0];
+        $is_num = (@$matches[0][1] === '#');
+        if ($is_num) {
+            $is_hex = (@$entity[2] === 'x');
+            $code = $is_hex ? hexdec($matches[1]) : (int) $matches[2];
+            // abort for special characters
+            if (isset($this->_special_dec2str[$code])) {
+                return $entity;
+            }
+            return HTMLPurifier_Encoder::unichr($code);
+        } else {
+            if (isset($this->_special_ent2dec[$matches[3]])) {
+                return $entity;
+            }
+            if (!$this->_entity_lookup) {
+                $this->_entity_lookup = HTMLPurifier_EntityLookup::instance();
+            }
+            if (isset($this->_entity_lookup->table[$matches[3]])) {
+                return $this->_entity_lookup->table[$matches[3]];
+            } else {
+                return $entity;
+            }
+        }
+    }
+
+    /**
+     * Substitutes only special entities with their parsed equivalents.
+     *
+     * @notice We try to avoid calling this function because otherwise, it
+     * would have to be called a lot (for every parsed section).
+     *
+     * @param string $string String to have non-special entities parsed.
+     * @return string Parsed string.
+     */
+    public function substituteSpecialEntities($string)
+    {
+        return preg_replace_callback(
+            $this->_substituteEntitiesRegex,
+            array($this, 'specialEntityCallback'),
+            $string
+        );
+    }
+
+    /**
+     * Callback function for substituteSpecialEntities() that does the work.
+     *
+     * This callback has same syntax as nonSpecialEntityCallback().
+     *
+     * @param array $matches  PCRE-style matches array, with 0 the entire match, and
+     *                  either index 1, 2 or 3 set with a hex value, dec value,
+     *                  or string (respectively).
+     * @return string Replacement string.
+     */
+    protected function specialEntityCallback($matches)
+    {
+        $entity = $matches[0];
+        $is_num = (@$matches[0][1] === '#');
+        if ($is_num) {
+            $is_hex = (@$entity[2] === 'x');
+            $int = $is_hex ? hexdec($matches[1]) : (int) $matches[2];
+            return isset($this->_special_dec2str[$int]) ?
+                $this->_special_dec2str[$int] :
+                $entity;
+        } else {
+            return isset($this->_special_ent2dec[$matches[3]]) ?
+                $this->_special_ent2dec[$matches[3]] :
+                $entity;
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ErrorCollector.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ErrorCollector.php
new file mode 100644 (file)
index 0000000..d47e3f2
--- /dev/null
@@ -0,0 +1,244 @@
+<?php
+
+/**
+ * Error collection class that enables HTML Purifier to report HTML
+ * problems back to the user
+ */
+class HTMLPurifier_ErrorCollector
+{
+
+    /**
+     * Identifiers for the returned error array. These are purposely numeric
+     * so list() can be used.
+     */
+    const LINENO   = 0;
+    const SEVERITY = 1;
+    const MESSAGE  = 2;
+    const CHILDREN = 3;
+
+    /**
+     * @type array
+     */
+    protected $errors;
+
+    /**
+     * @type array
+     */
+    protected $_current;
+
+    /**
+     * @type array
+     */
+    protected $_stacks = array(array());
+
+    /**
+     * @type HTMLPurifier_Language
+     */
+    protected $locale;
+
+    /**
+     * @type HTMLPurifier_Generator
+     */
+    protected $generator;
+
+    /**
+     * @type HTMLPurifier_Context
+     */
+    protected $context;
+
+    /**
+     * @type array
+     */
+    protected $lines = array();
+
+    /**
+     * @param HTMLPurifier_Context $context
+     */
+    public function __construct($context)
+    {
+        $this->locale    =& $context->get('Locale');
+        $this->context   = $context;
+        $this->_current  =& $this->_stacks[0];
+        $this->errors    =& $this->_stacks[0];
+    }
+
+    /**
+     * Sends an error message to the collector for later use
+     * @param int $severity Error severity, PHP error style (don't use E_USER_)
+     * @param string $msg Error message text
+     */
+    public function send($severity, $msg)
+    {
+        $args = array();
+        if (func_num_args() > 2) {
+            $args = func_get_args();
+            array_shift($args);
+            unset($args[0]);
+        }
+
+        $token = $this->context->get('CurrentToken', true);
+        $line  = $token ? $token->line : $this->context->get('CurrentLine', true);
+        $col   = $token ? $token->col  : $this->context->get('CurrentCol', true);
+        $attr  = $this->context->get('CurrentAttr', true);
+
+        // perform special substitutions, also add custom parameters
+        $subst = array();
+        if (!is_null($token)) {
+            $args['CurrentToken'] = $token;
+        }
+        if (!is_null($attr)) {
+            $subst['$CurrentAttr.Name'] = $attr;
+            if (isset($token->attr[$attr])) {
+                $subst['$CurrentAttr.Value'] = $token->attr[$attr];
+            }
+        }
+
+        if (empty($args)) {
+            $msg = $this->locale->getMessage($msg);
+        } else {
+            $msg = $this->locale->formatMessage($msg, $args);
+        }
+
+        if (!empty($subst)) {
+            $msg = strtr($msg, $subst);
+        }
+
+        // (numerically indexed)
+        $error = array(
+            self::LINENO   => $line,
+            self::SEVERITY => $severity,
+            self::MESSAGE  => $msg,
+            self::CHILDREN => array()
+        );
+        $this->_current[] = $error;
+
+        // NEW CODE BELOW ...
+        // Top-level errors are either:
+        //  TOKEN type, if $value is set appropriately, or
+        //  "syntax" type, if $value is null
+        $new_struct = new HTMLPurifier_ErrorStruct();
+        $new_struct->type = HTMLPurifier_ErrorStruct::TOKEN;
+        if ($token) {
+            $new_struct->value = clone $token;
+        }
+        if (is_int($line) && is_int($col)) {
+            if (isset($this->lines[$line][$col])) {
+                $struct = $this->lines[$line][$col];
+            } else {
+                $struct = $this->lines[$line][$col] = $new_struct;
+            }
+            // These ksorts may present a performance problem
+            ksort($this->lines[$line], SORT_NUMERIC);
+        } else {
+            if (isset($this->lines[-1])) {
+                $struct = $this->lines[-1];
+            } else {
+                $struct = $this->lines[-1] = $new_struct;
+            }
+        }
+        ksort($this->lines, SORT_NUMERIC);
+
+        // Now, check if we need to operate on a lower structure
+        if (!empty($attr)) {
+            $struct = $struct->getChild(HTMLPurifier_ErrorStruct::ATTR, $attr);
+            if (!$struct->value) {
+                $struct->value = array($attr, 'PUT VALUE HERE');
+            }
+        }
+        if (!empty($cssprop)) {
+            $struct = $struct->getChild(HTMLPurifier_ErrorStruct::CSSPROP, $cssprop);
+            if (!$struct->value) {
+                // if we tokenize CSS this might be a little more difficult to do
+                $struct->value = array($cssprop, 'PUT VALUE HERE');
+            }
+        }
+
+        // Ok, structs are all setup, now time to register the error
+        $struct->addError($severity, $msg);
+    }
+
+    /**
+     * Retrieves raw error data for custom formatter to use
+     */
+    public function getRaw()
+    {
+        return $this->errors;
+    }
+
+    /**
+     * Default HTML formatting implementation for error messages
+     * @param HTMLPurifier_Config $config Configuration, vital for HTML output nature
+     * @param array $errors Errors array to display; used for recursion.
+     * @return string
+     */
+    public function getHTMLFormatted($config, $errors = null)
+    {
+        $ret = array();
+
+        $this->generator = new HTMLPurifier_Generator($config, $this->context);
+        if ($errors === null) {
+            $errors = $this->errors;
+        }
+
+        // 'At line' message needs to be removed
+
+        // generation code for new structure goes here. It needs to be recursive.
+        foreach ($this->lines as $line => $col_array) {
+            if ($line == -1) {
+                continue;
+            }
+            foreach ($col_array as $col => $struct) {
+                $this->_renderStruct($ret, $struct, $line, $col);
+            }
+        }
+        if (isset($this->lines[-1])) {
+            $this->_renderStruct($ret, $this->lines[-1]);
+        }
+
+        if (empty($errors)) {
+            return '<p>' . $this->locale->getMessage('ErrorCollector: No errors') . '</p>';
+        } else {
+            return '<ul><li>' . implode('</li><li>', $ret) . '</li></ul>';
+        }
+
+    }
+
+    private function _renderStruct(&$ret, $struct, $line = null, $col = null)
+    {
+        $stack = array($struct);
+        $context_stack = array(array());
+        while ($current = array_pop($stack)) {
+            $context = array_pop($context_stack);
+            foreach ($current->errors as $error) {
+                list($severity, $msg) = $error;
+                $string = '';
+                $string .= '<div>';
+                // W3C uses an icon to indicate the severity of the error.
+                $error = $this->locale->getErrorName($severity);
+                $string .= "<span class=\"error e$severity\"><strong>$error</strong></span> ";
+                if (!is_null($line) && !is_null($col)) {
+                    $string .= "<em class=\"location\">Line $line, Column $col: </em> ";
+                } else {
+                    $string .= '<em class="location">End of Document: </em> ';
+                }
+                $string .= '<strong class="description">' . $this->generator->escape($msg) . '</strong> ';
+                $string .= '</div>';
+                // Here, have a marker for the character on the column appropriate.
+                // Be sure to clip extremely long lines.
+                //$string .= '<pre>';
+                //$string .= '';
+                //$string .= '</pre>';
+                $ret[] = $string;
+            }
+            foreach ($current->children as $array) {
+                $context[] = $current;
+                $stack = array_merge($stack, array_reverse($array, true));
+                for ($i = count($array); $i > 0; $i--) {
+                    $context_stack[] = $context;
+                }
+            }
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/ErrorStruct.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/ErrorStruct.php
new file mode 100644 (file)
index 0000000..cf869d3
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * Records errors for particular segments of an HTML document such as tokens,
+ * attributes or CSS properties. They can contain error structs (which apply
+ * to components of what they represent), but their main purpose is to hold
+ * errors applying to whatever struct is being used.
+ */
+class HTMLPurifier_ErrorStruct
+{
+
+    /**
+     * Possible values for $children first-key. Note that top-level structures
+     * are automatically token-level.
+     */
+    const TOKEN     = 0;
+    const ATTR      = 1;
+    const CSSPROP   = 2;
+
+    /**
+     * Type of this struct.
+     * @type string
+     */
+    public $type;
+
+    /**
+     * Value of the struct we are recording errors for. There are various
+     * values for this:
+     *  - TOKEN: Instance of HTMLPurifier_Token
+     *  - ATTR: array('attr-name', 'value')
+     *  - CSSPROP: array('prop-name', 'value')
+     * @type mixed
+     */
+    public $value;
+
+    /**
+     * Errors registered for this structure.
+     * @type array
+     */
+    public $errors = array();
+
+    /**
+     * Child ErrorStructs that are from this structure. For example, a TOKEN
+     * ErrorStruct would contain ATTR ErrorStructs. This is a multi-dimensional
+     * array in structure: [TYPE]['identifier']
+     * @type array
+     */
+    public $children = array();
+
+    /**
+     * @param string $type
+     * @param string $id
+     * @return mixed
+     */
+    public function getChild($type, $id)
+    {
+        if (!isset($this->children[$type][$id])) {
+            $this->children[$type][$id] = new HTMLPurifier_ErrorStruct();
+            $this->children[$type][$id]->type = $type;
+        }
+        return $this->children[$type][$id];
+    }
+
+    /**
+     * @param int $severity
+     * @param string $message
+     */
+    public function addError($severity, $message)
+    {
+        $this->errors[] = array($severity, $message);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Exception.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Exception.php
new file mode 100644 (file)
index 0000000..be85b4c
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+/**
+ * Global exception class for HTML Purifier; any exceptions we throw
+ * are from here.
+ */
+class HTMLPurifier_Exception extends Exception
+{
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Filter.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Filter.php
new file mode 100644 (file)
index 0000000..c1f41ee
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * Represents a pre or post processing filter on HTML Purifier's output
+ *
+ * Sometimes, a little ad-hoc fixing of HTML has to be done before
+ * it gets sent through HTML Purifier: you can use filters to acheive
+ * this effect. For instance, YouTube videos can be preserved using
+ * this manner. You could have used a decorator for this task, but
+ * PHP's support for them is not terribly robust, so we're going
+ * to just loop through the filters.
+ *
+ * Filters should be exited first in, last out. If there are three filters,
+ * named 1, 2 and 3, the order of execution should go 1->preFilter,
+ * 2->preFilter, 3->preFilter, purify, 3->postFilter, 2->postFilter,
+ * 1->postFilter.
+ *
+ * @note Methods are not declared abstract as it is perfectly legitimate
+ *       for an implementation not to want anything to happen on a step
+ */
+
+class HTMLPurifier_Filter
+{
+
+    /**
+     * Name of the filter for identification purposes.
+     * @type string
+     */
+    public $name;
+
+    /**
+     * Pre-processor function, handles HTML before HTML Purifier
+     * @param string $html
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return string
+     */
+    public function preFilter($html, $config, $context)
+    {
+        return $html;
+    }
+
+    /**
+     * Post-processor function, handles HTML after HTML Purifier
+     * @param string $html
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return string
+     */
+    public function postFilter($html, $config, $context)
+    {
+        return $html;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php
new file mode 100644 (file)
index 0000000..08e62c1
--- /dev/null
@@ -0,0 +1,338 @@
+<?php
+
+// why is this a top level function? Because PHP 5.2.0 doesn't seem to
+// understand how to interpret this filter if it's a static method.
+// It's all really silly, but if we go this route it might be reasonable
+// to coalesce all of these methods into one.
+function htmlpurifier_filter_extractstyleblocks_muteerrorhandler()
+{
+}
+
+/**
+ * This filter extracts <style> blocks from input HTML, cleans them up
+ * using CSSTidy, and then places them in $purifier->context->get('StyleBlocks')
+ * so they can be used elsewhere in the document.
+ *
+ * @note
+ *      See tests/HTMLPurifier/Filter/ExtractStyleBlocksTest.php for
+ *      sample usage.
+ *
+ * @note
+ *      This filter can also be used on stylesheets not included in the
+ *      document--something purists would probably prefer. Just directly
+ *      call HTMLPurifier_Filter_ExtractStyleBlocks->cleanCSS()
+ */
+class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
+{
+    /**
+     * @type string
+     */
+    public $name = 'ExtractStyleBlocks';
+
+    /**
+     * @type array
+     */
+    private $_styleMatches = array();
+
+    /**
+     * @type csstidy
+     */
+    private $_tidy;
+
+    /**
+     * @type HTMLPurifier_AttrDef_HTML_ID
+     */
+    private $_id_attrdef;
+
+    /**
+     * @type HTMLPurifier_AttrDef_CSS_Ident
+     */
+    private $_class_attrdef;
+
+    /**
+     * @type HTMLPurifier_AttrDef_Enum
+     */
+    private $_enum_attrdef;
+
+    public function __construct()
+    {
+        $this->_tidy = new csstidy();
+        $this->_tidy->set_cfg('lowercase_s', false);
+        $this->_id_attrdef = new HTMLPurifier_AttrDef_HTML_ID(true);
+        $this->_class_attrdef = new HTMLPurifier_AttrDef_CSS_Ident();
+        $this->_enum_attrdef = new HTMLPurifier_AttrDef_Enum(
+            array(
+                'first-child',
+                'link',
+                'visited',
+                'active',
+                'hover',
+                'focus'
+            )
+        );
+    }
+
+    /**
+     * Save the contents of CSS blocks to style matches
+     * @param array $matches preg_replace style $matches array
+     */
+    protected function styleCallback($matches)
+    {
+        $this->_styleMatches[] = $matches[1];
+    }
+
+    /**
+     * Removes inline <style> tags from HTML, saves them for later use
+     * @param string $html
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return string
+     * @todo Extend to indicate non-text/css style blocks
+     */
+    public function preFilter($html, $config, $context)
+    {
+        $tidy = $config->get('Filter.ExtractStyleBlocks.TidyImpl');
+        if ($tidy !== null) {
+            $this->_tidy = $tidy;
+        }
+        $html = preg_replace_callback('#<style(?:\s.*)?>(.+)</style>#isU', array($this, 'styleCallback'), $html);
+        $style_blocks = $this->_styleMatches;
+        $this->_styleMatches = array(); // reset
+        $context->register('StyleBlocks', $style_blocks); // $context must not be reused
+        if ($this->_tidy) {
+            foreach ($style_blocks as &$style) {
+                $style = $this->cleanCSS($style, $config, $context);
+            }
+        }
+        return $html;
+    }
+
+    /**
+     * Takes CSS (the stuff found in <style>) and cleans it.
+     * @warning Requires CSSTidy <http://csstidy.sourceforge.net/>
+     * @param string $css CSS styling to clean
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @throws HTMLPurifier_Exception
+     * @return string Cleaned CSS
+     */
+    public function cleanCSS($css, $config, $context)
+    {
+        // prepare scope
+        $scope = $config->get('Filter.ExtractStyleBlocks.Scope');
+        if ($scope !== null) {
+            $scopes = array_map('trim', explode(',', $scope));
+        } else {
+            $scopes = array();
+        }
+        // remove comments from CSS
+        $css = trim($css);
+        if (strncmp('<!--', $css, 4) === 0) {
+            $css = substr($css, 4);
+        }
+        if (strlen($css) > 3 && substr($css, -3) == '-->') {
+            $css = substr($css, 0, -3);
+        }
+        $css = trim($css);
+        set_error_handler('htmlpurifier_filter_extractstyleblocks_muteerrorhandler');
+        $this->_tidy->parse($css);
+        restore_error_handler();
+        $css_definition = $config->getDefinition('CSS');
+        $html_definition = $config->getDefinition('HTML');
+        $new_css = array();
+        foreach ($this->_tidy->css as $k => $decls) {
+            // $decls are all CSS declarations inside an @ selector
+            $new_decls = array();
+            foreach ($decls as $selector => $style) {
+                $selector = trim($selector);
+                if ($selector === '') {
+                    continue;
+                } // should not happen
+                // Parse the selector
+                // Here is the relevant part of the CSS grammar:
+                //
+                // ruleset
+                //   : selector [ ',' S* selector ]* '{' ...
+                // selector
+                //   : simple_selector [ combinator selector | S+ [ combinator? selector ]? ]?
+                // combinator
+                //   : '+' S*
+                //   : '>' S*
+                // simple_selector
+                //   : element_name [ HASH | class | attrib | pseudo ]*
+                //   | [ HASH | class | attrib | pseudo ]+
+                // element_name
+                //   : IDENT | '*'
+                //   ;
+                // class
+                //   : '.' IDENT
+                //   ;
+                // attrib
+                //   : '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*
+                //     [ IDENT | STRING ] S* ]? ']'
+                //   ;
+                // pseudo
+                //   : ':' [ IDENT | FUNCTION S* [IDENT S*]? ')' ]
+                //   ;
+                //
+                // For reference, here are the relevant tokens:
+                //
+                // HASH         #{name}
+                // IDENT        {ident}
+                // INCLUDES     ==
+                // DASHMATCH    |=
+                // STRING       {string}
+                // FUNCTION     {ident}\(
+                //
+                // And the lexical scanner tokens
+                //
+                // name         {nmchar}+
+                // nmchar       [_a-z0-9-]|{nonascii}|{escape}
+                // nonascii     [\240-\377]
+                // escape       {unicode}|\\[^\r\n\f0-9a-f]
+                // unicode      \\{h}}{1,6}(\r\n|[ \t\r\n\f])?
+                // ident        -?{nmstart}{nmchar*}
+                // nmstart      [_a-z]|{nonascii}|{escape}
+                // string       {string1}|{string2}
+                // string1      \"([^\n\r\f\\"]|\\{nl}|{escape})*\"
+                // string2      \'([^\n\r\f\\"]|\\{nl}|{escape})*\'
+                //
+                // We'll implement a subset (in order to reduce attack
+                // surface); in particular:
+                //
+                //      - No Unicode support
+                //      - No escapes support
+                //      - No string support (by proxy no attrib support)
+                //      - element_name is matched against allowed
+                //        elements (some people might find this
+                //        annoying...)
+                //      - Pseudo-elements one of :first-child, :link,
+                //        :visited, :active, :hover, :focus
+
+                // handle ruleset
+                $selectors = array_map('trim', explode(',', $selector));
+                $new_selectors = array();
+                foreach ($selectors as $sel) {
+                    // split on +, > and spaces
+                    $basic_selectors = preg_split('/\s*([+> ])\s*/', $sel, -1, PREG_SPLIT_DELIM_CAPTURE);
+                    // even indices are chunks, odd indices are
+                    // delimiters
+                    $nsel = null;
+                    $delim = null; // guaranteed to be non-null after
+                    // two loop iterations
+                    for ($i = 0, $c = count($basic_selectors); $i < $c; $i++) {
+                        $x = $basic_selectors[$i];
+                        if ($i % 2) {
+                            // delimiter
+                            if ($x === ' ') {
+                                $delim = ' ';
+                            } else {
+                                $delim = ' ' . $x . ' ';
+                            }
+                        } else {
+                            // simple selector
+                            $components = preg_split('/([#.:])/', $x, -1, PREG_SPLIT_DELIM_CAPTURE);
+                            $sdelim = null;
+                            $nx = null;
+                            for ($j = 0, $cc = count($components); $j < $cc; $j++) {
+                                $y = $components[$j];
+                                if ($j === 0) {
+                                    if ($y === '*' || isset($html_definition->info[$y = strtolower($y)])) {
+                                        $nx = $y;
+                                    } else {
+                                        // $nx stays null; this matters
+                                        // if we don't manage to find
+                                        // any valid selector content,
+                                        // in which case we ignore the
+                                        // outer $delim
+                                    }
+                                } elseif ($j % 2) {
+                                    // set delimiter
+                                    $sdelim = $y;
+                                } else {
+                                    $attrdef = null;
+                                    if ($sdelim === '#') {
+                                        $attrdef = $this->_id_attrdef;
+                                    } elseif ($sdelim === '.') {
+                                        $attrdef = $this->_class_attrdef;
+                                    } elseif ($sdelim === ':') {
+                                        $attrdef = $this->_enum_attrdef;
+                                    } else {
+                                        throw new HTMLPurifier_Exception('broken invariant sdelim and preg_split');
+                                    }
+                                    $r = $attrdef->validate($y, $config, $context);
+                                    if ($r !== false) {
+                                        if ($r !== true) {
+                                            $y = $r;
+                                        }
+                                        if ($nx === null) {
+                                            $nx = '';
+                                        }
+                                        $nx .= $sdelim . $y;
+                                    }
+                                }
+                            }
+                            if ($nx !== null) {
+                                if ($nsel === null) {
+                                    $nsel = $nx;
+                                } else {
+                                    $nsel .= $delim . $nx;
+                                }
+                            } else {
+                                // delimiters to the left of invalid
+                                // basic selector ignored
+                            }
+                        }
+                    }
+                    if ($nsel !== null) {
+                        if (!empty($scopes)) {
+                            foreach ($scopes as $s) {
+                                $new_selectors[] = "$s $nsel";
+                            }
+                        } else {
+                            $new_selectors[] = $nsel;
+                        }
+                    }
+                }
+                if (empty($new_selectors)) {
+                    continue;
+                }
+                $selector = implode(', ', $new_selectors);
+                foreach ($style as $name => $value) {
+                    if (!isset($css_definition->info[$name])) {
+                        unset($style[$name]);
+                        continue;
+                    }
+                    $def = $css_definition->info[$name];
+                    $ret = $def->validate($value, $config, $context);
+                    if ($ret === false) {
+                        unset($style[$name]);
+                    } else {
+                        $style[$name] = $ret;
+                    }
+                }
+                $new_decls[$selector] = $style;
+            }
+            $new_css[$k] = $new_decls;
+        }
+        // remove stuff that shouldn't be used, could be reenabled
+        // after security risks are analyzed
+        $this->_tidy->css = $new_css;
+        $this->_tidy->import = array();
+        $this->_tidy->charset = null;
+        $this->_tidy->namespace = null;
+        $css = $this->_tidy->print->plain();
+        // we are going to escape any special characters <>& to ensure
+        // that no funny business occurs (i.e. </style> in a font-family prop).
+        if ($config->get('Filter.ExtractStyleBlocks.Escaping')) {
+            $css = str_replace(
+                array('<', '>', '&'),
+                array('\3C ', '\3E ', '\26 '),
+                $css
+            );
+        }
+        return $css;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php
new file mode 100644 (file)
index 0000000..276d836
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+
+class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter
+{
+
+    /**
+     * @type string
+     */
+    public $name = 'YouTube';
+
+    /**
+     * @param string $html
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return string
+     */
+    public function preFilter($html, $config, $context)
+    {
+        $pre_regex = '#<object[^>]+>.+?' .
+            '(?:http:)?//www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s';
+        $pre_replace = '<span class="youtube-embed">\1</span>';
+        return preg_replace($pre_regex, $pre_replace, $html);
+    }
+
+    /**
+     * @param string $html
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return string
+     */
+    public function postFilter($html, $config, $context)
+    {
+        $post_regex = '#<span class="youtube-embed">((?:v|cp)/[A-Za-z0-9\-_=]+)</span>#';
+        return preg_replace_callback($post_regex, array($this, 'postFilterCallback'), $html);
+    }
+
+    /**
+     * @param $url
+     * @return string
+     */
+    protected function armorUrl($url)
+    {
+        return str_replace('--', '-&#45;', $url);
+    }
+
+    /**
+     * @param array $matches
+     * @return string
+     */
+    protected function postFilterCallback($matches)
+    {
+        $url = $this->armorUrl($matches[1]);
+        return '<object width="425" height="350" type="application/x-shockwave-flash" ' .
+        'data="//www.youtube.com/' . $url . '">' .
+        '<param name="movie" value="//www.youtube.com/' . $url . '"></param>' .
+        '<!--[if IE]>' .
+        '<embed src="//www.youtube.com/' . $url . '"' .
+        'type="application/x-shockwave-flash"' .
+        'wmode="transparent" width="425" height="350" />' .
+        '<![endif]-->' .
+        '</object>';
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Generator.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Generator.php
new file mode 100644 (file)
index 0000000..6fb5687
--- /dev/null
@@ -0,0 +1,286 @@
+<?php
+
+/**
+ * Generates HTML from tokens.
+ * @todo Refactor interface so that configuration/context is determined
+ *       upon instantiation, no need for messy generateFromTokens() calls
+ * @todo Make some of the more internal functions protected, and have
+ *       unit tests work around that
+ */
+class HTMLPurifier_Generator
+{
+
+    /**
+     * Whether or not generator should produce XML output.
+     * @type bool
+     */
+    private $_xhtml = true;
+
+    /**
+     * :HACK: Whether or not generator should comment the insides of <script> tags.
+     * @type bool
+     */
+    private $_scriptFix = false;
+
+    /**
+     * Cache of HTMLDefinition during HTML output to determine whether or
+     * not attributes should be minimized.
+     * @type HTMLPurifier_HTMLDefinition
+     */
+    private $_def;
+
+    /**
+     * Cache of %Output.SortAttr.
+     * @type bool
+     */
+    private $_sortAttr;
+
+    /**
+     * Cache of %Output.FlashCompat.
+     * @type bool
+     */
+    private $_flashCompat;
+
+    /**
+     * Cache of %Output.FixInnerHTML.
+     * @type bool
+     */
+    private $_innerHTMLFix;
+
+    /**
+     * Stack for keeping track of object information when outputting IE
+     * compatibility code.
+     * @type array
+     */
+    private $_flashStack = array();
+
+    /**
+     * Configuration for the generator
+     * @type HTMLPurifier_Config
+     */
+    protected $config;
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     */
+    public function __construct($config, $context)
+    {
+        $this->config = $config;
+        $this->_scriptFix = $config->get('Output.CommentScriptContents');
+        $this->_innerHTMLFix = $config->get('Output.FixInnerHTML');
+        $this->_sortAttr = $config->get('Output.SortAttr');
+        $this->_flashCompat = $config->get('Output.FlashCompat');
+        $this->_def = $config->getHTMLDefinition();
+        $this->_xhtml = $this->_def->doctype->xml;
+    }
+
+    /**
+     * Generates HTML from an array of tokens.
+     * @param HTMLPurifier_Token[] $tokens Array of HTMLPurifier_Token
+     * @return string Generated HTML
+     */
+    public function generateFromTokens($tokens)
+    {
+        if (!$tokens) {
+            return '';
+        }
+
+        // Basic algorithm
+        $html = '';
+        for ($i = 0, $size = count($tokens); $i < $size; $i++) {
+            if ($this->_scriptFix && $tokens[$i]->name === 'script'
+                && $i + 2 < $size && $tokens[$i+2] instanceof HTMLPurifier_Token_End) {
+                // script special case
+                // the contents of the script block must be ONE token
+                // for this to work.
+                $html .= $this->generateFromToken($tokens[$i++]);
+                $html .= $this->generateScriptFromToken($tokens[$i++]);
+            }
+            $html .= $this->generateFromToken($tokens[$i]);
+        }
+
+        // Tidy cleanup
+        if (extension_loaded('tidy') && $this->config->get('Output.TidyFormat')) {
+            $tidy = new Tidy;
+            $tidy->parseString(
+                $html,
+                array(
+                   'indent'=> true,
+                   'output-xhtml' => $this->_xhtml,
+                   'show-body-only' => true,
+                   'indent-spaces' => 2,
+                   'wrap' => 68,
+                ),
+                'utf8'
+            );
+            $tidy->cleanRepair();
+            $html = (string) $tidy; // explicit cast necessary
+        }
+
+        // Normalize newlines to system defined value
+        if ($this->config->get('Core.NormalizeNewlines')) {
+            $nl = $this->config->get('Output.Newline');
+            if ($nl === null) {
+                $nl = PHP_EOL;
+            }
+            if ($nl !== "\n") {
+                $html = str_replace("\n", $nl, $html);
+            }
+        }
+        return $html;
+    }
+
+    /**
+     * Generates HTML from a single token.
+     * @param HTMLPurifier_Token $token HTMLPurifier_Token object.
+     * @return string Generated HTML
+     */
+    public function generateFromToken($token)
+    {
+        if (!$token instanceof HTMLPurifier_Token) {
+            trigger_error('Cannot generate HTML from non-HTMLPurifier_Token object', E_USER_WARNING);
+            return '';
+
+        } elseif ($token instanceof HTMLPurifier_Token_Start) {
+            $attr = $this->generateAttributes($token->attr, $token->name);
+            if ($this->_flashCompat) {
+                if ($token->name == "object") {
+                    $flash = new stdclass();
+                    $flash->attr = $token->attr;
+                    $flash->param = array();
+                    $this->_flashStack[] = $flash;
+                }
+            }
+            return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>';
+
+        } elseif ($token instanceof HTMLPurifier_Token_End) {
+            $_extra = '';
+            if ($this->_flashCompat) {
+                if ($token->name == "object" && !empty($this->_flashStack)) {
+                    // doesn't do anything for now
+                }
+            }
+            return $_extra . '</' . $token->name . '>';
+
+        } elseif ($token instanceof HTMLPurifier_Token_Empty) {
+            if ($this->_flashCompat && $token->name == "param" && !empty($this->_flashStack)) {
+                $this->_flashStack[count($this->_flashStack)-1]->param[$token->attr['name']] = $token->attr['value'];
+            }
+            $attr = $this->generateAttributes($token->attr, $token->name);
+             return '<' . $token->name . ($attr ? ' ' : '') . $attr .
+                ( $this->_xhtml ? ' /': '' ) // <br /> v. <br>
+                . '>';
+
+        } elseif ($token instanceof HTMLPurifier_Token_Text) {
+            return $this->escape($token->data, ENT_NOQUOTES);
+
+        } elseif ($token instanceof HTMLPurifier_Token_Comment) {
+            return '<!--' . $token->data . '-->';
+        } else {
+            return '';
+
+        }
+    }
+
+    /**
+     * Special case processor for the contents of script tags
+     * @param HTMLPurifier_Token $token HTMLPurifier_Token object.
+     * @return string
+     * @warning This runs into problems if there's already a literal
+     *          --> somewhere inside the script contents.
+     */
+    public function generateScriptFromToken($token)
+    {
+        if (!$token instanceof HTMLPurifier_Token_Text) {
+            return $this->generateFromToken($token);
+        }
+        // Thanks <http://lachy.id.au/log/2005/05/script-comments>
+        $data = preg_replace('#//\s*$#', '', $token->data);
+        return '<!--//--><![CDATA[//><!--' . "\n" . trim($data) . "\n" . '//--><!]]>';
+    }
+
+    /**
+     * Generates attribute declarations from attribute array.
+     * @note This does not include the leading or trailing space.
+     * @param array $assoc_array_of_attributes Attribute array
+     * @param string $element Name of element attributes are for, used to check
+     *        attribute minimization.
+     * @return string Generated HTML fragment for insertion.
+     */
+    public function generateAttributes($assoc_array_of_attributes, $element = '')
+    {
+        $html = '';
+        if ($this->_sortAttr) {
+            ksort($assoc_array_of_attributes);
+        }
+        foreach ($assoc_array_of_attributes as $key => $value) {
+            if (!$this->_xhtml) {
+                // Remove namespaced attributes
+                if (strpos($key, ':') !== false) {
+                    continue;
+                }
+                // Check if we should minimize the attribute: val="val" -> val
+                if ($element && !empty($this->_def->info[$element]->attr[$key]->minimized)) {
+                    $html .= $key . ' ';
+                    continue;
+                }
+            }
+            // Workaround for Internet Explorer innerHTML bug.
+            // Essentially, Internet Explorer, when calculating
+            // innerHTML, omits quotes if there are no instances of
+            // angled brackets, quotes or spaces.  However, when parsing
+            // HTML (for example, when you assign to innerHTML), it
+            // treats backticks as quotes.  Thus,
+            //      <img alt="``" />
+            // becomes
+            //      <img alt=`` />
+            // becomes
+            //      <img alt='' />
+            // Fortunately, all we need to do is trigger an appropriate
+            // quoting style, which we do by adding an extra space.
+            // This also is consistent with the W3C spec, which states
+            // that user agents may ignore leading or trailing
+            // whitespace (in fact, most don't, at least for attributes
+            // like alt, but an extra space at the end is barely
+            // noticeable).  Still, we have a configuration knob for
+            // this, since this transformation is not necesary if you
+            // don't process user input with innerHTML or you don't plan
+            // on supporting Internet Explorer.
+            if ($this->_innerHTMLFix) {
+                if (strpos($value, '`') !== false) {
+                    // check if correct quoting style would not already be
+                    // triggered
+                    if (strcspn($value, '"\' <>') === strlen($value)) {
+                        // protect!
+                        $value .= ' ';
+                    }
+                }
+            }
+            $html .= $key.'="'.$this->escape($value).'" ';
+        }
+        return rtrim($html);
+    }
+
+    /**
+     * Escapes raw text data.
+     * @todo This really ought to be protected, but until we have a facility
+     *       for properly generating HTML here w/o using tokens, it stays
+     *       public.
+     * @param string $string String data to escape for HTML.
+     * @param int $quote Quoting style, like htmlspecialchars. ENT_NOQUOTES is
+     *               permissible for non-attribute output.
+     * @return string escaped data.
+     */
+    public function escape($string, $quote = null)
+    {
+        // Workaround for APC bug on Mac Leopard reported by sidepodcast
+        // http://htmlpurifier.org/phorum/read.php?3,4823,4846
+        if ($quote === null) {
+            $quote = ENT_COMPAT;
+        }
+        return htmlspecialchars($string, $quote, 'UTF-8');
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php
new file mode 100644 (file)
index 0000000..9b7b334
--- /dev/null
@@ -0,0 +1,493 @@
+<?php
+
+/**
+ * Definition of the purified HTML that describes allowed children,
+ * attributes, and many other things.
+ *
+ * Conventions:
+ *
+ * All member variables that are prefixed with info
+ * (including the main $info array) are used by HTML Purifier internals
+ * and should not be directly edited when customizing the HTMLDefinition.
+ * They can usually be set via configuration directives or custom
+ * modules.
+ *
+ * On the other hand, member variables without the info prefix are used
+ * internally by the HTMLDefinition and MUST NOT be used by other HTML
+ * Purifier internals. Many of them, however, are public, and may be
+ * edited by userspace code to tweak the behavior of HTMLDefinition.
+ *
+ * @note This class is inspected by Printer_HTMLDefinition; please
+ *       update that class if things here change.
+ *
+ * @warning Directives that change this object's structure must be in
+ *          the HTML or Attr namespace!
+ */
+class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
+{
+
+    // FULLY-PUBLIC VARIABLES ---------------------------------------------
+
+    /**
+     * Associative array of element names to HTMLPurifier_ElementDef.
+     * @type HTMLPurifier_ElementDef[]
+     */
+    public $info = array();
+
+    /**
+     * Associative array of global attribute name to attribute definition.
+     * @type array
+     */
+    public $info_global_attr = array();
+
+    /**
+     * String name of parent element HTML will be going into.
+     * @type string
+     */
+    public $info_parent = 'div';
+
+    /**
+     * Definition for parent element, allows parent element to be a
+     * tag that's not allowed inside the HTML fragment.
+     * @type HTMLPurifier_ElementDef
+     */
+    public $info_parent_def;
+
+    /**
+     * String name of element used to wrap inline elements in block context.
+     * @type string
+     * @note This is rarely used except for BLOCKQUOTEs in strict mode
+     */
+    public $info_block_wrapper = 'p';
+
+    /**
+     * Associative array of deprecated tag name to HTMLPurifier_TagTransform.
+     * @type array
+     */
+    public $info_tag_transform = array();
+
+    /**
+     * Indexed list of HTMLPurifier_AttrTransform to be performed before validation.
+     * @type HTMLPurifier_AttrTransform[]
+     */
+    public $info_attr_transform_pre = array();
+
+    /**
+     * Indexed list of HTMLPurifier_AttrTransform to be performed after validation.
+     * @type HTMLPurifier_AttrTransform[]
+     */
+    public $info_attr_transform_post = array();
+
+    /**
+     * Nested lookup array of content set name (Block, Inline) to
+     * element name to whether or not it belongs in that content set.
+     * @type array
+     */
+    public $info_content_sets = array();
+
+    /**
+     * Indexed list of HTMLPurifier_Injector to be used.
+     * @type HTMLPurifier_Injector[]
+     */
+    public $info_injector = array();
+
+    /**
+     * Doctype object
+     * @type HTMLPurifier_Doctype
+     */
+    public $doctype;
+
+
+
+    // RAW CUSTOMIZATION STUFF --------------------------------------------
+
+    /**
+     * Adds a custom attribute to a pre-existing element
+     * @note This is strictly convenience, and does not have a corresponding
+     *       method in HTMLPurifier_HTMLModule
+     * @param string $element_name Element name to add attribute to
+     * @param string $attr_name Name of attribute
+     * @param mixed $def Attribute definition, can be string or object, see
+     *             HTMLPurifier_AttrTypes for details
+     */
+    public function addAttribute($element_name, $attr_name, $def)
+    {
+        $module = $this->getAnonymousModule();
+        if (!isset($module->info[$element_name])) {
+            $element = $module->addBlankElement($element_name);
+        } else {
+            $element = $module->info[$element_name];
+        }
+        $element->attr[$attr_name] = $def;
+    }
+
+    /**
+     * Adds a custom element to your HTML definition
+     * @see HTMLPurifier_HTMLModule::addElement() for detailed
+     *       parameter and return value descriptions.
+     */
+    public function addElement($element_name, $type, $contents, $attr_collections, $attributes = array())
+    {
+        $module = $this->getAnonymousModule();
+        // assume that if the user is calling this, the element
+        // is safe. This may not be a good idea
+        $element = $module->addElement($element_name, $type, $contents, $attr_collections, $attributes);
+        return $element;
+    }
+
+    /**
+     * Adds a blank element to your HTML definition, for overriding
+     * existing behavior
+     * @param string $element_name
+     * @return HTMLPurifier_ElementDef
+     * @see HTMLPurifier_HTMLModule::addBlankElement() for detailed
+     *       parameter and return value descriptions.
+     */
+    public function addBlankElement($element_name)
+    {
+        $module  = $this->getAnonymousModule();
+        $element = $module->addBlankElement($element_name);
+        return $element;
+    }
+
+    /**
+     * Retrieves a reference to the anonymous module, so you can
+     * bust out advanced features without having to make your own
+     * module.
+     * @return HTMLPurifier_HTMLModule
+     */
+    public function getAnonymousModule()
+    {
+        if (!$this->_anonModule) {
+            $this->_anonModule = new HTMLPurifier_HTMLModule();
+            $this->_anonModule->name = 'Anonymous';
+        }
+        return $this->_anonModule;
+    }
+
+    private $_anonModule = null;
+
+    // PUBLIC BUT INTERNAL VARIABLES --------------------------------------
+
+    /**
+     * @type string
+     */
+    public $type = 'HTML';
+
+    /**
+     * @type HTMLPurifier_HTMLModuleManager
+     */
+    public $manager;
+
+    /**
+     * Performs low-cost, preliminary initialization.
+     */
+    public function __construct()
+    {
+        $this->manager = new HTMLPurifier_HTMLModuleManager();
+    }
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    protected function doSetup($config)
+    {
+        $this->processModules($config);
+        $this->setupConfigStuff($config);
+        unset($this->manager);
+
+        // cleanup some of the element definitions
+        foreach ($this->info as $k => $v) {
+            unset($this->info[$k]->content_model);
+            unset($this->info[$k]->content_model_type);
+        }
+    }
+
+    /**
+     * Extract out the information from the manager
+     * @param HTMLPurifier_Config $config
+     */
+    protected function processModules($config)
+    {
+        if ($this->_anonModule) {
+            // for user specific changes
+            // this is late-loaded so we don't have to deal with PHP4
+            // reference wonky-ness
+            $this->manager->addModule($this->_anonModule);
+            unset($this->_anonModule);
+        }
+
+        $this->manager->setup($config);
+        $this->doctype = $this->manager->doctype;
+
+        foreach ($this->manager->modules as $module) {
+            foreach ($module->info_tag_transform as $k => $v) {
+                if ($v === false) {
+                    unset($this->info_tag_transform[$k]);
+                } else {
+                    $this->info_tag_transform[$k] = $v;
+                }
+            }
+            foreach ($module->info_attr_transform_pre as $k => $v) {
+                if ($v === false) {
+                    unset($this->info_attr_transform_pre[$k]);
+                } else {
+                    $this->info_attr_transform_pre[$k] = $v;
+                }
+            }
+            foreach ($module->info_attr_transform_post as $k => $v) {
+                if ($v === false) {
+                    unset($this->info_attr_transform_post[$k]);
+                } else {
+                    $this->info_attr_transform_post[$k] = $v;
+                }
+            }
+            foreach ($module->info_injector as $k => $v) {
+                if ($v === false) {
+                    unset($this->info_injector[$k]);
+                } else {
+                    $this->info_injector[$k] = $v;
+                }
+            }
+        }
+        $this->info = $this->manager->getElements();
+        $this->info_content_sets = $this->manager->contentSets->lookup;
+    }
+
+    /**
+     * Sets up stuff based on config. We need a better way of doing this.
+     * @param HTMLPurifier_Config $config
+     */
+    protected function setupConfigStuff($config)
+    {
+        $block_wrapper = $config->get('HTML.BlockWrapper');
+        if (isset($this->info_content_sets['Block'][$block_wrapper])) {
+            $this->info_block_wrapper = $block_wrapper;
+        } else {
+            trigger_error(
+                'Cannot use non-block element as block wrapper',
+                E_USER_ERROR
+            );
+        }
+
+        $parent = $config->get('HTML.Parent');
+        $def = $this->manager->getElement($parent, true);
+        if ($def) {
+            $this->info_parent = $parent;
+            $this->info_parent_def = $def;
+        } else {
+            trigger_error(
+                'Cannot use unrecognized element as parent',
+                E_USER_ERROR
+            );
+            $this->info_parent_def = $this->manager->getElement($this->info_parent, true);
+        }
+
+        // support template text
+        $support = "(for information on implementing this, see the support forums) ";
+
+        // setup allowed elements -----------------------------------------
+
+        $allowed_elements = $config->get('HTML.AllowedElements');
+        $allowed_attributes = $config->get('HTML.AllowedAttributes'); // retrieve early
+
+        if (!is_array($allowed_elements) && !is_array($allowed_attributes)) {
+            $allowed = $config->get('HTML.Allowed');
+            if (is_string($allowed)) {
+                list($allowed_elements, $allowed_attributes) = $this->parseTinyMCEAllowedList($allowed);
+            }
+        }
+
+        if (is_array($allowed_elements)) {
+            foreach ($this->info as $name => $d) {
+                if (!isset($allowed_elements[$name])) {
+                    unset($this->info[$name]);
+                }
+                unset($allowed_elements[$name]);
+            }
+            // emit errors
+            foreach ($allowed_elements as $element => $d) {
+                $element = htmlspecialchars($element); // PHP doesn't escape errors, be careful!
+                trigger_error("Element '$element' is not supported $support", E_USER_WARNING);
+            }
+        }
+
+        // setup allowed attributes ---------------------------------------
+
+        $allowed_attributes_mutable = $allowed_attributes; // by copy!
+        if (is_array($allowed_attributes)) {
+            // This actually doesn't do anything, since we went away from
+            // global attributes. It's possible that userland code uses
+            // it, but HTMLModuleManager doesn't!
+            foreach ($this->info_global_attr as $attr => $x) {
+                $keys = array($attr, "*@$attr", "*.$attr");
+                $delete = true;
+                foreach ($keys as $key) {
+                    if ($delete && isset($allowed_attributes[$key])) {
+                        $delete = false;
+                    }
+                    if (isset($allowed_attributes_mutable[$key])) {
+                        unset($allowed_attributes_mutable[$key]);
+                    }
+                }
+                if ($delete) {
+                    unset($this->info_global_attr[$attr]);
+                }
+            }
+
+            foreach ($this->info as $tag => $info) {
+                foreach ($info->attr as $attr => $x) {
+                    $keys = array("$tag@$attr", $attr, "*@$attr", "$tag.$attr", "*.$attr");
+                    $delete = true;
+                    foreach ($keys as $key) {
+                        if ($delete && isset($allowed_attributes[$key])) {
+                            $delete = false;
+                        }
+                        if (isset($allowed_attributes_mutable[$key])) {
+                            unset($allowed_attributes_mutable[$key]);
+                        }
+                    }
+                    if ($delete) {
+                        if ($this->info[$tag]->attr[$attr]->required) {
+                            trigger_error(
+                                "Required attribute '$attr' in element '$tag' " .
+                                "was not allowed, which means '$tag' will not be allowed either",
+                                E_USER_WARNING
+                            );
+                        }
+                        unset($this->info[$tag]->attr[$attr]);
+                    }
+                }
+            }
+            // emit errors
+            foreach ($allowed_attributes_mutable as $elattr => $d) {
+                $bits = preg_split('/[.@]/', $elattr, 2);
+                $c = count($bits);
+                switch ($c) {
+                    case 2:
+                        if ($bits[0] !== '*') {
+                            $element = htmlspecialchars($bits[0]);
+                            $attribute = htmlspecialchars($bits[1]);
+                            if (!isset($this->info[$element])) {
+                                trigger_error(
+                                    "Cannot allow attribute '$attribute' if element " .
+                                    "'$element' is not allowed/supported $support"
+                                );
+                            } else {
+                                trigger_error(
+                                    "Attribute '$attribute' in element '$element' not supported $support",
+                                    E_USER_WARNING
+                                );
+                            }
+                            break;
+                        }
+                        // otherwise fall through
+                    case 1:
+                        $attribute = htmlspecialchars($bits[0]);
+                        trigger_error(
+                            "Global attribute '$attribute' is not ".
+                            "supported in any elements $support",
+                            E_USER_WARNING
+                        );
+                        break;
+                }
+            }
+        }
+
+        // setup forbidden elements ---------------------------------------
+
+        $forbidden_elements   = $config->get('HTML.ForbiddenElements');
+        $forbidden_attributes = $config->get('HTML.ForbiddenAttributes');
+
+        foreach ($this->info as $tag => $info) {
+            if (isset($forbidden_elements[$tag])) {
+                unset($this->info[$tag]);
+                continue;
+            }
+            foreach ($info->attr as $attr => $x) {
+                if (isset($forbidden_attributes["$tag@$attr"]) ||
+                    isset($forbidden_attributes["*@$attr"]) ||
+                    isset($forbidden_attributes[$attr])
+                ) {
+                    unset($this->info[$tag]->attr[$attr]);
+                    continue;
+                } elseif (isset($forbidden_attributes["$tag.$attr"])) { // this segment might get removed eventually
+                    // $tag.$attr are not user supplied, so no worries!
+                    trigger_error(
+                        "Error with $tag.$attr: tag.attr syntax not supported for " .
+                        "HTML.ForbiddenAttributes; use tag@attr instead",
+                        E_USER_WARNING
+                    );
+                }
+            }
+        }
+        foreach ($forbidden_attributes as $key => $v) {
+            if (strlen($key) < 2) {
+                continue;
+            }
+            if ($key[0] != '*') {
+                continue;
+            }
+            if ($key[1] == '.') {
+                trigger_error(
+                    "Error with $key: *.attr syntax not supported for HTML.ForbiddenAttributes; use attr instead",
+                    E_USER_WARNING
+                );
+            }
+        }
+
+        // setup injectors -----------------------------------------------------
+        foreach ($this->info_injector as $i => $injector) {
+            if ($injector->checkNeeded($config) !== false) {
+                // remove injector that does not have it's required
+                // elements/attributes present, and is thus not needed.
+                unset($this->info_injector[$i]);
+            }
+        }
+    }
+
+    /**
+     * Parses a TinyMCE-flavored Allowed Elements and Attributes list into
+     * separate lists for processing. Format is element[attr1|attr2],element2...
+     * @warning Although it's largely drawn from TinyMCE's implementation,
+     *      it is different, and you'll probably have to modify your lists
+     * @param array $list String list to parse
+     * @return array
+     * @todo Give this its own class, probably static interface
+     */
+    public function parseTinyMCEAllowedList($list)
+    {
+        $list = str_replace(array(' ', "\t"), '', $list);
+
+        $elements = array();
+        $attributes = array();
+
+        $chunks = preg_split('/(,|[\n\r]+)/', $list);
+        foreach ($chunks as $chunk) {
+            if (empty($chunk)) {
+                continue;
+            }
+            // remove TinyMCE element control characters
+            if (!strpos($chunk, '[')) {
+                $element = $chunk;
+                $attr = false;
+            } else {
+                list($element, $attr) = explode('[', $chunk);
+            }
+            if ($element !== '*') {
+                $elements[$element] = true;
+            }
+            if (!$attr) {
+                continue;
+            }
+            $attr = substr($attr, 0, strlen($attr) - 1); // remove trailing ]
+            $attr = explode('|', $attr);
+            foreach ($attr as $key) {
+                $attributes["$element.$key"] = true;
+            }
+        }
+        return array($elements, $attributes);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule.php
new file mode 100644 (file)
index 0000000..bb3a923
--- /dev/null
@@ -0,0 +1,284 @@
+<?php
+
+/**
+ * Represents an XHTML 1.1 module, with information on elements, tags
+ * and attributes.
+ * @note Even though this is technically XHTML 1.1, it is also used for
+ *       regular HTML parsing. We are using modulization as a convenient
+ *       way to represent the internals of HTMLDefinition, and our
+ *       implementation is by no means conforming and does not directly
+ *       use the normative DTDs or XML schemas.
+ * @note The public variables in a module should almost directly
+ *       correspond to the variables in HTMLPurifier_HTMLDefinition.
+ *       However, the prefix info carries no special meaning in these
+ *       objects (include it anyway if that's the correspondence though).
+ * @todo Consider making some member functions protected
+ */
+
+class HTMLPurifier_HTMLModule
+{
+
+    // -- Overloadable ----------------------------------------------------
+
+    /**
+     * Short unique string identifier of the module.
+     * @type string
+     */
+    public $name;
+
+    /**
+     * Informally, a list of elements this module changes.
+     * Not used in any significant way.
+     * @type array
+     */
+    public $elements = array();
+
+    /**
+     * Associative array of element names to element definitions.
+     * Some definitions may be incomplete, to be merged in later
+     * with the full definition.
+     * @type array
+     */
+    public $info = array();
+
+    /**
+     * Associative array of content set names to content set additions.
+     * This is commonly used to, say, add an A element to the Inline
+     * content set. This corresponds to an internal variable $content_sets
+     * and NOT info_content_sets member variable of HTMLDefinition.
+     * @type array
+     */
+    public $content_sets = array();
+
+    /**
+     * Associative array of attribute collection names to attribute
+     * collection additions. More rarely used for adding attributes to
+     * the global collections. Example is the StyleAttribute module adding
+     * the style attribute to the Core. Corresponds to HTMLDefinition's
+     * attr_collections->info, since the object's data is only info,
+     * with extra behavior associated with it.
+     * @type array
+     */
+    public $attr_collections = array();
+
+    /**
+     * Associative array of deprecated tag name to HTMLPurifier_TagTransform.
+     * @type array
+     */
+    public $info_tag_transform = array();
+
+    /**
+     * List of HTMLPurifier_AttrTransform to be performed before validation.
+     * @type array
+     */
+    public $info_attr_transform_pre = array();
+
+    /**
+     * List of HTMLPurifier_AttrTransform to be performed after validation.
+     * @type array
+     */
+    public $info_attr_transform_post = array();
+
+    /**
+     * List of HTMLPurifier_Injector to be performed during well-formedness fixing.
+     * An injector will only be invoked if all of it's pre-requisites are met;
+     * if an injector fails setup, there will be no error; it will simply be
+     * silently disabled.
+     * @type array
+     */
+    public $info_injector = array();
+
+    /**
+     * Boolean flag that indicates whether or not getChildDef is implemented.
+     * For optimization reasons: may save a call to a function. Be sure
+     * to set it if you do implement getChildDef(), otherwise it will have
+     * no effect!
+     * @type bool
+     */
+    public $defines_child_def = false;
+
+    /**
+     * Boolean flag whether or not this module is safe. If it is not safe, all
+     * of its members are unsafe. Modules are safe by default (this might be
+     * slightly dangerous, but it doesn't make much sense to force HTML Purifier,
+     * which is based off of safe HTML, to explicitly say, "This is safe," even
+     * though there are modules which are "unsafe")
+     *
+     * @type bool
+     * @note Previously, safety could be applied at an element level granularity.
+     *       We've removed this ability, so in order to add "unsafe" elements
+     *       or attributes, a dedicated module with this property set to false
+     *       must be used.
+     */
+    public $safe = true;
+
+    /**
+     * Retrieves a proper HTMLPurifier_ChildDef subclass based on
+     * content_model and content_model_type member variables of
+     * the HTMLPurifier_ElementDef class. There is a similar function
+     * in HTMLPurifier_HTMLDefinition.
+     * @param HTMLPurifier_ElementDef $def
+     * @return HTMLPurifier_ChildDef subclass
+     */
+    public function getChildDef($def)
+    {
+        return false;
+    }
+
+    // -- Convenience -----------------------------------------------------
+
+    /**
+     * Convenience function that sets up a new element
+     * @param string $element Name of element to add
+     * @param string|bool $type What content set should element be registered to?
+     *              Set as false to skip this step.
+     * @param string $contents Allowed children in form of:
+     *              "$content_model_type: $content_model"
+     * @param array $attr_includes What attribute collections to register to
+     *              element?
+     * @param array $attr What unique attributes does the element define?
+     * @see HTMLPurifier_ElementDef:: for in-depth descriptions of these parameters.
+     * @return HTMLPurifier_ElementDef Created element definition object, so you
+     *         can set advanced parameters
+     */
+    public function addElement($element, $type, $contents, $attr_includes = array(), $attr = array())
+    {
+        $this->elements[] = $element;
+        // parse content_model
+        list($content_model_type, $content_model) = $this->parseContents($contents);
+        // merge in attribute inclusions
+        $this->mergeInAttrIncludes($attr, $attr_includes);
+        // add element to content sets
+        if ($type) {
+            $this->addElementToContentSet($element, $type);
+        }
+        // create element
+        $this->info[$element] = HTMLPurifier_ElementDef::create(
+            $content_model,
+            $content_model_type,
+            $attr
+        );
+        // literal object $contents means direct child manipulation
+        if (!is_string($contents)) {
+            $this->info[$element]->child = $contents;
+        }
+        return $this->info[$element];
+    }
+
+    /**
+     * Convenience function that creates a totally blank, non-standalone
+     * element.
+     * @param string $element Name of element to create
+     * @return HTMLPurifier_ElementDef Created element
+     */
+    public function addBlankElement($element)
+    {
+        if (!isset($this->info[$element])) {
+            $this->elements[] = $element;
+            $this->info[$element] = new HTMLPurifier_ElementDef();
+            $this->info[$element]->standalone = false;
+        } else {
+            trigger_error("Definition for $element already exists in module, cannot redefine");
+        }
+        return $this->info[$element];
+    }
+
+    /**
+     * Convenience function that registers an element to a content set
+     * @param string $element Element to register
+     * @param string $type Name content set (warning: case sensitive, usually upper-case
+     *        first letter)
+     */
+    public function addElementToContentSet($element, $type)
+    {
+        if (!isset($this->content_sets[$type])) {
+            $this->content_sets[$type] = '';
+        } else {
+            $this->content_sets[$type] .= ' | ';
+        }
+        $this->content_sets[$type] .= $element;
+    }
+
+    /**
+     * Convenience function that transforms single-string contents
+     * into separate content model and content model type
+     * @param string $contents Allowed children in form of:
+     *                  "$content_model_type: $content_model"
+     * @return array
+     * @note If contents is an object, an array of two nulls will be
+     *       returned, and the callee needs to take the original $contents
+     *       and use it directly.
+     */
+    public function parseContents($contents)
+    {
+        if (!is_string($contents)) {
+            return array(null, null);
+        } // defer
+        switch ($contents) {
+            // check for shorthand content model forms
+            case 'Empty':
+                return array('empty', '');
+            case 'Inline':
+                return array('optional', 'Inline | #PCDATA');
+            case 'Flow':
+                return array('optional', 'Flow | #PCDATA');
+        }
+        list($content_model_type, $content_model) = explode(':', $contents);
+        $content_model_type = strtolower(trim($content_model_type));
+        $content_model = trim($content_model);
+        return array($content_model_type, $content_model);
+    }
+
+    /**
+     * Convenience function that merges a list of attribute includes into
+     * an attribute array.
+     * @param array $attr Reference to attr array to modify
+     * @param array $attr_includes Array of includes / string include to merge in
+     */
+    public function mergeInAttrIncludes(&$attr, $attr_includes)
+    {
+        if (!is_array($attr_includes)) {
+            if (empty($attr_includes)) {
+                $attr_includes = array();
+            } else {
+                $attr_includes = array($attr_includes);
+            }
+        }
+        $attr[0] = $attr_includes;
+    }
+
+    /**
+     * Convenience function that generates a lookup table with boolean
+     * true as value.
+     * @param string $list List of values to turn into a lookup
+     * @note You can also pass an arbitrary number of arguments in
+     *       place of the regular argument
+     * @return array array equivalent of list
+     */
+    public function makeLookup($list)
+    {
+        if (is_string($list)) {
+            $list = func_get_args();
+        }
+        $ret = array();
+        foreach ($list as $value) {
+            if (is_null($value)) {
+                continue;
+            }
+            $ret[$value] = true;
+        }
+        return $ret;
+    }
+
+    /**
+     * Lazy load construction of the module after determining whether
+     * or not it's needed, and also when a finalized configuration object
+     * is available.
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Bdo.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Bdo.php
new file mode 100644 (file)
index 0000000..1e67c79
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * XHTML 1.1 Bi-directional Text Module, defines elements that
+ * declare directionality of content. Text Extension Module.
+ */
+class HTMLPurifier_HTMLModule_Bdo extends HTMLPurifier_HTMLModule
+{
+
+    /**
+     * @type string
+     */
+    public $name = 'Bdo';
+
+    /**
+     * @type array
+     */
+    public $attr_collections = array(
+        'I18N' => array('dir' => false)
+    );
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $bdo = $this->addElement(
+            'bdo',
+            'Inline',
+            'Inline',
+            array('Core', 'Lang'),
+            array(
+                'dir' => 'Enum#ltr,rtl', // required
+                // The Abstract Module specification has the attribute
+                // inclusions wrong for bdo: bdo allows Lang
+            )
+        );
+        $bdo->attr_transform_post[] = new HTMLPurifier_AttrTransform_BdoDir();
+
+        $this->attr_collections['I18N']['dir'] = 'Enum#ltr,rtl';
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/CommonAttributes.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/CommonAttributes.php
new file mode 100644 (file)
index 0000000..a96ab1b
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+
+class HTMLPurifier_HTMLModule_CommonAttributes extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'CommonAttributes';
+
+    /**
+     * @type array
+     */
+    public $attr_collections = array(
+        'Core' => array(
+            0 => array('Style'),
+            // 'xml:space' => false,
+            'class' => 'Class',
+            'id' => 'ID',
+            'title' => 'CDATA',
+        ),
+        'Lang' => array(),
+        'I18N' => array(
+            0 => array('Lang'), // proprietary, for xml:lang/lang
+        ),
+        'Common' => array(
+            0 => array('Core', 'I18N')
+        )
+    );
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Edit.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Edit.php
new file mode 100644 (file)
index 0000000..a9042a3
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * XHTML 1.1 Edit Module, defines editing-related elements. Text Extension
+ * Module.
+ */
+class HTMLPurifier_HTMLModule_Edit extends HTMLPurifier_HTMLModule
+{
+
+    /**
+     * @type string
+     */
+    public $name = 'Edit';
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $contents = 'Chameleon: #PCDATA | Inline ! #PCDATA | Flow';
+        $attr = array(
+            'cite' => 'URI',
+            // 'datetime' => 'Datetime', // not implemented
+        );
+        $this->addElement('del', 'Inline', $contents, 'Common', $attr);
+        $this->addElement('ins', 'Inline', $contents, 'Common', $attr);
+    }
+
+    // HTML 4.01 specifies that ins/del must not contain block
+    // elements when used in an inline context, chameleon is
+    // a complicated workaround to acheive this effect
+
+    // Inline context ! Block context (exclamation mark is
+    // separator, see getChildDef for parsing)
+
+    /**
+     * @type bool
+     */
+    public $defines_child_def = true;
+
+    /**
+     * @param HTMLPurifier_ElementDef $def
+     * @return HTMLPurifier_ChildDef_Chameleon
+     */
+    public function getChildDef($def)
+    {
+        if ($def->content_model_type != 'chameleon') {
+            return false;
+        }
+        $value = explode('!', $def->content_model);
+        return new HTMLPurifier_ChildDef_Chameleon($value[0], $value[1]);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Forms.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Forms.php
new file mode 100644 (file)
index 0000000..6f7ddbc
--- /dev/null
@@ -0,0 +1,190 @@
+<?php
+
+/**
+ * XHTML 1.1 Forms module, defines all form-related elements found in HTML 4.
+ */
+class HTMLPurifier_HTMLModule_Forms extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'Forms';
+
+    /**
+     * @type bool
+     */
+    public $safe = false;
+
+    /**
+     * @type array
+     */
+    public $content_sets = array(
+        'Block' => 'Form',
+        'Inline' => 'Formctrl',
+    );
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $form = $this->addElement(
+            'form',
+            'Form',
+            'Required: Heading | List | Block | fieldset',
+            'Common',
+            array(
+                'accept' => 'ContentTypes',
+                'accept-charset' => 'Charsets',
+                'action*' => 'URI',
+                'method' => 'Enum#get,post',
+                // really ContentType, but these two are the only ones used today
+                'enctype' => 'Enum#application/x-www-form-urlencoded,multipart/form-data',
+            )
+        );
+        $form->excludes = array('form' => true);
+
+        $input = $this->addElement(
+            'input',
+            'Formctrl',
+            'Empty',
+            'Common',
+            array(
+                'accept' => 'ContentTypes',
+                'accesskey' => 'Character',
+                'alt' => 'Text',
+                'checked' => 'Bool#checked',
+                'disabled' => 'Bool#disabled',
+                'maxlength' => 'Number',
+                'name' => 'CDATA',
+                'readonly' => 'Bool#readonly',
+                'size' => 'Number',
+                'src' => 'URI#embedded',
+                'tabindex' => 'Number',
+                'type' => 'Enum#text,password,checkbox,button,radio,submit,reset,file,hidden,image',
+                'value' => 'CDATA',
+            )
+        );
+        $input->attr_transform_post[] = new HTMLPurifier_AttrTransform_Input();
+
+        $this->addElement(
+            'select',
+            'Formctrl',
+            'Required: optgroup | option',
+            'Common',
+            array(
+                'disabled' => 'Bool#disabled',
+                'multiple' => 'Bool#multiple',
+                'name' => 'CDATA',
+                'size' => 'Number',
+                'tabindex' => 'Number',
+            )
+        );
+
+        $this->addElement(
+            'option',
+            false,
+            'Optional: #PCDATA',
+            'Common',
+            array(
+                'disabled' => 'Bool#disabled',
+                'label' => 'Text',
+                'selected' => 'Bool#selected',
+                'value' => 'CDATA',
+            )
+        );
+        // It's illegal for there to be more than one selected, but not
+        // be multiple. Also, no selected means undefined behavior. This might
+        // be difficult to implement; perhaps an injector, or a context variable.
+
+        $textarea = $this->addElement(
+            'textarea',
+            'Formctrl',
+            'Optional: #PCDATA',
+            'Common',
+            array(
+                'accesskey' => 'Character',
+                'cols*' => 'Number',
+                'disabled' => 'Bool#disabled',
+                'name' => 'CDATA',
+                'readonly' => 'Bool#readonly',
+                'rows*' => 'Number',
+                'tabindex' => 'Number',
+            )
+        );
+        $textarea->attr_transform_pre[] = new HTMLPurifier_AttrTransform_Textarea();
+
+        $button = $this->addElement(
+            'button',
+            'Formctrl',
+            'Optional: #PCDATA | Heading | List | Block | Inline',
+            'Common',
+            array(
+                'accesskey' => 'Character',
+                'disabled' => 'Bool#disabled',
+                'name' => 'CDATA',
+                'tabindex' => 'Number',
+                'type' => 'Enum#button,submit,reset',
+                'value' => 'CDATA',
+            )
+        );
+
+        // For exclusions, ideally we'd specify content sets, not literal elements
+        $button->excludes = $this->makeLookup(
+            'form',
+            'fieldset', // Form
+            'input',
+            'select',
+            'textarea',
+            'label',
+            'button', // Formctrl
+            'a', // as per HTML 4.01 spec, this is omitted by modularization
+            'isindex',
+            'iframe' // legacy items
+        );
+
+        // Extra exclusion: img usemap="" is not permitted within this element.
+        // We'll omit this for now, since we don't have any good way of
+        // indicating it yet.
+
+        // This is HIGHLY user-unfriendly; we need a custom child-def for this
+        $this->addElement('fieldset', 'Form', 'Custom: (#WS?,legend,(Flow|#PCDATA)*)', 'Common');
+
+        $label = $this->addElement(
+            'label',
+            'Formctrl',
+            'Optional: #PCDATA | Inline',
+            'Common',
+            array(
+                'accesskey' => 'Character',
+                // 'for' => 'IDREF', // IDREF not implemented, cannot allow
+            )
+        );
+        $label->excludes = array('label' => true);
+
+        $this->addElement(
+            'legend',
+            false,
+            'Optional: #PCDATA | Inline',
+            'Common',
+            array(
+                'accesskey' => 'Character',
+            )
+        );
+
+        $this->addElement(
+            'optgroup',
+            false,
+            'Required: option',
+            'Common',
+            array(
+                'disabled' => 'Bool#disabled',
+                'label*' => 'Text',
+            )
+        );
+        // Don't forget an injector for <isindex>. This one's a little complex
+        // because it maps to multiple elements.
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Hypertext.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Hypertext.php
new file mode 100644 (file)
index 0000000..72d7a31
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * XHTML 1.1 Hypertext Module, defines hypertext links. Core Module.
+ */
+class HTMLPurifier_HTMLModule_Hypertext extends HTMLPurifier_HTMLModule
+{
+
+    /**
+     * @type string
+     */
+    public $name = 'Hypertext';
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $a = $this->addElement(
+            'a',
+            'Inline',
+            'Inline',
+            'Common',
+            array(
+                // 'accesskey' => 'Character',
+                // 'charset' => 'Charset',
+                'href' => 'URI',
+                // 'hreflang' => 'LanguageCode',
+                'rel' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rel'),
+                'rev' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rev'),
+                // 'tabindex' => 'Number',
+                // 'type' => 'ContentType',
+            )
+        );
+        $a->formatting = true;
+        $a->excludes = array('a' => true);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Iframe.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Iframe.php
new file mode 100644 (file)
index 0000000..f7e7c91
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * XHTML 1.1 Iframe Module provides inline frames.
+ *
+ * @note This module is not considered safe unless an Iframe
+ * whitelisting mechanism is specified.  Currently, the only
+ * such mechanism is %URL.SafeIframeRegexp
+ */
+class HTMLPurifier_HTMLModule_Iframe extends HTMLPurifier_HTMLModule
+{
+
+    /**
+     * @type string
+     */
+    public $name = 'Iframe';
+
+    /**
+     * @type bool
+     */
+    public $safe = false;
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        if ($config->get('HTML.SafeIframe')) {
+            $this->safe = true;
+        }
+        $this->addElement(
+            'iframe',
+            'Inline',
+            'Flow',
+            'Common',
+            array(
+                'src' => 'URI#embedded',
+                'width' => 'Length',
+                'height' => 'Length',
+                'name' => 'ID',
+                'scrolling' => 'Enum#yes,no,auto',
+                'frameborder' => 'Enum#0,1',
+                'longdesc' => 'URI',
+                'marginheight' => 'Pixels',
+                'marginwidth' => 'Pixels',
+            )
+        );
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Image.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Image.php
new file mode 100644 (file)
index 0000000..0f5fdb3
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * XHTML 1.1 Image Module provides basic image embedding.
+ * @note There is specialized code for removing empty images in
+ *       HTMLPurifier_Strategy_RemoveForeignElements
+ */
+class HTMLPurifier_HTMLModule_Image extends HTMLPurifier_HTMLModule
+{
+
+    /**
+     * @type string
+     */
+    public $name = 'Image';
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $max = $config->get('HTML.MaxImgLength');
+        $img = $this->addElement(
+            'img',
+            'Inline',
+            'Empty',
+            'Common',
+            array(
+                'alt*' => 'Text',
+                // According to the spec, it's Length, but percents can
+                // be abused, so we allow only Pixels.
+                'height' => 'Pixels#' . $max,
+                'width' => 'Pixels#' . $max,
+                'longdesc' => 'URI',
+                'src*' => new HTMLPurifier_AttrDef_URI(true), // embedded
+            )
+        );
+        if ($max === null || $config->get('HTML.Trusted')) {
+            $img->attr['height'] =
+            $img->attr['width'] = 'Length';
+        }
+
+        // kind of strange, but splitting things up would be inefficient
+        $img->attr_transform_pre[] =
+        $img->attr_transform_post[] =
+            new HTMLPurifier_AttrTransform_ImgRequired();
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Legacy.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Legacy.php
new file mode 100644 (file)
index 0000000..86b5299
--- /dev/null
@@ -0,0 +1,186 @@
+<?php
+
+/**
+ * XHTML 1.1 Legacy module defines elements that were previously
+ * deprecated.
+ *
+ * @note Not all legacy elements have been implemented yet, which
+ *       is a bit of a reverse problem as compared to browsers! In
+ *       addition, this legacy module may implement a bit more than
+ *       mandated by XHTML 1.1.
+ *
+ * This module can be used in combination with TransformToStrict in order
+ * to transform as many deprecated elements as possible, but retain
+ * questionably deprecated elements that do not have good alternatives
+ * as well as transform elements that don't have an implementation.
+ * See docs/ref-strictness.txt for more details.
+ */
+
+class HTMLPurifier_HTMLModule_Legacy extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'Legacy';
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $this->addElement(
+            'basefont',
+            'Inline',
+            'Empty',
+            null,
+            array(
+                'color' => 'Color',
+                'face' => 'Text', // extremely broad, we should
+                'size' => 'Text', // tighten it
+                'id' => 'ID'
+            )
+        );
+        $this->addElement('center', 'Block', 'Flow', 'Common');
+        $this->addElement(
+            'dir',
+            'Block',
+            'Required: li',
+            'Common',
+            array(
+                'compact' => 'Bool#compact'
+            )
+        );
+        $this->addElement(
+            'font',
+            'Inline',
+            'Inline',
+            array('Core', 'I18N'),
+            array(
+                'color' => 'Color',
+                'face' => 'Text', // extremely broad, we should
+                'size' => 'Text', // tighten it
+            )
+        );
+        $this->addElement(
+            'menu',
+            'Block',
+            'Required: li',
+            'Common',
+            array(
+                'compact' => 'Bool#compact'
+            )
+        );
+
+        $s = $this->addElement('s', 'Inline', 'Inline', 'Common');
+        $s->formatting = true;
+
+        $strike = $this->addElement('strike', 'Inline', 'Inline', 'Common');
+        $strike->formatting = true;
+
+        $u = $this->addElement('u', 'Inline', 'Inline', 'Common');
+        $u->formatting = true;
+
+        // setup modifications to old elements
+
+        $align = 'Enum#left,right,center,justify';
+
+        $address = $this->addBlankElement('address');
+        $address->content_model = 'Inline | #PCDATA | p';
+        $address->content_model_type = 'optional';
+        $address->child = false;
+
+        $blockquote = $this->addBlankElement('blockquote');
+        $blockquote->content_model = 'Flow | #PCDATA';
+        $blockquote->content_model_type = 'optional';
+        $blockquote->child = false;
+
+        $br = $this->addBlankElement('br');
+        $br->attr['clear'] = 'Enum#left,all,right,none';
+
+        $caption = $this->addBlankElement('caption');
+        $caption->attr['align'] = 'Enum#top,bottom,left,right';
+
+        $div = $this->addBlankElement('div');
+        $div->attr['align'] = $align;
+
+        $dl = $this->addBlankElement('dl');
+        $dl->attr['compact'] = 'Bool#compact';
+
+        for ($i = 1; $i <= 6; $i++) {
+            $h = $this->addBlankElement("h$i");
+            $h->attr['align'] = $align;
+        }
+
+        $hr = $this->addBlankElement('hr');
+        $hr->attr['align'] = $align;
+        $hr->attr['noshade'] = 'Bool#noshade';
+        $hr->attr['size'] = 'Pixels';
+        $hr->attr['width'] = 'Length';
+
+        $img = $this->addBlankElement('img');
+        $img->attr['align'] = 'IAlign';
+        $img->attr['border'] = 'Pixels';
+        $img->attr['hspace'] = 'Pixels';
+        $img->attr['vspace'] = 'Pixels';
+
+        // figure out this integer business
+
+        $li = $this->addBlankElement('li');
+        $li->attr['value'] = new HTMLPurifier_AttrDef_Integer();
+        $li->attr['type'] = 'Enum#s:1,i,I,a,A,disc,square,circle';
+
+        $ol = $this->addBlankElement('ol');
+        $ol->attr['compact'] = 'Bool#compact';
+        $ol->attr['start'] = new HTMLPurifier_AttrDef_Integer();
+        $ol->attr['type'] = 'Enum#s:1,i,I,a,A';
+
+        $p = $this->addBlankElement('p');
+        $p->attr['align'] = $align;
+
+        $pre = $this->addBlankElement('pre');
+        $pre->attr['width'] = 'Number';
+
+        // script omitted
+
+        $table = $this->addBlankElement('table');
+        $table->attr['align'] = 'Enum#left,center,right';
+        $table->attr['bgcolor'] = 'Color';
+
+        $tr = $this->addBlankElement('tr');
+        $tr->attr['bgcolor'] = 'Color';
+
+        $th = $this->addBlankElement('th');
+        $th->attr['bgcolor'] = 'Color';
+        $th->attr['height'] = 'Length';
+        $th->attr['nowrap'] = 'Bool#nowrap';
+        $th->attr['width'] = 'Length';
+
+        $td = $this->addBlankElement('td');
+        $td->attr['bgcolor'] = 'Color';
+        $td->attr['height'] = 'Length';
+        $td->attr['nowrap'] = 'Bool#nowrap';
+        $td->attr['width'] = 'Length';
+
+        $ul = $this->addBlankElement('ul');
+        $ul->attr['compact'] = 'Bool#compact';
+        $ul->attr['type'] = 'Enum#square,disc,circle';
+
+        // "safe" modifications to "unsafe" elements
+        // WARNING: If you want to add support for an unsafe, legacy
+        // attribute, make a new TrustedLegacy module with the trusted
+        // bit set appropriately
+
+        $form = $this->addBlankElement('form');
+        $form->content_model = 'Flow | #PCDATA';
+        $form->content_model_type = 'optional';
+        $form->attr['target'] = 'FrameTarget';
+
+        $input = $this->addBlankElement('input');
+        $input->attr['align'] = 'IAlign';
+
+        $legend = $this->addBlankElement('legend');
+        $legend->attr['align'] = 'LAlign';
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/List.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/List.php
new file mode 100644 (file)
index 0000000..7a20ff7
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * XHTML 1.1 List Module, defines list-oriented elements. Core Module.
+ */
+class HTMLPurifier_HTMLModule_List extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'List';
+
+    // According to the abstract schema, the List content set is a fully formed
+    // one or more expr, but it invariably occurs in an optional declaration
+    // so we're not going to do that subtlety. It might cause trouble
+    // if a user defines "List" and expects that multiple lists are
+    // allowed to be specified, but then again, that's not very intuitive.
+    // Furthermore, the actual XML Schema may disagree. Regardless,
+    // we don't have support for such nested expressions without using
+    // the incredibly inefficient and draconic Custom ChildDef.
+
+    /**
+     * @type array
+     */
+    public $content_sets = array('Flow' => 'List');
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $ol = $this->addElement('ol', 'List', new HTMLPurifier_ChildDef_List(), 'Common');
+        $ul = $this->addElement('ul', 'List', new HTMLPurifier_ChildDef_List(), 'Common');
+        // XXX The wrap attribute is handled by MakeWellFormed.  This is all
+        // quite unsatisfactory, because we generated this
+        // *specifically* for lists, and now a big chunk of the handling
+        // is done properly by the List ChildDef.  So actually, we just
+        // want enough information to make autoclosing work properly,
+        // and then hand off the tricky stuff to the ChildDef.
+        $ol->wrap = 'li';
+        $ul->wrap = 'li';
+        $this->addElement('dl', 'List', 'Required: dt | dd', 'Common');
+
+        $this->addElement('li', false, 'Flow', 'Common');
+
+        $this->addElement('dd', false, 'Flow', 'Common');
+        $this->addElement('dt', false, 'Inline', 'Common');
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Name.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Name.php
new file mode 100644 (file)
index 0000000..60c0545
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+
+class HTMLPurifier_HTMLModule_Name extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'Name';
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $elements = array('a', 'applet', 'form', 'frame', 'iframe', 'img', 'map');
+        foreach ($elements as $name) {
+            $element = $this->addBlankElement($name);
+            $element->attr['name'] = 'CDATA';
+            if (!$config->get('HTML.Attr.Name.UseCDATA')) {
+                $element->attr_transform_post[] = new HTMLPurifier_AttrTransform_NameSync();
+            }
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Nofollow.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Nofollow.php
new file mode 100644 (file)
index 0000000..dc9410a
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * Module adds the nofollow attribute transformation to a tags.  It
+ * is enabled by HTML.Nofollow
+ */
+class HTMLPurifier_HTMLModule_Nofollow extends HTMLPurifier_HTMLModule
+{
+
+    /**
+     * @type string
+     */
+    public $name = 'Nofollow';
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $a = $this->addBlankElement('a');
+        $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_Nofollow();
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php
new file mode 100644 (file)
index 0000000..da72225
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+
+class HTMLPurifier_HTMLModule_NonXMLCommonAttributes extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'NonXMLCommonAttributes';
+
+    /**
+     * @type array
+     */
+    public $attr_collections = array(
+        'Lang' => array(
+            'lang' => 'LanguageCode',
+        )
+    );
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Object.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Object.php
new file mode 100644 (file)
index 0000000..2f9efc5
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * XHTML 1.1 Object Module, defines elements for generic object inclusion
+ * @warning Users will commonly use <embed> to cater to legacy browsers: this
+ *      module does not allow this sort of behavior
+ */
+class HTMLPurifier_HTMLModule_Object extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'Object';
+
+    /**
+     * @type bool
+     */
+    public $safe = false;
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $this->addElement(
+            'object',
+            'Inline',
+            'Optional: #PCDATA | Flow | param',
+            'Common',
+            array(
+                'archive' => 'URI',
+                'classid' => 'URI',
+                'codebase' => 'URI',
+                'codetype' => 'Text',
+                'data' => 'URI',
+                'declare' => 'Bool#declare',
+                'height' => 'Length',
+                'name' => 'CDATA',
+                'standby' => 'Text',
+                'tabindex' => 'Number',
+                'type' => 'ContentType',
+                'width' => 'Length'
+            )
+        );
+
+        $this->addElement(
+            'param',
+            false,
+            'Empty',
+            null,
+            array(
+                'id' => 'ID',
+                'name*' => 'Text',
+                'type' => 'Text',
+                'value' => 'Text',
+                'valuetype' => 'Enum#data,ref,object'
+            )
+        );
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Presentation.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Presentation.php
new file mode 100644 (file)
index 0000000..6458ce9
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * XHTML 1.1 Presentation Module, defines simple presentation-related
+ * markup. Text Extension Module.
+ * @note The official XML Schema and DTD specs further divide this into
+ *       two modules:
+ *          - Block Presentation (hr)
+ *          - Inline Presentation (b, big, i, small, sub, sup, tt)
+ *       We have chosen not to heed this distinction, as content_sets
+ *       provides satisfactory disambiguation.
+ */
+class HTMLPurifier_HTMLModule_Presentation extends HTMLPurifier_HTMLModule
+{
+
+    /**
+     * @type string
+     */
+    public $name = 'Presentation';
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $this->addElement('hr', 'Block', 'Empty', 'Common');
+        $this->addElement('sub', 'Inline', 'Inline', 'Common');
+        $this->addElement('sup', 'Inline', 'Inline', 'Common');
+        $b = $this->addElement('b', 'Inline', 'Inline', 'Common');
+        $b->formatting = true;
+        $big = $this->addElement('big', 'Inline', 'Inline', 'Common');
+        $big->formatting = true;
+        $i = $this->addElement('i', 'Inline', 'Inline', 'Common');
+        $i->formatting = true;
+        $small = $this->addElement('small', 'Inline', 'Inline', 'Common');
+        $small->formatting = true;
+        $tt = $this->addElement('tt', 'Inline', 'Inline', 'Common');
+        $tt->formatting = true;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Proprietary.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Proprietary.php
new file mode 100644 (file)
index 0000000..5ee3c8e
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * Module defines proprietary tags and attributes in HTML.
+ * @warning If this module is enabled, standards-compliance is off!
+ */
+class HTMLPurifier_HTMLModule_Proprietary extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'Proprietary';
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $this->addElement(
+            'marquee',
+            'Inline',
+            'Flow',
+            'Common',
+            array(
+                'direction' => 'Enum#left,right,up,down',
+                'behavior' => 'Enum#alternate',
+                'width' => 'Length',
+                'height' => 'Length',
+                'scrolldelay' => 'Number',
+                'scrollamount' => 'Number',
+                'loop' => 'Number',
+                'bgcolor' => 'Color',
+                'hspace' => 'Pixels',
+                'vspace' => 'Pixels',
+            )
+        );
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Ruby.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Ruby.php
new file mode 100644 (file)
index 0000000..a0d4892
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * XHTML 1.1 Ruby Annotation Module, defines elements that indicate
+ * short runs of text alongside base text for annotation or pronounciation.
+ */
+class HTMLPurifier_HTMLModule_Ruby extends HTMLPurifier_HTMLModule
+{
+
+    /**
+     * @type string
+     */
+    public $name = 'Ruby';
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $this->addElement(
+            'ruby',
+            'Inline',
+            'Custom: ((rb, (rt | (rp, rt, rp))) | (rbc, rtc, rtc?))',
+            'Common'
+        );
+        $this->addElement('rbc', false, 'Required: rb', 'Common');
+        $this->addElement('rtc', false, 'Required: rt', 'Common');
+        $rb = $this->addElement('rb', false, 'Inline', 'Common');
+        $rb->excludes = array('ruby' => true);
+        $rt = $this->addElement('rt', false, 'Inline', 'Common', array('rbspan' => 'Number'));
+        $rt->excludes = array('ruby' => true);
+        $this->addElement('rp', false, 'Optional: #PCDATA', 'Common');
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php
new file mode 100644 (file)
index 0000000..04e6689
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * A "safe" embed module. See SafeObject. This is a proprietary element.
+ */
+class HTMLPurifier_HTMLModule_SafeEmbed extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'SafeEmbed';
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $max = $config->get('HTML.MaxImgLength');
+        $embed = $this->addElement(
+            'embed',
+            'Inline',
+            'Empty',
+            'Common',
+            array(
+                'src*' => 'URI#embedded',
+                'type' => 'Enum#application/x-shockwave-flash',
+                'width' => 'Pixels#' . $max,
+                'height' => 'Pixels#' . $max,
+                'allowscriptaccess' => 'Enum#never',
+                'allownetworking' => 'Enum#internal',
+                'flashvars' => 'Text',
+                'wmode' => 'Enum#window,transparent,opaque',
+                'name' => 'ID',
+            )
+        );
+        $embed->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeEmbed();
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php
new file mode 100644 (file)
index 0000000..1297f80
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * A "safe" object module. In theory, objects permitted by this module will
+ * be safe, and untrusted users can be allowed to embed arbitrary flash objects
+ * (maybe other types too, but only Flash is supported as of right now).
+ * Highly experimental.
+ */
+class HTMLPurifier_HTMLModule_SafeObject extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'SafeObject';
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        // These definitions are not intrinsically safe: the attribute transforms
+        // are a vital part of ensuring safety.
+
+        $max = $config->get('HTML.MaxImgLength');
+        $object = $this->addElement(
+            'object',
+            'Inline',
+            'Optional: param | Flow | #PCDATA',
+            'Common',
+            array(
+                // While technically not required by the spec, we're forcing
+                // it to this value.
+                'type' => 'Enum#application/x-shockwave-flash',
+                'width' => 'Pixels#' . $max,
+                'height' => 'Pixels#' . $max,
+                'data' => 'URI#embedded',
+                'codebase' => new HTMLPurifier_AttrDef_Enum(
+                    array(
+                        'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0'
+                    )
+                ),
+            )
+        );
+        $object->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeObject();
+
+        $param = $this->addElement(
+            'param',
+            false,
+            'Empty',
+            false,
+            array(
+                'id' => 'ID',
+                'name*' => 'Text',
+                'value' => 'Text'
+            )
+        );
+        $param->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeParam();
+        $this->info_injector[] = 'SafeObject';
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeScripting.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeScripting.php
new file mode 100644 (file)
index 0000000..0330cd9
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * A "safe" script module. No inline JS is allowed, and pointed to JS
+ * files must match whitelist.
+ */
+class HTMLPurifier_HTMLModule_SafeScripting extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'SafeScripting';
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        // These definitions are not intrinsically safe: the attribute transforms
+        // are a vital part of ensuring safety.
+
+        $allowed = $config->get('HTML.SafeScripting');
+        $script = $this->addElement(
+            'script',
+            'Inline',
+            'Empty',
+            null,
+            array(
+                // While technically not required by the spec, we're forcing
+                // it to this value.
+                'type' => 'Enum#text/javascript',
+                'src*' => new HTMLPurifier_AttrDef_Enum(array_keys($allowed))
+            )
+        );
+        $script->attr_transform_pre[] =
+        $script->attr_transform_post[] = new HTMLPurifier_AttrTransform_ScriptRequired();
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Scripting.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Scripting.php
new file mode 100644 (file)
index 0000000..8b28a7b
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+
+/*
+
+WARNING: THIS MODULE IS EXTREMELY DANGEROUS AS IT ENABLES INLINE SCRIPTING
+INSIDE HTML PURIFIER DOCUMENTS. USE ONLY WITH TRUSTED USER INPUT!!!
+
+*/
+
+/**
+ * XHTML 1.1 Scripting module, defines elements that are used to contain
+ * information pertaining to executable scripts or the lack of support
+ * for executable scripts.
+ * @note This module does not contain inline scripting elements
+ */
+class HTMLPurifier_HTMLModule_Scripting extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'Scripting';
+
+    /**
+     * @type array
+     */
+    public $elements = array('script', 'noscript');
+
+    /**
+     * @type array
+     */
+    public $content_sets = array('Block' => 'script | noscript', 'Inline' => 'script | noscript');
+
+    /**
+     * @type bool
+     */
+    public $safe = false;
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        // TODO: create custom child-definition for noscript that
+        // auto-wraps stray #PCDATA in a similar manner to
+        // blockquote's custom definition (we would use it but
+        // blockquote's contents are optional while noscript's contents
+        // are required)
+
+        // TODO: convert this to new syntax, main problem is getting
+        // both content sets working
+
+        // In theory, this could be safe, but I don't see any reason to
+        // allow it.
+        $this->info['noscript'] = new HTMLPurifier_ElementDef();
+        $this->info['noscript']->attr = array(0 => array('Common'));
+        $this->info['noscript']->content_model = 'Heading | List | Block';
+        $this->info['noscript']->content_model_type = 'required';
+
+        $this->info['script'] = new HTMLPurifier_ElementDef();
+        $this->info['script']->attr = array(
+            'defer' => new HTMLPurifier_AttrDef_Enum(array('defer')),
+            'src' => new HTMLPurifier_AttrDef_URI(true),
+            'type' => new HTMLPurifier_AttrDef_Enum(array('text/javascript'))
+        );
+        $this->info['script']->content_model = '#PCDATA';
+        $this->info['script']->content_model_type = 'optional';
+        $this->info['script']->attr_transform_pre[] =
+        $this->info['script']->attr_transform_post[] =
+            new HTMLPurifier_AttrTransform_ScriptRequired();
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/StyleAttribute.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/StyleAttribute.php
new file mode 100644 (file)
index 0000000..497b832
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * XHTML 1.1 Edit Module, defines editing-related elements. Text Extension
+ * Module.
+ */
+class HTMLPurifier_HTMLModule_StyleAttribute extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'StyleAttribute';
+
+    /**
+     * @type array
+     */
+    public $attr_collections = array(
+        // The inclusion routine differs from the Abstract Modules but
+        // is in line with the DTD and XML Schemas.
+        'Style' => array('style' => false), // see constructor
+        'Core' => array(0 => array('Style'))
+    );
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $this->attr_collections['Style']['style'] = new HTMLPurifier_AttrDef_CSS();
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tables.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tables.php
new file mode 100644 (file)
index 0000000..8a0b3b4
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * XHTML 1.1 Tables Module, fully defines accessible table elements.
+ */
+class HTMLPurifier_HTMLModule_Tables extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'Tables';
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $this->addElement('caption', false, 'Inline', 'Common');
+
+        $this->addElement(
+            'table',
+            'Block',
+            new HTMLPurifier_ChildDef_Table(),
+            'Common',
+            array(
+                'border' => 'Pixels',
+                'cellpadding' => 'Length',
+                'cellspacing' => 'Length',
+                'frame' => 'Enum#void,above,below,hsides,lhs,rhs,vsides,box,border',
+                'rules' => 'Enum#none,groups,rows,cols,all',
+                'summary' => 'Text',
+                'width' => 'Length'
+            )
+        );
+
+        // common attributes
+        $cell_align = array(
+            'align' => 'Enum#left,center,right,justify,char',
+            'charoff' => 'Length',
+            'valign' => 'Enum#top,middle,bottom,baseline',
+        );
+
+        $cell_t = array_merge(
+            array(
+                'abbr' => 'Text',
+                'colspan' => 'Number',
+                'rowspan' => 'Number',
+                // Apparently, as of HTML5 this attribute only applies
+                // to 'th' elements.
+                'scope' => 'Enum#row,col,rowgroup,colgroup',
+            ),
+            $cell_align
+        );
+        $this->addElement('td', false, 'Flow', 'Common', $cell_t);
+        $this->addElement('th', false, 'Flow', 'Common', $cell_t);
+
+        $this->addElement('tr', false, 'Required: td | th', 'Common', $cell_align);
+
+        $cell_col = array_merge(
+            array(
+                'span' => 'Number',
+                'width' => 'MultiLength',
+            ),
+            $cell_align
+        );
+        $this->addElement('col', false, 'Empty', 'Common', $cell_col);
+        $this->addElement('colgroup', false, 'Optional: col', 'Common', $cell_col);
+
+        $this->addElement('tbody', false, 'Required: tr', 'Common', $cell_align);
+        $this->addElement('thead', false, 'Required: tr', 'Common', $cell_align);
+        $this->addElement('tfoot', false, 'Required: tr', 'Common', $cell_align);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Target.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Target.php
new file mode 100644 (file)
index 0000000..b188ac9
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * XHTML 1.1 Target Module, defines target attribute in link elements.
+ */
+class HTMLPurifier_HTMLModule_Target extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'Target';
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $elements = array('a');
+        foreach ($elements as $name) {
+            $e = $this->addBlankElement($name);
+            $e->attr = array(
+                'target' => new HTMLPurifier_AttrDef_HTML_FrameTarget()
+            );
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetBlank.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetBlank.php
new file mode 100644 (file)
index 0000000..58ccc68
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * Module adds the target=blank attribute transformation to a tags.  It
+ * is enabled by HTML.TargetBlank
+ */
+class HTMLPurifier_HTMLModule_TargetBlank extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'TargetBlank';
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $a = $this->addBlankElement('a');
+        $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetBlank();
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Text.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Text.php
new file mode 100644 (file)
index 0000000..7a65e00
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+
+/**
+ * XHTML 1.1 Text Module, defines basic text containers. Core Module.
+ * @note In the normative XML Schema specification, this module
+ *       is further abstracted into the following modules:
+ *          - Block Phrasal (address, blockquote, pre, h1, h2, h3, h4, h5, h6)
+ *          - Block Structural (div, p)
+ *          - Inline Phrasal (abbr, acronym, cite, code, dfn, em, kbd, q, samp, strong, var)
+ *          - Inline Structural (br, span)
+ *       This module, functionally, does not distinguish between these
+ *       sub-modules, but the code is internally structured to reflect
+ *       these distinctions.
+ */
+class HTMLPurifier_HTMLModule_Text extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'Text';
+
+    /**
+     * @type array
+     */
+    public $content_sets = array(
+        'Flow' => 'Heading | Block | Inline'
+    );
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        // Inline Phrasal -------------------------------------------------
+        $this->addElement('abbr', 'Inline', 'Inline', 'Common');
+        $this->addElement('acronym', 'Inline', 'Inline', 'Common');
+        $this->addElement('cite', 'Inline', 'Inline', 'Common');
+        $this->addElement('dfn', 'Inline', 'Inline', 'Common');
+        $this->addElement('kbd', 'Inline', 'Inline', 'Common');
+        $this->addElement('q', 'Inline', 'Inline', 'Common', array('cite' => 'URI'));
+        $this->addElement('samp', 'Inline', 'Inline', 'Common');
+        $this->addElement('var', 'Inline', 'Inline', 'Common');
+
+        $em = $this->addElement('em', 'Inline', 'Inline', 'Common');
+        $em->formatting = true;
+
+        $strong = $this->addElement('strong', 'Inline', 'Inline', 'Common');
+        $strong->formatting = true;
+
+        $code = $this->addElement('code', 'Inline', 'Inline', 'Common');
+        $code->formatting = true;
+
+        // Inline Structural ----------------------------------------------
+        $this->addElement('span', 'Inline', 'Inline', 'Common');
+        $this->addElement('br', 'Inline', 'Empty', 'Core');
+
+        // Block Phrasal --------------------------------------------------
+        $this->addElement('address', 'Block', 'Inline', 'Common');
+        $this->addElement('blockquote', 'Block', 'Optional: Heading | Block | List', 'Common', array('cite' => 'URI'));
+        $pre = $this->addElement('pre', 'Block', 'Inline', 'Common');
+        $pre->excludes = $this->makeLookup(
+            'img',
+            'big',
+            'small',
+            'object',
+            'applet',
+            'font',
+            'basefont'
+        );
+        $this->addElement('h1', 'Heading', 'Inline', 'Common');
+        $this->addElement('h2', 'Heading', 'Inline', 'Common');
+        $this->addElement('h3', 'Heading', 'Inline', 'Common');
+        $this->addElement('h4', 'Heading', 'Inline', 'Common');
+        $this->addElement('h5', 'Heading', 'Inline', 'Common');
+        $this->addElement('h6', 'Heading', 'Inline', 'Common');
+
+        // Block Structural -----------------------------------------------
+        $p = $this->addElement('p', 'Block', 'Inline', 'Common');
+        $p->autoclose = array_flip(
+            array("address", "blockquote", "center", "dir", "div", "dl", "fieldset", "ol", "p", "ul")
+        );
+
+        $this->addElement('div', 'Block', 'Flow', 'Common');
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy.php
new file mode 100644 (file)
index 0000000..08aa232
--- /dev/null
@@ -0,0 +1,230 @@
+<?php
+
+/**
+ * Abstract class for a set of proprietary modules that clean up (tidy)
+ * poorly written HTML.
+ * @todo Figure out how to protect some of these methods/properties
+ */
+class HTMLPurifier_HTMLModule_Tidy extends HTMLPurifier_HTMLModule
+{
+    /**
+     * List of supported levels.
+     * Index zero is a special case "no fixes" level.
+     * @type array
+     */
+    public $levels = array(0 => 'none', 'light', 'medium', 'heavy');
+
+    /**
+     * Default level to place all fixes in.
+     * Disabled by default.
+     * @type string
+     */
+    public $defaultLevel = null;
+
+    /**
+     * Lists of fixes used by getFixesForLevel().
+     * Format is:
+     *      HTMLModule_Tidy->fixesForLevel[$level] = array('fix-1', 'fix-2');
+     * @type array
+     */
+    public $fixesForLevel = array(
+        'light' => array(),
+        'medium' => array(),
+        'heavy' => array()
+    );
+
+    /**
+     * Lazy load constructs the module by determining the necessary
+     * fixes to create and then delegating to the populate() function.
+     * @param HTMLPurifier_Config $config
+     * @todo Wildcard matching and error reporting when an added or
+     *       subtracted fix has no effect.
+     */
+    public function setup($config)
+    {
+        // create fixes, initialize fixesForLevel
+        $fixes = $this->makeFixes();
+        $this->makeFixesForLevel($fixes);
+
+        // figure out which fixes to use
+        $level = $config->get('HTML.TidyLevel');
+        $fixes_lookup = $this->getFixesForLevel($level);
+
+        // get custom fix declarations: these need namespace processing
+        $add_fixes = $config->get('HTML.TidyAdd');
+        $remove_fixes = $config->get('HTML.TidyRemove');
+
+        foreach ($fixes as $name => $fix) {
+            // needs to be refactored a little to implement globbing
+            if (isset($remove_fixes[$name]) ||
+                (!isset($add_fixes[$name]) && !isset($fixes_lookup[$name]))) {
+                unset($fixes[$name]);
+            }
+        }
+
+        // populate this module with necessary fixes
+        $this->populate($fixes);
+    }
+
+    /**
+     * Retrieves all fixes per a level, returning fixes for that specific
+     * level as well as all levels below it.
+     * @param string $level level identifier, see $levels for valid values
+     * @return array Lookup up table of fixes
+     */
+    public function getFixesForLevel($level)
+    {
+        if ($level == $this->levels[0]) {
+            return array();
+        }
+        $activated_levels = array();
+        for ($i = 1, $c = count($this->levels); $i < $c; $i++) {
+            $activated_levels[] = $this->levels[$i];
+            if ($this->levels[$i] == $level) {
+                break;
+            }
+        }
+        if ($i == $c) {
+            trigger_error(
+                'Tidy level ' . htmlspecialchars($level) . ' not recognized',
+                E_USER_WARNING
+            );
+            return array();
+        }
+        $ret = array();
+        foreach ($activated_levels as $level) {
+            foreach ($this->fixesForLevel[$level] as $fix) {
+                $ret[$fix] = true;
+            }
+        }
+        return $ret;
+    }
+
+    /**
+     * Dynamically populates the $fixesForLevel member variable using
+     * the fixes array. It may be custom overloaded, used in conjunction
+     * with $defaultLevel, or not used at all.
+     * @param array $fixes
+     */
+    public function makeFixesForLevel($fixes)
+    {
+        if (!isset($this->defaultLevel)) {
+            return;
+        }
+        if (!isset($this->fixesForLevel[$this->defaultLevel])) {
+            trigger_error(
+                'Default level ' . $this->defaultLevel . ' does not exist',
+                E_USER_ERROR
+            );
+            return;
+        }
+        $this->fixesForLevel[$this->defaultLevel] = array_keys($fixes);
+    }
+
+    /**
+     * Populates the module with transforms and other special-case code
+     * based on a list of fixes passed to it
+     * @param array $fixes Lookup table of fixes to activate
+     */
+    public function populate($fixes)
+    {
+        foreach ($fixes as $name => $fix) {
+            // determine what the fix is for
+            list($type, $params) = $this->getFixType($name);
+            switch ($type) {
+                case 'attr_transform_pre':
+                case 'attr_transform_post':
+                    $attr = $params['attr'];
+                    if (isset($params['element'])) {
+                        $element = $params['element'];
+                        if (empty($this->info[$element])) {
+                            $e = $this->addBlankElement($element);
+                        } else {
+                            $e = $this->info[$element];
+                        }
+                    } else {
+                        $type = "info_$type";
+                        $e = $this;
+                    }
+                    // PHP does some weird parsing when I do
+                    // $e->$type[$attr], so I have to assign a ref.
+                    $f =& $e->$type;
+                    $f[$attr] = $fix;
+                    break;
+                case 'tag_transform':
+                    $this->info_tag_transform[$params['element']] = $fix;
+                    break;
+                case 'child':
+                case 'content_model_type':
+                    $element = $params['element'];
+                    if (empty($this->info[$element])) {
+                        $e = $this->addBlankElement($element);
+                    } else {
+                        $e = $this->info[$element];
+                    }
+                    $e->$type = $fix;
+                    break;
+                default:
+                    trigger_error("Fix type $type not supported", E_USER_ERROR);
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Parses a fix name and determines what kind of fix it is, as well
+     * as other information defined by the fix
+     * @param $name String name of fix
+     * @return array(string $fix_type, array $fix_parameters)
+     * @note $fix_parameters is type dependant, see populate() for usage
+     *       of these parameters
+     */
+    public function getFixType($name)
+    {
+        // parse it
+        $property = $attr = null;
+        if (strpos($name, '#') !== false) {
+            list($name, $property) = explode('#', $name);
+        }
+        if (strpos($name, '@') !== false) {
+            list($name, $attr) = explode('@', $name);
+        }
+
+        // figure out the parameters
+        $params = array();
+        if ($name !== '') {
+            $params['element'] = $name;
+        }
+        if (!is_null($attr)) {
+            $params['attr'] = $attr;
+        }
+
+        // special case: attribute transform
+        if (!is_null($attr)) {
+            if (is_null($property)) {
+                $property = 'pre';
+            }
+            $type = 'attr_transform_' . $property;
+            return array($type, $params);
+        }
+
+        // special case: tag transform
+        if (is_null($property)) {
+            return array('tag_transform', $params);
+        }
+
+        return array($property, $params);
+
+    }
+
+    /**
+     * Defines all fixes the module will perform in a compact
+     * associative array of fix name to fix implementation.
+     * @return array
+     */
+    public function makeFixes()
+    {
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Name.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Name.php
new file mode 100644 (file)
index 0000000..a995161
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * Name is deprecated, but allowed in strict doctypes, so onl
+ */
+class HTMLPurifier_HTMLModule_Tidy_Name extends HTMLPurifier_HTMLModule_Tidy
+{
+    /**
+     * @type string
+     */
+    public $name = 'Tidy_Name';
+
+    /**
+     * @type string
+     */
+    public $defaultLevel = 'heavy';
+
+    /**
+     * @return array
+     */
+    public function makeFixes()
+    {
+        $r = array();
+        // @name for img, a -----------------------------------------------
+        // Technically, it's allowed even on strict, so we allow authors to use
+        // it. However, it's deprecated in future versions of XHTML.
+        $r['img@name'] =
+        $r['a@name'] = new HTMLPurifier_AttrTransform_Name();
+        return $r;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Proprietary.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Proprietary.php
new file mode 100644 (file)
index 0000000..3326438
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+class HTMLPurifier_HTMLModule_Tidy_Proprietary extends HTMLPurifier_HTMLModule_Tidy
+{
+
+    /**
+     * @type string
+     */
+    public $name = 'Tidy_Proprietary';
+
+    /**
+     * @type string
+     */
+    public $defaultLevel = 'light';
+
+    /**
+     * @return array
+     */
+    public function makeFixes()
+    {
+        $r = array();
+        $r['table@background'] = new HTMLPurifier_AttrTransform_Background();
+        $r['td@background']    = new HTMLPurifier_AttrTransform_Background();
+        $r['th@background']    = new HTMLPurifier_AttrTransform_Background();
+        $r['tr@background']    = new HTMLPurifier_AttrTransform_Background();
+        $r['thead@background'] = new HTMLPurifier_AttrTransform_Background();
+        $r['tfoot@background'] = new HTMLPurifier_AttrTransform_Background();
+        $r['tbody@background'] = new HTMLPurifier_AttrTransform_Background();
+        $r['table@height']     = new HTMLPurifier_AttrTransform_Length('height');
+        return $r;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Strict.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Strict.php
new file mode 100644 (file)
index 0000000..803c44f
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+
+class HTMLPurifier_HTMLModule_Tidy_Strict extends HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4
+{
+    /**
+     * @type string
+     */
+    public $name = 'Tidy_Strict';
+
+    /**
+     * @type string
+     */
+    public $defaultLevel = 'light';
+
+    /**
+     * @return array
+     */
+    public function makeFixes()
+    {
+        $r = parent::makeFixes();
+        $r['blockquote#content_model_type'] = 'strictblockquote';
+        return $r;
+    }
+
+    /**
+     * @type bool
+     */
+    public $defines_child_def = true;
+
+    /**
+     * @param HTMLPurifier_ElementDef $def
+     * @return HTMLPurifier_ChildDef_StrictBlockquote
+     */
+    public function getChildDef($def)
+    {
+        if ($def->content_model_type != 'strictblockquote') {
+            return parent::getChildDef($def);
+        }
+        return new HTMLPurifier_ChildDef_StrictBlockquote($def->content_model);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Transitional.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Transitional.php
new file mode 100644 (file)
index 0000000..c095ad9
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+
+class HTMLPurifier_HTMLModule_Tidy_Transitional extends HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4
+{
+    /**
+     * @type string
+     */
+    public $name = 'Tidy_Transitional';
+
+    /**
+     * @type string
+     */
+    public $defaultLevel = 'heavy';
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/XHTML.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/XHTML.php
new file mode 100644 (file)
index 0000000..3ecddc4
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+
+class HTMLPurifier_HTMLModule_Tidy_XHTML extends HTMLPurifier_HTMLModule_Tidy
+{
+    /**
+     * @type string
+     */
+    public $name = 'Tidy_XHTML';
+
+    /**
+     * @type string
+     */
+    public $defaultLevel = 'medium';
+
+    /**
+     * @return array
+     */
+    public function makeFixes()
+    {
+        $r = array();
+        $r['@lang'] = new HTMLPurifier_AttrTransform_Lang();
+        return $r;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php
new file mode 100644 (file)
index 0000000..c4f16a4
--- /dev/null
@@ -0,0 +1,179 @@
+<?php
+
+class HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4 extends HTMLPurifier_HTMLModule_Tidy
+{
+
+    /**
+     * @return array
+     */
+    public function makeFixes()
+    {
+        $r = array();
+
+        // == deprecated tag transforms ===================================
+
+        $r['font'] = new HTMLPurifier_TagTransform_Font();
+        $r['menu'] = new HTMLPurifier_TagTransform_Simple('ul');
+        $r['dir'] = new HTMLPurifier_TagTransform_Simple('ul');
+        $r['center'] = new HTMLPurifier_TagTransform_Simple('div', 'text-align:center;');
+        $r['u'] = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:underline;');
+        $r['s'] = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:line-through;');
+        $r['strike'] = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:line-through;');
+
+        // == deprecated attribute transforms =============================
+
+        $r['caption@align'] =
+            new HTMLPurifier_AttrTransform_EnumToCSS(
+                'align',
+                array(
+                    // we're following IE's behavior, not Firefox's, due
+                    // to the fact that no one supports caption-side:right,
+                    // W3C included (with CSS 2.1). This is a slightly
+                    // unreasonable attribute!
+                    'left' => 'text-align:left;',
+                    'right' => 'text-align:right;',
+                    'top' => 'caption-side:top;',
+                    'bottom' => 'caption-side:bottom;' // not supported by IE
+                )
+            );
+
+        // @align for img -------------------------------------------------
+        $r['img@align'] =
+            new HTMLPurifier_AttrTransform_EnumToCSS(
+                'align',
+                array(
+                    'left' => 'float:left;',
+                    'right' => 'float:right;',
+                    'top' => 'vertical-align:top;',
+                    'middle' => 'vertical-align:middle;',
+                    'bottom' => 'vertical-align:baseline;',
+                )
+            );
+
+        // @align for table -----------------------------------------------
+        $r['table@align'] =
+            new HTMLPurifier_AttrTransform_EnumToCSS(
+                'align',
+                array(
+                    'left' => 'float:left;',
+                    'center' => 'margin-left:auto;margin-right:auto;',
+                    'right' => 'float:right;'
+                )
+            );
+
+        // @align for hr -----------------------------------------------
+        $r['hr@align'] =
+            new HTMLPurifier_AttrTransform_EnumToCSS(
+                'align',
+                array(
+                    // we use both text-align and margin because these work
+                    // for different browsers (IE and Firefox, respectively)
+                    // and the melange makes for a pretty cross-compatible
+                    // solution
+                    'left' => 'margin-left:0;margin-right:auto;text-align:left;',
+                    'center' => 'margin-left:auto;margin-right:auto;text-align:center;',
+                    'right' => 'margin-left:auto;margin-right:0;text-align:right;'
+                )
+            );
+
+        // @align for h1, h2, h3, h4, h5, h6, p, div ----------------------
+        // {{{
+        $align_lookup = array();
+        $align_values = array('left', 'right', 'center', 'justify');
+        foreach ($align_values as $v) {
+            $align_lookup[$v] = "text-align:$v;";
+        }
+        // }}}
+        $r['h1@align'] =
+        $r['h2@align'] =
+        $r['h3@align'] =
+        $r['h4@align'] =
+        $r['h5@align'] =
+        $r['h6@align'] =
+        $r['p@align'] =
+        $r['div@align'] =
+            new HTMLPurifier_AttrTransform_EnumToCSS('align', $align_lookup);
+
+        // @bgcolor for table, tr, td, th ---------------------------------
+        $r['table@bgcolor'] =
+        $r['td@bgcolor'] =
+        $r['th@bgcolor'] =
+            new HTMLPurifier_AttrTransform_BgColor();
+
+        // @border for img ------------------------------------------------
+        $r['img@border'] = new HTMLPurifier_AttrTransform_Border();
+
+        // @clear for br --------------------------------------------------
+        $r['br@clear'] =
+            new HTMLPurifier_AttrTransform_EnumToCSS(
+                'clear',
+                array(
+                    'left' => 'clear:left;',
+                    'right' => 'clear:right;',
+                    'all' => 'clear:both;',
+                    'none' => 'clear:none;',
+                )
+            );
+
+        // @height for td, th ---------------------------------------------
+        $r['td@height'] =
+        $r['th@height'] =
+            new HTMLPurifier_AttrTransform_Length('height');
+
+        // @hspace for img ------------------------------------------------
+        $r['img@hspace'] = new HTMLPurifier_AttrTransform_ImgSpace('hspace');
+
+        // @noshade for hr ------------------------------------------------
+        // this transformation is not precise but often good enough.
+        // different browsers use different styles to designate noshade
+        $r['hr@noshade'] =
+            new HTMLPurifier_AttrTransform_BoolToCSS(
+                'noshade',
+                'color:#808080;background-color:#808080;border:0;'
+            );
+
+        // @nowrap for td, th ---------------------------------------------
+        $r['td@nowrap'] =
+        $r['th@nowrap'] =
+            new HTMLPurifier_AttrTransform_BoolToCSS(
+                'nowrap',
+                'white-space:nowrap;'
+            );
+
+        // @size for hr  --------------------------------------------------
+        $r['hr@size'] = new HTMLPurifier_AttrTransform_Length('size', 'height');
+
+        // @type for li, ol, ul -------------------------------------------
+        // {{{
+        $ul_types = array(
+            'disc' => 'list-style-type:disc;',
+            'square' => 'list-style-type:square;',
+            'circle' => 'list-style-type:circle;'
+        );
+        $ol_types = array(
+            '1' => 'list-style-type:decimal;',
+            'i' => 'list-style-type:lower-roman;',
+            'I' => 'list-style-type:upper-roman;',
+            'a' => 'list-style-type:lower-alpha;',
+            'A' => 'list-style-type:upper-alpha;'
+        );
+        $li_types = $ul_types + $ol_types;
+        // }}}
+
+        $r['ul@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $ul_types);
+        $r['ol@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $ol_types, true);
+        $r['li@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $li_types, true);
+
+        // @vspace for img ------------------------------------------------
+        $r['img@vspace'] = new HTMLPurifier_AttrTransform_ImgSpace('vspace');
+
+        // @width for hr, td, th ------------------------------------------
+        $r['td@width'] =
+        $r['th@width'] =
+        $r['hr@width'] = new HTMLPurifier_AttrTransform_Length('width');
+
+        return $r;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/XMLCommonAttributes.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/XMLCommonAttributes.php
new file mode 100644 (file)
index 0000000..01dbe9d
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+
+class HTMLPurifier_HTMLModule_XMLCommonAttributes extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'XMLCommonAttributes';
+
+    /**
+     * @type array
+     */
+    public $attr_collections = array(
+        'Lang' => array(
+            'xml:lang' => 'LanguageCode',
+        )
+    );
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php
new file mode 100644 (file)
index 0000000..f3a17cb
--- /dev/null
@@ -0,0 +1,459 @@
+<?php
+
+class HTMLPurifier_HTMLModuleManager
+{
+
+    /**
+     * @type HTMLPurifier_DoctypeRegistry
+     */
+    public $doctypes;
+
+    /**
+     * Instance of current doctype.
+     * @type string
+     */
+    public $doctype;
+
+    /**
+     * @type HTMLPurifier_AttrTypes
+     */
+    public $attrTypes;
+
+    /**
+     * Active instances of modules for the specified doctype are
+     * indexed, by name, in this array.
+     * @type HTMLPurifier_HTMLModule[]
+     */
+    public $modules = array();
+
+    /**
+     * Array of recognized HTMLPurifier_HTMLModule instances,
+     * indexed by module's class name. This array is usually lazy loaded, but a
+     * user can overload a module by pre-emptively registering it.
+     * @type HTMLPurifier_HTMLModule[]
+     */
+    public $registeredModules = array();
+
+    /**
+     * List of extra modules that were added by the user
+     * using addModule(). These get unconditionally merged into the current doctype, whatever
+     * it may be.
+     * @type HTMLPurifier_HTMLModule[]
+     */
+    public $userModules = array();
+
+    /**
+     * Associative array of element name to list of modules that have
+     * definitions for the element; this array is dynamically filled.
+     * @type array
+     */
+    public $elementLookup = array();
+
+    /**
+     * List of prefixes we should use for registering small names.
+     * @type array
+     */
+    public $prefixes = array('HTMLPurifier_HTMLModule_');
+
+    /**
+     * @type HTMLPurifier_ContentSets
+     */
+    public $contentSets;
+
+    /**
+     * @type HTMLPurifier_AttrCollections
+     */
+    public $attrCollections;
+
+    /**
+     * If set to true, unsafe elements and attributes will be allowed.
+     * @type bool
+     */
+    public $trusted = false;
+
+    public function __construct()
+    {
+        // editable internal objects
+        $this->attrTypes = new HTMLPurifier_AttrTypes();
+        $this->doctypes  = new HTMLPurifier_DoctypeRegistry();
+
+        // setup basic modules
+        $common = array(
+            'CommonAttributes', 'Text', 'Hypertext', 'List',
+            'Presentation', 'Edit', 'Bdo', 'Tables', 'Image',
+            'StyleAttribute',
+            // Unsafe:
+            'Scripting', 'Object', 'Forms',
+            // Sorta legacy, but present in strict:
+            'Name',
+        );
+        $transitional = array('Legacy', 'Target', 'Iframe');
+        $xml = array('XMLCommonAttributes');
+        $non_xml = array('NonXMLCommonAttributes');
+
+        // setup basic doctypes
+        $this->doctypes->register(
+            'HTML 4.01 Transitional',
+            false,
+            array_merge($common, $transitional, $non_xml),
+            array('Tidy_Transitional', 'Tidy_Proprietary'),
+            array(),
+            '-//W3C//DTD HTML 4.01 Transitional//EN',
+            'http://www.w3.org/TR/html4/loose.dtd'
+        );
+
+        $this->doctypes->register(
+            'HTML 4.01 Strict',
+            false,
+            array_merge($common, $non_xml),
+            array('Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'),
+            array(),
+            '-//W3C//DTD HTML 4.01//EN',
+            'http://www.w3.org/TR/html4/strict.dtd'
+        );
+
+        $this->doctypes->register(
+            'XHTML 1.0 Transitional',
+            true,
+            array_merge($common, $transitional, $xml, $non_xml),
+            array('Tidy_Transitional', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Name'),
+            array(),
+            '-//W3C//DTD XHTML 1.0 Transitional//EN',
+            'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'
+        );
+
+        $this->doctypes->register(
+            'XHTML 1.0 Strict',
+            true,
+            array_merge($common, $xml, $non_xml),
+            array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'),
+            array(),
+            '-//W3C//DTD XHTML 1.0 Strict//EN',
+            'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
+        );
+
+        $this->doctypes->register(
+            'XHTML 1.1',
+            true,
+            // Iframe is a real XHTML 1.1 module, despite being
+            // "transitional"!
+            array_merge($common, $xml, array('Ruby', 'Iframe')),
+            array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Strict', 'Tidy_Name'), // Tidy_XHTML1_1
+            array(),
+            '-//W3C//DTD XHTML 1.1//EN',
+            'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'
+        );
+
+    }
+
+    /**
+     * Registers a module to the recognized module list, useful for
+     * overloading pre-existing modules.
+     * @param $module Mixed: string module name, with or without
+     *                HTMLPurifier_HTMLModule prefix, or instance of
+     *                subclass of HTMLPurifier_HTMLModule.
+     * @param $overload Boolean whether or not to overload previous modules.
+     *                  If this is not set, and you do overload a module,
+     *                  HTML Purifier will complain with a warning.
+     * @note This function will not call autoload, you must instantiate
+     *       (and thus invoke) autoload outside the method.
+     * @note If a string is passed as a module name, different variants
+     *       will be tested in this order:
+     *          - Check for HTMLPurifier_HTMLModule_$name
+     *          - Check all prefixes with $name in order they were added
+     *          - Check for literal object name
+     *          - Throw fatal error
+     *       If your object name collides with an internal class, specify
+     *       your module manually. All modules must have been included
+     *       externally: registerModule will not perform inclusions for you!
+     */
+    public function registerModule($module, $overload = false)
+    {
+        if (is_string($module)) {
+            // attempt to load the module
+            $original_module = $module;
+            $ok = false;
+            foreach ($this->prefixes as $prefix) {
+                $module = $prefix . $original_module;
+                if (class_exists($module)) {
+                    $ok = true;
+                    break;
+                }
+            }
+            if (!$ok) {
+                $module = $original_module;
+                if (!class_exists($module)) {
+                    trigger_error(
+                        $original_module . ' module does not exist',
+                        E_USER_ERROR
+                    );
+                    return;
+                }
+            }
+            $module = new $module();
+        }
+        if (empty($module->name)) {
+            trigger_error('Module instance of ' . get_class($module) . ' must have name');
+            return;
+        }
+        if (!$overload && isset($this->registeredModules[$module->name])) {
+            trigger_error('Overloading ' . $module->name . ' without explicit overload parameter', E_USER_WARNING);
+        }
+        $this->registeredModules[$module->name] = $module;
+    }
+
+    /**
+     * Adds a module to the current doctype by first registering it,
+     * and then tacking it on to the active doctype
+     */
+    public function addModule($module)
+    {
+        $this->registerModule($module);
+        if (is_object($module)) {
+            $module = $module->name;
+        }
+        $this->userModules[] = $module;
+    }
+
+    /**
+     * Adds a class prefix that registerModule() will use to resolve a
+     * string name to a concrete class
+     */
+    public function addPrefix($prefix)
+    {
+        $this->prefixes[] = $prefix;
+    }
+
+    /**
+     * Performs processing on modules, after being called you may
+     * use getElement() and getElements()
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config)
+    {
+        $this->trusted = $config->get('HTML.Trusted');
+
+        // generate
+        $this->doctype = $this->doctypes->make($config);
+        $modules = $this->doctype->modules;
+
+        // take out the default modules that aren't allowed
+        $lookup = $config->get('HTML.AllowedModules');
+        $special_cases = $config->get('HTML.CoreModules');
+
+        if (is_array($lookup)) {
+            foreach ($modules as $k => $m) {
+                if (isset($special_cases[$m])) {
+                    continue;
+                }
+                if (!isset($lookup[$m])) {
+                    unset($modules[$k]);
+                }
+            }
+        }
+
+        // custom modules
+        if ($config->get('HTML.Proprietary')) {
+            $modules[] = 'Proprietary';
+        }
+        if ($config->get('HTML.SafeObject')) {
+            $modules[] = 'SafeObject';
+        }
+        if ($config->get('HTML.SafeEmbed')) {
+            $modules[] = 'SafeEmbed';
+        }
+        if ($config->get('HTML.SafeScripting') !== array()) {
+            $modules[] = 'SafeScripting';
+        }
+        if ($config->get('HTML.Nofollow')) {
+            $modules[] = 'Nofollow';
+        }
+        if ($config->get('HTML.TargetBlank')) {
+            $modules[] = 'TargetBlank';
+        }
+
+        // merge in custom modules
+        $modules = array_merge($modules, $this->userModules);
+
+        foreach ($modules as $module) {
+            $this->processModule($module);
+            $this->modules[$module]->setup($config);
+        }
+
+        foreach ($this->doctype->tidyModules as $module) {
+            $this->processModule($module);
+            $this->modules[$module]->setup($config);
+        }
+
+        // prepare any injectors
+        foreach ($this->modules as $module) {
+            $n = array();
+            foreach ($module->info_injector as $injector) {
+                if (!is_object($injector)) {
+                    $class = "HTMLPurifier_Injector_$injector";
+                    $injector = new $class;
+                }
+                $n[$injector->name] = $injector;
+            }
+            $module->info_injector = $n;
+        }
+
+        // setup lookup table based on all valid modules
+        foreach ($this->modules as $module) {
+            foreach ($module->info as $name => $def) {
+                if (!isset($this->elementLookup[$name])) {
+                    $this->elementLookup[$name] = array();
+                }
+                $this->elementLookup[$name][] = $module->name;
+            }
+        }
+
+        // note the different choice
+        $this->contentSets = new HTMLPurifier_ContentSets(
+            // content set assembly deals with all possible modules,
+            // not just ones deemed to be "safe"
+            $this->modules
+        );
+        $this->attrCollections = new HTMLPurifier_AttrCollections(
+            $this->attrTypes,
+            // there is no way to directly disable a global attribute,
+            // but using AllowedAttributes or simply not including
+            // the module in your custom doctype should be sufficient
+            $this->modules
+        );
+    }
+
+    /**
+     * Takes a module and adds it to the active module collection,
+     * registering it if necessary.
+     */
+    public function processModule($module)
+    {
+        if (!isset($this->registeredModules[$module]) || is_object($module)) {
+            $this->registerModule($module);
+        }
+        $this->modules[$module] = $this->registeredModules[$module];
+    }
+
+    /**
+     * Retrieves merged element definitions.
+     * @return Array of HTMLPurifier_ElementDef
+     */
+    public function getElements()
+    {
+        $elements = array();
+        foreach ($this->modules as $module) {
+            if (!$this->trusted && !$module->safe) {
+                continue;
+            }
+            foreach ($module->info as $name => $v) {
+                if (isset($elements[$name])) {
+                    continue;
+                }
+                $elements[$name] = $this->getElement($name);
+            }
+        }
+
+        // remove dud elements, this happens when an element that
+        // appeared to be safe actually wasn't
+        foreach ($elements as $n => $v) {
+            if ($v === false) {
+                unset($elements[$n]);
+            }
+        }
+
+        return $elements;
+
+    }
+
+    /**
+     * Retrieves a single merged element definition
+     * @param string $name Name of element
+     * @param bool $trusted Boolean trusted overriding parameter: set to true
+     *                 if you want the full version of an element
+     * @return HTMLPurifier_ElementDef Merged HTMLPurifier_ElementDef
+     * @note You may notice that modules are getting iterated over twice (once
+     *       in getElements() and once here). This
+     *       is because
+     */
+    public function getElement($name, $trusted = null)
+    {
+        if (!isset($this->elementLookup[$name])) {
+            return false;
+        }
+
+        // setup global state variables
+        $def = false;
+        if ($trusted === null) {
+            $trusted = $this->trusted;
+        }
+
+        // iterate through each module that has registered itself to this
+        // element
+        foreach ($this->elementLookup[$name] as $module_name) {
+            $module = $this->modules[$module_name];
+
+            // refuse to create/merge from a module that is deemed unsafe--
+            // pretend the module doesn't exist--when trusted mode is not on.
+            if (!$trusted && !$module->safe) {
+                continue;
+            }
+
+            // clone is used because, ideally speaking, the original
+            // definition should not be modified. Usually, this will
+            // make no difference, but for consistency's sake
+            $new_def = clone $module->info[$name];
+
+            if (!$def && $new_def->standalone) {
+                $def = $new_def;
+            } elseif ($def) {
+                // This will occur even if $new_def is standalone. In practice,
+                // this will usually result in a full replacement.
+                $def->mergeIn($new_def);
+            } else {
+                // :TODO:
+                // non-standalone definitions that don't have a standalone
+                // to merge into could be deferred to the end
+                // HOWEVER, it is perfectly valid for a non-standalone
+                // definition to lack a standalone definition, even
+                // after all processing: this allows us to safely
+                // specify extra attributes for elements that may not be
+                // enabled all in one place.  In particular, this might
+                // be the case for trusted elements.  WARNING: care must
+                // be taken that the /extra/ definitions are all safe.
+                continue;
+            }
+
+            // attribute value expansions
+            $this->attrCollections->performInclusions($def->attr);
+            $this->attrCollections->expandIdentifiers($def->attr, $this->attrTypes);
+
+            // descendants_are_inline, for ChildDef_Chameleon
+            if (is_string($def->content_model) &&
+                strpos($def->content_model, 'Inline') !== false) {
+                if ($name != 'del' && $name != 'ins') {
+                    // this is for you, ins/del
+                    $def->descendants_are_inline = true;
+                }
+            }
+
+            $this->contentSets->generateChildDef($def, $module);
+        }
+
+        // This can occur if there is a blank definition, but no base to
+        // mix it in with
+        if (!$def) {
+            return false;
+        }
+
+        // add information on required attributes
+        foreach ($def->attr as $attr_name => $attr_def) {
+            if ($attr_def->required) {
+                $def->required_attr[] = $attr_name;
+            }
+        }
+        return $def;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/IDAccumulator.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/IDAccumulator.php
new file mode 100644 (file)
index 0000000..65c902c
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * Component of HTMLPurifier_AttrContext that accumulates IDs to prevent dupes
+ * @note In Slashdot-speak, dupe means duplicate.
+ * @note The default constructor does not accept $config or $context objects:
+ *       use must use the static build() factory method to perform initialization.
+ */
+class HTMLPurifier_IDAccumulator
+{
+
+    /**
+     * Lookup table of IDs we've accumulated.
+     * @public
+     */
+    public $ids = array();
+
+    /**
+     * Builds an IDAccumulator, also initializing the default blacklist
+     * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config
+     * @param HTMLPurifier_Context $context Instance of HTMLPurifier_Context
+     * @return HTMLPurifier_IDAccumulator Fully initialized HTMLPurifier_IDAccumulator
+     */
+    public static function build($config, $context)
+    {
+        $id_accumulator = new HTMLPurifier_IDAccumulator();
+        $id_accumulator->load($config->get('Attr.IDBlacklist'));
+        return $id_accumulator;
+    }
+
+    /**
+     * Add an ID to the lookup table.
+     * @param string $id ID to be added.
+     * @return bool status, true if success, false if there's a dupe
+     */
+    public function add($id)
+    {
+        if (isset($this->ids[$id])) {
+            return false;
+        }
+        return $this->ids[$id] = true;
+    }
+
+    /**
+     * Load a list of IDs into the lookup table
+     * @param $array_of_ids Array of IDs to load
+     * @note This function doesn't care about duplicates
+     */
+    public function load($array_of_ids)
+    {
+        foreach ($array_of_ids as $id) {
+            $this->ids[$id] = true;
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Injector.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Injector.php
new file mode 100644 (file)
index 0000000..5060eef
--- /dev/null
@@ -0,0 +1,281 @@
+<?php
+
+/**
+ * Injects tokens into the document while parsing for well-formedness.
+ * This enables "formatter-like" functionality such as auto-paragraphing,
+ * smiley-ification and linkification to take place.
+ *
+ * A note on how handlers create changes; this is done by assigning a new
+ * value to the $token reference. These values can take a variety of forms and
+ * are best described HTMLPurifier_Strategy_MakeWellFormed->processToken()
+ * documentation.
+ *
+ * @todo Allow injectors to request a re-run on their output. This
+ *       would help if an operation is recursive.
+ */
+abstract class HTMLPurifier_Injector
+{
+
+    /**
+     * Advisory name of injector, this is for friendly error messages.
+     * @type string
+     */
+    public $name;
+
+    /**
+     * @type HTMLPurifier_HTMLDefinition
+     */
+    protected $htmlDefinition;
+
+    /**
+     * Reference to CurrentNesting variable in Context. This is an array
+     * list of tokens that we are currently "inside"
+     * @type array
+     */
+    protected $currentNesting;
+
+    /**
+     * Reference to current token.
+     * @type HTMLPurifier_Token
+     */
+    protected $currentToken;
+
+    /**
+     * Reference to InputZipper variable in Context.
+     * @type HTMLPurifier_Zipper
+     */
+    protected $inputZipper;
+
+    /**
+     * Array of elements and attributes this injector creates and therefore
+     * need to be allowed by the definition. Takes form of
+     * array('element' => array('attr', 'attr2'), 'element2')
+     * @type array
+     */
+    public $needed = array();
+
+    /**
+     * Number of elements to rewind backwards (relative).
+     * @type bool|int
+     */
+    protected $rewindOffset = false;
+
+    /**
+     * Rewind to a spot to re-perform processing. This is useful if you
+     * deleted a node, and now need to see if this change affected any
+     * earlier nodes. Rewinding does not affect other injectors, and can
+     * result in infinite loops if not used carefully.
+     * @param bool|int $offset
+     * @warning HTML Purifier will prevent you from fast-forwarding with this
+     *          function.
+     */
+    public function rewindOffset($offset)
+    {
+        $this->rewindOffset = $offset;
+    }
+
+    /**
+     * Retrieves rewind offset, and then unsets it.
+     * @return bool|int
+     */
+    public function getRewindOffset()
+    {
+        $r = $this->rewindOffset;
+        $this->rewindOffset = false;
+        return $r;
+    }
+
+    /**
+     * Prepares the injector by giving it the config and context objects:
+     * this allows references to important variables to be made within
+     * the injector. This function also checks if the HTML environment
+     * will work with the Injector (see checkNeeded()).
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool|string Boolean false if success, string of missing needed element/attribute if failure
+     */
+    public function prepare($config, $context)
+    {
+        $this->htmlDefinition = $config->getHTMLDefinition();
+        // Even though this might fail, some unit tests ignore this and
+        // still test checkNeeded, so be careful. Maybe get rid of that
+        // dependency.
+        $result = $this->checkNeeded($config);
+        if ($result !== false) {
+            return $result;
+        }
+        $this->currentNesting =& $context->get('CurrentNesting');
+        $this->currentToken   =& $context->get('CurrentToken');
+        $this->inputZipper    =& $context->get('InputZipper');
+        return false;
+    }
+
+    /**
+     * This function checks if the HTML environment
+     * will work with the Injector: if p tags are not allowed, the
+     * Auto-Paragraphing injector should not be enabled.
+     * @param HTMLPurifier_Config $config
+     * @return bool|string Boolean false if success, string of missing needed element/attribute if failure
+     */
+    public function checkNeeded($config)
+    {
+        $def = $config->getHTMLDefinition();
+        foreach ($this->needed as $element => $attributes) {
+            if (is_int($element)) {
+                $element = $attributes;
+            }
+            if (!isset($def->info[$element])) {
+                return $element;
+            }
+            if (!is_array($attributes)) {
+                continue;
+            }
+            foreach ($attributes as $name) {
+                if (!isset($def->info[$element]->attr[$name])) {
+                    return "$element.$name";
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Tests if the context node allows a certain element
+     * @param string $name Name of element to test for
+     * @return bool True if element is allowed, false if it is not
+     */
+    public function allowsElement($name)
+    {
+        if (!empty($this->currentNesting)) {
+            $parent_token = array_pop($this->currentNesting);
+            $this->currentNesting[] = $parent_token;
+            $parent = $this->htmlDefinition->info[$parent_token->name];
+        } else {
+            $parent = $this->htmlDefinition->info_parent_def;
+        }
+        if (!isset($parent->child->elements[$name]) || isset($parent->excludes[$name])) {
+            return false;
+        }
+        // check for exclusion
+        for ($i = count($this->currentNesting) - 2; $i >= 0; $i--) {
+            $node = $this->currentNesting[$i];
+            $def  = $this->htmlDefinition->info[$node->name];
+            if (isset($def->excludes[$name])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Iterator function, which starts with the next token and continues until
+     * you reach the end of the input tokens.
+     * @warning Please prevent previous references from interfering with this
+     *          functions by setting $i = null beforehand!
+     * @param int $i Current integer index variable for inputTokens
+     * @param HTMLPurifier_Token $current Current token variable.
+     *          Do NOT use $token, as that variable is also a reference
+     * @return bool
+     */
+    protected function forward(&$i, &$current)
+    {
+        if ($i === null) {
+            $i = count($this->inputZipper->back) - 1;
+        } else {
+            $i--;
+        }
+        if ($i < 0) {
+            return false;
+        }
+        $current = $this->inputZipper->back[$i];
+        return true;
+    }
+
+    /**
+     * Similar to _forward, but accepts a third parameter $nesting (which
+     * should be initialized at 0) and stops when we hit the end tag
+     * for the node $this->inputIndex starts in.
+     * @param int $i Current integer index variable for inputTokens
+     * @param HTMLPurifier_Token $current Current token variable.
+     *          Do NOT use $token, as that variable is also a reference
+     * @param int $nesting
+     * @return bool
+     */
+    protected function forwardUntilEndToken(&$i, &$current, &$nesting)
+    {
+        $result = $this->forward($i, $current);
+        if (!$result) {
+            return false;
+        }
+        if ($nesting === null) {
+            $nesting = 0;
+        }
+        if ($current instanceof HTMLPurifier_Token_Start) {
+            $nesting++;
+        } elseif ($current instanceof HTMLPurifier_Token_End) {
+            if ($nesting <= 0) {
+                return false;
+            }
+            $nesting--;
+        }
+        return true;
+    }
+
+    /**
+     * Iterator function, starts with the previous token and continues until
+     * you reach the beginning of input tokens.
+     * @warning Please prevent previous references from interfering with this
+     *          functions by setting $i = null beforehand!
+     * @param int $i Current integer index variable for inputTokens
+     * @param HTMLPurifier_Token $current Current token variable.
+     *          Do NOT use $token, as that variable is also a reference
+     * @return bool
+     */
+    protected function backward(&$i, &$current)
+    {
+        if ($i === null) {
+            $i = count($this->inputZipper->front) - 1;
+        } else {
+            $i--;
+        }
+        if ($i < 0) {
+            return false;
+        }
+        $current = $this->inputZipper->front[$i];
+        return true;
+    }
+
+    /**
+     * Handler that is called when a text token is processed
+     */
+    public function handleText(&$token)
+    {
+    }
+
+    /**
+     * Handler that is called when a start or empty token is processed
+     */
+    public function handleElement(&$token)
+    {
+    }
+
+    /**
+     * Handler that is called when an end token is processed
+     */
+    public function handleEnd(&$token)
+    {
+        $this->notifyEnd($token);
+    }
+
+    /**
+     * Notifier that is called when an end token is processed
+     * @param HTMLPurifier_Token $token Current token variable.
+     * @note This differs from handlers in that the token is read-only
+     * @deprecated
+     */
+    public function notifyEnd($token)
+    {
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/AutoParagraph.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/AutoParagraph.php
new file mode 100644 (file)
index 0000000..4afdd12
--- /dev/null
@@ -0,0 +1,356 @@
+<?php
+
+/**
+ * Injector that auto paragraphs text in the root node based on
+ * double-spacing.
+ * @todo Ensure all states are unit tested, including variations as well.
+ * @todo Make a graph of the flow control for this Injector.
+ */
+class HTMLPurifier_Injector_AutoParagraph extends HTMLPurifier_Injector
+{
+    /**
+     * @type string
+     */
+    public $name = 'AutoParagraph';
+
+    /**
+     * @type array
+     */
+    public $needed = array('p');
+
+    /**
+     * @return HTMLPurifier_Token_Start
+     */
+    private function _pStart()
+    {
+        $par = new HTMLPurifier_Token_Start('p');
+        $par->armor['MakeWellFormed_TagClosedError'] = true;
+        return $par;
+    }
+
+    /**
+     * @param HTMLPurifier_Token_Text $token
+     */
+    public function handleText(&$token)
+    {
+        $text = $token->data;
+        // Does the current parent allow <p> tags?
+        if ($this->allowsElement('p')) {
+            if (empty($this->currentNesting) || strpos($text, "\n\n") !== false) {
+                // Note that we have differing behavior when dealing with text
+                // in the anonymous root node, or a node inside the document.
+                // If the text as a double-newline, the treatment is the same;
+                // if it doesn't, see the next if-block if you're in the document.
+
+                $i = $nesting = null;
+                if (!$this->forwardUntilEndToken($i, $current, $nesting) && $token->is_whitespace) {
+                    // State 1.1: ...    ^ (whitespace, then document end)
+                    //               ----
+                    // This is a degenerate case
+                } else {
+                    if (!$token->is_whitespace || $this->_isInline($current)) {
+                        // State 1.2: PAR1
+                        //            ----
+
+                        // State 1.3: PAR1\n\nPAR2
+                        //            ------------
+
+                        // State 1.4: <div>PAR1\n\nPAR2 (see State 2)
+                        //                 ------------
+                        $token = array($this->_pStart());
+                        $this->_splitText($text, $token);
+                    } else {
+                        // State 1.5: \n<hr />
+                        //            --
+                    }
+                }
+            } else {
+                // State 2:   <div>PAR1... (similar to 1.4)
+                //                 ----
+
+                // We're in an element that allows paragraph tags, but we're not
+                // sure if we're going to need them.
+                if ($this->_pLookAhead()) {
+                    // State 2.1: <div>PAR1<b>PAR1\n\nPAR2
+                    //                 ----
+                    // Note: This will always be the first child, since any
+                    // previous inline element would have triggered this very
+                    // same routine, and found the double newline. One possible
+                    // exception would be a comment.
+                    $token = array($this->_pStart(), $token);
+                } else {
+                    // State 2.2.1: <div>PAR1<div>
+                    //                   ----
+
+                    // State 2.2.2: <div>PAR1<b>PAR1</b></div>
+                    //                   ----
+                }
+            }
+            // Is the current parent a <p> tag?
+        } elseif (!empty($this->currentNesting) &&
+            $this->currentNesting[count($this->currentNesting) - 1]->name == 'p') {
+            // State 3.1: ...<p>PAR1
+            //                  ----
+
+            // State 3.2: ...<p>PAR1\n\nPAR2
+            //                  ------------
+            $token = array();
+            $this->_splitText($text, $token);
+            // Abort!
+        } else {
+            // State 4.1: ...<b>PAR1
+            //                  ----
+
+            // State 4.2: ...<b>PAR1\n\nPAR2
+            //                  ------------
+        }
+    }
+
+    /**
+     * @param HTMLPurifier_Token $token
+     */
+    public function handleElement(&$token)
+    {
+        // We don't have to check if we're already in a <p> tag for block
+        // tokens, because the tag would have been autoclosed by MakeWellFormed.
+        if ($this->allowsElement('p')) {
+            if (!empty($this->currentNesting)) {
+                if ($this->_isInline($token)) {
+                    // State 1: <div>...<b>
+                    //                  ---
+                    // Check if this token is adjacent to the parent token
+                    // (seek backwards until token isn't whitespace)
+                    $i = null;
+                    $this->backward($i, $prev);
+
+                    if (!$prev instanceof HTMLPurifier_Token_Start) {
+                        // Token wasn't adjacent
+                        if ($prev instanceof HTMLPurifier_Token_Text &&
+                            substr($prev->data, -2) === "\n\n"
+                        ) {
+                            // State 1.1.4: <div><p>PAR1</p>\n\n<b>
+                            //                                  ---
+                            // Quite frankly, this should be handled by splitText
+                            $token = array($this->_pStart(), $token);
+                        } else {
+                            // State 1.1.1: <div><p>PAR1</p><b>
+                            //                              ---
+                            // State 1.1.2: <div><br /><b>
+                            //                         ---
+                            // State 1.1.3: <div>PAR<b>
+                            //                      ---
+                        }
+                    } else {
+                        // State 1.2.1: <div><b>
+                        //                   ---
+                        // Lookahead to see if <p> is needed.
+                        if ($this->_pLookAhead()) {
+                            // State 1.3.1: <div><b>PAR1\n\nPAR2
+                            //                   ---
+                            $token = array($this->_pStart(), $token);
+                        } else {
+                            // State 1.3.2: <div><b>PAR1</b></div>
+                            //                   ---
+
+                            // State 1.3.3: <div><b>PAR1</b><div></div>\n\n</div>
+                            //                   ---
+                        }
+                    }
+                } else {
+                    // State 2.3: ...<div>
+                    //               -----
+                }
+            } else {
+                if ($this->_isInline($token)) {
+                    // State 3.1: <b>
+                    //            ---
+                    // This is where the {p} tag is inserted, not reflected in
+                    // inputTokens yet, however.
+                    $token = array($this->_pStart(), $token);
+                } else {
+                    // State 3.2: <div>
+                    //            -----
+                }
+
+                $i = null;
+                if ($this->backward($i, $prev)) {
+                    if (!$prev instanceof HTMLPurifier_Token_Text) {
+                        // State 3.1.1: ...</p>{p}<b>
+                        //                        ---
+                        // State 3.2.1: ...</p><div>
+                        //                     -----
+                        if (!is_array($token)) {
+                            $token = array($token);
+                        }
+                        array_unshift($token, new HTMLPurifier_Token_Text("\n\n"));
+                    } else {
+                        // State 3.1.2: ...</p>\n\n{p}<b>
+                        //                            ---
+                        // State 3.2.2: ...</p>\n\n<div>
+                        //                         -----
+                        // Note: PAR<ELEM> cannot occur because PAR would have been
+                        // wrapped in <p> tags.
+                    }
+                }
+            }
+        } else {
+            // State 2.2: <ul><li>
+            //                ----
+            // State 2.4: <p><b>
+            //               ---
+        }
+    }
+
+    /**
+     * Splits up a text in paragraph tokens and appends them
+     * to the result stream that will replace the original
+     * @param string $data String text data that will be processed
+     *    into paragraphs
+     * @param HTMLPurifier_Token[] $result Reference to array of tokens that the
+     *    tags will be appended onto
+     */
+    private function _splitText($data, &$result)
+    {
+        $raw_paragraphs = explode("\n\n", $data);
+        $paragraphs = array(); // without empty paragraphs
+        $needs_start = false;
+        $needs_end = false;
+
+        $c = count($raw_paragraphs);
+        if ($c == 1) {
+            // There were no double-newlines, abort quickly. In theory this
+            // should never happen.
+            $result[] = new HTMLPurifier_Token_Text($data);
+            return;
+        }
+        for ($i = 0; $i < $c; $i++) {
+            $par = $raw_paragraphs[$i];
+            if (trim($par) !== '') {
+                $paragraphs[] = $par;
+            } else {
+                if ($i == 0) {
+                    // Double newline at the front
+                    if (empty($result)) {
+                        // The empty result indicates that the AutoParagraph
+                        // injector did not add any start paragraph tokens.
+                        // This means that we have been in a paragraph for
+                        // a while, and the newline means we should start a new one.
+                        $result[] = new HTMLPurifier_Token_End('p');
+                        $result[] = new HTMLPurifier_Token_Text("\n\n");
+                        // However, the start token should only be added if
+                        // there is more processing to be done (i.e. there are
+                        // real paragraphs in here). If there are none, the
+                        // next start paragraph tag will be handled by the
+                        // next call to the injector
+                        $needs_start = true;
+                    } else {
+                        // We just started a new paragraph!
+                        // Reinstate a double-newline for presentation's sake, since
+                        // it was in the source code.
+                        array_unshift($result, new HTMLPurifier_Token_Text("\n\n"));
+                    }
+                } elseif ($i + 1 == $c) {
+                    // Double newline at the end
+                    // There should be a trailing </p> when we're finally done.
+                    $needs_end = true;
+                }
+            }
+        }
+
+        // Check if this was just a giant blob of whitespace. Move this earlier,
+        // perhaps?
+        if (empty($paragraphs)) {
+            return;
+        }
+
+        // Add the start tag indicated by \n\n at the beginning of $data
+        if ($needs_start) {
+            $result[] = $this->_pStart();
+        }
+
+        // Append the paragraphs onto the result
+        foreach ($paragraphs as $par) {
+            $result[] = new HTMLPurifier_Token_Text($par);
+            $result[] = new HTMLPurifier_Token_End('p');
+            $result[] = new HTMLPurifier_Token_Text("\n\n");
+            $result[] = $this->_pStart();
+        }
+
+        // Remove trailing start token; Injector will handle this later if
+        // it was indeed needed. This prevents from needing to do a lookahead,
+        // at the cost of a lookbehind later.
+        array_pop($result);
+
+        // If there is no need for an end tag, remove all of it and let
+        // MakeWellFormed close it later.
+        if (!$needs_end) {
+            array_pop($result); // removes \n\n
+            array_pop($result); // removes </p>
+        }
+    }
+
+    /**
+     * Returns true if passed token is inline (and, ergo, allowed in
+     * paragraph tags)
+     * @param HTMLPurifier_Token $token
+     * @return bool
+     */
+    private function _isInline($token)
+    {
+        return isset($this->htmlDefinition->info['p']->child->elements[$token->name]);
+    }
+
+    /**
+     * Looks ahead in the token list and determines whether or not we need
+     * to insert a <p> tag.
+     * @return bool
+     */
+    private function _pLookAhead()
+    {
+        if ($this->currentToken instanceof HTMLPurifier_Token_Start) {
+            $nesting = 1;
+        } else {
+            $nesting = 0;
+        }
+        $ok = false;
+        $i = null;
+        while ($this->forwardUntilEndToken($i, $current, $nesting)) {
+            $result = $this->_checkNeedsP($current);
+            if ($result !== null) {
+                $ok = $result;
+                break;
+            }
+        }
+        return $ok;
+    }
+
+    /**
+     * Determines if a particular token requires an earlier inline token
+     * to get a paragraph. This should be used with _forwardUntilEndToken
+     * @param HTMLPurifier_Token $current
+     * @return bool
+     */
+    private function _checkNeedsP($current)
+    {
+        if ($current instanceof HTMLPurifier_Token_Start) {
+            if (!$this->_isInline($current)) {
+                // <div>PAR1<div>
+                //      ----
+                // Terminate early, since we hit a block element
+                return false;
+            }
+        } elseif ($current instanceof HTMLPurifier_Token_Text) {
+            if (strpos($current->data, "\n\n") !== false) {
+                // <div>PAR1<b>PAR1\n\nPAR2
+                //      ----
+                return true;
+            } else {
+                // <div>PAR1<b>PAR1...
+                //      ----
+            }
+        }
+        return null;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/DisplayLinkURI.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/DisplayLinkURI.php
new file mode 100644 (file)
index 0000000..c19b1bc
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * Injector that displays the URL of an anchor instead of linking to it, in addition to showing the text of the link.
+ */
+class HTMLPurifier_Injector_DisplayLinkURI extends HTMLPurifier_Injector
+{
+    /**
+     * @type string
+     */
+    public $name = 'DisplayLinkURI';
+
+    /**
+     * @type array
+     */
+    public $needed = array('a');
+
+    /**
+     * @param $token
+     */
+    public function handleElement(&$token)
+    {
+    }
+
+    /**
+     * @param HTMLPurifier_Token $token
+     */
+    public function handleEnd(&$token)
+    {
+        if (isset($token->start->attr['href'])) {
+            $url = $token->start->attr['href'];
+            unset($token->start->attr['href']);
+            $token = array($token, new HTMLPurifier_Token_Text(" ($url)"));
+        } else {
+            // nothing to display
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/Linkify.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/Linkify.php
new file mode 100644 (file)
index 0000000..069708c
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * Injector that converts http, https and ftp text URLs to actual links.
+ */
+class HTMLPurifier_Injector_Linkify extends HTMLPurifier_Injector
+{
+    /**
+     * @type string
+     */
+    public $name = 'Linkify';
+
+    /**
+     * @type array
+     */
+    public $needed = array('a' => array('href'));
+
+    /**
+     * @param HTMLPurifier_Token $token
+     */
+    public function handleText(&$token)
+    {
+        if (!$this->allowsElement('a')) {
+            return;
+        }
+
+        if (strpos($token->data, '://') === false) {
+            // our really quick heuristic failed, abort
+            // this may not work so well if we want to match things like
+            // "google.com", but then again, most people don't
+            return;
+        }
+
+        // there is/are URL(s). Let's split the string:
+        // Note: this regex is extremely permissive
+        $bits = preg_split('#((?:https?|ftp)://[^\s\'",<>()]+)#Su', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
+
+
+        $token = array();
+
+        // $i = index
+        // $c = count
+        // $l = is link
+        for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) {
+            if (!$l) {
+                if ($bits[$i] === '') {
+                    continue;
+                }
+                $token[] = new HTMLPurifier_Token_Text($bits[$i]);
+            } else {
+                $token[] = new HTMLPurifier_Token_Start('a', array('href' => $bits[$i]));
+                $token[] = new HTMLPurifier_Token_Text($bits[$i]);
+                $token[] = new HTMLPurifier_Token_End('a');
+            }
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/PurifierLinkify.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/PurifierLinkify.php
new file mode 100644 (file)
index 0000000..cb9046f
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * Injector that converts configuration directive syntax %Namespace.Directive
+ * to links
+ */
+class HTMLPurifier_Injector_PurifierLinkify extends HTMLPurifier_Injector
+{
+    /**
+     * @type string
+     */
+    public $name = 'PurifierLinkify';
+
+    /**
+     * @type string
+     */
+    public $docURL;
+
+    /**
+     * @type array
+     */
+    public $needed = array('a' => array('href'));
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return string
+     */
+    public function prepare($config, $context)
+    {
+        $this->docURL = $config->get('AutoFormat.PurifierLinkify.DocURL');
+        return parent::prepare($config, $context);
+    }
+
+    /**
+     * @param HTMLPurifier_Token $token
+     */
+    public function handleText(&$token)
+    {
+        if (!$this->allowsElement('a')) {
+            return;
+        }
+        if (strpos($token->data, '%') === false) {
+            return;
+        }
+
+        $bits = preg_split('#%([a-z0-9]+\.[a-z0-9]+)#Si', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
+        $token = array();
+
+        // $i = index
+        // $c = count
+        // $l = is link
+        for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) {
+            if (!$l) {
+                if ($bits[$i] === '') {
+                    continue;
+                }
+                $token[] = new HTMLPurifier_Token_Text($bits[$i]);
+            } else {
+                $token[] = new HTMLPurifier_Token_Start(
+                    'a',
+                    array('href' => str_replace('%s', $bits[$i], $this->docURL))
+                );
+                $token[] = new HTMLPurifier_Token_Text('%' . $bits[$i]);
+                $token[] = new HTMLPurifier_Token_End('a');
+            }
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/RemoveEmpty.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/RemoveEmpty.php
new file mode 100644 (file)
index 0000000..01353ff
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+
+class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
+{
+    /**
+     * @type HTMLPurifier_Context
+     */
+    private $context;
+
+    /**
+     * @type HTMLPurifier_Config
+     */
+    private $config;
+
+    /**
+     * @type HTMLPurifier_AttrValidator
+     */
+    private $attrValidator;
+
+    /**
+     * @type bool
+     */
+    private $removeNbsp;
+
+    /**
+     * @type bool
+     */
+    private $removeNbspExceptions;
+
+    /**
+     * Cached contents of %AutoFormat.RemoveEmpty.Predicate
+     * @type array
+     */
+    private $exclude;
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return void
+     */
+    public function prepare($config, $context)
+    {
+        parent::prepare($config, $context);
+        $this->config = $config;
+        $this->context = $context;
+        $this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp');
+        $this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions');
+        $this->exclude = $config->get('AutoFormat.RemoveEmpty.Predicate');
+        $this->attrValidator = new HTMLPurifier_AttrValidator();
+    }
+
+    /**
+     * @param HTMLPurifier_Token $token
+     */
+    public function handleElement(&$token)
+    {
+        if (!$token instanceof HTMLPurifier_Token_Start) {
+            return;
+        }
+        $next = false;
+        $deleted = 1; // the current tag
+        for ($i = count($this->inputZipper->back) - 1; $i >= 0; $i--, $deleted++) {
+            $next = $this->inputZipper->back[$i];
+            if ($next instanceof HTMLPurifier_Token_Text) {
+                if ($next->is_whitespace) {
+                    continue;
+                }
+                if ($this->removeNbsp && !isset($this->removeNbspExceptions[$token->name])) {
+                    $plain = str_replace("\xC2\xA0", "", $next->data);
+                    $isWsOrNbsp = $plain === '' || ctype_space($plain);
+                    if ($isWsOrNbsp) {
+                        continue;
+                    }
+                }
+            }
+            break;
+        }
+        if (!$next || ($next instanceof HTMLPurifier_Token_End && $next->name == $token->name)) {
+            $this->attrValidator->validateToken($token, $this->config, $this->context);
+            $token->armor['ValidateAttributes'] = true;
+            if (isset($this->exclude[$token->name])) {
+                $r = true;
+                foreach ($this->exclude[$token->name] as $elem) {
+                    if (!isset($token->attr[$elem])) $r = false;
+                }
+                if ($r) return;
+            }
+            if (isset($token->attr['id']) || isset($token->attr['name'])) {
+                return;
+            }
+            $token = $deleted + 1;
+            for ($b = 0, $c = count($this->inputZipper->front); $b < $c; $b++) {
+                $prev = $this->inputZipper->front[$b];
+                if ($prev instanceof HTMLPurifier_Token_Text && $prev->is_whitespace) {
+                    continue;
+                }
+                break;
+            }
+            // This is safe because we removed the token that triggered this.
+            $this->rewindOffset($b+$deleted);
+            return;
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php
new file mode 100644 (file)
index 0000000..9ee7aa8
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * Injector that removes spans with no attributes
+ */
+class HTMLPurifier_Injector_RemoveSpansWithoutAttributes extends HTMLPurifier_Injector
+{
+    /**
+     * @type string
+     */
+    public $name = 'RemoveSpansWithoutAttributes';
+
+    /**
+     * @type array
+     */
+    public $needed = array('span');
+
+    /**
+     * @type HTMLPurifier_AttrValidator
+     */
+    private $attrValidator;
+
+    /**
+     * Used by AttrValidator.
+     * @type HTMLPurifier_Config
+     */
+    private $config;
+
+    /**
+     * @type HTMLPurifier_Context
+     */
+    private $context;
+
+    public function prepare($config, $context)
+    {
+        $this->attrValidator = new HTMLPurifier_AttrValidator();
+        $this->config = $config;
+        $this->context = $context;
+        return parent::prepare($config, $context);
+    }
+
+    /**
+     * @param HTMLPurifier_Token $token
+     */
+    public function handleElement(&$token)
+    {
+        if ($token->name !== 'span' || !$token instanceof HTMLPurifier_Token_Start) {
+            return;
+        }
+
+        // We need to validate the attributes now since this doesn't normally
+        // happen until after MakeWellFormed. If all the attributes are removed
+        // the span needs to be removed too.
+        $this->attrValidator->validateToken($token, $this->config, $this->context);
+        $token->armor['ValidateAttributes'] = true;
+
+        if (!empty($token->attr)) {
+            return;
+        }
+
+        $nesting = 0;
+        while ($this->forwardUntilEndToken($i, $current, $nesting)) {
+        }
+
+        if ($current instanceof HTMLPurifier_Token_End && $current->name === 'span') {
+            // Mark closing span tag for deletion
+            $current->markForDeletion = true;
+            // Delete open span tag
+            $token = false;
+        }
+    }
+
+    /**
+     * @param HTMLPurifier_Token $token
+     */
+    public function handleEnd(&$token)
+    {
+        if ($token->markForDeletion) {
+            $token = false;
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/SafeObject.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Injector/SafeObject.php
new file mode 100644 (file)
index 0000000..3d17e07
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+
+/**
+ * Adds important param elements to inside of object in order to make
+ * things safe.
+ */
+class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector
+{
+    /**
+     * @type string
+     */
+    public $name = 'SafeObject';
+
+    /**
+     * @type array
+     */
+    public $needed = array('object', 'param');
+
+    /**
+     * @type array
+     */
+    protected $objectStack = array();
+
+    /**
+     * @type array
+     */
+    protected $paramStack = array();
+
+    /**
+     * Keep this synchronized with AttrTransform/SafeParam.php.
+     * @type array
+     */
+    protected $addParam = array(
+        'allowScriptAccess' => 'never',
+        'allowNetworking' => 'internal',
+    );
+
+    /**
+     * @type array
+     */
+    protected $allowedParam = array(
+        'wmode' => true,
+        'movie' => true,
+        'flashvars' => true,
+        'src' => true,
+        'allowFullScreen' => true, // if omitted, assume to be 'false'
+    );
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return void
+     */
+    public function prepare($config, $context)
+    {
+        parent::prepare($config, $context);
+    }
+
+    /**
+     * @param HTMLPurifier_Token $token
+     */
+    public function handleElement(&$token)
+    {
+        if ($token->name == 'object') {
+            $this->objectStack[] = $token;
+            $this->paramStack[] = array();
+            $new = array($token);
+            foreach ($this->addParam as $name => $value) {
+                $new[] = new HTMLPurifier_Token_Empty('param', array('name' => $name, 'value' => $value));
+            }
+            $token = $new;
+        } elseif ($token->name == 'param') {
+            $nest = count($this->currentNesting) - 1;
+            if ($nest >= 0 && $this->currentNesting[$nest]->name === 'object') {
+                $i = count($this->objectStack) - 1;
+                if (!isset($token->attr['name'])) {
+                    $token = false;
+                    return;
+                }
+                $n = $token->attr['name'];
+                // We need this fix because YouTube doesn't supply a data
+                // attribute, which we need if a type is specified. This is
+                // *very* Flash specific.
+                if (!isset($this->objectStack[$i]->attr['data']) &&
+                    ($token->attr['name'] == 'movie' || $token->attr['name'] == 'src')
+                ) {
+                    $this->objectStack[$i]->attr['data'] = $token->attr['value'];
+                }
+                // Check if the parameter is the correct value but has not
+                // already been added
+                if (!isset($this->paramStack[$i][$n]) &&
+                    isset($this->addParam[$n]) &&
+                    $token->attr['name'] === $this->addParam[$n]) {
+                    // keep token, and add to param stack
+                    $this->paramStack[$i][$n] = true;
+                } elseif (isset($this->allowedParam[$n])) {
+                    // keep token, don't do anything to it
+                    // (could possibly check for duplicates here)
+                } else {
+                    $token = false;
+                }
+            } else {
+                // not directly inside an object, DENY!
+                $token = false;
+            }
+        }
+    }
+
+    public function handleEnd(&$token)
+    {
+        // This is the WRONG way of handling the object and param stacks;
+        // we should be inserting them directly on the relevant object tokens
+        // so that the global stack handling handles it.
+        if ($token->name == 'object') {
+            array_pop($this->objectStack);
+            array_pop($this->paramStack);
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Language.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Language.php
new file mode 100644 (file)
index 0000000..65277dd
--- /dev/null
@@ -0,0 +1,204 @@
+<?php
+
+/**
+ * Represents a language and defines localizable string formatting and
+ * other functions, as well as the localized messages for HTML Purifier.
+ */
+class HTMLPurifier_Language
+{
+
+    /**
+     * ISO 639 language code of language. Prefers shortest possible version.
+     * @type string
+     */
+    public $code = 'en';
+
+    /**
+     * Fallback language code.
+     * @type bool|string
+     */
+    public $fallback = false;
+
+    /**
+     * Array of localizable messages.
+     * @type array
+     */
+    public $messages = array();
+
+    /**
+     * Array of localizable error codes.
+     * @type array
+     */
+    public $errorNames = array();
+
+    /**
+     * True if no message file was found for this language, so English
+     * is being used instead. Check this if you'd like to notify the
+     * user that they've used a non-supported language.
+     * @type bool
+     */
+    public $error = false;
+
+    /**
+     * Has the language object been loaded yet?
+     * @type bool
+     * @todo Make it private, fix usage in HTMLPurifier_LanguageTest
+     */
+    public $_loaded = false;
+
+    /**
+     * @type HTMLPurifier_Config
+     */
+    protected $config;
+
+    /**
+     * @type HTMLPurifier_Context
+     */
+    protected $context;
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     */
+    public function __construct($config, $context)
+    {
+        $this->config  = $config;
+        $this->context = $context;
+    }
+
+    /**
+     * Loads language object with necessary info from factory cache
+     * @note This is a lazy loader
+     */
+    public function load()
+    {
+        if ($this->_loaded) {
+            return;
+        }
+        $factory = HTMLPurifier_LanguageFactory::instance();
+        $factory->loadLanguage($this->code);
+        foreach ($factory->keys as $key) {
+            $this->$key = $factory->cache[$this->code][$key];
+        }
+        $this->_loaded = true;
+    }
+
+    /**
+     * Retrieves a localised message.
+     * @param string $key string identifier of message
+     * @return string localised message
+     */
+    public function getMessage($key)
+    {
+        if (!$this->_loaded) {
+            $this->load();
+        }
+        if (!isset($this->messages[$key])) {
+            return "[$key]";
+        }
+        return $this->messages[$key];
+    }
+
+    /**
+     * Retrieves a localised error name.
+     * @param int $int error number, corresponding to PHP's error reporting
+     * @return string localised message
+     */
+    public function getErrorName($int)
+    {
+        if (!$this->_loaded) {
+            $this->load();
+        }
+        if (!isset($this->errorNames[$int])) {
+            return "[Error: $int]";
+        }
+        return $this->errorNames[$int];
+    }
+
+    /**
+     * Converts an array list into a string readable representation
+     * @param array $array
+     * @return string
+     */
+    public function listify($array)
+    {
+        $sep      = $this->getMessage('Item separator');
+        $sep_last = $this->getMessage('Item separator last');
+        $ret = '';
+        for ($i = 0, $c = count($array); $i < $c; $i++) {
+            if ($i == 0) {
+            } elseif ($i + 1 < $c) {
+                $ret .= $sep;
+            } else {
+                $ret .= $sep_last;
+            }
+            $ret .= $array[$i];
+        }
+        return $ret;
+    }
+
+    /**
+     * Formats a localised message with passed parameters
+     * @param string $key string identifier of message
+     * @param array $args Parameters to substitute in
+     * @return string localised message
+     * @todo Implement conditionals? Right now, some messages make
+     *     reference to line numbers, but those aren't always available
+     */
+    public function formatMessage($key, $args = array())
+    {
+        if (!$this->_loaded) {
+            $this->load();
+        }
+        if (!isset($this->messages[$key])) {
+            return "[$key]";
+        }
+        $raw = $this->messages[$key];
+        $subst = array();
+        $generator = false;
+        foreach ($args as $i => $value) {
+            if (is_object($value)) {
+                if ($value instanceof HTMLPurifier_Token) {
+                    // factor this out some time
+                    if (!$generator) {
+                        $generator = $this->context->get('Generator');
+                    }
+                    if (isset($value->name)) {
+                        $subst['$'.$i.'.Name'] = $value->name;
+                    }
+                    if (isset($value->data)) {
+                        $subst['$'.$i.'.Data'] = $value->data;
+                    }
+                    $subst['$'.$i.'.Compact'] =
+                    $subst['$'.$i.'.Serialized'] = $generator->generateFromToken($value);
+                    // a more complex algorithm for compact representation
+                    // could be introduced for all types of tokens. This
+                    // may need to be factored out into a dedicated class
+                    if (!empty($value->attr)) {
+                        $stripped_token = clone $value;
+                        $stripped_token->attr = array();
+                        $subst['$'.$i.'.Compact'] = $generator->generateFromToken($stripped_token);
+                    }
+                    $subst['$'.$i.'.Line'] = $value->line ? $value->line : 'unknown';
+                }
+                continue;
+            } elseif (is_array($value)) {
+                $keys = array_keys($value);
+                if (array_keys($keys) === $keys) {
+                    // list
+                    $subst['$'.$i] = $this->listify($value);
+                } else {
+                    // associative array
+                    // no $i implementation yet, sorry
+                    $subst['$'.$i.'.Keys'] = $this->listify($keys);
+                    $subst['$'.$i.'.Values'] = $this->listify(array_values($value));
+                }
+                continue;
+            }
+            $subst['$' . $i] = $value;
+        }
+        return strtr($raw, $subst);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Language/classes/en-x-test.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Language/classes/en-x-test.php
new file mode 100644 (file)
index 0000000..8828f5c
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+
+// private class for unit testing
+
+class HTMLPurifier_Language_en_x_test extends HTMLPurifier_Language
+{
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Language/messages/en-x-test.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Language/messages/en-x-test.php
new file mode 100644 (file)
index 0000000..1c046f3
--- /dev/null
@@ -0,0 +1,11 @@
+<?php
+
+// private language message file for unit testing purposes
+
+$fallback = 'en';
+
+$messages = array(
+    'HTMLPurifier' => 'HTML Purifier X'
+);
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Language/messages/en-x-testmini.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Language/messages/en-x-testmini.php
new file mode 100644 (file)
index 0000000..806c83f
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+// private language message file for unit testing purposes
+// this language file has no class associated with it
+
+$fallback = 'en';
+
+$messages = array(
+    'HTMLPurifier' => 'HTML Purifier XNone'
+);
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Language/messages/en.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Language/messages/en.php
new file mode 100644 (file)
index 0000000..c7f197e
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+$fallback = false;
+
+$messages = array(
+
+    'HTMLPurifier' => 'HTML Purifier',
+// for unit testing purposes
+    'LanguageFactoryTest: Pizza' => 'Pizza',
+    'LanguageTest: List' => '$1',
+    'LanguageTest: Hash' => '$1.Keys; $1.Values',
+    'Item separator' => ', ',
+    'Item separator last' => ' and ', // non-Harvard style
+
+    'ErrorCollector: No errors' => 'No errors detected. However, because error reporting is still incomplete, there may have been errors that the error collector was not notified of; please inspect the output HTML carefully.',
+    'ErrorCollector: At line' => ' at line $line',
+    'ErrorCollector: Incidental errors' => 'Incidental errors',
+    'Lexer: Unclosed comment' => 'Unclosed comment',
+    'Lexer: Unescaped lt' => 'Unescaped less-than sign (<) should be &lt;',
+    'Lexer: Missing gt' => 'Missing greater-than sign (>), previous less-than sign (<) should be escaped',
+    'Lexer: Missing attribute key' => 'Attribute declaration has no key',
+    'Lexer: Missing end quote' => 'Attribute declaration has no end quote',
+    'Lexer: Extracted body' => 'Removed document metadata tags',
+    'Strategy_RemoveForeignElements: Tag transform' => '<$1> element transformed into $CurrentToken.Serialized',
+    'Strategy_RemoveForeignElements: Missing required attribute' => '$CurrentToken.Compact element missing required attribute $1',
+    'Strategy_RemoveForeignElements: Foreign element to text' => 'Unrecognized $CurrentToken.Serialized tag converted to text',
+    'Strategy_RemoveForeignElements: Foreign element removed' => 'Unrecognized $CurrentToken.Serialized tag removed',
+    'Strategy_RemoveForeignElements: Comment removed' => 'Comment containing "$CurrentToken.Data" removed',
+    'Strategy_RemoveForeignElements: Foreign meta element removed' => 'Unrecognized $CurrentToken.Serialized meta tag and all descendants removed',
+    'Strategy_RemoveForeignElements: Token removed to end' => 'Tags and text starting from $1 element where removed to end',
+    'Strategy_RemoveForeignElements: Trailing hyphen in comment removed' => 'Trailing hyphen(s) in comment removed',
+    'Strategy_RemoveForeignElements: Hyphens in comment collapsed' => 'Double hyphens in comments are not allowed, and were collapsed into single hyphens',
+    'Strategy_MakeWellFormed: Unnecessary end tag removed' => 'Unnecessary $CurrentToken.Serialized tag removed',
+    'Strategy_MakeWellFormed: Unnecessary end tag to text' => 'Unnecessary $CurrentToken.Serialized tag converted to text',
+    'Strategy_MakeWellFormed: Tag auto closed' => '$1.Compact started on line $1.Line auto-closed by $CurrentToken.Compact',
+    'Strategy_MakeWellFormed: Tag carryover' => '$1.Compact started on line $1.Line auto-continued into $CurrentToken.Compact',
+    'Strategy_MakeWellFormed: Stray end tag removed' => 'Stray $CurrentToken.Serialized tag removed',
+    'Strategy_MakeWellFormed: Stray end tag to text' => 'Stray $CurrentToken.Serialized tag converted to text',
+    'Strategy_MakeWellFormed: Tag closed by element end' => '$1.Compact tag started on line $1.Line closed by end of $CurrentToken.Serialized',
+    'Strategy_MakeWellFormed: Tag closed by document end' => '$1.Compact tag started on line $1.Line closed by end of document',
+    'Strategy_FixNesting: Node removed' => '$CurrentToken.Compact node removed',
+    'Strategy_FixNesting: Node excluded' => '$CurrentToken.Compact node removed due to descendant exclusion by ancestor element',
+    'Strategy_FixNesting: Node reorganized' => 'Contents of $CurrentToken.Compact node reorganized to enforce its content model',
+    'Strategy_FixNesting: Node contents removed' => 'Contents of $CurrentToken.Compact node removed',
+    'AttrValidator: Attributes transformed' => 'Attributes on $CurrentToken.Compact transformed from $1.Keys to $2.Keys',
+    'AttrValidator: Attribute removed' => '$CurrentAttr.Name attribute on $CurrentToken.Compact removed',
+);
+
+$errorNames = array(
+    E_ERROR => 'Error',
+    E_WARNING => 'Warning',
+    E_NOTICE => 'Notice'
+);
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/LanguageFactory.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/LanguageFactory.php
new file mode 100644 (file)
index 0000000..4e35272
--- /dev/null
@@ -0,0 +1,209 @@
+<?php
+
+/**
+ * Class responsible for generating HTMLPurifier_Language objects, managing
+ * caching and fallbacks.
+ * @note Thanks to MediaWiki for the general logic, although this version
+ *       has been entirely rewritten
+ * @todo Serialized cache for languages
+ */
+class HTMLPurifier_LanguageFactory
+{
+
+    /**
+     * Cache of language code information used to load HTMLPurifier_Language objects.
+     * Structure is: $factory->cache[$language_code][$key] = $value
+     * @type array
+     */
+    public $cache;
+
+    /**
+     * Valid keys in the HTMLPurifier_Language object. Designates which
+     * variables to slurp out of a message file.
+     * @type array
+     */
+    public $keys = array('fallback', 'messages', 'errorNames');
+
+    /**
+     * Instance to validate language codes.
+     * @type HTMLPurifier_AttrDef_Lang
+     *
+     */
+    protected $validator;
+
+    /**
+     * Cached copy of dirname(__FILE__), directory of current file without
+     * trailing slash.
+     * @type string
+     */
+    protected $dir;
+
+    /**
+     * Keys whose contents are a hash map and can be merged.
+     * @type array
+     */
+    protected $mergeable_keys_map = array('messages' => true, 'errorNames' => true);
+
+    /**
+     * Keys whose contents are a list and can be merged.
+     * @value array lookup
+     */
+    protected $mergeable_keys_list = array();
+
+    /**
+     * Retrieve sole instance of the factory.
+     * @param HTMLPurifier_LanguageFactory $prototype Optional prototype to overload sole instance with,
+     *                   or bool true to reset to default factory.
+     * @return HTMLPurifier_LanguageFactory
+     */
+    public static function instance($prototype = null)
+    {
+        static $instance = null;
+        if ($prototype !== null) {
+            $instance = $prototype;
+        } elseif ($instance === null || $prototype == true) {
+            $instance = new HTMLPurifier_LanguageFactory();
+            $instance->setup();
+        }
+        return $instance;
+    }
+
+    /**
+     * Sets up the singleton, much like a constructor
+     * @note Prevents people from getting this outside of the singleton
+     */
+    public function setup()
+    {
+        $this->validator = new HTMLPurifier_AttrDef_Lang();
+        $this->dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier';
+    }
+
+    /**
+     * Creates a language object, handles class fallbacks
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @param bool|string $code Code to override configuration with. Private parameter.
+     * @return HTMLPurifier_Language
+     */
+    public function create($config, $context, $code = false)
+    {
+        // validate language code
+        if ($code === false) {
+            $code = $this->validator->validate(
+                $config->get('Core.Language'),
+                $config,
+                $context
+            );
+        } else {
+            $code = $this->validator->validate($code, $config, $context);
+        }
+        if ($code === false) {
+            $code = 'en'; // malformed code becomes English
+        }
+
+        $pcode = str_replace('-', '_', $code); // make valid PHP classname
+        static $depth = 0; // recursion protection
+
+        if ($code == 'en') {
+            $lang = new HTMLPurifier_Language($config, $context);
+        } else {
+            $class = 'HTMLPurifier_Language_' . $pcode;
+            $file  = $this->dir . '/Language/classes/' . $code . '.php';
+            if (file_exists($file) || class_exists($class, false)) {
+                $lang = new $class($config, $context);
+            } else {
+                // Go fallback
+                $raw_fallback = $this->getFallbackFor($code);
+                $fallback = $raw_fallback ? $raw_fallback : 'en';
+                $depth++;
+                $lang = $this->create($config, $context, $fallback);
+                if (!$raw_fallback) {
+                    $lang->error = true;
+                }
+                $depth--;
+            }
+        }
+        $lang->code = $code;
+        return $lang;
+    }
+
+    /**
+     * Returns the fallback language for language
+     * @note Loads the original language into cache
+     * @param string $code language code
+     * @return string|bool
+     */
+    public function getFallbackFor($code)
+    {
+        $this->loadLanguage($code);
+        return $this->cache[$code]['fallback'];
+    }
+
+    /**
+     * Loads language into the cache, handles message file and fallbacks
+     * @param string $code language code
+     */
+    public function loadLanguage($code)
+    {
+        static $languages_seen = array(); // recursion guard
+
+        // abort if we've already loaded it
+        if (isset($this->cache[$code])) {
+            return;
+        }
+
+        // generate filename
+        $filename = $this->dir . '/Language/messages/' . $code . '.php';
+
+        // default fallback : may be overwritten by the ensuing include
+        $fallback = ($code != 'en') ? 'en' : false;
+
+        // load primary localisation
+        if (!file_exists($filename)) {
+            // skip the include: will rely solely on fallback
+            $filename = $this->dir . '/Language/messages/en.php';
+            $cache = array();
+        } else {
+            include $filename;
+            $cache = compact($this->keys);
+        }
+
+        // load fallback localisation
+        if (!empty($fallback)) {
+
+            // infinite recursion guard
+            if (isset($languages_seen[$code])) {
+                trigger_error(
+                    'Circular fallback reference in language ' .
+                    $code,
+                    E_USER_ERROR
+                );
+                $fallback = 'en';
+            }
+            $language_seen[$code] = true;
+
+            // load the fallback recursively
+            $this->loadLanguage($fallback);
+            $fallback_cache = $this->cache[$fallback];
+
+            // merge fallback with current language
+            foreach ($this->keys as $key) {
+                if (isset($cache[$key]) && isset($fallback_cache[$key])) {
+                    if (isset($this->mergeable_keys_map[$key])) {
+                        $cache[$key] = $cache[$key] + $fallback_cache[$key];
+                    } elseif (isset($this->mergeable_keys_list[$key])) {
+                        $cache[$key] = array_merge($fallback_cache[$key], $cache[$key]);
+                    }
+                } else {
+                    $cache[$key] = $fallback_cache[$key];
+                }
+            }
+        }
+
+        // save to cache for later retrieval
+        $this->cache[$code] = $cache;
+        return;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Length.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Length.php
new file mode 100644 (file)
index 0000000..bbfbe66
--- /dev/null
@@ -0,0 +1,160 @@
+<?php
+
+/**
+ * Represents a measurable length, with a string numeric magnitude
+ * and a unit. This object is immutable.
+ */
+class HTMLPurifier_Length
+{
+
+    /**
+     * String numeric magnitude.
+     * @type string
+     */
+    protected $n;
+
+    /**
+     * String unit. False is permitted if $n = 0.
+     * @type string|bool
+     */
+    protected $unit;
+
+    /**
+     * Whether or not this length is valid. Null if not calculated yet.
+     * @type bool
+     */
+    protected $isValid;
+
+    /**
+     * Array Lookup array of units recognized by CSS 2.1
+     * @type array
+     */
+    protected static $allowedUnits = array(
+        'em' => true, 'ex' => true, 'px' => true, 'in' => true,
+        'cm' => true, 'mm' => true, 'pt' => true, 'pc' => true
+    );
+
+    /**
+     * @param string $n Magnitude
+     * @param bool|string $u Unit
+     */
+    public function __construct($n = '0', $u = false)
+    {
+        $this->n = (string) $n;
+        $this->unit = $u !== false ? (string) $u : false;
+    }
+
+    /**
+     * @param string $s Unit string, like '2em' or '3.4in'
+     * @return HTMLPurifier_Length
+     * @warning Does not perform validation.
+     */
+    public static function make($s)
+    {
+        if ($s instanceof HTMLPurifier_Length) {
+            return $s;
+        }
+        $n_length = strspn($s, '1234567890.+-');
+        $n = substr($s, 0, $n_length);
+        $unit = substr($s, $n_length);
+        if ($unit === '') {
+            $unit = false;
+        }
+        return new HTMLPurifier_Length($n, $unit);
+    }
+
+    /**
+     * Validates the number and unit.
+     * @return bool
+     */
+    protected function validate()
+    {
+        // Special case:
+        if ($this->n === '+0' || $this->n === '-0') {
+            $this->n = '0';
+        }
+        if ($this->n === '0' && $this->unit === false) {
+            return true;
+        }
+        if (!ctype_lower($this->unit)) {
+            $this->unit = strtolower($this->unit);
+        }
+        if (!isset(HTMLPurifier_Length::$allowedUnits[$this->unit])) {
+            return false;
+        }
+        // Hack:
+        $def = new HTMLPurifier_AttrDef_CSS_Number();
+        $result = $def->validate($this->n, false, false);
+        if ($result === false) {
+            return false;
+        }
+        $this->n = $result;
+        return true;
+    }
+
+    /**
+     * Returns string representation of number.
+     * @return string
+     */
+    public function toString()
+    {
+        if (!$this->isValid()) {
+            return false;
+        }
+        return $this->n . $this->unit;
+    }
+
+    /**
+     * Retrieves string numeric magnitude.
+     * @return string
+     */
+    public function getN()
+    {
+        return $this->n;
+    }
+
+    /**
+     * Retrieves string unit.
+     * @return string
+     */
+    public function getUnit()
+    {
+        return $this->unit;
+    }
+
+    /**
+     * Returns true if this length unit is valid.
+     * @return bool
+     */
+    public function isValid()
+    {
+        if ($this->isValid === null) {
+            $this->isValid = $this->validate();
+        }
+        return $this->isValid;
+    }
+
+    /**
+     * Compares two lengths, and returns 1 if greater, -1 if less and 0 if equal.
+     * @param HTMLPurifier_Length $l
+     * @return int
+     * @warning If both values are too large or small, this calculation will
+     *          not work properly
+     */
+    public function compareTo($l)
+    {
+        if ($l === false) {
+            return false;
+        }
+        if ($l->unit !== $this->unit) {
+            $converter = new HTMLPurifier_UnitConverter();
+            $l = $converter->convert($l, $this->unit);
+            if ($l === false) {
+                return false;
+            }
+        }
+        return $this->n - $l->n;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Lexer.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Lexer.php
new file mode 100644 (file)
index 0000000..4373262
--- /dev/null
@@ -0,0 +1,357 @@
+<?php
+
+/**
+ * Forgivingly lexes HTML (SGML-style) markup into tokens.
+ *
+ * A lexer parses a string of SGML-style markup and converts them into
+ * corresponding tokens.  It doesn't check for well-formedness, although its
+ * internal mechanism may make this automatic (such as the case of
+ * HTMLPurifier_Lexer_DOMLex).  There are several implementations to choose
+ * from.
+ *
+ * A lexer is HTML-oriented: it might work with XML, but it's not
+ * recommended, as we adhere to a subset of the specification for optimization
+ * reasons. This might change in the future. Also, most tokenizers are not
+ * expected to handle DTDs or PIs.
+ *
+ * This class should not be directly instantiated, but you may use create() to
+ * retrieve a default copy of the lexer.  Being a supertype, this class
+ * does not actually define any implementation, but offers commonly used
+ * convenience functions for subclasses.
+ *
+ * @note The unit tests will instantiate this class for testing purposes, as
+ *       many of the utility functions require a class to be instantiated.
+ *       This means that, even though this class is not runnable, it will
+ *       not be declared abstract.
+ *
+ * @par
+ *
+ * @note
+ * We use tokens rather than create a DOM representation because DOM would:
+ *
+ * @par
+ *  -# Require more processing and memory to create,
+ *  -# Is not streamable, and
+ *  -# Has the entire document structure (html and body not needed).
+ *
+ * @par
+ * However, DOM is helpful in that it makes it easy to move around nodes
+ * without a lot of lookaheads to see when a tag is closed. This is a
+ * limitation of the token system and some workarounds would be nice.
+ */
+class HTMLPurifier_Lexer
+{
+
+    /**
+     * Whether or not this lexer implements line-number/column-number tracking.
+     * If it does, set to true.
+     */
+    public $tracksLineNumbers = false;
+
+    // -- STATIC ----------------------------------------------------------
+
+    /**
+     * Retrieves or sets the default Lexer as a Prototype Factory.
+     *
+     * By default HTMLPurifier_Lexer_DOMLex will be returned. There are
+     * a few exceptions involving special features that only DirectLex
+     * implements.
+     *
+     * @note The behavior of this class has changed, rather than accepting
+     *       a prototype object, it now accepts a configuration object.
+     *       To specify your own prototype, set %Core.LexerImpl to it.
+     *       This change in behavior de-singletonizes the lexer object.
+     *
+     * @param HTMLPurifier_Config $config
+     * @return HTMLPurifier_Lexer
+     * @throws HTMLPurifier_Exception
+     */
+    public static function create($config)
+    {
+        if (!($config instanceof HTMLPurifier_Config)) {
+            $lexer = $config;
+            trigger_error(
+                "Passing a prototype to
+                HTMLPurifier_Lexer::create() is deprecated, please instead
+                use %Core.LexerImpl",
+                E_USER_WARNING
+            );
+        } else {
+            $lexer = $config->get('Core.LexerImpl');
+        }
+
+        $needs_tracking =
+            $config->get('Core.MaintainLineNumbers') ||
+            $config->get('Core.CollectErrors');
+
+        $inst = null;
+        if (is_object($lexer)) {
+            $inst = $lexer;
+        } else {
+            if (is_null($lexer)) {
+                do {
+                    // auto-detection algorithm
+                    if ($needs_tracking) {
+                        $lexer = 'DirectLex';
+                        break;
+                    }
+
+                    if (class_exists('DOMDocument') &&
+                        method_exists('DOMDocument', 'loadHTML') &&
+                        !extension_loaded('domxml')
+                    ) {
+                        // check for DOM support, because while it's part of the
+                        // core, it can be disabled compile time. Also, the PECL
+                        // domxml extension overrides the default DOM, and is evil
+                        // and nasty and we shan't bother to support it
+                        $lexer = 'DOMLex';
+                    } else {
+                        $lexer = 'DirectLex';
+                    }
+                } while (0);
+            } // do..while so we can break
+
+            // instantiate recognized string names
+            switch ($lexer) {
+                case 'DOMLex':
+                    $inst = new HTMLPurifier_Lexer_DOMLex();
+                    break;
+                case 'DirectLex':
+                    $inst = new HTMLPurifier_Lexer_DirectLex();
+                    break;
+                case 'PH5P':
+                    $inst = new HTMLPurifier_Lexer_PH5P();
+                    break;
+                default:
+                    throw new HTMLPurifier_Exception(
+                        "Cannot instantiate unrecognized Lexer type " .
+                        htmlspecialchars($lexer)
+                    );
+            }
+        }
+
+        if (!$inst) {
+            throw new HTMLPurifier_Exception('No lexer was instantiated');
+        }
+
+        // once PHP DOM implements native line numbers, or we
+        // hack out something using XSLT, remove this stipulation
+        if ($needs_tracking && !$inst->tracksLineNumbers) {
+            throw new HTMLPurifier_Exception(
+                'Cannot use lexer that does not support line numbers with ' .
+                'Core.MaintainLineNumbers or Core.CollectErrors (use DirectLex instead)'
+            );
+        }
+
+        return $inst;
+
+    }
+
+    // -- CONVENIENCE MEMBERS ---------------------------------------------
+
+    public function __construct()
+    {
+        $this->_entity_parser = new HTMLPurifier_EntityParser();
+    }
+
+    /**
+     * Most common entity to raw value conversion table for special entities.
+     * @type array
+     */
+    protected $_special_entity2str =
+        array(
+            '&quot;' => '"',
+            '&amp;' => '&',
+            '&lt;' => '<',
+            '&gt;' => '>',
+            '&#39;' => "'",
+            '&#039;' => "'",
+            '&#x27;' => "'"
+        );
+
+    /**
+     * Parses special entities into the proper characters.
+     *
+     * This string will translate escaped versions of the special characters
+     * into the correct ones.
+     *
+     * @warning
+     * You should be able to treat the output of this function as
+     * completely parsed, but that's only because all other entities should
+     * have been handled previously in substituteNonSpecialEntities()
+     *
+     * @param string $string String character data to be parsed.
+     * @return string Parsed character data.
+     */
+    public function parseData($string)
+    {
+        // following functions require at least one character
+        if ($string === '') {
+            return '';
+        }
+
+        // subtracts amps that cannot possibly be escaped
+        $num_amp = substr_count($string, '&') - substr_count($string, '& ') -
+            ($string[strlen($string) - 1] === '&' ? 1 : 0);
+
+        if (!$num_amp) {
+            return $string;
+        } // abort if no entities
+        $num_esc_amp = substr_count($string, '&amp;');
+        $string = strtr($string, $this->_special_entity2str);
+
+        // code duplication for sake of optimization, see above
+        $num_amp_2 = substr_count($string, '&') - substr_count($string, '& ') -
+            ($string[strlen($string) - 1] === '&' ? 1 : 0);
+
+        if ($num_amp_2 <= $num_esc_amp) {
+            return $string;
+        }
+
+        // hmm... now we have some uncommon entities. Use the callback.
+        $string = $this->_entity_parser->substituteSpecialEntities($string);
+        return $string;
+    }
+
+    /**
+     * Lexes an HTML string into tokens.
+     * @param $string String HTML.
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return HTMLPurifier_Token[] array representation of HTML.
+     */
+    public function tokenizeHTML($string, $config, $context)
+    {
+        trigger_error('Call to abstract class', E_USER_ERROR);
+    }
+
+    /**
+     * Translates CDATA sections into regular sections (through escaping).
+     * @param string $string HTML string to process.
+     * @return string HTML with CDATA sections escaped.
+     */
+    protected static function escapeCDATA($string)
+    {
+        return preg_replace_callback(
+            '/<!\[CDATA\[(.+?)\]\]>/s',
+            array('HTMLPurifier_Lexer', 'CDATACallback'),
+            $string
+        );
+    }
+
+    /**
+     * Special CDATA case that is especially convoluted for <script>
+     * @param string $string HTML string to process.
+     * @return string HTML with CDATA sections escaped.
+     */
+    protected static function escapeCommentedCDATA($string)
+    {
+        return preg_replace_callback(
+            '#<!--//--><!\[CDATA\[//><!--(.+?)//--><!\]\]>#s',
+            array('HTMLPurifier_Lexer', 'CDATACallback'),
+            $string
+        );
+    }
+
+    /**
+     * Special Internet Explorer conditional comments should be removed.
+     * @param string $string HTML string to process.
+     * @return string HTML with conditional comments removed.
+     */
+    protected static function removeIEConditional($string)
+    {
+        return preg_replace(
+            '#<!--\[if [^>]+\]>.*?<!\[endif\]-->#si', // probably should generalize for all strings
+            '',
+            $string
+        );
+    }
+
+    /**
+     * Callback function for escapeCDATA() that does the work.
+     *
+     * @warning Though this is public in order to let the callback happen,
+     *          calling it directly is not recommended.
+     * @param array $matches PCRE matches array, with index 0 the entire match
+     *                  and 1 the inside of the CDATA section.
+     * @return string Escaped internals of the CDATA section.
+     */
+    protected static function CDATACallback($matches)
+    {
+        // not exactly sure why the character set is needed, but whatever
+        return htmlspecialchars($matches[1], ENT_COMPAT, 'UTF-8');
+    }
+
+    /**
+     * Takes a piece of HTML and normalizes it by converting entities, fixing
+     * encoding, extracting bits, and other good stuff.
+     * @param string $html HTML.
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return string
+     * @todo Consider making protected
+     */
+    public function normalize($html, $config, $context)
+    {
+        // normalize newlines to \n
+        if ($config->get('Core.NormalizeNewlines')) {
+            $html = str_replace("\r\n", "\n", $html);
+            $html = str_replace("\r", "\n", $html);
+        }
+
+        if ($config->get('HTML.Trusted')) {
+            // escape convoluted CDATA
+            $html = $this->escapeCommentedCDATA($html);
+        }
+
+        // escape CDATA
+        $html = $this->escapeCDATA($html);
+
+        $html = $this->removeIEConditional($html);
+
+        // extract body from document if applicable
+        if ($config->get('Core.ConvertDocumentToFragment')) {
+            $e = false;
+            if ($config->get('Core.CollectErrors')) {
+                $e =& $context->get('ErrorCollector');
+            }
+            $new_html = $this->extractBody($html);
+            if ($e && $new_html != $html) {
+                $e->send(E_WARNING, 'Lexer: Extracted body');
+            }
+            $html = $new_html;
+        }
+
+        // expand entities that aren't the big five
+        $html = $this->_entity_parser->substituteNonSpecialEntities($html);
+
+        // clean into wellformed UTF-8 string for an SGML context: this has
+        // to be done after entity expansion because the entities sometimes
+        // represent non-SGML characters (horror, horror!)
+        $html = HTMLPurifier_Encoder::cleanUTF8($html);
+
+        // if processing instructions are to removed, remove them now
+        if ($config->get('Core.RemoveProcessingInstructions')) {
+            $html = preg_replace('#<\?.+?\?>#s', '', $html);
+        }
+
+        return $html;
+    }
+
+    /**
+     * Takes a string of HTML (fragment or document) and returns the content
+     * @todo Consider making protected
+     */
+    public function extractBody($html)
+    {
+        $matches = array();
+        $result = preg_match('!<body[^>]*>(.*)</body>!is', $html, $matches);
+        if ($result) {
+            return $matches[1];
+        } else {
+            return $html;
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php
new file mode 100644 (file)
index 0000000..b818192
--- /dev/null
@@ -0,0 +1,279 @@
+<?php
+
+/**
+ * Parser that uses PHP 5's DOM extension (part of the core).
+ *
+ * In PHP 5, the DOM XML extension was revamped into DOM and added to the core.
+ * It gives us a forgiving HTML parser, which we use to transform the HTML
+ * into a DOM, and then into the tokens.  It is blazingly fast (for large
+ * documents, it performs twenty times faster than
+ * HTMLPurifier_Lexer_DirectLex,and is the default choice for PHP 5.
+ *
+ * @note Any empty elements will have empty tokens associated with them, even if
+ * this is prohibited by the spec. This is cannot be fixed until the spec
+ * comes into play.
+ *
+ * @note PHP's DOM extension does not actually parse any entities, we use
+ *       our own function to do that.
+ *
+ * @warning DOM tends to drop whitespace, which may wreak havoc on indenting.
+ *          If this is a huge problem, due to the fact that HTML is hand
+ *          edited and you are unable to get a parser cache that caches the
+ *          the output of HTML Purifier while keeping the original HTML lying
+ *          around, you may want to run Tidy on the resulting output or use
+ *          HTMLPurifier_DirectLex
+ */
+
+class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
+{
+
+    /**
+     * @type HTMLPurifier_TokenFactory
+     */
+    private $factory;
+
+    public function __construct()
+    {
+        // setup the factory
+        parent::__construct();
+        $this->factory = new HTMLPurifier_TokenFactory();
+    }
+
+    /**
+     * @param string $html
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return HTMLPurifier_Token[]
+     */
+    public function tokenizeHTML($html, $config, $context)
+    {
+        $html = $this->normalize($html, $config, $context);
+
+        // attempt to armor stray angled brackets that cannot possibly
+        // form tags and thus are probably being used as emoticons
+        if ($config->get('Core.AggressivelyFixLt')) {
+            $char = '[^a-z!\/]';
+            $comment = "/<!--(.*?)(-->|\z)/is";
+            $html = preg_replace_callback($comment, array($this, 'callbackArmorCommentEntities'), $html);
+            do {
+                $old = $html;
+                $html = preg_replace("/<($char)/i", '&lt;\\1', $html);
+            } while ($html !== $old);
+            $html = preg_replace_callback($comment, array($this, 'callbackUndoCommentSubst'), $html); // fix comments
+        }
+
+        // preprocess html, essential for UTF-8
+        $html = $this->wrapHTML($html, $config, $context);
+
+        $doc = new DOMDocument();
+        $doc->encoding = 'UTF-8'; // theoretically, the above has this covered
+
+        set_error_handler(array($this, 'muteErrorHandler'));
+        $doc->loadHTML($html);
+        restore_error_handler();
+
+        $tokens = array();
+        $this->tokenizeDOM(
+            $doc->getElementsByTagName('html')->item(0)-> // <html>
+            getElementsByTagName('body')->item(0), //   <body>
+            $tokens
+        );
+        return $tokens;
+    }
+
+    /**
+     * Iterative function that tokenizes a node, putting it into an accumulator.
+     * To iterate is human, to recurse divine - L. Peter Deutsch
+     * @param DOMNode $node DOMNode to be tokenized.
+     * @param HTMLPurifier_Token[] $tokens   Array-list of already tokenized tokens.
+     * @return HTMLPurifier_Token of node appended to previously passed tokens.
+     */
+    protected function tokenizeDOM($node, &$tokens)
+    {
+        $level = 0;
+        $nodes = array($level => new HTMLPurifier_Queue(array($node)));
+        $closingNodes = array();
+        do {
+            while (!$nodes[$level]->isEmpty()) {
+                $node = $nodes[$level]->shift(); // FIFO
+                $collect = $level > 0 ? true : false;
+                $needEndingTag = $this->createStartNode($node, $tokens, $collect);
+                if ($needEndingTag) {
+                    $closingNodes[$level][] = $node;
+                }
+                if ($node->childNodes && $node->childNodes->length) {
+                    $level++;
+                    $nodes[$level] = new HTMLPurifier_Queue();
+                    foreach ($node->childNodes as $childNode) {
+                        $nodes[$level]->push($childNode);
+                    }
+                }
+            }
+            $level--;
+            if ($level && isset($closingNodes[$level])) {
+                while ($node = array_pop($closingNodes[$level])) {
+                    $this->createEndNode($node, $tokens);
+                }
+            }
+        } while ($level > 0);
+    }
+
+    /**
+     * @param DOMNode $node DOMNode to be tokenized.
+     * @param HTMLPurifier_Token[] $tokens   Array-list of already tokenized tokens.
+     * @param bool $collect  Says whether or start and close are collected, set to
+     *                    false at first recursion because it's the implicit DIV
+     *                    tag you're dealing with.
+     * @return bool if the token needs an endtoken
+     * @todo data and tagName properties don't seem to exist in DOMNode?
+     */
+    protected function createStartNode($node, &$tokens, $collect)
+    {
+        // intercept non element nodes. WE MUST catch all of them,
+        // but we're not getting the character reference nodes because
+        // those should have been preprocessed
+        if ($node->nodeType === XML_TEXT_NODE) {
+            $tokens[] = $this->factory->createText($node->data);
+            return false;
+        } elseif ($node->nodeType === XML_CDATA_SECTION_NODE) {
+            // undo libxml's special treatment of <script> and <style> tags
+            $last = end($tokens);
+            $data = $node->data;
+            // (note $node->tagname is already normalized)
+            if ($last instanceof HTMLPurifier_Token_Start && ($last->name == 'script' || $last->name == 'style')) {
+                $new_data = trim($data);
+                if (substr($new_data, 0, 4) === '<!--') {
+                    $data = substr($new_data, 4);
+                    if (substr($data, -3) === '-->') {
+                        $data = substr($data, 0, -3);
+                    } else {
+                        // Highly suspicious! Not sure what to do...
+                    }
+                }
+            }
+            $tokens[] = $this->factory->createText($this->parseData($data));
+            return false;
+        } elseif ($node->nodeType === XML_COMMENT_NODE) {
+            // this is code is only invoked for comments in script/style in versions
+            // of libxml pre-2.6.28 (regular comments, of course, are still
+            // handled regularly)
+            $tokens[] = $this->factory->createComment($node->data);
+            return false;
+        } elseif ($node->nodeType !== XML_ELEMENT_NODE) {
+            // not-well tested: there may be other nodes we have to grab
+            return false;
+        }
+
+        $attr = $node->hasAttributes() ? $this->transformAttrToAssoc($node->attributes) : array();
+
+        // We still have to make sure that the element actually IS empty
+        if (!$node->childNodes->length) {
+            if ($collect) {
+                $tokens[] = $this->factory->createEmpty($node->tagName, $attr);
+            }
+            return false;
+        } else {
+            if ($collect) {
+                $tokens[] = $this->factory->createStart(
+                    $tag_name = $node->tagName, // somehow, it get's dropped
+                    $attr
+                );
+            }
+            return true;
+        }
+    }
+
+    /**
+     * @param DOMNode $node
+     * @param HTMLPurifier_Token[] $tokens
+     */
+    protected function createEndNode($node, &$tokens)
+    {
+        $tokens[] = $this->factory->createEnd($node->tagName);
+    }
+
+
+    /**
+     * Converts a DOMNamedNodeMap of DOMAttr objects into an assoc array.
+     *
+     * @param DOMNamedNodeMap $node_map DOMNamedNodeMap of DOMAttr objects.
+     * @return array Associative array of attributes.
+     */
+    protected function transformAttrToAssoc($node_map)
+    {
+        // NamedNodeMap is documented very well, so we're using undocumented
+        // features, namely, the fact that it implements Iterator and
+        // has a ->length attribute
+        if ($node_map->length === 0) {
+            return array();
+        }
+        $array = array();
+        foreach ($node_map as $attr) {
+            $array[$attr->name] = $attr->value;
+        }
+        return $array;
+    }
+
+    /**
+     * An error handler that mutes all errors
+     * @param int $errno
+     * @param string $errstr
+     */
+    public function muteErrorHandler($errno, $errstr)
+    {
+    }
+
+    /**
+     * Callback function for undoing escaping of stray angled brackets
+     * in comments
+     * @param array $matches
+     * @return string
+     */
+    public function callbackUndoCommentSubst($matches)
+    {
+        return '<!--' . strtr($matches[1], array('&amp;' => '&', '&lt;' => '<')) . $matches[2];
+    }
+
+    /**
+     * Callback function that entity-izes ampersands in comments so that
+     * callbackUndoCommentSubst doesn't clobber them
+     * @param array $matches
+     * @return string
+     */
+    public function callbackArmorCommentEntities($matches)
+    {
+        return '<!--' . str_replace('&', '&amp;', $matches[1]) . $matches[2];
+    }
+
+    /**
+     * Wraps an HTML fragment in the necessary HTML
+     * @param string $html
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return string
+     */
+    protected function wrapHTML($html, $config, $context)
+    {
+        $def = $config->getDefinition('HTML');
+        $ret = '';
+
+        if (!empty($def->doctype->dtdPublic) || !empty($def->doctype->dtdSystem)) {
+            $ret .= '<!DOCTYPE html ';
+            if (!empty($def->doctype->dtdPublic)) {
+                $ret .= 'PUBLIC "' . $def->doctype->dtdPublic . '" ';
+            }
+            if (!empty($def->doctype->dtdSystem)) {
+                $ret .= '"' . $def->doctype->dtdSystem . '" ';
+            }
+            $ret .= '>';
+        }
+
+        $ret .= '<html><head>';
+        $ret .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
+        // No protection if $html contains a stray </div>!
+        $ret .= '</head><body>' . $html . '</body></html>';
+        return $ret;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DirectLex.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DirectLex.php
new file mode 100644 (file)
index 0000000..746b6e3
--- /dev/null
@@ -0,0 +1,539 @@
+<?php
+
+/**
+ * Our in-house implementation of a parser.
+ *
+ * A pure PHP parser, DirectLex has absolutely no dependencies, making
+ * it a reasonably good default for PHP4.  Written with efficiency in mind,
+ * it can be four times faster than HTMLPurifier_Lexer_PEARSax3, although it
+ * pales in comparison to HTMLPurifier_Lexer_DOMLex.
+ *
+ * @todo Reread XML spec and document differences.
+ */
+class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
+{
+    /**
+     * @type bool
+     */
+    public $tracksLineNumbers = true;
+
+    /**
+     * Whitespace characters for str(c)spn.
+     * @type string
+     */
+    protected $_whitespace = "\x20\x09\x0D\x0A";
+
+    /**
+     * Callback function for script CDATA fudge
+     * @param array $matches, in form of array(opening tag, contents, closing tag)
+     * @return string
+     */
+    protected function scriptCallback($matches)
+    {
+        return $matches[1] . htmlspecialchars($matches[2], ENT_COMPAT, 'UTF-8') . $matches[3];
+    }
+
+    /**
+     * @param String $html
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array|HTMLPurifier_Token[]
+     */
+    public function tokenizeHTML($html, $config, $context)
+    {
+        // special normalization for script tags without any armor
+        // our "armor" heurstic is a < sign any number of whitespaces after
+        // the first script tag
+        if ($config->get('HTML.Trusted')) {
+            $html = preg_replace_callback(
+                '#(<script[^>]*>)(\s*[^<].+?)(</script>)#si',
+                array($this, 'scriptCallback'),
+                $html
+            );
+        }
+
+        $html = $this->normalize($html, $config, $context);
+
+        $cursor = 0; // our location in the text
+        $inside_tag = false; // whether or not we're parsing the inside of a tag
+        $array = array(); // result array
+
+        // This is also treated to mean maintain *column* numbers too
+        $maintain_line_numbers = $config->get('Core.MaintainLineNumbers');
+
+        if ($maintain_line_numbers === null) {
+            // automatically determine line numbering by checking
+            // if error collection is on
+            $maintain_line_numbers = $config->get('Core.CollectErrors');
+        }
+
+        if ($maintain_line_numbers) {
+            $current_line = 1;
+            $current_col = 0;
+            $length = strlen($html);
+        } else {
+            $current_line = false;
+            $current_col = false;
+            $length = false;
+        }
+        $context->register('CurrentLine', $current_line);
+        $context->register('CurrentCol', $current_col);
+        $nl = "\n";
+        // how often to manually recalculate. This will ALWAYS be right,
+        // but it's pretty wasteful. Set to 0 to turn off
+        $synchronize_interval = $config->get('Core.DirectLexLineNumberSyncInterval');
+
+        $e = false;
+        if ($config->get('Core.CollectErrors')) {
+            $e =& $context->get('ErrorCollector');
+        }
+
+        // for testing synchronization
+        $loops = 0;
+
+        while (++$loops) {
+            // $cursor is either at the start of a token, or inside of
+            // a tag (i.e. there was a < immediately before it), as indicated
+            // by $inside_tag
+
+            if ($maintain_line_numbers) {
+                // $rcursor, however, is always at the start of a token.
+                $rcursor = $cursor - (int)$inside_tag;
+
+                // Column number is cheap, so we calculate it every round.
+                // We're interested at the *end* of the newline string, so
+                // we need to add strlen($nl) == 1 to $nl_pos before subtracting it
+                // from our "rcursor" position.
+                $nl_pos = strrpos($html, $nl, $rcursor - $length);
+                $current_col = $rcursor - (is_bool($nl_pos) ? 0 : $nl_pos + 1);
+
+                // recalculate lines
+                if ($synchronize_interval && // synchronization is on
+                    $cursor > 0 && // cursor is further than zero
+                    $loops % $synchronize_interval === 0) { // time to synchronize!
+                    $current_line = 1 + $this->substrCount($html, $nl, 0, $cursor);
+                }
+            }
+
+            $position_next_lt = strpos($html, '<', $cursor);
+            $position_next_gt = strpos($html, '>', $cursor);
+
+            // triggers on "<b>asdf</b>" but not "asdf <b></b>"
+            // special case to set up context
+            if ($position_next_lt === $cursor) {
+                $inside_tag = true;
+                $cursor++;
+            }
+
+            if (!$inside_tag && $position_next_lt !== false) {
+                // We are not inside tag and there still is another tag to parse
+                $token = new
+                HTMLPurifier_Token_Text(
+                    $this->parseData(
+                        substr(
+                            $html,
+                            $cursor,
+                            $position_next_lt - $cursor
+                        )
+                    )
+                );
+                if ($maintain_line_numbers) {
+                    $token->rawPosition($current_line, $current_col);
+                    $current_line += $this->substrCount($html, $nl, $cursor, $position_next_lt - $cursor);
+                }
+                $array[] = $token;
+                $cursor = $position_next_lt + 1;
+                $inside_tag = true;
+                continue;
+            } elseif (!$inside_tag) {
+                // We are not inside tag but there are no more tags
+                // If we're already at the end, break
+                if ($cursor === strlen($html)) {
+                    break;
+                }
+                // Create Text of rest of string
+                $token = new
+                HTMLPurifier_Token_Text(
+                    $this->parseData(
+                        substr(
+                            $html,
+                            $cursor
+                        )
+                    )
+                );
+                if ($maintain_line_numbers) {
+                    $token->rawPosition($current_line, $current_col);
+                }
+                $array[] = $token;
+                break;
+            } elseif ($inside_tag && $position_next_gt !== false) {
+                // We are in tag and it is well formed
+                // Grab the internals of the tag
+                $strlen_segment = $position_next_gt - $cursor;
+
+                if ($strlen_segment < 1) {
+                    // there's nothing to process!
+                    $token = new HTMLPurifier_Token_Text('<');
+                    $cursor++;
+                    continue;
+                }
+
+                $segment = substr($html, $cursor, $strlen_segment);
+
+                if ($segment === false) {
+                    // somehow, we attempted to access beyond the end of
+                    // the string, defense-in-depth, reported by Nate Abele
+                    break;
+                }
+
+                // Check if it's a comment
+                if (substr($segment, 0, 3) === '!--') {
+                    // re-determine segment length, looking for -->
+                    $position_comment_end = strpos($html, '-->', $cursor);
+                    if ($position_comment_end === false) {
+                        // uh oh, we have a comment that extends to
+                        // infinity. Can't be helped: set comment
+                        // end position to end of string
+                        if ($e) {
+                            $e->send(E_WARNING, 'Lexer: Unclosed comment');
+                        }
+                        $position_comment_end = strlen($html);
+                        $end = true;
+                    } else {
+                        $end = false;
+                    }
+                    $strlen_segment = $position_comment_end - $cursor;
+                    $segment = substr($html, $cursor, $strlen_segment);
+                    $token = new
+                    HTMLPurifier_Token_Comment(
+                        substr(
+                            $segment,
+                            3,
+                            $strlen_segment - 3
+                        )
+                    );
+                    if ($maintain_line_numbers) {
+                        $token->rawPosition($current_line, $current_col);
+                        $current_line += $this->substrCount($html, $nl, $cursor, $strlen_segment);
+                    }
+                    $array[] = $token;
+                    $cursor = $end ? $position_comment_end : $position_comment_end + 3;
+                    $inside_tag = false;
+                    continue;
+                }
+
+                // Check if it's an end tag
+                $is_end_tag = (strpos($segment, '/') === 0);
+                if ($is_end_tag) {
+                    $type = substr($segment, 1);
+                    $token = new HTMLPurifier_Token_End($type);
+                    if ($maintain_line_numbers) {
+                        $token->rawPosition($current_line, $current_col);
+                        $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
+                    }
+                    $array[] = $token;
+                    $inside_tag = false;
+                    $cursor = $position_next_gt + 1;
+                    continue;
+                }
+
+                // Check leading character is alnum, if not, we may
+                // have accidently grabbed an emoticon. Translate into
+                // text and go our merry way
+                if (!ctype_alpha($segment[0])) {
+                    // XML:  $segment[0] !== '_' && $segment[0] !== ':'
+                    if ($e) {
+                        $e->send(E_NOTICE, 'Lexer: Unescaped lt');
+                    }
+                    $token = new HTMLPurifier_Token_Text('<');
+                    if ($maintain_line_numbers) {
+                        $token->rawPosition($current_line, $current_col);
+                        $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
+                    }
+                    $array[] = $token;
+                    $inside_tag = false;
+                    continue;
+                }
+
+                // Check if it is explicitly self closing, if so, remove
+                // trailing slash. Remember, we could have a tag like <br>, so
+                // any later token processing scripts must convert improperly
+                // classified EmptyTags from StartTags.
+                $is_self_closing = (strrpos($segment, '/') === $strlen_segment - 1);
+                if ($is_self_closing) {
+                    $strlen_segment--;
+                    $segment = substr($segment, 0, $strlen_segment);
+                }
+
+                // Check if there are any attributes
+                $position_first_space = strcspn($segment, $this->_whitespace);
+
+                if ($position_first_space >= $strlen_segment) {
+                    if ($is_self_closing) {
+                        $token = new HTMLPurifier_Token_Empty($segment);
+                    } else {
+                        $token = new HTMLPurifier_Token_Start($segment);
+                    }
+                    if ($maintain_line_numbers) {
+                        $token->rawPosition($current_line, $current_col);
+                        $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
+                    }
+                    $array[] = $token;
+                    $inside_tag = false;
+                    $cursor = $position_next_gt + 1;
+                    continue;
+                }
+
+                // Grab out all the data
+                $type = substr($segment, 0, $position_first_space);
+                $attribute_string =
+                    trim(
+                        substr(
+                            $segment,
+                            $position_first_space
+                        )
+                    );
+                if ($attribute_string) {
+                    $attr = $this->parseAttributeString(
+                        $attribute_string,
+                        $config,
+                        $context
+                    );
+                } else {
+                    $attr = array();
+                }
+
+                if ($is_self_closing) {
+                    $token = new HTMLPurifier_Token_Empty($type, $attr);
+                } else {
+                    $token = new HTMLPurifier_Token_Start($type, $attr);
+                }
+                if ($maintain_line_numbers) {
+                    $token->rawPosition($current_line, $current_col);
+                    $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
+                }
+                $array[] = $token;
+                $cursor = $position_next_gt + 1;
+                $inside_tag = false;
+                continue;
+            } else {
+                // inside tag, but there's no ending > sign
+                if ($e) {
+                    $e->send(E_WARNING, 'Lexer: Missing gt');
+                }
+                $token = new
+                HTMLPurifier_Token_Text(
+                    '<' .
+                    $this->parseData(
+                        substr($html, $cursor)
+                    )
+                );
+                if ($maintain_line_numbers) {
+                    $token->rawPosition($current_line, $current_col);
+                }
+                // no cursor scroll? Hmm...
+                $array[] = $token;
+                break;
+            }
+            break;
+        }
+
+        $context->destroy('CurrentLine');
+        $context->destroy('CurrentCol');
+        return $array;
+    }
+
+    /**
+     * PHP 5.0.x compatible substr_count that implements offset and length
+     * @param string $haystack
+     * @param string $needle
+     * @param int $offset
+     * @param int $length
+     * @return int
+     */
+    protected function substrCount($haystack, $needle, $offset, $length)
+    {
+        static $oldVersion;
+        if ($oldVersion === null) {
+            $oldVersion = version_compare(PHP_VERSION, '5.1', '<');
+        }
+        if ($oldVersion) {
+            $haystack = substr($haystack, $offset, $length);
+            return substr_count($haystack, $needle);
+        } else {
+            return substr_count($haystack, $needle, $offset, $length);
+        }
+    }
+
+    /**
+     * Takes the inside of an HTML tag and makes an assoc array of attributes.
+     *
+     * @param string $string Inside of tag excluding name.
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array Assoc array of attributes.
+     */
+    public function parseAttributeString($string, $config, $context)
+    {
+        $string = (string)$string; // quick typecast
+
+        if ($string == '') {
+            return array();
+        } // no attributes
+
+        $e = false;
+        if ($config->get('Core.CollectErrors')) {
+            $e =& $context->get('ErrorCollector');
+        }
+
+        // let's see if we can abort as quickly as possible
+        // one equal sign, no spaces => one attribute
+        $num_equal = substr_count($string, '=');
+        $has_space = strpos($string, ' ');
+        if ($num_equal === 0 && !$has_space) {
+            // bool attribute
+            return array($string => $string);
+        } elseif ($num_equal === 1 && !$has_space) {
+            // only one attribute
+            list($key, $quoted_value) = explode('=', $string);
+            $quoted_value = trim($quoted_value);
+            if (!$key) {
+                if ($e) {
+                    $e->send(E_ERROR, 'Lexer: Missing attribute key');
+                }
+                return array();
+            }
+            if (!$quoted_value) {
+                return array($key => '');
+            }
+            $first_char = @$quoted_value[0];
+            $last_char = @$quoted_value[strlen($quoted_value) - 1];
+
+            $same_quote = ($first_char == $last_char);
+            $open_quote = ($first_char == '"' || $first_char == "'");
+
+            if ($same_quote && $open_quote) {
+                // well behaved
+                $value = substr($quoted_value, 1, strlen($quoted_value) - 2);
+            } else {
+                // not well behaved
+                if ($open_quote) {
+                    if ($e) {
+                        $e->send(E_ERROR, 'Lexer: Missing end quote');
+                    }
+                    $value = substr($quoted_value, 1);
+                } else {
+                    $value = $quoted_value;
+                }
+            }
+            if ($value === false) {
+                $value = '';
+            }
+            return array($key => $this->parseData($value));
+        }
+
+        // setup loop environment
+        $array = array(); // return assoc array of attributes
+        $cursor = 0; // current position in string (moves forward)
+        $size = strlen($string); // size of the string (stays the same)
+
+        // if we have unquoted attributes, the parser expects a terminating
+        // space, so let's guarantee that there's always a terminating space.
+        $string .= ' ';
+
+        $old_cursor = -1;
+        while ($cursor < $size) {
+            if ($old_cursor >= $cursor) {
+                throw new Exception("Infinite loop detected");
+            }
+            $old_cursor = $cursor;
+
+            $cursor += ($value = strspn($string, $this->_whitespace, $cursor));
+            // grab the key
+
+            $key_begin = $cursor; //we're currently at the start of the key
+
+            // scroll past all characters that are the key (not whitespace or =)
+            $cursor += strcspn($string, $this->_whitespace . '=', $cursor);
+
+            $key_end = $cursor; // now at the end of the key
+
+            $key = substr($string, $key_begin, $key_end - $key_begin);
+
+            if (!$key) {
+                if ($e) {
+                    $e->send(E_ERROR, 'Lexer: Missing attribute key');
+                }
+                $cursor += 1 + strcspn($string, $this->_whitespace, $cursor + 1); // prevent infinite loop
+                continue; // empty key
+            }
+
+            // scroll past all whitespace
+            $cursor += strspn($string, $this->_whitespace, $cursor);
+
+            if ($cursor >= $size) {
+                $array[$key] = $key;
+                break;
+            }
+
+            // if the next character is an equal sign, we've got a regular
+            // pair, otherwise, it's a bool attribute
+            $first_char = @$string[$cursor];
+
+            if ($first_char == '=') {
+                // key="value"
+
+                $cursor++;
+                $cursor += strspn($string, $this->_whitespace, $cursor);
+
+                if ($cursor === false) {
+                    $array[$key] = '';
+                    break;
+                }
+
+                // we might be in front of a quote right now
+
+                $char = @$string[$cursor];
+
+                if ($char == '"' || $char == "'") {
+                    // it's quoted, end bound is $char
+                    $cursor++;
+                    $value_begin = $cursor;
+                    $cursor = strpos($string, $char, $cursor);
+                    $value_end = $cursor;
+                } else {
+                    // it's not quoted, end bound is whitespace
+                    $value_begin = $cursor;
+                    $cursor += strcspn($string, $this->_whitespace, $cursor);
+                    $value_end = $cursor;
+                }
+
+                // we reached a premature end
+                if ($cursor === false) {
+                    $cursor = $size;
+                    $value_end = $cursor;
+                }
+
+                $value = substr($string, $value_begin, $value_end - $value_begin);
+                if ($value === false) {
+                    $value = '';
+                }
+                $array[$key] = $this->parseData($value);
+                $cursor++;
+            } else {
+                // boolattr
+                if ($key !== '') {
+                    $array[$key] = $key;
+                } else {
+                    // purely theoretical
+                    if ($e) {
+                        $e->send(E_ERROR, 'Lexer: Missing attribute key');
+                    }
+                }
+            }
+        }
+        return $array;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php
new file mode 100644 (file)
index 0000000..ff4fa21
--- /dev/null
@@ -0,0 +1,4787 @@
+<?php
+
+/**
+ * Experimental HTML5-based parser using Jeroen van der Meer's PH5P library.
+ * Occupies space in the HTML5 pseudo-namespace, which may cause conflicts.
+ *
+ * @note
+ *    Recent changes to PHP's DOM extension have resulted in some fatal
+ *    error conditions with the original version of PH5P. Pending changes,
+ *    this lexer will punt to DirectLex if DOM throws an exception.
+ */
+
+class HTMLPurifier_Lexer_PH5P extends HTMLPurifier_Lexer_DOMLex
+{
+    /**
+     * @param string $html
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return HTMLPurifier_Token[]
+     */
+    public function tokenizeHTML($html, $config, $context)
+    {
+        $new_html = $this->normalize($html, $config, $context);
+        $new_html = $this->wrapHTML($new_html, $config, $context);
+        try {
+            $parser = new HTML5($new_html);
+            $doc = $parser->save();
+        } catch (DOMException $e) {
+            // Uh oh, it failed. Punt to DirectLex.
+            $lexer = new HTMLPurifier_Lexer_DirectLex();
+            $context->register('PH5PError', $e); // save the error, so we can detect it
+            return $lexer->tokenizeHTML($html, $config, $context); // use original HTML
+        }
+        $tokens = array();
+        $this->tokenizeDOM(
+            $doc->getElementsByTagName('html')->item(0)-> // <html>
+                getElementsByTagName('body')->item(0) //   <body>
+            ,
+            $tokens
+        );
+        return $tokens;
+    }
+}
+
+/*
+
+Copyright 2007 Jeroen van der Meer <http://jero.net/>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+class HTML5
+{
+    private $data;
+    private $char;
+    private $EOF;
+    private $state;
+    private $tree;
+    private $token;
+    private $content_model;
+    private $escape = false;
+    private $entities = array(
+        'AElig;',
+        'AElig',
+        'AMP;',
+        'AMP',
+        'Aacute;',
+        'Aacute',
+        'Acirc;',
+        'Acirc',
+        'Agrave;',
+        'Agrave',
+        'Alpha;',
+        'Aring;',
+        'Aring',
+        'Atilde;',
+        'Atilde',
+        'Auml;',
+        'Auml',
+        'Beta;',
+        'COPY;',
+        'COPY',
+        'Ccedil;',
+        'Ccedil',
+        'Chi;',
+        'Dagger;',
+        'Delta;',
+        'ETH;',
+        'ETH',
+        'Eacute;',
+        'Eacute',
+        'Ecirc;',
+        'Ecirc',
+        'Egrave;',
+        'Egrave',
+        'Epsilon;',
+        'Eta;',
+        'Euml;',
+        'Euml',
+        'GT;',
+        'GT',
+        'Gamma;',
+        'Iacute;',
+        'Iacute',
+        'Icirc;',
+        'Icirc',
+        'Igrave;',
+        'Igrave',
+        'Iota;',
+        'Iuml;',
+        'Iuml',
+        'Kappa;',
+        'LT;',
+        'LT',
+        'Lambda;',
+        'Mu;',
+        'Ntilde;',
+        'Ntilde',
+        'Nu;',
+        'OElig;',
+        'Oacute;',
+        'Oacute',
+        'Ocirc;',
+        'Ocirc',
+        'Ograve;',
+        'Ograve',
+        'Omega;',
+        'Omicron;',
+        'Oslash;',
+        'Oslash',
+        'Otilde;',
+        'Otilde',
+        'Ouml;',
+        'Ouml',
+        'Phi;',
+        'Pi;',
+        'Prime;',
+        'Psi;',
+        'QUOT;',
+        'QUOT',
+        'REG;',
+        'REG',
+        'Rho;',
+        'Scaron;',
+        'Sigma;',
+        'THORN;',
+        'THORN',
+        'TRADE;',
+        'Tau;',
+        'Theta;',
+        'Uacute;',
+        'Uacute',
+        'Ucirc;',
+        'Ucirc',
+        'Ugrave;',
+        'Ugrave',
+        'Upsilon;',
+        'Uuml;',
+        'Uuml',
+        'Xi;',
+        'Yacute;',
+        'Yacute',
+        'Yuml;',
+        'Zeta;',
+        'aacute;',
+        'aacute',
+        'acirc;',
+        'acirc',
+        'acute;',
+        'acute',
+        'aelig;',
+        'aelig',
+        'agrave;',
+        'agrave',
+        'alefsym;',
+        'alpha;',
+        'amp;',
+        'amp',
+        'and;',
+        'ang;',
+        'apos;',
+        'aring;',
+        'aring',
+        'asymp;',
+        'atilde;',
+        'atilde',
+        'auml;',
+        'auml',
+        'bdquo;',
+        'beta;',
+        'brvbar;',
+        'brvbar',
+        'bull;',
+        'cap;',
+        'ccedil;',
+        'ccedil',
+        'cedil;',
+        'cedil',
+        'cent;',
+        'cent',
+        'chi;',
+        'circ;',
+        'clubs;',
+        'cong;',
+        'copy;',
+        'copy',
+        'crarr;',
+        'cup;',
+        'curren;',
+        'curren',
+        'dArr;',
+        'dagger;',
+        'darr;',
+        'deg;',
+        'deg',
+        'delta;',
+        'diams;',
+        'divide;',
+        'divide',
+        'eacute;',
+        'eacute',
+        'ecirc;',
+        'ecirc',
+        'egrave;',
+        'egrave',
+        'empty;',
+        'emsp;',
+        'ensp;',
+        'epsilon;',
+        'equiv;',
+        'eta;',
+        'eth;',
+        'eth',
+        'euml;',
+        'euml',
+        'euro;',
+        'exist;',
+        'fnof;',
+        'forall;',
+        'frac12;',
+        'frac12',
+        'frac14;',
+        'frac14',
+        'frac34;',
+        'frac34',
+        'frasl;',
+        'gamma;',
+        'ge;',
+        'gt;',
+        'gt',
+        'hArr;',
+        'harr;',
+        'hearts;',
+        'hellip;',
+        'iacute;',
+        'iacute',
+        'icirc;',
+        'icirc',
+        'iexcl;',
+        'iexcl',
+        'igrave;',
+        'igrave',
+        'image;',
+        'infin;',
+        'int;',
+        'iota;',
+        'iquest;',
+        'iquest',
+        'isin;',
+        'iuml;',
+        'iuml',
+        'kappa;',
+        'lArr;',
+        'lambda;',
+        'lang;',
+        'laquo;',
+        'laquo',
+        'larr;',
+        'lceil;',
+        'ldquo;',
+        'le;',
+        'lfloor;',
+        'lowast;',
+        'loz;',
+        'lrm;',
+        'lsaquo;',
+        'lsquo;',
+        'lt;',
+        'lt',
+        'macr;',
+        'macr',
+        'mdash;',
+        'micro;',
+        'micro',
+        'middot;',
+        'middot',
+        'minus;',
+        'mu;',
+        'nabla;',
+        'nbsp;',
+        'nbsp',
+        'ndash;',
+        'ne;',
+        'ni;',
+        'not;',
+        'not',
+        'notin;',
+        'nsub;',
+        'ntilde;',
+        'ntilde',
+        'nu;',
+        'oacute;',
+        'oacute',
+        'ocirc;',
+        'ocirc',
+        'oelig;',
+        'ograve;',
+        'ograve',
+        'oline;',
+        'omega;',
+        'omicron;',
+        'oplus;',
+        'or;',
+        'ordf;',
+        'ordf',
+        'ordm;',
+        'ordm',
+        'oslash;',
+        'oslash',
+        'otilde;',
+        'otilde',
+        'otimes;',
+        'ouml;',
+        'ouml',
+        'para;',
+        'para',
+        'part;',
+        'permil;',
+        'perp;',
+        'phi;',
+        'pi;',
+        'piv;',
+        'plusmn;',
+        'plusmn',
+        'pound;',
+        'pound',
+        'prime;',
+        'prod;',
+        'prop;',
+        'psi;',
+        'quot;',
+        'quot',
+        'rArr;',
+        'radic;',
+        'rang;',
+        'raquo;',
+        'raquo',
+        'rarr;',
+        'rceil;',
+        'rdquo;',
+        'real;',
+        'reg;',
+        'reg',
+        'rfloor;',
+        'rho;',
+        'rlm;',
+        'rsaquo;',
+        'rsquo;',
+        'sbquo;',
+        'scaron;',
+        'sdot;',
+        'sect;',
+        'sect',
+        'shy;',
+        'shy',
+        'sigma;',
+        'sigmaf;',
+        'sim;',
+        'spades;',
+        'sub;',
+        'sube;',
+        'sum;',
+        'sup1;',
+        'sup1',
+        'sup2;',
+        'sup2',
+        'sup3;',
+        'sup3',
+        'sup;',
+        'supe;',
+        'szlig;',
+        'szlig',
+        'tau;',
+        'there4;',
+        'theta;',
+        'thetasym;',
+        'thinsp;',
+        'thorn;',
+        'thorn',
+        'tilde;',
+        'times;',
+        'times',
+        'trade;',
+        'uArr;',
+        'uacute;',
+        'uacute',
+        'uarr;',
+        'ucirc;',
+        'ucirc',
+        'ugrave;',
+        'ugrave',
+        'uml;',
+        'uml',
+        'upsih;',
+        'upsilon;',
+        'uuml;',
+        'uuml',
+        'weierp;',
+        'xi;',
+        'yacute;',
+        'yacute',
+        'yen;',
+        'yen',
+        'yuml;',
+        'yuml',
+        'zeta;',
+        'zwj;',
+        'zwnj;'
+    );
+
+    const PCDATA = 0;
+    const RCDATA = 1;
+    const CDATA = 2;
+    const PLAINTEXT = 3;
+
+    const DOCTYPE = 0;
+    const STARTTAG = 1;
+    const ENDTAG = 2;
+    const COMMENT = 3;
+    const CHARACTR = 4;
+    const EOF = 5;
+
+    public function __construct($data)
+    {
+        $this->data = $data;
+        $this->char = -1;
+        $this->EOF = strlen($data);
+        $this->tree = new HTML5TreeConstructer;
+        $this->content_model = self::PCDATA;
+
+        $this->state = 'data';
+
+        while ($this->state !== null) {
+            $this->{$this->state . 'State'}();
+        }
+    }
+
+    public function save()
+    {
+        return $this->tree->save();
+    }
+
+    private function char()
+    {
+        return ($this->char < $this->EOF)
+            ? $this->data[$this->char]
+            : false;
+    }
+
+    private function character($s, $l = 0)
+    {
+        if ($s + $l < $this->EOF) {
+            if ($l === 0) {
+                return $this->data[$s];
+            } else {
+                return substr($this->data, $s, $l);
+            }
+        }
+    }
+
+    private function characters($char_class, $start)
+    {
+        return preg_replace('#^([' . $char_class . ']+).*#s', '\\1', substr($this->data, $start));
+    }
+
+    private function dataState()
+    {
+        // Consume the next input character
+        $this->char++;
+        $char = $this->char();
+
+        if ($char === '&' && ($this->content_model === self::PCDATA || $this->content_model === self::RCDATA)) {
+            /* U+0026 AMPERSAND (&)
+            When the content model flag is set to one of the PCDATA or RCDATA
+            states: switch to the entity data state. Otherwise: treat it as per
+            the "anything else"    entry below. */
+            $this->state = 'entityData';
+
+        } elseif ($char === '-') {
+            /* If the content model flag is set to either the RCDATA state or
+            the CDATA state, and the escape flag is false, and there are at
+            least three characters before this one in the input stream, and the
+            last four characters in the input stream, including this one, are
+            U+003C LESS-THAN SIGN, U+0021 EXCLAMATION MARK, U+002D HYPHEN-MINUS,
+            and U+002D HYPHEN-MINUS ("<!--"), then set the escape flag to true. */
+            if (($this->content_model === self::RCDATA || $this->content_model ===
+                    self::CDATA) && $this->escape === false &&
+                $this->char >= 3 && $this->character($this->char - 4, 4) === '<!--'
+            ) {
+                $this->escape = true;
+            }
+
+            /* In any case, emit the input character as a character token. Stay
+            in the data state. */
+            $this->emitToken(
+                array(
+                    'type' => self::CHARACTR,
+                    'data' => $char
+                )
+            );
+
+            /* U+003C LESS-THAN SIGN (<) */
+        } elseif ($char === '<' && ($this->content_model === self::PCDATA ||
+                (($this->content_model === self::RCDATA ||
+                        $this->content_model === self::CDATA) && $this->escape === false))
+        ) {
+            /* When the content model flag is set to the PCDATA state: switch
+            to the tag open state.
+
+            When the content model flag is set to either the RCDATA state or
+            the CDATA state and the escape flag is false: switch to the tag
+            open state.
+
+            Otherwise: treat it as per the "anything else" entry below. */
+            $this->state = 'tagOpen';
+
+            /* U+003E GREATER-THAN SIGN (>) */
+        } elseif ($char === '>') {
+            /* If the content model flag is set to either the RCDATA state or
+            the CDATA state, and the escape flag is true, and the last three
+            characters in the input stream including this one are U+002D
+            HYPHEN-MINUS, U+002D HYPHEN-MINUS, U+003E GREATER-THAN SIGN ("-->"),
+            set the escape flag to false. */
+            if (($this->content_model === self::RCDATA ||
+                    $this->content_model === self::CDATA) && $this->escape === true &&
+                $this->character($this->char, 3) === '-->'
+            ) {
+                $this->escape = false;
+            }
+
+            /* In any case, emit the input character as a character token.
+            Stay in the data state. */
+            $this->emitToken(
+                array(
+                    'type' => self::CHARACTR,
+                    'data' => $char
+                )
+            );
+
+        } elseif ($this->char === $this->EOF) {
+            /* EOF
+            Emit an end-of-file token. */
+            $this->EOF();
+
+        } elseif ($this->content_model === self::PLAINTEXT) {
+            /* When the content model flag is set to the PLAINTEXT state
+            THIS DIFFERS GREATLY FROM THE SPEC: Get the remaining characters of
+            the text and emit it as a character token. */
+            $this->emitToken(
+                array(
+                    'type' => self::CHARACTR,
+                    'data' => substr($this->data, $this->char)
+                )
+            );
+
+            $this->EOF();
+
+        } else {
+            /* Anything else
+            THIS DIFFERS GREATLY FROM THE SPEC: Get as many character that
+            otherwise would also be treated as a character token and emit it
+            as a single character token. Stay in the data state. */
+            $len = strcspn($this->data, '<&', $this->char);
+            $char = substr($this->data, $this->char, $len);
+            $this->char += $len - 1;
+
+            $this->emitToken(
+                array(
+                    'type' => self::CHARACTR,
+                    'data' => $char
+                )
+            );
+
+            $this->state = 'data';
+        }
+    }
+
+    private function entityDataState()
+    {
+        // Attempt to consume an entity.
+        $entity = $this->entity();
+
+        // If nothing is returned, emit a U+0026 AMPERSAND character token.
+        // Otherwise, emit the character token that was returned.
+        $char = (!$entity) ? '&' : $entity;
+        $this->emitToken(
+            array(
+                'type' => self::CHARACTR,
+                'data' => $char
+            )
+        );
+
+        // Finally, switch to the data state.
+        $this->state = 'data';
+    }
+
+    private function tagOpenState()
+    {
+        switch ($this->content_model) {
+            case self::RCDATA:
+            case self::CDATA:
+                /* If the next input character is a U+002F SOLIDUS (/) character,
+                consume it and switch to the close tag open state. If the next
+                input character is not a U+002F SOLIDUS (/) character, emit a
+                U+003C LESS-THAN SIGN character token and switch to the data
+                state to process the next input character. */
+                if ($this->character($this->char + 1) === '/') {
+                    $this->char++;
+                    $this->state = 'closeTagOpen';
+
+                } else {
+                    $this->emitToken(
+                        array(
+                            'type' => self::CHARACTR,
+                            'data' => '<'
+                        )
+                    );
+
+                    $this->state = 'data';
+                }
+                break;
+
+            case self::PCDATA:
+                // If the content model flag is set to the PCDATA state
+                // Consume the next input character:
+                $this->char++;
+                $char = $this->char();
+
+                if ($char === '!') {
+                    /* U+0021 EXCLAMATION MARK (!)
+                    Switch to the markup declaration open state. */
+                    $this->state = 'markupDeclarationOpen';
+
+                } elseif ($char === '/') {
+                    /* U+002F SOLIDUS (/)
+                    Switch to the close tag open state. */
+                    $this->state = 'closeTagOpen';
+
+                } elseif (preg_match('/^[A-Za-z]$/', $char)) {
+                    /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z
+                    Create a new start tag token, set its tag name to the lowercase
+                    version of the input character (add 0x0020 to the character's code
+                    point), then switch to the tag name state. (Don't emit the token
+                    yet; further details will be filled in before it is emitted.) */
+                    $this->token = array(
+                        'name' => strtolower($char),
+                        'type' => self::STARTTAG,
+                        'attr' => array()
+                    );
+
+                    $this->state = 'tagName';
+
+                } elseif ($char === '>') {
+                    /* U+003E GREATER-THAN SIGN (>)
+                    Parse error. Emit a U+003C LESS-THAN SIGN character token and a
+                    U+003E GREATER-THAN SIGN character token. Switch to the data state. */
+                    $this->emitToken(
+                        array(
+                            'type' => self::CHARACTR,
+                            'data' => '<>'
+                        )
+                    );
+
+                    $this->state = 'data';
+
+                } elseif ($char === '?') {
+                    /* U+003F QUESTION MARK (?)
+                    Parse error. Switch to the bogus comment state. */
+                    $this->state = 'bogusComment';
+
+                } else {
+                    /* Anything else
+                    Parse error. Emit a U+003C LESS-THAN SIGN character token and
+                    reconsume the current input character in the data state. */
+                    $this->emitToken(
+                        array(
+                            'type' => self::CHARACTR,
+                            'data' => '<'
+                        )
+                    );
+
+                    $this->char--;
+                    $this->state = 'data';
+                }
+                break;
+        }
+    }
+
+    private function closeTagOpenState()
+    {
+        $next_node = strtolower($this->characters('A-Za-z', $this->char + 1));
+        $the_same = count($this->tree->stack) > 0 && $next_node === end($this->tree->stack)->nodeName;
+
+        if (($this->content_model === self::RCDATA || $this->content_model === self::CDATA) &&
+            (!$the_same || ($the_same && (!preg_match(
+                            '/[\t\n\x0b\x0c >\/]/',
+                            $this->character($this->char + 1 + strlen($next_node))
+                        ) || $this->EOF === $this->char)))
+        ) {
+            /* If the content model flag is set to the RCDATA or CDATA states then
+            examine the next few characters. If they do not match the tag name of
+            the last start tag token emitted (case insensitively), or if they do but
+            they are not immediately followed by one of the following characters:
+                * U+0009 CHARACTER TABULATION
+                * U+000A LINE FEED (LF)
+                * U+000B LINE TABULATION
+                * U+000C FORM FEED (FF)
+                * U+0020 SPACE
+                * U+003E GREATER-THAN SIGN (>)
+                * U+002F SOLIDUS (/)
+                * EOF
+            ...then there is a parse error. Emit a U+003C LESS-THAN SIGN character
+            token, a U+002F SOLIDUS character token, and switch to the data state
+            to process the next input character. */
+            $this->emitToken(
+                array(
+                    'type' => self::CHARACTR,
+                    'data' => '</'
+                )
+            );
+
+            $this->state = 'data';
+
+        } else {
+            /* Otherwise, if the content model flag is set to the PCDATA state,
+            or if the next few characters do match that tag name, consume the
+            next input character: */
+            $this->char++;
+            $char = $this->char();
+
+            if (preg_match('/^[A-Za-z]$/', $char)) {
+                /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z
+                Create a new end tag token, set its tag name to the lowercase version
+                of the input character (add 0x0020 to the character's code point), then
+                switch to the tag name state. (Don't emit the token yet; further details
+                will be filled in before it is emitted.) */
+                $this->token = array(
+                    'name' => strtolower($char),
+                    'type' => self::ENDTAG
+                );
+
+                $this->state = 'tagName';
+
+            } elseif ($char === '>') {
+                /* U+003E GREATER-THAN SIGN (>)
+                Parse error. Switch to the data state. */
+                $this->state = 'data';
+
+            } elseif ($this->char === $this->EOF) {
+                /* EOF
+                Parse error. Emit a U+003C LESS-THAN SIGN character token and a U+002F
+                SOLIDUS character token. Reconsume the EOF character in the data state. */
+                $this->emitToken(
+                    array(
+                        'type' => self::CHARACTR,
+                        'data' => '</'
+                    )
+                );
+
+                $this->char--;
+                $this->state = 'data';
+
+            } else {
+                /* Parse error. Switch to the bogus comment state. */
+                $this->state = 'bogusComment';
+            }
+        }
+    }
+
+    private function tagNameState()
+    {
+        // Consume the next input character:
+        $this->char++;
+        $char = $this->character($this->char);
+
+        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            /* U+0009 CHARACTER TABULATION
+            U+000A LINE FEED (LF)
+            U+000B LINE TABULATION
+            U+000C FORM FEED (FF)
+            U+0020 SPACE
+            Switch to the before attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } elseif ($char === '>') {
+            /* U+003E GREATER-THAN SIGN (>)
+            Emit the current tag token. Switch to the data state. */
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } elseif ($this->char === $this->EOF) {
+            /* EOF
+            Parse error. Emit the current tag token. Reconsume the EOF
+            character in the data state. */
+            $this->emitToken($this->token);
+
+            $this->char--;
+            $this->state = 'data';
+
+        } elseif ($char === '/') {
+            /* U+002F SOLIDUS (/)
+            Parse error unless this is a permitted slash. Switch to the before
+            attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } else {
+            /* Anything else
+            Append the current input character to the current tag token's tag name.
+            Stay in the tag name state. */
+            $this->token['name'] .= strtolower($char);
+            $this->state = 'tagName';
+        }
+    }
+
+    private function beforeAttributeNameState()
+    {
+        // Consume the next input character:
+        $this->char++;
+        $char = $this->character($this->char);
+
+        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            /* U+0009 CHARACTER TABULATION
+            U+000A LINE FEED (LF)
+            U+000B LINE TABULATION
+            U+000C FORM FEED (FF)
+            U+0020 SPACE
+            Stay in the before attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } elseif ($char === '>') {
+            /* U+003E GREATER-THAN SIGN (>)
+            Emit the current tag token. Switch to the data state. */
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } elseif ($char === '/') {
+            /* U+002F SOLIDUS (/)
+            Parse error unless this is a permitted slash. Stay in the before
+            attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } elseif ($this->char === $this->EOF) {
+            /* EOF
+            Parse error. Emit the current tag token. Reconsume the EOF
+            character in the data state. */
+            $this->emitToken($this->token);
+
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            /* Anything else
+            Start a new attribute in the current tag token. Set that attribute's
+            name to the current input character, and its value to the empty string.
+            Switch to the attribute name state. */
+            $this->token['attr'][] = array(
+                'name' => strtolower($char),
+                'value' => null
+            );
+
+            $this->state = 'attributeName';
+        }
+    }
+
+    private function attributeNameState()
+    {
+        // Consume the next input character:
+        $this->char++;
+        $char = $this->character($this->char);
+
+        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            /* U+0009 CHARACTER TABULATION
+            U+000A LINE FEED (LF)
+            U+000B LINE TABULATION
+            U+000C FORM FEED (FF)
+            U+0020 SPACE
+            Stay in the before attribute name state. */
+            $this->state = 'afterAttributeName';
+
+        } elseif ($char === '=') {
+            /* U+003D EQUALS SIGN (=)
+            Switch to the before attribute value state. */
+            $this->state = 'beforeAttributeValue';
+
+        } elseif ($char === '>') {
+            /* U+003E GREATER-THAN SIGN (>)
+            Emit the current tag token. Switch to the data state. */
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } elseif ($char === '/' && $this->character($this->char + 1) !== '>') {
+            /* U+002F SOLIDUS (/)
+            Parse error unless this is a permitted slash. Switch to the before
+            attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } elseif ($this->char === $this->EOF) {
+            /* EOF
+            Parse error. Emit the current tag token. Reconsume the EOF
+            character in the data state. */
+            $this->emitToken($this->token);
+
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            /* Anything else
+            Append the current input character to the current attribute's name.
+            Stay in the attribute name state. */
+            $last = count($this->token['attr']) - 1;
+            $this->token['attr'][$last]['name'] .= strtolower($char);
+
+            $this->state = 'attributeName';
+        }
+    }
+
+    private function afterAttributeNameState()
+    {
+        // Consume the next input character:
+        $this->char++;
+        $char = $this->character($this->char);
+
+        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            /* U+0009 CHARACTER TABULATION
+            U+000A LINE FEED (LF)
+            U+000B LINE TABULATION
+            U+000C FORM FEED (FF)
+            U+0020 SPACE
+            Stay in the after attribute name state. */
+            $this->state = 'afterAttributeName';
+
+        } elseif ($char === '=') {
+            /* U+003D EQUALS SIGN (=)
+            Switch to the before attribute value state. */
+            $this->state = 'beforeAttributeValue';
+
+        } elseif ($char === '>') {
+            /* U+003E GREATER-THAN SIGN (>)
+            Emit the current tag token. Switch to the data state. */
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } elseif ($char === '/' && $this->character($this->char + 1) !== '>') {
+            /* U+002F SOLIDUS (/)
+            Parse error unless this is a permitted slash. Switch to the
+            before attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } elseif ($this->char === $this->EOF) {
+            /* EOF
+            Parse error. Emit the current tag token. Reconsume the EOF
+            character in the data state. */
+            $this->emitToken($this->token);
+
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            /* Anything else
+            Start a new attribute in the current tag token. Set that attribute's
+            name to the current input character, and its value to the empty string.
+            Switch to the attribute name state. */
+            $this->token['attr'][] = array(
+                'name' => strtolower($char),
+                'value' => null
+            );
+
+            $this->state = 'attributeName';
+        }
+    }
+
+    private function beforeAttributeValueState()
+    {
+        // Consume the next input character:
+        $this->char++;
+        $char = $this->character($this->char);
+
+        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            /* U+0009 CHARACTER TABULATION
+            U+000A LINE FEED (LF)
+            U+000B LINE TABULATION
+            U+000C FORM FEED (FF)
+            U+0020 SPACE
+            Stay in the before attribute value state. */
+            $this->state = 'beforeAttributeValue';
+
+        } elseif ($char === '"') {
+            /* U+0022 QUOTATION MARK (")
+            Switch to the attribute value (double-quoted) state. */
+            $this->state = 'attributeValueDoubleQuoted';
+
+        } elseif ($char === '&') {
+            /* U+0026 AMPERSAND (&)
+            Switch to the attribute value (unquoted) state and reconsume
+            this input character. */
+            $this->char--;
+            $this->state = 'attributeValueUnquoted';
+
+        } elseif ($char === '\'') {
+            /* U+0027 APOSTROPHE (')
+            Switch to the attribute value (single-quoted) state. */
+            $this->state = 'attributeValueSingleQuoted';
+
+        } elseif ($char === '>') {
+            /* U+003E GREATER-THAN SIGN (>)
+            Emit the current tag token. Switch to the data state. */
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } else {
+            /* Anything else
+            Append the current input character to the current attribute's value.
+            Switch to the attribute value (unquoted) state. */
+            $last = count($this->token['attr']) - 1;
+            $this->token['attr'][$last]['value'] .= $char;
+
+            $this->state = 'attributeValueUnquoted';
+        }
+    }
+
+    private function attributeValueDoubleQuotedState()
+    {
+        // Consume the next input character:
+        $this->char++;
+        $char = $this->character($this->char);
+
+        if ($char === '"') {
+            /* U+0022 QUOTATION MARK (")
+            Switch to the before attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } elseif ($char === '&') {
+            /* U+0026 AMPERSAND (&)
+            Switch to the entity in attribute value state. */
+            $this->entityInAttributeValueState('double');
+
+        } elseif ($this->char === $this->EOF) {
+            /* EOF
+            Parse error. Emit the current tag token. Reconsume the character
+            in the data state. */
+            $this->emitToken($this->token);
+
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            /* Anything else
+            Append the current input character to the current attribute's value.
+            Stay in the attribute value (double-quoted) state. */
+            $last = count($this->token['attr']) - 1;
+            $this->token['attr'][$last]['value'] .= $char;
+
+            $this->state = 'attributeValueDoubleQuoted';
+        }
+    }
+
+    private function attributeValueSingleQuotedState()
+    {
+        // Consume the next input character:
+        $this->char++;
+        $char = $this->character($this->char);
+
+        if ($char === '\'') {
+            /* U+0022 QUOTATION MARK (')
+            Switch to the before attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } elseif ($char === '&') {
+            /* U+0026 AMPERSAND (&)
+            Switch to the entity in attribute value state. */
+            $this->entityInAttributeValueState('single');
+
+        } elseif ($this->char === $this->EOF) {
+            /* EOF
+            Parse error. Emit the current tag token. Reconsume the character
+            in the data state. */
+            $this->emitToken($this->token);
+
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            /* Anything else
+            Append the current input character to the current attribute's value.
+            Stay in the attribute value (single-quoted) state. */
+            $last = count($this->token['attr']) - 1;
+            $this->token['attr'][$last]['value'] .= $char;
+
+            $this->state = 'attributeValueSingleQuoted';
+        }
+    }
+
+    private function attributeValueUnquotedState()
+    {
+        // Consume the next input character:
+        $this->char++;
+        $char = $this->character($this->char);
+
+        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            /* U+0009 CHARACTER TABULATION
+            U+000A LINE FEED (LF)
+            U+000B LINE TABULATION
+            U+000C FORM FEED (FF)
+            U+0020 SPACE
+            Switch to the before attribute name state. */
+            $this->state = 'beforeAttributeName';
+
+        } elseif ($char === '&') {
+            /* U+0026 AMPERSAND (&)
+            Switch to the entity in attribute value state. */
+            $this->entityInAttributeValueState();
+
+        } elseif ($char === '>') {
+            /* U+003E GREATER-THAN SIGN (>)
+            Emit the current tag token. Switch to the data state. */
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } else {
+            /* Anything else
+            Append the current input character to the current attribute's value.
+            Stay in the attribute value (unquoted) state. */
+            $last = count($this->token['attr']) - 1;
+            $this->token['attr'][$last]['value'] .= $char;
+
+            $this->state = 'attributeValueUnquoted';
+        }
+    }
+
+    private function entityInAttributeValueState()
+    {
+        // Attempt to consume an entity.
+        $entity = $this->entity();
+
+        // If nothing is returned, append a U+0026 AMPERSAND character to the
+        // current attribute's value. Otherwise, emit the character token that
+        // was returned.
+        $char = (!$entity)
+            ? '&'
+            : $entity;
+
+        $last = count($this->token['attr']) - 1;
+        $this->token['attr'][$last]['value'] .= $char;
+    }
+
+    private function bogusCommentState()
+    {
+        /* Consume every character up to the first U+003E GREATER-THAN SIGN
+        character (>) or the end of the file (EOF), whichever comes first. Emit
+        a comment token whose data is the concatenation of all the characters
+        starting from and including the character that caused the state machine
+        to switch into the bogus comment state, up to and including the last
+        consumed character before the U+003E character, if any, or up to the
+        end of the file otherwise. (If the comment was started by the end of
+        the file (EOF), the token is empty.) */
+        $data = $this->characters('^>', $this->char);
+        $this->emitToken(
+            array(
+                'data' => $data,
+                'type' => self::COMMENT
+            )
+        );
+
+        $this->char += strlen($data);
+
+        /* Switch to the data state. */
+        $this->state = 'data';
+
+        /* If the end of the file was reached, reconsume the EOF character. */
+        if ($this->char === $this->EOF) {
+            $this->char = $this->EOF - 1;
+        }
+    }
+
+    private function markupDeclarationOpenState()
+    {
+        /* If the next two characters are both U+002D HYPHEN-MINUS (-)
+        characters, consume those two characters, create a comment token whose
+        data is the empty string, and switch to the comment state. */
+        if ($this->character($this->char + 1, 2) === '--') {
+            $this->char += 2;
+            $this->state = 'comment';
+            $this->token = array(
+                'data' => null,
+                'type' => self::COMMENT
+            );
+
+            /* Otherwise if the next seven chacacters are a case-insensitive match
+            for the word "DOCTYPE", then consume those characters and switch to the
+            DOCTYPE state. */
+        } elseif (strtolower($this->character($this->char + 1, 7)) === 'doctype') {
+            $this->char += 7;
+            $this->state = 'doctype';
+
+            /* Otherwise, is is a parse error. Switch to the bogus comment state.
+            The next character that is consumed, if any, is the first character
+            that will be in the comment. */
+        } else {
+            $this->char++;
+            $this->state = 'bogusComment';
+        }
+    }
+
+    private function commentState()
+    {
+        /* Consume the next input character: */
+        $this->char++;
+        $char = $this->char();
+
+        /* U+002D HYPHEN-MINUS (-) */
+        if ($char === '-') {
+            /* Switch to the comment dash state  */
+            $this->state = 'commentDash';
+
+            /* EOF */
+        } elseif ($this->char === $this->EOF) {
+            /* Parse error. Emit the comment token. Reconsume the EOF character
+            in the data state. */
+            $this->emitToken($this->token);
+            $this->char--;
+            $this->state = 'data';
+
+            /* Anything else */
+        } else {
+            /* Append the input character to the comment token's data. Stay in
+            the comment state. */
+            $this->token['data'] .= $char;
+        }
+    }
+
+    private function commentDashState()
+    {
+        /* Consume the next input character: */
+        $this->char++;
+        $char = $this->char();
+
+        /* U+002D HYPHEN-MINUS (-) */
+        if ($char === '-') {
+            /* Switch to the comment end state  */
+            $this->state = 'commentEnd';
+
+            /* EOF */
+        } elseif ($this->char === $this->EOF) {
+            /* Parse error. Emit the comment token. Reconsume the EOF character
+            in the data state. */
+            $this->emitToken($this->token);
+            $this->char--;
+            $this->state = 'data';
+
+            /* Anything else */
+        } else {
+            /* Append a U+002D HYPHEN-MINUS (-) character and the input
+            character to the comment token's data. Switch to the comment state. */
+            $this->token['data'] .= '-' . $char;
+            $this->state = 'comment';
+        }
+    }
+
+    private function commentEndState()
+    {
+        /* Consume the next input character: */
+        $this->char++;
+        $char = $this->char();
+
+        if ($char === '>') {
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } elseif ($char === '-') {
+            $this->token['data'] .= '-';
+
+        } elseif ($this->char === $this->EOF) {
+            $this->emitToken($this->token);
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            $this->token['data'] .= '--' . $char;
+            $this->state = 'comment';
+        }
+    }
+
+    private function doctypeState()
+    {
+        /* Consume the next input character: */
+        $this->char++;
+        $char = $this->char();
+
+        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            $this->state = 'beforeDoctypeName';
+
+        } else {
+            $this->char--;
+            $this->state = 'beforeDoctypeName';
+        }
+    }
+
+    private function beforeDoctypeNameState()
+    {
+        /* Consume the next input character: */
+        $this->char++;
+        $char = $this->char();
+
+        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            // Stay in the before DOCTYPE name state.
+
+        } elseif (preg_match('/^[a-z]$/', $char)) {
+            $this->token = array(
+                'name' => strtoupper($char),
+                'type' => self::DOCTYPE,
+                'error' => true
+            );
+
+            $this->state = 'doctypeName';
+
+        } elseif ($char === '>') {
+            $this->emitToken(
+                array(
+                    'name' => null,
+                    'type' => self::DOCTYPE,
+                    'error' => true
+                )
+            );
+
+            $this->state = 'data';
+
+        } elseif ($this->char === $this->EOF) {
+            $this->emitToken(
+                array(
+                    'name' => null,
+                    'type' => self::DOCTYPE,
+                    'error' => true
+                )
+            );
+
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            $this->token = array(
+                'name' => $char,
+                'type' => self::DOCTYPE,
+                'error' => true
+            );
+
+            $this->state = 'doctypeName';
+        }
+    }
+
+    private function doctypeNameState()
+    {
+        /* Consume the next input character: */
+        $this->char++;
+        $char = $this->char();
+
+        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            $this->state = 'AfterDoctypeName';
+
+        } elseif ($char === '>') {
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } elseif (preg_match('/^[a-z]$/', $char)) {
+            $this->token['name'] .= strtoupper($char);
+
+        } elseif ($this->char === $this->EOF) {
+            $this->emitToken($this->token);
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            $this->token['name'] .= $char;
+        }
+
+        $this->token['error'] = ($this->token['name'] === 'HTML')
+            ? false
+            : true;
+    }
+
+    private function afterDoctypeNameState()
+    {
+        /* Consume the next input character: */
+        $this->char++;
+        $char = $this->char();
+
+        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
+            // Stay in the DOCTYPE name state.
+
+        } elseif ($char === '>') {
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } elseif ($this->char === $this->EOF) {
+            $this->emitToken($this->token);
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            $this->token['error'] = true;
+            $this->state = 'bogusDoctype';
+        }
+    }
+
+    private function bogusDoctypeState()
+    {
+        /* Consume the next input character: */
+        $this->char++;
+        $char = $this->char();
+
+        if ($char === '>') {
+            $this->emitToken($this->token);
+            $this->state = 'data';
+
+        } elseif ($this->char === $this->EOF) {
+            $this->emitToken($this->token);
+            $this->char--;
+            $this->state = 'data';
+
+        } else {
+            // Stay in the bogus DOCTYPE state.
+        }
+    }
+
+    private function entity()
+    {
+        $start = $this->char;
+
+        // This section defines how to consume an entity. This definition is
+        // used when parsing entities in text and in attributes.
+
+        // The behaviour depends on the identity of the next character (the
+        // one immediately after the U+0026 AMPERSAND character):
+
+        switch ($this->character($this->char + 1)) {
+            // U+0023 NUMBER SIGN (#)
+            case '#':
+
+                // The behaviour further depends on the character after the
+                // U+0023 NUMBER SIGN:
+                switch ($this->character($this->char + 1)) {
+                    // U+0078 LATIN SMALL LETTER X
+                    // U+0058 LATIN CAPITAL LETTER X
+                    case 'x':
+                    case 'X':
+                        // Follow the steps below, but using the range of
+                        // characters U+0030 DIGIT ZERO through to U+0039 DIGIT
+                        // NINE, U+0061 LATIN SMALL LETTER A through to U+0066
+                        // LATIN SMALL LETTER F, and U+0041 LATIN CAPITAL LETTER
+                        // A, through to U+0046 LATIN CAPITAL LETTER F (in other
+                        // words, 0-9, A-F, a-f).
+                        $char = 1;
+                        $char_class = '0-9A-Fa-f';
+                        break;
+
+                    // Anything else
+                    default:
+                        // Follow the steps below, but using the range of
+                        // characters U+0030 DIGIT ZERO through to U+0039 DIGIT
+                        // NINE (i.e. just 0-9).
+                        $char = 0;
+                        $char_class = '0-9';
+                        break;
+                }
+
+                // Consume as many characters as match the range of characters
+                // given above.
+                $this->char++;
+                $e_name = $this->characters($char_class, $this->char + $char + 1);
+                $entity = $this->character($start, $this->char);
+                $cond = strlen($e_name) > 0;
+
+                // The rest of the parsing happens bellow.
+                break;
+
+            // Anything else
+            default:
+                // Consume the maximum number of characters possible, with the
+                // consumed characters case-sensitively matching one of the
+                // identifiers in the first column of the entities table.
+                $e_name = $this->characters('0-9A-Za-z;', $this->char + 1);
+                $len = strlen($e_name);
+
+                for ($c = 1; $c <= $len; $c++) {
+                    $id = substr($e_name, 0, $c);
+                    $this->char++;
+
+                    if (in_array($id, $this->entities)) {
+                        if ($e_name[$c - 1] !== ';') {
+                            if ($c < $len && $e_name[$c] == ';') {
+                                $this->char++; // consume extra semicolon
+                            }
+                        }
+                        $entity = $id;
+                        break;
+                    }
+                }
+
+                $cond = isset($entity);
+                // The rest of the parsing happens bellow.
+                break;
+        }
+
+        if (!$cond) {
+            // If no match can be made, then this is a parse error. No
+            // characters are consumed, and nothing is returned.
+            $this->char = $start;
+            return false;
+        }
+
+        // Return a character token for the character corresponding to the
+        // entity name (as given by the second column of the entities table).
+        return html_entity_decode('&' . $entity . ';', ENT_QUOTES, 'UTF-8');
+    }
+
+    private function emitToken($token)
+    {
+        $emit = $this->tree->emitToken($token);
+
+        if (is_int($emit)) {
+            $this->content_model = $emit;
+
+        } elseif ($token['type'] === self::ENDTAG) {
+            $this->content_model = self::PCDATA;
+        }
+    }
+
+    private function EOF()
+    {
+        $this->state = null;
+        $this->tree->emitToken(
+            array(
+                'type' => self::EOF
+            )
+        );
+    }
+}
+
+class HTML5TreeConstructer
+{
+    public $stack = array();
+
+    private $phase;
+    private $mode;
+    private $dom;
+    private $foster_parent = null;
+    private $a_formatting = array();
+
+    private $head_pointer = null;
+    private $form_pointer = null;
+
+    private $scoping = array('button', 'caption', 'html', 'marquee', 'object', 'table', 'td', 'th');
+    private $formatting = array(
+        'a',
+        'b',
+        'big',
+        'em',
+        'font',
+        'i',
+        'nobr',
+        's',
+        'small',
+        'strike',
+        'strong',
+        'tt',
+        'u'
+    );
+    private $special = array(
+        'address',
+        'area',
+        'base',
+        'basefont',
+        'bgsound',
+        'blockquote',
+        'body',
+        'br',
+        'center',
+        'col',
+        'colgroup',
+        'dd',
+        'dir',
+        'div',
+        'dl',
+        'dt',
+        'embed',
+        'fieldset',
+        'form',
+        'frame',
+        'frameset',
+        'h1',
+        'h2',
+        'h3',
+        'h4',
+        'h5',
+        'h6',
+        'head',
+        'hr',
+        'iframe',
+        'image',
+        'img',
+        'input',
+        'isindex',
+        'li',
+        'link',
+        'listing',
+        'menu',
+        'meta',
+        'noembed',
+        'noframes',
+        'noscript',
+        'ol',
+        'optgroup',
+        'option',
+        'p',
+        'param',
+        'plaintext',
+        'pre',
+        'script',
+        'select',
+        'spacer',
+        'style',
+        'tbody',
+        'textarea',
+        'tfoot',
+        'thead',
+        'title',
+        'tr',
+        'ul',
+        'wbr'
+    );
+
+    // The different phases.
+    const INIT_PHASE = 0;
+    const ROOT_PHASE = 1;
+    const MAIN_PHASE = 2;
+    const END_PHASE = 3;
+
+    // The different insertion modes for the main phase.
+    const BEFOR_HEAD = 0;
+    const IN_HEAD = 1;
+    const AFTER_HEAD = 2;
+    const IN_BODY = 3;
+    const IN_TABLE = 4;
+    const IN_CAPTION = 5;
+    const IN_CGROUP = 6;
+    const IN_TBODY = 7;
+    const IN_ROW = 8;
+    const IN_CELL = 9;
+    const IN_SELECT = 10;
+    const AFTER_BODY = 11;
+    const IN_FRAME = 12;
+    const AFTR_FRAME = 13;
+
+    // The different types of elements.
+    const SPECIAL = 0;
+    const SCOPING = 1;
+    const FORMATTING = 2;
+    const PHRASING = 3;
+
+    const MARKER = 0;
+
+    public function __construct()
+    {
+        $this->phase = self::INIT_PHASE;
+        $this->mode = self::BEFOR_HEAD;
+        $this->dom = new DOMDocument;
+
+        $this->dom->encoding = 'UTF-8';
+        $this->dom->preserveWhiteSpace = true;
+        $this->dom->substituteEntities = true;
+        $this->dom->strictErrorChecking = false;
+    }
+
+    // Process tag tokens
+    public function emitToken($token)
+    {
+        switch ($this->phase) {
+            case self::INIT_PHASE:
+                return $this->initPhase($token);
+                break;
+            case self::ROOT_PHASE:
+                return $this->rootElementPhase($token);
+                break;
+            case self::MAIN_PHASE:
+                return $this->mainPhase($token);
+                break;
+            case self::END_PHASE :
+                return $this->trailingEndPhase($token);
+                break;
+        }
+    }
+
+    private function initPhase($token)
+    {
+        /* Initially, the tree construction stage must handle each token
+        emitted from the tokenisation stage as follows: */
+
+        /* A DOCTYPE token that is marked as being in error
+        A comment token
+        A start tag token
+        An end tag token
+        A character token that is not one of one of U+0009 CHARACTER TABULATION,
+            U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+            or U+0020 SPACE
+        An end-of-file token */
+        if ((isset($token['error']) && $token['error']) ||
+            $token['type'] === HTML5::COMMENT ||
+            $token['type'] === HTML5::STARTTAG ||
+            $token['type'] === HTML5::ENDTAG ||
+            $token['type'] === HTML5::EOF ||
+            ($token['type'] === HTML5::CHARACTR && isset($token['data']) &&
+                !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']))
+        ) {
+            /* This specification does not define how to handle this case. In
+            particular, user agents may ignore the entirety of this specification
+            altogether for such documents, and instead invoke special parse modes
+            with a greater emphasis on backwards compatibility. */
+
+            $this->phase = self::ROOT_PHASE;
+            return $this->rootElementPhase($token);
+
+            /* A DOCTYPE token marked as being correct */
+        } elseif (isset($token['error']) && !$token['error']) {
+            /* Append a DocumentType node to the Document  node, with the name
+            attribute set to the name given in the DOCTYPE token (which will be
+            "HTML"), and the other attributes specific to DocumentType objects
+            set to null, empty lists, or the empty string as appropriate. */
+            $doctype = new DOMDocumentType(null, null, 'HTML');
+
+            /* Then, switch to the root element phase of the tree construction
+            stage. */
+            $this->phase = self::ROOT_PHASE;
+
+            /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+            U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+            or U+0020 SPACE */
+        } elseif (isset($token['data']) && preg_match(
+                '/^[\t\n\x0b\x0c ]+$/',
+                $token['data']
+            )
+        ) {
+            /* Append that character  to the Document node. */
+            $text = $this->dom->createTextNode($token['data']);
+            $this->dom->appendChild($text);
+        }
+    }
+
+    private function rootElementPhase($token)
+    {
+        /* After the initial phase, as each token is emitted from the tokenisation
+        stage, it must be processed as described in this section. */
+
+        /* A DOCTYPE token */
+        if ($token['type'] === HTML5::DOCTYPE) {
+            // Parse error. Ignore the token.
+
+            /* A comment token */
+        } elseif ($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the Document object with the data
+            attribute set to the data given in the comment token. */
+            $comment = $this->dom->createComment($token['data']);
+            $this->dom->appendChild($comment);
+
+            /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+            U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+            or U+0020 SPACE */
+        } elseif ($token['type'] === HTML5::CHARACTR &&
+            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+        ) {
+            /* Append that character  to the Document node. */
+            $text = $this->dom->createTextNode($token['data']);
+            $this->dom->appendChild($text);
+
+            /* A character token that is not one of U+0009 CHARACTER TABULATION,
+                U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED
+                (FF), or U+0020 SPACE
+            A start tag token
+            An end tag token
+            An end-of-file token */
+        } elseif (($token['type'] === HTML5::CHARACTR &&
+                !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) ||
+            $token['type'] === HTML5::STARTTAG ||
+            $token['type'] === HTML5::ENDTAG ||
+            $token['type'] === HTML5::EOF
+        ) {
+            /* Create an HTMLElement node with the tag name html, in the HTML
+            namespace. Append it to the Document object. Switch to the main
+            phase and reprocess the current token. */
+            $html = $this->dom->createElement('html');
+            $this->dom->appendChild($html);
+            $this->stack[] = $html;
+
+            $this->phase = self::MAIN_PHASE;
+            return $this->mainPhase($token);
+        }
+    }
+
+    private function mainPhase($token)
+    {
+        /* Tokens in the main phase must be handled as follows: */
+
+        /* A DOCTYPE token */
+        if ($token['type'] === HTML5::DOCTYPE) {
+            // Parse error. Ignore the token.
+
+            /* A start tag token with the tag name "html" */
+        } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'html') {
+            /* If this start tag token was not the first start tag token, then
+            it is a parse error. */
+
+            /* For each attribute on the token, check to see if the attribute
+            is already present on the top element of the stack of open elements.
+            If it is not, add the attribute and its corresponding value to that
+            element. */
+            foreach ($token['attr'] as $attr) {
+                if (!$this->stack[0]->hasAttribute($attr['name'])) {
+                    $this->stack[0]->setAttribute($attr['name'], $attr['value']);
+                }
+            }
+
+            /* An end-of-file token */
+        } elseif ($token['type'] === HTML5::EOF) {
+            /* Generate implied end tags. */
+            $this->generateImpliedEndTags();
+
+            /* Anything else. */
+        } else {
+            /* Depends on the insertion mode: */
+            switch ($this->mode) {
+                case self::BEFOR_HEAD:
+                    return $this->beforeHead($token);
+                    break;
+                case self::IN_HEAD:
+                    return $this->inHead($token);
+                    break;
+                case self::AFTER_HEAD:
+                    return $this->afterHead($token);
+                    break;
+                case self::IN_BODY:
+                    return $this->inBody($token);
+                    break;
+                case self::IN_TABLE:
+                    return $this->inTable($token);
+                    break;
+                case self::IN_CAPTION:
+                    return $this->inCaption($token);
+                    break;
+                case self::IN_CGROUP:
+                    return $this->inColumnGroup($token);
+                    break;
+                case self::IN_TBODY:
+                    return $this->inTableBody($token);
+                    break;
+                case self::IN_ROW:
+                    return $this->inRow($token);
+                    break;
+                case self::IN_CELL:
+                    return $this->inCell($token);
+                    break;
+                case self::IN_SELECT:
+                    return $this->inSelect($token);
+                    break;
+                case self::AFTER_BODY:
+                    return $this->afterBody($token);
+                    break;
+                case self::IN_FRAME:
+                    return $this->inFrameset($token);
+                    break;
+                case self::AFTR_FRAME:
+                    return $this->afterFrameset($token);
+                    break;
+                case self::END_PHASE:
+                    return $this->trailingEndPhase($token);
+                    break;
+            }
+        }
+    }
+
+    private function beforeHead($token)
+    {
+        /* Handle the token as follows: */
+
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        or U+0020 SPACE */
+        if ($token['type'] === HTML5::CHARACTR &&
+            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+        ) {
+            /* Append the character to the current node. */
+            $this->insertText($token['data']);
+
+            /* A comment token */
+        } elseif ($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the current node with the data attribute
+            set to the data given in the comment token. */
+            $this->insertComment($token['data']);
+
+            /* A start tag token with the tag name "head" */
+        } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') {
+            /* Create an element for the token, append the new element to the
+            current node and push it onto the stack of open elements. */
+            $element = $this->insertElement($token);
+
+            /* Set the head element pointer to this new element node. */
+            $this->head_pointer = $element;
+
+            /* Change the insertion mode to "in head". */
+            $this->mode = self::IN_HEAD;
+
+            /* A start tag token whose tag name is one of: "base", "link", "meta",
+            "script", "style", "title". Or an end tag with the tag name "html".
+            Or a character token that is not one of U+0009 CHARACTER TABULATION,
+            U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+            or U+0020 SPACE. Or any other start tag token */
+        } elseif ($token['type'] === HTML5::STARTTAG ||
+            ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') ||
+            ($token['type'] === HTML5::CHARACTR && !preg_match(
+                    '/^[\t\n\x0b\x0c ]$/',
+                    $token['data']
+                ))
+        ) {
+            /* Act as if a start tag token with the tag name "head" and no
+            attributes had been seen, then reprocess the current token. */
+            $this->beforeHead(
+                array(
+                    'name' => 'head',
+                    'type' => HTML5::STARTTAG,
+                    'attr' => array()
+                )
+            );
+
+            return $this->inHead($token);
+
+            /* Any other end tag */
+        } elseif ($token['type'] === HTML5::ENDTAG) {
+            /* Parse error. Ignore the token. */
+        }
+    }
+
+    private function inHead($token)
+    {
+        /* Handle the token as follows: */
+
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        or U+0020 SPACE.
+
+        THIS DIFFERS FROM THE SPEC: If the current node is either a title, style
+        or script element, append the character to the current node regardless
+        of its content. */
+        if (($token['type'] === HTML5::CHARACTR &&
+                preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || (
+                $token['type'] === HTML5::CHARACTR && in_array(
+                    end($this->stack)->nodeName,
+                    array('title', 'style', 'script')
+                ))
+        ) {
+            /* Append the character to the current node. */
+            $this->insertText($token['data']);
+
+            /* A comment token */
+        } elseif ($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the current node with the data attribute
+            set to the data given in the comment token. */
+            $this->insertComment($token['data']);
+
+        } elseif ($token['type'] === HTML5::ENDTAG &&
+            in_array($token['name'], array('title', 'style', 'script'))
+        ) {
+            array_pop($this->stack);
+            return HTML5::PCDATA;
+
+            /* A start tag with the tag name "title" */
+        } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'title') {
+            /* Create an element for the token and append the new element to the
+            node pointed to by the head element pointer, or, if that is null
+            (innerHTML case), to the current node. */
+            if ($this->head_pointer !== null) {
+                $element = $this->insertElement($token, false);
+                $this->head_pointer->appendChild($element);
+
+            } else {
+                $element = $this->insertElement($token);
+            }
+
+            /* Switch the tokeniser's content model flag  to the RCDATA state. */
+            return HTML5::RCDATA;
+
+            /* A start tag with the tag name "style" */
+        } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'style') {
+            /* Create an element for the token and append the new element to the
+            node pointed to by the head element pointer, or, if that is null
+            (innerHTML case), to the current node. */
+            if ($this->head_pointer !== null) {
+                $element = $this->insertElement($token, false);
+                $this->head_pointer->appendChild($element);
+
+            } else {
+                $this->insertElement($token);
+            }
+
+            /* Switch the tokeniser's content model flag  to the CDATA state. */
+            return HTML5::CDATA;
+
+            /* A start tag with the tag name "script" */
+        } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'script') {
+            /* Create an element for the token. */
+            $element = $this->insertElement($token, false);
+            $this->head_pointer->appendChild($element);
+
+            /* Switch the tokeniser's content model flag  to the CDATA state. */
+            return HTML5::CDATA;
+
+            /* A start tag with the tag name "base", "link", or "meta" */
+        } elseif ($token['type'] === HTML5::STARTTAG && in_array(
+                $token['name'],
+                array('base', 'link', 'meta')
+            )
+        ) {
+            /* Create an element for the token and append the new element to the
+            node pointed to by the head element pointer, or, if that is null
+            (innerHTML case), to the current node. */
+            if ($this->head_pointer !== null) {
+                $element = $this->insertElement($token, false);
+                $this->head_pointer->appendChild($element);
+                array_pop($this->stack);
+
+            } else {
+                $this->insertElement($token);
+            }
+
+            /* An end tag with the tag name "head" */
+        } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'head') {
+            /* If the current node is a head element, pop the current node off
+            the stack of open elements. */
+            if ($this->head_pointer->isSameNode(end($this->stack))) {
+                array_pop($this->stack);
+
+                /* Otherwise, this is a parse error. */
+            } else {
+                // k
+            }
+
+            /* Change the insertion mode to "after head". */
+            $this->mode = self::AFTER_HEAD;
+
+            /* A start tag with the tag name "head" or an end tag except "html". */
+        } elseif (($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') ||
+            ($token['type'] === HTML5::ENDTAG && $token['name'] !== 'html')
+        ) {
+            // Parse error. Ignore the token.
+
+            /* Anything else */
+        } else {
+            /* If the current node is a head element, act as if an end tag
+            token with the tag name "head" had been seen. */
+            if ($this->head_pointer->isSameNode(end($this->stack))) {
+                $this->inHead(
+                    array(
+                        'name' => 'head',
+                        'type' => HTML5::ENDTAG
+                    )
+                );
+
+                /* Otherwise, change the insertion mode to "after head". */
+            } else {
+                $this->mode = self::AFTER_HEAD;
+            }
+
+            /* Then, reprocess the current token. */
+            return $this->afterHead($token);
+        }
+    }
+
+    private function afterHead($token)
+    {
+        /* Handle the token as follows: */
+
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        or U+0020 SPACE */
+        if ($token['type'] === HTML5::CHARACTR &&
+            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+        ) {
+            /* Append the character to the current node. */
+            $this->insertText($token['data']);
+
+            /* A comment token */
+        } elseif ($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the current node with the data attribute
+            set to the data given in the comment token. */
+            $this->insertComment($token['data']);
+
+            /* A start tag token with the tag name "body" */
+        } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'body') {
+            /* Insert a body element for the token. */
+            $this->insertElement($token);
+
+            /* Change the insertion mode to "in body". */
+            $this->mode = self::IN_BODY;
+
+            /* A start tag token with the tag name "frameset" */
+        } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'frameset') {
+            /* Insert a frameset element for the token. */
+            $this->insertElement($token);
+
+            /* Change the insertion mode to "in frameset". */
+            $this->mode = self::IN_FRAME;
+
+            /* A start tag token whose tag name is one of: "base", "link", "meta",
+            "script", "style", "title" */
+        } elseif ($token['type'] === HTML5::STARTTAG && in_array(
+                $token['name'],
+                array('base', 'link', 'meta', 'script', 'style', 'title')
+            )
+        ) {
+            /* Parse error. Switch the insertion mode back to "in head" and
+            reprocess the token. */
+            $this->mode = self::IN_HEAD;
+            return $this->inHead($token);
+
+            /* Anything else */
+        } else {
+            /* Act as if a start tag token with the tag name "body" and no
+            attributes had been seen, and then reprocess the current token. */
+            $this->afterHead(
+                array(
+                    'name' => 'body',
+                    'type' => HTML5::STARTTAG,
+                    'attr' => array()
+                )
+            );
+
+            return $this->inBody($token);
+        }
+    }
+
+    private function inBody($token)
+    {
+        /* Handle the token as follows: */
+
+        switch ($token['type']) {
+            /* A character token */
+            case HTML5::CHARACTR:
+                /* Reconstruct the active formatting elements, if any. */
+                $this->reconstructActiveFormattingElements();
+
+                /* Append the token's character to the current node. */
+                $this->insertText($token['data']);
+                break;
+
+            /* A comment token */
+            case HTML5::COMMENT:
+                /* Append a Comment node to the current node with the data
+                attribute set to the data given in the comment token. */
+                $this->insertComment($token['data']);
+                break;
+
+            case HTML5::STARTTAG:
+                switch ($token['name']) {
+                    /* A start tag token whose tag name is one of: "script",
+                    "style" */
+                    case 'script':
+                    case 'style':
+                        /* Process the token as if the insertion mode had been "in
+                        head". */
+                        return $this->inHead($token);
+                        break;
+
+                    /* A start tag token whose tag name is one of: "base", "link",
+                    "meta", "title" */
+                    case 'base':
+                    case 'link':
+                    case 'meta':
+                    case 'title':
+                        /* Parse error. Process the token as if the insertion mode
+                        had    been "in head". */
+                        return $this->inHead($token);
+                        break;
+
+                    /* A start tag token with the tag name "body" */
+                    case 'body':
+                        /* Parse error. If the second element on the stack of open
+                        elements is not a body element, or, if the stack of open
+                        elements has only one node on it, then ignore the token.
+                        (innerHTML case) */
+                        if (count($this->stack) === 1 || $this->stack[1]->nodeName !== 'body') {
+                            // Ignore
+
+                            /* Otherwise, for each attribute on the token, check to see
+                            if the attribute is already present on the body element (the
+                            second element)    on the stack of open elements. If it is not,
+                            add the attribute and its corresponding value to that
+                            element. */
+                        } else {
+                            foreach ($token['attr'] as $attr) {
+                                if (!$this->stack[1]->hasAttribute($attr['name'])) {
+                                    $this->stack[1]->setAttribute($attr['name'], $attr['value']);
+                                }
+                            }
+                        }
+                        break;
+
+                    /* A start tag whose tag name is one of: "address",
+                    "blockquote", "center", "dir", "div", "dl", "fieldset",
+                    "listing", "menu", "ol", "p", "ul" */
+                    case 'address':
+                    case 'blockquote':
+                    case 'center':
+                    case 'dir':
+                    case 'div':
+                    case 'dl':
+                    case 'fieldset':
+                    case 'listing':
+                    case 'menu':
+                    case 'ol':
+                    case 'p':
+                    case 'ul':
+                        /* If the stack of open elements has a p element in scope,
+                        then act as if an end tag with the tag name p had been
+                        seen. */
+                        if ($this->elementInScope('p')) {
+                            $this->emitToken(
+                                array(
+                                    'name' => 'p',
+                                    'type' => HTML5::ENDTAG
+                                )
+                            );
+                        }
+
+                        /* Insert an HTML element for the token. */
+                        $this->insertElement($token);
+                        break;
+
+                    /* A start tag whose tag name is "form" */
+                    case 'form':
+                        /* If the form element pointer is not null, ignore the
+                        token with a parse error. */
+                        if ($this->form_pointer !== null) {
+                            // Ignore.
+
+                            /* Otherwise: */
+                        } else {
+                            /* If the stack of open elements has a p element in
+                            scope, then act as if an end tag with the tag name p
+                            had been seen. */
+                            if ($this->elementInScope('p')) {
+                                $this->emitToken(
+                                    array(
+                                        'name' => 'p',
+                                        'type' => HTML5::ENDTAG
+                                    )
+                                );
+                            }
+
+                            /* Insert an HTML element for the token, and set the
+                            form element pointer to point to the element created. */
+                            $element = $this->insertElement($token);
+                            $this->form_pointer = $element;
+                        }
+                        break;
+
+                    /* A start tag whose tag name is "li", "dd" or "dt" */
+                    case 'li':
+                    case 'dd':
+                    case 'dt':
+                        /* If the stack of open elements has a p  element in scope,
+                        then act as if an end tag with the tag name p had been
+                        seen. */
+                        if ($this->elementInScope('p')) {
+                            $this->emitToken(
+                                array(
+                                    'name' => 'p',
+                                    'type' => HTML5::ENDTAG
+                                )
+                            );
+                        }
+
+                        $stack_length = count($this->stack) - 1;
+
+                        for ($n = $stack_length; 0 <= $n; $n--) {
+                            /* 1. Initialise node to be the current node (the
+                            bottommost node of the stack). */
+                            $stop = false;
+                            $node = $this->stack[$n];
+                            $cat = $this->getElementCategory($node->tagName);
+
+                            /* 2. If node is an li, dd or dt element, then pop all
+                            the    nodes from the current node up to node, including
+                            node, then stop this algorithm. */
+                            if ($token['name'] === $node->tagName || ($token['name'] !== 'li'
+                                    && ($node->tagName === 'dd' || $node->tagName === 'dt'))
+                            ) {
+                                for ($x = $stack_length; $x >= $n; $x--) {
+                                    array_pop($this->stack);
+                                }
+
+                                break;
+                            }
+
+                            /* 3. If node is not in the formatting category, and is
+                            not    in the phrasing category, and is not an address or
+                            div element, then stop this algorithm. */
+                            if ($cat !== self::FORMATTING && $cat !== self::PHRASING &&
+                                $node->tagName !== 'address' && $node->tagName !== 'div'
+                            ) {
+                                break;
+                            }
+                        }
+
+                        /* Finally, insert an HTML element with the same tag
+                        name as the    token's. */
+                        $this->insertElement($token);
+                        break;
+
+                    /* A start tag token whose tag name is "plaintext" */
+                    case 'plaintext':
+                        /* If the stack of open elements has a p  element in scope,
+                        then act as if an end tag with the tag name p had been
+                        seen. */
+                        if ($this->elementInScope('p')) {
+                            $this->emitToken(
+                                array(
+                                    'name' => 'p',
+                                    'type' => HTML5::ENDTAG
+                                )
+                            );
+                        }
+
+                        /* Insert an HTML element for the token. */
+                        $this->insertElement($token);
+
+                        return HTML5::PLAINTEXT;
+                        break;
+
+                    /* A start tag whose tag name is one of: "h1", "h2", "h3", "h4",
+                    "h5", "h6" */
+                    case 'h1':
+                    case 'h2':
+                    case 'h3':
+                    case 'h4':
+                    case 'h5':
+                    case 'h6':
+                        /* If the stack of open elements has a p  element in scope,
+                        then act as if an end tag with the tag name p had been seen. */
+                        if ($this->elementInScope('p')) {
+                            $this->emitToken(
+                                array(
+                                    'name' => 'p',
+                                    'type' => HTML5::ENDTAG
+                                )
+                            );
+                        }
+
+                        /* If the stack of open elements has in scope an element whose
+                        tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then
+                        this is a parse error; pop elements from the stack until an
+                        element with one of those tag names has been popped from the
+                        stack. */
+                        while ($this->elementInScope(array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))) {
+                            array_pop($this->stack);
+                        }
+
+                        /* Insert an HTML element for the token. */
+                        $this->insertElement($token);
+                        break;
+
+                    /* A start tag whose tag name is "a" */
+                    case 'a':
+                        /* If the list of active formatting elements contains
+                        an element whose tag name is "a" between the end of the
+                        list and the last marker on the list (or the start of
+                        the list if there is no marker on the list), then this
+                        is a parse error; act as if an end tag with the tag name
+                        "a" had been seen, then remove that element from the list
+                        of active formatting elements and the stack of open
+                        elements if the end tag didn't already remove it (it
+                        might not have if the element is not in table scope). */
+                        $leng = count($this->a_formatting);
+
+                        for ($n = $leng - 1; $n >= 0; $n--) {
+                            if ($this->a_formatting[$n] === self::MARKER) {
+                                break;
+
+                            } elseif ($this->a_formatting[$n]->nodeName === 'a') {
+                                $this->emitToken(
+                                    array(
+                                        'name' => 'a',
+                                        'type' => HTML5::ENDTAG
+                                    )
+                                );
+                                break;
+                            }
+                        }
+
+                        /* Reconstruct the active formatting elements, if any. */
+                        $this->reconstructActiveFormattingElements();
+
+                        /* Insert an HTML element for the token. */
+                        $el = $this->insertElement($token);
+
+                        /* Add that element to the list of active formatting
+                        elements. */
+                        $this->a_formatting[] = $el;
+                        break;
+
+                    /* A start tag whose tag name is one of: "b", "big", "em", "font",
+                    "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */
+                    case 'b':
+                    case 'big':
+                    case 'em':
+                    case 'font':
+                    case 'i':
+                    case 'nobr':
+                    case 's':
+                    case 'small':
+                    case 'strike':
+                    case 'strong':
+                    case 'tt':
+                    case 'u':
+                        /* Reconstruct the active formatting elements, if any. */
+                        $this->reconstructActiveFormattingElements();
+
+                        /* Insert an HTML element for the token. */
+                        $el = $this->insertElement($token);
+
+                        /* Add that element to the list of active formatting
+                        elements. */
+                        $this->a_formatting[] = $el;
+                        break;
+
+                    /* A start tag token whose tag name is "button" */
+                    case 'button':
+                        /* If the stack of open elements has a button element in scope,
+                        then this is a parse error; act as if an end tag with the tag
+                        name "button" had been seen, then reprocess the token. (We don't
+                        do that. Unnecessary.) */
+                        if ($this->elementInScope('button')) {
+                            $this->inBody(
+                                array(
+                                    'name' => 'button',
+                                    'type' => HTML5::ENDTAG
+                                )
+                            );
+                        }
+
+                        /* Reconstruct the active formatting elements, if any. */
+                        $this->reconstructActiveFormattingElements();
+
+                        /* Insert an HTML element for the token. */
+                        $this->insertElement($token);
+
+                        /* Insert a marker at the end of the list of active
+                        formatting elements. */
+                        $this->a_formatting[] = self::MARKER;
+                        break;
+
+                    /* A start tag token whose tag name is one of: "marquee", "object" */
+                    case 'marquee':
+                    case 'object':
+                        /* Reconstruct the active formatting elements, if any. */
+                        $this->reconstructActiveFormattingElements();
+
+                        /* Insert an HTML element for the token. */
+                        $this->insertElement($token);
+
+                        /* Insert a marker at the end of the list of active
+                        formatting elements. */
+                        $this->a_formatting[] = self::MARKER;
+                        break;
+
+                    /* A start tag token whose tag name is "xmp" */
+                    case 'xmp':
+                        /* Reconstruct the active formatting elements, if any. */
+                        $this->reconstructActiveFormattingElements();
+
+                        /* Insert an HTML element for the token. */
+                        $this->insertElement($token);
+
+                        /* Switch the content model flag to the CDATA state. */
+                        return HTML5::CDATA;
+                        break;
+
+                    /* A start tag whose tag name is "table" */
+                    case 'table':
+                        /* If the stack of open elements has a p element in scope,
+                        then act as if an end tag with the tag name p had been seen. */
+                        if ($this->elementInScope('p')) {
+                            $this->emitToken(
+                                array(
+                                    'name' => 'p',
+                                    'type' => HTML5::ENDTAG
+                                )
+                            );
+                        }
+
+                        /* Insert an HTML element for the token. */
+                        $this->insertElement($token);
+
+                        /* Change the insertion mode to "in table". */
+                        $this->mode = self::IN_TABLE;
+                        break;
+
+                    /* A start tag whose tag name is one of: "area", "basefont",
+                    "bgsound", "br", "embed", "img", "param", "spacer", "wbr" */
+                    case 'area':
+                    case 'basefont':
+                    case 'bgsound':
+                    case 'br':
+                    case 'embed':
+                    case 'img':
+                    case 'param':
+                    case 'spacer':
+                    case 'wbr':
+                        /* Reconstruct the active formatting elements, if any. */
+                        $this->reconstructActiveFormattingElements();
+
+                        /* Insert an HTML element for the token. */
+                        $this->insertElement($token);
+
+                        /* Immediately pop the current node off the stack of open elements. */
+                        array_pop($this->stack);
+                        break;
+
+                    /* A start tag whose tag name is "hr" */
+                    case 'hr':
+                        /* If the stack of open elements has a p element in scope,
+                        then act as if an end tag with the tag name p had been seen. */
+                        if ($this->elementInScope('p')) {
+                            $this->emitToken(
+                                array(
+                                    'name' => 'p',
+                                    'type' => HTML5::ENDTAG
+                                )
+                            );
+                        }
+
+                        /* Insert an HTML element for the token. */
+                        $this->insertElement($token);
+
+                        /* Immediately pop the current node off the stack of open elements. */
+                        array_pop($this->stack);
+                        break;
+
+                    /* A start tag whose tag name is "image" */
+                    case 'image':
+                        /* Parse error. Change the token's tag name to "img" and
+                        reprocess it. (Don't ask.) */
+                        $token['name'] = 'img';
+                        return $this->inBody($token);
+                        break;
+
+                    /* A start tag whose tag name is "input" */
+                    case 'input':
+                        /* Reconstruct the active formatting elements, if any. */
+                        $this->reconstructActiveFormattingElements();
+
+                        /* Insert an input element for the token. */
+                        $element = $this->insertElement($token, false);
+
+                        /* If the form element pointer is not null, then associate the
+                        input element with the form element pointed to by the form
+                        element pointer. */
+                        $this->form_pointer !== null
+                            ? $this->form_pointer->appendChild($element)
+                            : end($this->stack)->appendChild($element);
+
+                        /* Pop that input element off the stack of open elements. */
+                        array_pop($this->stack);
+                        break;
+
+                    /* A start tag whose tag name is "isindex" */
+                    case 'isindex':
+                        /* Parse error. */
+                        // w/e
+
+                        /* If the form element pointer is not null,
+                        then ignore the token. */
+                        if ($this->form_pointer === null) {
+                            /* Act as if a start tag token with the tag name "form" had
+                            been seen. */
+                            $this->inBody(
+                                array(
+                                    'name' => 'body',
+                                    'type' => HTML5::STARTTAG,
+                                    'attr' => array()
+                                )
+                            );
+
+                            /* Act as if a start tag token with the tag name "hr" had
+                            been seen. */
+                            $this->inBody(
+                                array(
+                                    'name' => 'hr',
+                                    'type' => HTML5::STARTTAG,
+                                    'attr' => array()
+                                )
+                            );
+
+                            /* Act as if a start tag token with the tag name "p" had
+                            been seen. */
+                            $this->inBody(
+                                array(
+                                    'name' => 'p',
+                                    'type' => HTML5::STARTTAG,
+                                    'attr' => array()
+                                )
+                            );
+
+                            /* Act as if a start tag token with the tag name "label"
+                            had been seen. */
+                            $this->inBody(
+                                array(
+                                    'name' => 'label',
+                                    'type' => HTML5::STARTTAG,
+                                    'attr' => array()
+                                )
+                            );
+
+                            /* Act as if a stream of character tokens had been seen. */
+                            $this->insertText(
+                                'This is a searchable index. ' .
+                                'Insert your search keywords here: '
+                            );
+
+                            /* Act as if a start tag token with the tag name "input"
+                            had been seen, with all the attributes from the "isindex"
+                            token, except with the "name" attribute set to the value
+                            "isindex" (ignoring any explicit "name" attribute). */
+                            $attr = $token['attr'];
+                            $attr[] = array('name' => 'name', 'value' => 'isindex');
+
+                            $this->inBody(
+                                array(
+                                    'name' => 'input',
+                                    'type' => HTML5::STARTTAG,
+                                    'attr' => $attr
+                                )
+                            );
+
+                            /* Act as if a stream of character tokens had been seen
+                            (see below for what they should say). */
+                            $this->insertText(
+                                'This is a searchable index. ' .
+                                'Insert your search keywords here: '
+                            );
+
+                            /* Act as if an end tag token with the tag name "label"
+                            had been seen. */
+                            $this->inBody(
+                                array(
+                                    'name' => 'label',
+                                    'type' => HTML5::ENDTAG
+                                )
+                            );
+
+                            /* Act as if an end tag token with the tag name "p" had
+                            been seen. */
+                            $this->inBody(
+                                array(
+                                    'name' => 'p',
+                                    'type' => HTML5::ENDTAG
+                                )
+                            );
+
+                            /* Act as if a start tag token with the tag name "hr" had
+                            been seen. */
+                            $this->inBody(
+                                array(
+                                    'name' => 'hr',
+                                    'type' => HTML5::ENDTAG
+                                )
+                            );
+
+                            /* Act as if an end tag token with the tag name "form" had
+                            been seen. */
+                            $this->inBody(
+                                array(
+                                    'name' => 'form',
+                                    'type' => HTML5::ENDTAG
+                                )
+                            );
+                        }
+                        break;
+
+                    /* A start tag whose tag name is "textarea" */
+                    case 'textarea':
+                        $this->insertElement($token);
+
+                        /* Switch the tokeniser's content model flag to the
+                        RCDATA state. */
+                        return HTML5::RCDATA;
+                        break;
+
+                    /* A start tag whose tag name is one of: "iframe", "noembed",
+                    "noframes" */
+                    case 'iframe':
+                    case 'noembed':
+                    case 'noframes':
+                        $this->insertElement($token);
+
+                        /* Switch the tokeniser's content model flag to the CDATA state. */
+                        return HTML5::CDATA;
+                        break;
+
+                    /* A start tag whose tag name is "select" */
+                    case 'select':
+                        /* Reconstruct the active formatting elements, if any. */
+                        $this->reconstructActiveFormattingElements();
+
+                        /* Insert an HTML element for the token. */
+                        $this->insertElement($token);
+
+                        /* Change the insertion mode to "in select". */
+                        $this->mode = self::IN_SELECT;
+                        break;
+
+                    /* A start or end tag whose tag name is one of: "caption", "col",
+                    "colgroup", "frame", "frameset", "head", "option", "optgroup",
+                    "tbody", "td", "tfoot", "th", "thead", "tr". */
+                    case 'caption':
+                    case 'col':
+                    case 'colgroup':
+                    case 'frame':
+                    case 'frameset':
+                    case 'head':
+                    case 'option':
+                    case 'optgroup':
+                    case 'tbody':
+                    case 'td':
+                    case 'tfoot':
+                    case 'th':
+                    case 'thead':
+                    case 'tr':
+                        // Parse error. Ignore the token.
+                        break;
+
+                    /* A start or end tag whose tag name is one of: "event-source",
+                    "section", "nav", "article", "aside", "header", "footer",
+                    "datagrid", "command" */
+                    case 'event-source':
+                    case 'section':
+                    case 'nav':
+                    case 'article':
+                    case 'aside':
+                    case 'header':
+                    case 'footer':
+                    case 'datagrid':
+                    case 'command':
+                        // Work in progress!
+                        break;
+
+                    /* A start tag token not covered by the previous entries */
+                    default:
+                        /* Reconstruct the active formatting elements, if any. */
+                        $this->reconstructActiveFormattingElements();
+
+                        $this->insertElement($token, true, true);
+                        break;
+                }
+                break;
+
+            case HTML5::ENDTAG:
+                switch ($token['name']) {
+                    /* An end tag with the tag name "body" */
+                    case 'body':
+                        /* If the second element in the stack of open elements is
+                        not a body element, this is a parse error. Ignore the token.
+                        (innerHTML case) */
+                        if (count($this->stack) < 2 || $this->stack[1]->nodeName !== 'body') {
+                            // Ignore.
+
+                            /* If the current node is not the body element, then this
+                            is a parse error. */
+                        } elseif (end($this->stack)->nodeName !== 'body') {
+                            // Parse error.
+                        }
+
+                        /* Change the insertion mode to "after body". */
+                        $this->mode = self::AFTER_BODY;
+                        break;
+
+                    /* An end tag with the tag name "html" */
+                    case 'html':
+                        /* Act as if an end tag with tag name "body" had been seen,
+                        then, if that token wasn't ignored, reprocess the current
+                        token. */
+                        $this->inBody(
+                            array(
+                                'name' => 'body',
+                                'type' => HTML5::ENDTAG
+                            )
+                        );
+
+                        return $this->afterBody($token);
+                        break;
+
+                    /* An end tag whose tag name is one of: "address", "blockquote",
+                    "center", "dir", "div", "dl", "fieldset", "listing", "menu",
+                    "ol", "pre", "ul" */
+                    case 'address':
+                    case 'blockquote':
+                    case 'center':
+                    case 'dir':
+                    case 'div':
+                    case 'dl':
+                    case 'fieldset':
+                    case 'listing':
+                    case 'menu':
+                    case 'ol':
+                    case 'pre':
+                    case 'ul':
+                        /* If the stack of open elements has an element in scope
+                        with the same tag name as that of the token, then generate
+                        implied end tags. */
+                        if ($this->elementInScope($token['name'])) {
+                            $this->generateImpliedEndTags();
+
+                            /* Now, if the current node is not an element with
+                            the same tag name as that of the token, then this
+                            is a parse error. */
+                            // w/e
+
+                            /* If the stack of open elements has an element in
+                            scope with the same tag name as that of the token,
+                            then pop elements from this stack until an element
+                            with that tag name has been popped from the stack. */
+                            for ($n = count($this->stack) - 1; $n >= 0; $n--) {
+                                if ($this->stack[$n]->nodeName === $token['name']) {
+                                    $n = -1;
+                                }
+
+                                array_pop($this->stack);
+                            }
+                        }
+                        break;
+
+                    /* An end tag whose tag name is "form" */
+                    case 'form':
+                        /* If the stack of open elements has an element in scope
+                        with the same tag name as that of the token, then generate
+                        implied    end tags. */
+                        if ($this->elementInScope($token['name'])) {
+                            $this->generateImpliedEndTags();
+
+                        }
+
+                        if (end($this->stack)->nodeName !== $token['name']) {
+                            /* Now, if the current node is not an element with the
+                            same tag name as that of the token, then this is a parse
+                            error. */
+                            // w/e
+
+                        } else {
+                            /* Otherwise, if the current node is an element with
+                            the same tag name as that of the token pop that element
+                            from the stack. */
+                            array_pop($this->stack);
+                        }
+
+                        /* In any case, set the form element pointer to null. */
+                        $this->form_pointer = null;
+                        break;
+
+                    /* An end tag whose tag name is "p" */
+                    case 'p':
+                        /* If the stack of open elements has a p element in scope,
+                        then generate implied end tags, except for p elements. */
+                        if ($this->elementInScope('p')) {
+                            $this->generateImpliedEndTags(array('p'));
+
+                            /* If the current node is not a p element, then this is
+                            a parse error. */
+                            // k
+
+                            /* If the stack of open elements has a p element in
+                            scope, then pop elements from this stack until the stack
+                            no longer has a p element in scope. */
+                            for ($n = count($this->stack) - 1; $n >= 0; $n--) {
+                                if ($this->elementInScope('p')) {
+                                    array_pop($this->stack);
+
+                                } else {
+                                    break;
+                                }
+                            }
+                        }
+                        break;
+
+                    /* An end tag whose tag name is "dd", "dt", or "li" */
+                    case 'dd':
+                    case 'dt':
+                    case 'li':
+                        /* If the stack of open elements has an element in scope
+                        whose tag name matches the tag name of the token, then
+                        generate implied end tags, except for elements with the
+                        same tag name as the token. */
+                        if ($this->elementInScope($token['name'])) {
+                            $this->generateImpliedEndTags(array($token['name']));
+
+                            /* If the current node is not an element with the same
+                            tag name as the token, then this is a parse error. */
+                            // w/e
+
+                            /* If the stack of open elements has an element in scope
+                            whose tag name matches the tag name of the token, then
+                            pop elements from this stack until an element with that
+                            tag name has been popped from the stack. */
+                            for ($n = count($this->stack) - 1; $n >= 0; $n--) {
+                                if ($this->stack[$n]->nodeName === $token['name']) {
+                                    $n = -1;
+                                }
+
+                                array_pop($this->stack);
+                            }
+                        }
+                        break;
+
+                    /* An end tag whose tag name is one of: "h1", "h2", "h3", "h4",
+                    "h5", "h6" */
+                    case 'h1':
+                    case 'h2':
+                    case 'h3':
+                    case 'h4':
+                    case 'h5':
+                    case 'h6':
+                        $elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6');
+
+                        /* If the stack of open elements has in scope an element whose
+                        tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then
+                        generate implied end tags. */
+                        if ($this->elementInScope($elements)) {
+                            $this->generateImpliedEndTags();
+
+                            /* Now, if the current node is not an element with the same
+                            tag name as that of the token, then this is a parse error. */
+                            // w/e
+
+                            /* If the stack of open elements has in scope an element
+                            whose tag name is one of "h1", "h2", "h3", "h4", "h5", or
+                            "h6", then pop elements from the stack until an element
+                            with one of those tag names has been popped from the stack. */
+                            while ($this->elementInScope($elements)) {
+                                array_pop($this->stack);
+                            }
+                        }
+                        break;
+
+                    /* An end tag whose tag name is one of: "a", "b", "big", "em",
+                    "font", "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */
+                    case 'a':
+                    case 'b':
+                    case 'big':
+                    case 'em':
+                    case 'font':
+                    case 'i':
+                    case 'nobr':
+                    case 's':
+                    case 'small':
+                    case 'strike':
+                    case 'strong':
+                    case 'tt':
+                    case 'u':
+                        /* 1. Let the formatting element be the last element in
+                        the list of active formatting elements that:
+                            * is between the end of the list and the last scope
+                            marker in the list, if any, or the start of the list
+                            otherwise, and
+                            * has the same tag name as the token.
+                        */
+                        while (true) {
+                            for ($a = count($this->a_formatting) - 1; $a >= 0; $a--) {
+                                if ($this->a_formatting[$a] === self::MARKER) {
+                                    break;
+
+                                } elseif ($this->a_formatting[$a]->tagName === $token['name']) {
+                                    $formatting_element = $this->a_formatting[$a];
+                                    $in_stack = in_array($formatting_element, $this->stack, true);
+                                    $fe_af_pos = $a;
+                                    break;
+                                }
+                            }
+
+                            /* If there is no such node, or, if that node is
+                            also in the stack of open elements but the element
+                            is not in scope, then this is a parse error. Abort
+                            these steps. The token is ignored. */
+                            if (!isset($formatting_element) || ($in_stack &&
+                                    !$this->elementInScope($token['name']))
+                            ) {
+                                break;
+
+                                /* Otherwise, if there is such a node, but that node
+                                is not in the stack of open elements, then this is a
+                                parse error; remove the element from the list, and
+                                abort these steps. */
+                            } elseif (isset($formatting_element) && !$in_stack) {
+                                unset($this->a_formatting[$fe_af_pos]);
+                                $this->a_formatting = array_merge($this->a_formatting);
+                                break;
+                            }
+
+                            /* 2. Let the furthest block be the topmost node in the
+                            stack of open elements that is lower in the stack
+                            than the formatting element, and is not an element in
+                            the phrasing or formatting categories. There might
+                            not be one. */
+                            $fe_s_pos = array_search($formatting_element, $this->stack, true);
+                            $length = count($this->stack);
+
+                            for ($s = $fe_s_pos + 1; $s < $length; $s++) {
+                                $category = $this->getElementCategory($this->stack[$s]->nodeName);
+
+                                if ($category !== self::PHRASING && $category !== self::FORMATTING) {
+                                    $furthest_block = $this->stack[$s];
+                                }
+                            }
+
+                            /* 3. If there is no furthest block, then the UA must
+                            skip the subsequent steps and instead just pop all
+                            the nodes from the bottom of the stack of open
+                            elements, from the current node up to the formatting
+                            element, and remove the formatting element from the
+                            list of active formatting elements. */
+                            if (!isset($furthest_block)) {
+                                for ($n = $length - 1; $n >= $fe_s_pos; $n--) {
+                                    array_pop($this->stack);
+                                }
+
+                                unset($this->a_formatting[$fe_af_pos]);
+                                $this->a_formatting = array_merge($this->a_formatting);
+                                break;
+                            }
+
+                            /* 4. Let the common ancestor be the element
+                            immediately above the formatting element in the stack
+                            of open elements. */
+                            $common_ancestor = $this->stack[$fe_s_pos - 1];
+
+                            /* 5. If the furthest block has a parent node, then
+                            remove the furthest block from its parent node. */
+                            if ($furthest_block->parentNode !== null) {
+                                $furthest_block->parentNode->removeChild($furthest_block);
+                            }
+
+                            /* 6. Let a bookmark note the position of the
+                            formatting element in the list of active formatting
+                            elements relative to the elements on either side
+                            of it in the list. */
+                            $bookmark = $fe_af_pos;
+
+                            /* 7. Let node and last node  be the furthest block.
+                            Follow these steps: */
+                            $node = $furthest_block;
+                            $last_node = $furthest_block;
+
+                            while (true) {
+                                for ($n = array_search($node, $this->stack, true) - 1; $n >= 0; $n--) {
+                                    /* 7.1 Let node be the element immediately
+                                    prior to node in the stack of open elements. */
+                                    $node = $this->stack[$n];
+
+                                    /* 7.2 If node is not in the list of active
+                                    formatting elements, then remove node from
+                                    the stack of open elements and then go back
+                                    to step 1. */
+                                    if (!in_array($node, $this->a_formatting, true)) {
+                                        unset($this->stack[$n]);
+                                        $this->stack = array_merge($this->stack);
+
+                                    } else {
+                                        break;
+                                    }
+                                }
+
+                                /* 7.3 Otherwise, if node is the formatting
+                                element, then go to the next step in the overall
+                                algorithm. */
+                                if ($node === $formatting_element) {
+                                    break;
+
+                                    /* 7.4 Otherwise, if last node is the furthest
+                                    block, then move the aforementioned bookmark to
+                                    be immediately after the node in the list of
+                                    active formatting elements. */
+                                } elseif ($last_node === $furthest_block) {
+                                    $bookmark = array_search($node, $this->a_formatting, true) + 1;
+                                }
+
+                                /* 7.5 If node has any children, perform a
+                                shallow clone of node, replace the entry for
+                                node in the list of active formatting elements
+                                with an entry for the clone, replace the entry
+                                for node in the stack of open elements with an
+                                entry for the clone, and let node be the clone. */
+                                if ($node->hasChildNodes()) {
+                                    $clone = $node->cloneNode();
+                                    $s_pos = array_search($node, $this->stack, true);
+                                    $a_pos = array_search($node, $this->a_formatting, true);
+
+                                    $this->stack[$s_pos] = $clone;
+                                    $this->a_formatting[$a_pos] = $clone;
+                                    $node = $clone;
+                                }
+
+                                /* 7.6 Insert last node into node, first removing
+                                it from its previous parent node if any. */
+                                if ($last_node->parentNode !== null) {
+                                    $last_node->parentNode->removeChild($last_node);
+                                }
+
+                                $node->appendChild($last_node);
+
+                                /* 7.7 Let last node be node. */
+                                $last_node = $node;
+                            }
+
+                            /* 8. Insert whatever last node ended up being in
+                            the previous step into the common ancestor node,
+                            first removing it from its previous parent node if
+                            any. */
+                            if ($last_node->parentNode !== null) {
+                                $last_node->parentNode->removeChild($last_node);
+                            }
+
+                            $common_ancestor->appendChild($last_node);
+
+                            /* 9. Perform a shallow clone of the formatting
+                            element. */
+                            $clone = $formatting_element->cloneNode();
+
+                            /* 10. Take all of the child nodes of the furthest
+                            block and append them to the clone created in the
+                            last step. */
+                            while ($furthest_block->hasChildNodes()) {
+                                $child = $furthest_block->firstChild;
+                                $furthest_block->removeChild($child);
+                                $clone->appendChild($child);
+                            }
+
+                            /* 11. Append that clone to the furthest block. */
+                            $furthest_block->appendChild($clone);
+
+                            /* 12. Remove the formatting element from the list
+                            of active formatting elements, and insert the clone
+                            into the list of active formatting elements at the
+                            position of the aforementioned bookmark. */
+                            $fe_af_pos = array_search($formatting_element, $this->a_formatting, true);
+                            unset($this->a_formatting[$fe_af_pos]);
+                            $this->a_formatting = array_merge($this->a_formatting);
+
+                            $af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1);
+                            $af_part2 = array_slice($this->a_formatting, $bookmark, count($this->a_formatting));
+                            $this->a_formatting = array_merge($af_part1, array($clone), $af_part2);
+
+                            /* 13. Remove the formatting element from the stack
+                            of open elements, and insert the clone into the stack
+                            of open elements immediately after (i.e. in a more
+                            deeply nested position than) the position of the
+                            furthest block in that stack. */
+                            $fe_s_pos = array_search($formatting_element, $this->stack, true);
+                            $fb_s_pos = array_search($furthest_block, $this->stack, true);
+                            unset($this->stack[$fe_s_pos]);
+
+                            $s_part1 = array_slice($this->stack, 0, $fb_s_pos);
+                            $s_part2 = array_slice($this->stack, $fb_s_pos + 1, count($this->stack));
+                            $this->stack = array_merge($s_part1, array($clone), $s_part2);
+
+                            /* 14. Jump back to step 1 in this series of steps. */
+                            unset($formatting_element, $fe_af_pos, $fe_s_pos, $furthest_block);
+                        }
+                        break;
+
+                    /* An end tag token whose tag name is one of: "button",
+                    "marquee", "object" */
+                    case 'button':
+                    case 'marquee':
+                    case 'object':
+                        /* If the stack of open elements has an element in scope whose
+                        tag name matches the tag name of the token, then generate implied
+                        tags. */
+                        if ($this->elementInScope($token['name'])) {
+                            $this->generateImpliedEndTags();
+
+                            /* Now, if the current node is not an element with the same
+                            tag name as the token, then this is a parse error. */
+                            // k
+
+                            /* Now, if the stack of open elements has an element in scope
+                            whose tag name matches the tag name of the token, then pop
+                            elements from the stack until that element has been popped from
+                            the stack, and clear the list of active formatting elements up
+                            to the last marker. */
+                            for ($n = count($this->stack) - 1; $n >= 0; $n--) {
+                                if ($this->stack[$n]->nodeName === $token['name']) {
+                                    $n = -1;
+                                }
+
+                                array_pop($this->stack);
+                            }
+
+                            $marker = end(array_keys($this->a_formatting, self::MARKER, true));
+
+                            for ($n = count($this->a_formatting) - 1; $n > $marker; $n--) {
+                                array_pop($this->a_formatting);
+                            }
+                        }
+                        break;
+
+                    /* Or an end tag whose tag name is one of: "area", "basefont",
+                    "bgsound", "br", "embed", "hr", "iframe", "image", "img",
+                    "input", "isindex", "noembed", "noframes", "param", "select",
+                    "spacer", "table", "textarea", "wbr" */
+                    case 'area':
+                    case 'basefont':
+                    case 'bgsound':
+                    case 'br':
+                    case 'embed':
+                    case 'hr':
+                    case 'iframe':
+                    case 'image':
+                    case 'img':
+                    case 'input':
+                    case 'isindex':
+                    case 'noembed':
+                    case 'noframes':
+                    case 'param':
+                    case 'select':
+                    case 'spacer':
+                    case 'table':
+                    case 'textarea':
+                    case 'wbr':
+                        // Parse error. Ignore the token.
+                        break;
+
+                    /* An end tag token not covered by the previous entries */
+                    default:
+                        for ($n = count($this->stack) - 1; $n >= 0; $n--) {
+                            /* Initialise node to be the current node (the bottommost
+                            node of the stack). */
+                            $node = end($this->stack);
+
+                            /* If node has the same tag name as the end tag token,
+                            then: */
+                            if ($token['name'] === $node->nodeName) {
+                                /* Generate implied end tags. */
+                                $this->generateImpliedEndTags();
+
+                                /* If the tag name of the end tag token does not
+                                match the tag name of the current node, this is a
+                                parse error. */
+                                // k
+
+                                /* Pop all the nodes from the current node up to
+                                node, including node, then stop this algorithm. */
+                                for ($x = count($this->stack) - $n; $x >= $n; $x--) {
+                                    array_pop($this->stack);
+                                }
+
+                            } else {
+                                $category = $this->getElementCategory($node);
+
+                                if ($category !== self::SPECIAL && $category !== self::SCOPING) {
+                                    /* Otherwise, if node is in neither the formatting
+                                    category nor the phrasing category, then this is a
+                                    parse error. Stop this algorithm. The end tag token
+                                    is ignored. */
+                                    return false;
+                                }
+                            }
+                        }
+                        break;
+                }
+                break;
+        }
+    }
+
+    private function inTable($token)
+    {
+        $clear = array('html', 'table');
+
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        or U+0020 SPACE */
+        if ($token['type'] === HTML5::CHARACTR &&
+            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+        ) {
+            /* Append the character to the current node. */
+            $text = $this->dom->createTextNode($token['data']);
+            end($this->stack)->appendChild($text);
+
+            /* A comment token */
+        } elseif ($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the current node with the data
+            attribute set to the data given in the comment token. */
+            $comment = $this->dom->createComment($token['data']);
+            end($this->stack)->appendChild($comment);
+
+            /* A start tag whose tag name is "caption" */
+        } elseif ($token['type'] === HTML5::STARTTAG &&
+            $token['name'] === 'caption'
+        ) {
+            /* Clear the stack back to a table context. */
+            $this->clearStackToTableContext($clear);
+
+            /* Insert a marker at the end of the list of active
+            formatting elements. */
+            $this->a_formatting[] = self::MARKER;
+
+            /* Insert an HTML element for the token, then switch the
+            insertion mode to "in caption". */
+            $this->insertElement($token);
+            $this->mode = self::IN_CAPTION;
+
+            /* A start tag whose tag name is "colgroup" */
+        } elseif ($token['type'] === HTML5::STARTTAG &&
+            $token['name'] === 'colgroup'
+        ) {
+            /* Clear the stack back to a table context. */
+            $this->clearStackToTableContext($clear);
+
+            /* Insert an HTML element for the token, then switch the
+            insertion mode to "in column group". */
+            $this->insertElement($token);
+            $this->mode = self::IN_CGROUP;
+
+            /* A start tag whose tag name is "col" */
+        } elseif ($token['type'] === HTML5::STARTTAG &&
+            $token['name'] === 'col'
+        ) {
+            $this->inTable(
+                array(
+                    'name' => 'colgroup',
+                    'type' => HTML5::STARTTAG,
+                    'attr' => array()
+                )
+            );
+
+            $this->inColumnGroup($token);
+
+            /* A start tag whose tag name is one of: "tbody", "tfoot", "thead" */
+        } elseif ($token['type'] === HTML5::STARTTAG && in_array(
+                $token['name'],
+                array('tbody', 'tfoot', 'thead')
+            )
+        ) {
+            /* Clear the stack back to a table context. */
+            $this->clearStackToTableContext($clear);
+
+            /* Insert an HTML element for the token, then switch the insertion
+            mode to "in table body". */
+            $this->insertElement($token);
+            $this->mode = self::IN_TBODY;
+
+            /* A start tag whose tag name is one of: "td", "th", "tr" */
+        } elseif ($token['type'] === HTML5::STARTTAG &&
+            in_array($token['name'], array('td', 'th', 'tr'))
+        ) {
+            /* Act as if a start tag token with the tag name "tbody" had been
+            seen, then reprocess the current token. */
+            $this->inTable(
+                array(
+                    'name' => 'tbody',
+                    'type' => HTML5::STARTTAG,
+                    'attr' => array()
+                )
+            );
+
+            return $this->inTableBody($token);
+
+            /* A start tag whose tag name is "table" */
+        } elseif ($token['type'] === HTML5::STARTTAG &&
+            $token['name'] === 'table'
+        ) {
+            /* Parse error. Act as if an end tag token with the tag name "table"
+            had been seen, then, if that token wasn't ignored, reprocess the
+            current token. */
+            $this->inTable(
+                array(
+                    'name' => 'table',
+                    'type' => HTML5::ENDTAG
+                )
+            );
+
+            return $this->mainPhase($token);
+
+            /* An end tag whose tag name is "table" */
+        } elseif ($token['type'] === HTML5::ENDTAG &&
+            $token['name'] === 'table'
+        ) {
+            /* If the stack of open elements does not have an element in table
+            scope with the same tag name as the token, this is a parse error.
+            Ignore the token. (innerHTML case) */
+            if (!$this->elementInScope($token['name'], true)) {
+                return false;
+
+                /* Otherwise: */
+            } else {
+                /* Generate implied end tags. */
+                $this->generateImpliedEndTags();
+
+                /* Now, if the current node is not a table element, then this
+                is a parse error. */
+                // w/e
+
+                /* Pop elements from this stack until a table element has been
+                popped from the stack. */
+                while (true) {
+                    $current = end($this->stack)->nodeName;
+                    array_pop($this->stack);
+
+                    if ($current === 'table') {
+                        break;
+                    }
+                }
+
+                /* Reset the insertion mode appropriately. */
+                $this->resetInsertionMode();
+            }
+
+            /* An end tag whose tag name is one of: "body", "caption", "col",
+            "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr" */
+        } elseif ($token['type'] === HTML5::ENDTAG && in_array(
+                $token['name'],
+                array(
+                    'body',
+                    'caption',
+                    'col',
+                    'colgroup',
+                    'html',
+                    'tbody',
+                    'td',
+                    'tfoot',
+                    'th',
+                    'thead',
+                    'tr'
+                )
+            )
+        ) {
+            // Parse error. Ignore the token.
+
+            /* Anything else */
+        } else {
+            /* Parse error. Process the token as if the insertion mode was "in
+            body", with the following exception: */
+
+            /* If the current node is a table, tbody, tfoot, thead, or tr
+            element, then, whenever a node would be inserted into the current
+            node, it must instead be inserted into the foster parent element. */
+            if (in_array(
+                end($this->stack)->nodeName,
+                array('table', 'tbody', 'tfoot', 'thead', 'tr')
+            )
+            ) {
+                /* The foster parent element is the parent element of the last
+                table element in the stack of open elements, if there is a
+                table element and it has such a parent element. If there is no
+                table element in the stack of open elements (innerHTML case),
+                then the foster parent element is the first element in the
+                stack of open elements (the html  element). Otherwise, if there
+                is a table element in the stack of open elements, but the last
+                table element in the stack of open elements has no parent, or
+                its parent node is not an element, then the foster parent
+                element is the element before the last table element in the
+                stack of open elements. */
+                for ($n = count($this->stack) - 1; $n >= 0; $n--) {
+                    if ($this->stack[$n]->nodeName === 'table') {
+                        $table = $this->stack[$n];
+                        break;
+                    }
+                }
+
+                if (isset($table) && $table->parentNode !== null) {
+                    $this->foster_parent = $table->parentNode;
+
+                } elseif (!isset($table)) {
+                    $this->foster_parent = $this->stack[0];
+
+                } elseif (isset($table) && ($table->parentNode === null ||
+                        $table->parentNode->nodeType !== XML_ELEMENT_NODE)
+                ) {
+                    $this->foster_parent = $this->stack[$n - 1];
+                }
+            }
+
+            $this->inBody($token);
+        }
+    }
+
+    private function inCaption($token)
+    {
+        /* An end tag whose tag name is "caption" */
+        if ($token['type'] === HTML5::ENDTAG && $token['name'] === 'caption') {
+            /* If the stack of open elements does not have an element in table
+            scope with the same tag name as the token, this is a parse error.
+            Ignore the token. (innerHTML case) */
+            if (!$this->elementInScope($token['name'], true)) {
+                // Ignore
+
+                /* Otherwise: */
+            } else {
+                /* Generate implied end tags. */
+                $this->generateImpliedEndTags();
+
+                /* Now, if the current node is not a caption element, then this
+                is a parse error. */
+                // w/e
+
+                /* Pop elements from this stack until a caption element has
+                been popped from the stack. */
+                while (true) {
+                    $node = end($this->stack)->nodeName;
+                    array_pop($this->stack);
+
+                    if ($node === 'caption') {
+                        break;
+                    }
+                }
+
+                /* Clear the list of active formatting elements up to the last
+                marker. */
+                $this->clearTheActiveFormattingElementsUpToTheLastMarker();
+
+                /* Switch the insertion mode to "in table". */
+                $this->mode = self::IN_TABLE;
+            }
+
+            /* A start tag whose tag name is one of: "caption", "col", "colgroup",
+            "tbody", "td", "tfoot", "th", "thead", "tr", or an end tag whose tag
+            name is "table" */
+        } elseif (($token['type'] === HTML5::STARTTAG && in_array(
+                    $token['name'],
+                    array(
+                        'caption',
+                        'col',
+                        'colgroup',
+                        'tbody',
+                        'td',
+                        'tfoot',
+                        'th',
+                        'thead',
+                        'tr'
+                    )
+                )) || ($token['type'] === HTML5::ENDTAG &&
+                $token['name'] === 'table')
+        ) {
+            /* Parse error. Act as if an end tag with the tag name "caption"
+            had been seen, then, if that token wasn't ignored, reprocess the
+            current token. */
+            $this->inCaption(
+                array(
+                    'name' => 'caption',
+                    'type' => HTML5::ENDTAG
+                )
+            );
+
+            return $this->inTable($token);
+
+            /* An end tag whose tag name is one of: "body", "col", "colgroup",
+            "html", "tbody", "td", "tfoot", "th", "thead", "tr" */
+        } elseif ($token['type'] === HTML5::ENDTAG && in_array(
+                $token['name'],
+                array(
+                    'body',
+                    'col',
+                    'colgroup',
+                    'html',
+                    'tbody',
+                    'tfoot',
+                    'th',
+                    'thead',
+                    'tr'
+                )
+            )
+        ) {
+            // Parse error. Ignore the token.
+
+            /* Anything else */
+        } else {
+            /* Process the token as if the insertion mode was "in body". */
+            $this->inBody($token);
+        }
+    }
+
+    private function inColumnGroup($token)
+    {
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        or U+0020 SPACE */
+        if ($token['type'] === HTML5::CHARACTR &&
+            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+        ) {
+            /* Append the character to the current node. */
+            $text = $this->dom->createTextNode($token['data']);
+            end($this->stack)->appendChild($text);
+
+            /* A comment token */
+        } elseif ($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the current node with the data
+            attribute set to the data given in the comment token. */
+            $comment = $this->dom->createComment($token['data']);
+            end($this->stack)->appendChild($comment);
+
+            /* A start tag whose tag name is "col" */
+        } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'col') {
+            /* Insert a col element for the token. Immediately pop the current
+            node off the stack of open elements. */
+            $this->insertElement($token);
+            array_pop($this->stack);
+
+            /* An end tag whose tag name is "colgroup" */
+        } elseif ($token['type'] === HTML5::ENDTAG &&
+            $token['name'] === 'colgroup'
+        ) {
+            /* If the current node is the root html element, then this is a
+            parse error, ignore the token. (innerHTML case) */
+            if (end($this->stack)->nodeName === 'html') {
+                // Ignore
+
+                /* Otherwise, pop the current node (which will be a colgroup
+                element) from the stack of open elements. Switch the insertion
+                mode to "in table". */
+            } else {
+                array_pop($this->stack);
+                $this->mode = self::IN_TABLE;
+            }
+
+            /* An end tag whose tag name is "col" */
+        } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'col') {
+            /* Parse error. Ignore the token. */
+
+            /* Anything else */
+        } else {
+            /* Act as if an end tag with the tag name "colgroup" had been seen,
+            and then, if that token wasn't ignored, reprocess the current token. */
+            $this->inColumnGroup(
+                array(
+                    'name' => 'colgroup',
+                    'type' => HTML5::ENDTAG
+                )
+            );
+
+            return $this->inTable($token);
+        }
+    }
+
+    private function inTableBody($token)
+    {
+        $clear = array('tbody', 'tfoot', 'thead', 'html');
+
+        /* A start tag whose tag name is "tr" */
+        if ($token['type'] === HTML5::STARTTAG && $token['name'] === 'tr') {
+            /* Clear the stack back to a table body context. */
+            $this->clearStackToTableContext($clear);
+
+            /* Insert a tr element for the token, then switch the insertion
+            mode to "in row". */
+            $this->insertElement($token);
+            $this->mode = self::IN_ROW;
+
+            /* A start tag whose tag name is one of: "th", "td" */
+        } elseif ($token['type'] === HTML5::STARTTAG &&
+            ($token['name'] === 'th' || $token['name'] === 'td')
+        ) {
+            /* Parse error. Act as if a start tag with the tag name "tr" had
+            been seen, then reprocess the current token. */
+            $this->inTableBody(
+                array(
+                    'name' => 'tr',
+                    'type' => HTML5::STARTTAG,
+                    'attr' => array()
+                )
+            );
+
+            return $this->inRow($token);
+
+            /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
+        } elseif ($token['type'] === HTML5::ENDTAG &&
+            in_array($token['name'], array('tbody', 'tfoot', 'thead'))
+        ) {
+            /* If the stack of open elements does not have an element in table
+            scope with the same tag name as the token, this is a parse error.
+            Ignore the token. */
+            if (!$this->elementInScope($token['name'], true)) {
+                // Ignore
+
+                /* Otherwise: */
+            } else {
+                /* Clear the stack back to a table body context. */
+                $this->clearStackToTableContext($clear);
+
+                /* Pop the current node from the stack of open elements. Switch
+                the insertion mode to "in table". */
+                array_pop($this->stack);
+                $this->mode = self::IN_TABLE;
+            }
+
+            /* A start tag whose tag name is one of: "caption", "col", "colgroup",
+            "tbody", "tfoot", "thead", or an end tag whose tag name is "table" */
+        } elseif (($token['type'] === HTML5::STARTTAG && in_array(
+                    $token['name'],
+                    array('caption', 'col', 'colgroup', 'tbody', 'tfoor', 'thead')
+                )) ||
+            ($token['type'] === HTML5::STARTTAG && $token['name'] === 'table')
+        ) {
+            /* If the stack of open elements does not have a tbody, thead, or
+            tfoot element in table scope, this is a parse error. Ignore the
+            token. (innerHTML case) */
+            if (!$this->elementInScope(array('tbody', 'thead', 'tfoot'), true)) {
+                // Ignore.
+
+                /* Otherwise: */
+            } else {
+                /* Clear the stack back to a table body context. */
+                $this->clearStackToTableContext($clear);
+
+                /* Act as if an end tag with the same tag name as the current
+                node ("tbody", "tfoot", or "thead") had been seen, then
+                reprocess the current token. */
+                $this->inTableBody(
+                    array(
+                        'name' => end($this->stack)->nodeName,
+                        'type' => HTML5::ENDTAG
+                    )
+                );
+
+                return $this->mainPhase($token);
+            }
+
+            /* An end tag whose tag name is one of: "body", "caption", "col",
+            "colgroup", "html", "td", "th", "tr" */
+        } elseif ($token['type'] === HTML5::ENDTAG && in_array(
+                $token['name'],
+                array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr')
+            )
+        ) {
+            /* Parse error. Ignore the token. */
+
+            /* Anything else */
+        } else {
+            /* Process the token as if the insertion mode was "in table". */
+            $this->inTable($token);
+        }
+    }
+
+    private function inRow($token)
+    {
+        $clear = array('tr', 'html');
+
+        /* A start tag whose tag name is one of: "th", "td" */
+        if ($token['type'] === HTML5::STARTTAG &&
+            ($token['name'] === 'th' || $token['name'] === 'td')
+        ) {
+            /* Clear the stack back to a table row context. */
+            $this->clearStackToTableContext($clear);
+
+            /* Insert an HTML element for the token, then switch the insertion
+            mode to "in cell". */
+            $this->insertElement($token);
+            $this->mode = self::IN_CELL;
+
+            /* Insert a marker at the end of the list of active formatting
+            elements. */
+            $this->a_formatting[] = self::MARKER;
+
+            /* An end tag whose tag name is "tr" */
+        } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'tr') {
+            /* If the stack of open elements does not have an element in table
+            scope with the same tag name as the token, this is a parse error.
+            Ignore the token. (innerHTML case) */
+            if (!$this->elementInScope($token['name'], true)) {
+                // Ignore.
+
+                /* Otherwise: */
+            } else {
+                /* Clear the stack back to a table row context. */
+                $this->clearStackToTableContext($clear);
+
+                /* Pop the current node (which will be a tr element) from the
+                stack of open elements. Switch the insertion mode to "in table
+                body". */
+                array_pop($this->stack);
+                $this->mode = self::IN_TBODY;
+            }
+
+            /* A start tag whose tag name is one of: "caption", "col", "colgroup",
+            "tbody", "tfoot", "thead", "tr" or an end tag whose tag name is "table" */
+        } elseif ($token['type'] === HTML5::STARTTAG && in_array(
+                $token['name'],
+                array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr')
+            )
+        ) {
+            /* Act as if an end tag with the tag name "tr" had been seen, then,
+            if that token wasn't ignored, reprocess the current token. */
+            $this->inRow(
+                array(
+                    'name' => 'tr',
+                    'type' => HTML5::ENDTAG
+                )
+            );
+
+            return $this->inCell($token);
+
+            /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
+        } elseif ($token['type'] === HTML5::ENDTAG &&
+            in_array($token['name'], array('tbody', 'tfoot', 'thead'))
+        ) {
+            /* If the stack of open elements does not have an element in table
+            scope with the same tag name as the token, this is a parse error.
+            Ignore the token. */
+            if (!$this->elementInScope($token['name'], true)) {
+                // Ignore.
+
+                /* Otherwise: */
+            } else {
+                /* Otherwise, act as if an end tag with the tag name "tr" had
+                been seen, then reprocess the current token. */
+                $this->inRow(
+                    array(
+                        'name' => 'tr',
+                        'type' => HTML5::ENDTAG
+                    )
+                );
+
+                return $this->inCell($token);
+            }
+
+            /* An end tag whose tag name is one of: "body", "caption", "col",
+            "colgroup", "html", "td", "th" */
+        } elseif ($token['type'] === HTML5::ENDTAG && in_array(
+                $token['name'],
+                array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr')
+            )
+        ) {
+            /* Parse error. Ignore the token. */
+
+            /* Anything else */
+        } else {
+            /* Process the token as if the insertion mode was "in table". */
+            $this->inTable($token);
+        }
+    }
+
+    private function inCell($token)
+    {
+        /* An end tag whose tag name is one of: "td", "th" */
+        if ($token['type'] === HTML5::ENDTAG &&
+            ($token['name'] === 'td' || $token['name'] === 'th')
+        ) {
+            /* If the stack of open elements does not have an element in table
+            scope with the same tag name as that of the token, then this is a
+            parse error and the token must be ignored. */
+            if (!$this->elementInScope($token['name'], true)) {
+                // Ignore.
+
+                /* Otherwise: */
+            } else {
+                /* Generate implied end tags, except for elements with the same
+                tag name as the token. */
+                $this->generateImpliedEndTags(array($token['name']));
+
+                /* Now, if the current node is not an element with the same tag
+                name as the token, then this is a parse error. */
+                // k
+
+                /* Pop elements from this stack until an element with the same
+                tag name as the token has been popped from the stack. */
+                while (true) {
+                    $node = end($this->stack)->nodeName;
+                    array_pop($this->stack);
+
+                    if ($node === $token['name']) {
+                        break;
+                    }
+                }
+
+                /* Clear the list of active formatting elements up to the last
+                marker. */
+                $this->clearTheActiveFormattingElementsUpToTheLastMarker();
+
+                /* Switch the insertion mode to "in row". (The current node
+                will be a tr element at this point.) */
+                $this->mode = self::IN_ROW;
+            }
+
+            /* A start tag whose tag name is one of: "caption", "col", "colgroup",
+            "tbody", "td", "tfoot", "th", "thead", "tr" */
+        } elseif ($token['type'] === HTML5::STARTTAG && in_array(
+                $token['name'],
+                array(
+                    'caption',
+                    'col',
+                    'colgroup',
+                    'tbody',
+                    'td',
+                    'tfoot',
+                    'th',
+                    'thead',
+                    'tr'
+                )
+            )
+        ) {
+            /* If the stack of open elements does not have a td or th element
+            in table scope, then this is a parse error; ignore the token.
+            (innerHTML case) */
+            if (!$this->elementInScope(array('td', 'th'), true)) {
+                // Ignore.
+
+                /* Otherwise, close the cell (see below) and reprocess the current
+                token. */
+            } else {
+                $this->closeCell();
+                return $this->inRow($token);
+            }
+
+            /* A start tag whose tag name is one of: "caption", "col", "colgroup",
+            "tbody", "td", "tfoot", "th", "thead", "tr" */
+        } elseif ($token['type'] === HTML5::STARTTAG && in_array(
+                $token['name'],
+                array(
+                    'caption',
+                    'col',
+                    'colgroup',
+                    'tbody',
+                    'td',
+                    'tfoot',
+                    'th',
+                    'thead',
+                    'tr'
+                )
+            )
+        ) {
+            /* If the stack of open elements does not have a td or th element
+            in table scope, then this is a parse error; ignore the token.
+            (innerHTML case) */
+            if (!$this->elementInScope(array('td', 'th'), true)) {
+                // Ignore.
+
+                /* Otherwise, close the cell (see below) and reprocess the current
+                token. */
+            } else {
+                $this->closeCell();
+                return $this->inRow($token);
+            }
+
+            /* An end tag whose tag name is one of: "body", "caption", "col",
+            "colgroup", "html" */
+        } elseif ($token['type'] === HTML5::ENDTAG && in_array(
+                $token['name'],
+                array('body', 'caption', 'col', 'colgroup', 'html')
+            )
+        ) {
+            /* Parse error. Ignore the token. */
+
+            /* An end tag whose tag name is one of: "table", "tbody", "tfoot",
+            "thead", "tr" */
+        } elseif ($token['type'] === HTML5::ENDTAG && in_array(
+                $token['name'],
+                array('table', 'tbody', 'tfoot', 'thead', 'tr')
+            )
+        ) {
+            /* If the stack of open elements does not have an element in table
+            scope with the same tag name as that of the token (which can only
+            happen for "tbody", "tfoot" and "thead", or, in the innerHTML case),
+            then this is a parse error and the token must be ignored. */
+            if (!$this->elementInScope($token['name'], true)) {
+                // Ignore.
+
+                /* Otherwise, close the cell (see below) and reprocess the current
+                token. */
+            } else {
+                $this->closeCell();
+                return $this->inRow($token);
+            }
+
+            /* Anything else */
+        } else {
+            /* Process the token as if the insertion mode was "in body". */
+            $this->inBody($token);
+        }
+    }
+
+    private function inSelect($token)
+    {
+        /* Handle the token as follows: */
+
+        /* A character token */
+        if ($token['type'] === HTML5::CHARACTR) {
+            /* Append the token's character to the current node. */
+            $this->insertText($token['data']);
+
+            /* A comment token */
+        } elseif ($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the current node with the data
+            attribute set to the data given in the comment token. */
+            $this->insertComment($token['data']);
+
+            /* A start tag token whose tag name is "option" */
+        } elseif ($token['type'] === HTML5::STARTTAG &&
+            $token['name'] === 'option'
+        ) {
+            /* If the current node is an option element, act as if an end tag
+            with the tag name "option" had been seen. */
+            if (end($this->stack)->nodeName === 'option') {
+                $this->inSelect(
+                    array(
+                        'name' => 'option',
+                        'type' => HTML5::ENDTAG
+                    )
+                );
+            }
+
+            /* Insert an HTML element for the token. */
+            $this->insertElement($token);
+
+            /* A start tag token whose tag name is "optgroup" */
+        } elseif ($token['type'] === HTML5::STARTTAG &&
+            $token['name'] === 'optgroup'
+        ) {
+            /* If the current node is an option element, act as if an end tag
+            with the tag name "option" had been seen. */
+            if (end($this->stack)->nodeName === 'option') {
+                $this->inSelect(
+                    array(
+                        'name' => 'option',
+                        'type' => HTML5::ENDTAG
+                    )
+                );
+            }
+
+            /* If the current node is an optgroup element, act as if an end tag
+            with the tag name "optgroup" had been seen. */
+            if (end($this->stack)->nodeName === 'optgroup') {
+                $this->inSelect(
+                    array(
+                        'name' => 'optgroup',
+                        'type' => HTML5::ENDTAG
+                    )
+                );
+            }
+
+            /* Insert an HTML element for the token. */
+            $this->insertElement($token);
+
+            /* An end tag token whose tag name is "optgroup" */
+        } elseif ($token['type'] === HTML5::ENDTAG &&
+            $token['name'] === 'optgroup'
+        ) {
+            /* First, if the current node is an option element, and the node
+            immediately before it in the stack of open elements is an optgroup
+            element, then act as if an end tag with the tag name "option" had
+            been seen. */
+            $elements_in_stack = count($this->stack);
+
+            if ($this->stack[$elements_in_stack - 1]->nodeName === 'option' &&
+                $this->stack[$elements_in_stack - 2]->nodeName === 'optgroup'
+            ) {
+                $this->inSelect(
+                    array(
+                        'name' => 'option',
+                        'type' => HTML5::ENDTAG
+                    )
+                );
+            }
+
+            /* If the current node is an optgroup element, then pop that node
+            from the stack of open elements. Otherwise, this is a parse error,
+            ignore the token. */
+            if ($this->stack[$elements_in_stack - 1] === 'optgroup') {
+                array_pop($this->stack);
+            }
+
+            /* An end tag token whose tag name is "option" */
+        } elseif ($token['type'] === HTML5::ENDTAG &&
+            $token['name'] === 'option'
+        ) {
+            /* If the current node is an option element, then pop that node
+            from the stack of open elements. Otherwise, this is a parse error,
+            ignore the token. */
+            if (end($this->stack)->nodeName === 'option') {
+                array_pop($this->stack);
+            }
+
+            /* An end tag whose tag name is "select" */
+        } elseif ($token['type'] === HTML5::ENDTAG &&
+            $token['name'] === 'select'
+        ) {
+            /* If the stack of open elements does not have an element in table
+            scope with the same tag name as the token, this is a parse error.
+            Ignore the token. (innerHTML case) */
+            if (!$this->elementInScope($token['name'], true)) {
+                // w/e
+
+                /* Otherwise: */
+            } else {
+                /* Pop elements from the stack of open elements until a select
+                element has been popped from the stack. */
+                while (true) {
+                    $current = end($this->stack)->nodeName;
+                    array_pop($this->stack);
+
+                    if ($current === 'select') {
+                        break;
+                    }
+                }
+
+                /* Reset the insertion mode appropriately. */
+                $this->resetInsertionMode();
+            }
+
+            /* A start tag whose tag name is "select" */
+        } elseif ($token['name'] === 'select' &&
+            $token['type'] === HTML5::STARTTAG
+        ) {
+            /* Parse error. Act as if the token had been an end tag with the
+            tag name "select" instead. */
+            $this->inSelect(
+                array(
+                    'name' => 'select',
+                    'type' => HTML5::ENDTAG
+                )
+            );
+
+            /* An end tag whose tag name is one of: "caption", "table", "tbody",
+            "tfoot", "thead", "tr", "td", "th" */
+        } elseif (in_array(
+                $token['name'],
+                array(
+                    'caption',
+                    'table',
+                    'tbody',
+                    'tfoot',
+                    'thead',
+                    'tr',
+                    'td',
+                    'th'
+                )
+            ) && $token['type'] === HTML5::ENDTAG
+        ) {
+            /* Parse error. */
+            // w/e
+
+            /* If the stack of open elements has an element in table scope with
+            the same tag name as that of the token, then act as if an end tag
+            with the tag name "select" had been seen, and reprocess the token.
+            Otherwise, ignore the token. */
+            if ($this->elementInScope($token['name'], true)) {
+                $this->inSelect(
+                    array(
+                        'name' => 'select',
+                        'type' => HTML5::ENDTAG
+                    )
+                );
+
+                $this->mainPhase($token);
+            }
+
+            /* Anything else */
+        } else {
+            /* Parse error. Ignore the token. */
+        }
+    }
+
+    private function afterBody($token)
+    {
+        /* Handle the token as follows: */
+
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        or U+0020 SPACE */
+        if ($token['type'] === HTML5::CHARACTR &&
+            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+        ) {
+            /* Process the token as it would be processed if the insertion mode
+            was "in body". */
+            $this->inBody($token);
+
+            /* A comment token */
+        } elseif ($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the first element in the stack of open
+            elements (the html element), with the data attribute set to the
+            data given in the comment token. */
+            $comment = $this->dom->createComment($token['data']);
+            $this->stack[0]->appendChild($comment);
+
+            /* An end tag with the tag name "html" */
+        } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') {
+            /* If the parser was originally created in order to handle the
+            setting of an element's innerHTML attribute, this is a parse error;
+            ignore the token. (The element will be an html element in this
+            case.) (innerHTML case) */
+
+            /* Otherwise, switch to the trailing end phase. */
+            $this->phase = self::END_PHASE;
+
+            /* Anything else */
+        } else {
+            /* Parse error. Set the insertion mode to "in body" and reprocess
+            the token. */
+            $this->mode = self::IN_BODY;
+            return $this->inBody($token);
+        }
+    }
+
+    private function inFrameset($token)
+    {
+        /* Handle the token as follows: */
+
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */
+        if ($token['type'] === HTML5::CHARACTR &&
+            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+        ) {
+            /* Append the character to the current node. */
+            $this->insertText($token['data']);
+
+            /* A comment token */
+        } elseif ($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the current node with the data
+            attribute set to the data given in the comment token. */
+            $this->insertComment($token['data']);
+
+            /* A start tag with the tag name "frameset" */
+        } elseif ($token['name'] === 'frameset' &&
+            $token['type'] === HTML5::STARTTAG
+        ) {
+            $this->insertElement($token);
+
+            /* An end tag with the tag name "frameset" */
+        } elseif ($token['name'] === 'frameset' &&
+            $token['type'] === HTML5::ENDTAG
+        ) {
+            /* If the current node is the root html element, then this is a
+            parse error; ignore the token. (innerHTML case) */
+            if (end($this->stack)->nodeName === 'html') {
+                // Ignore
+
+            } else {
+                /* Otherwise, pop the current node from the stack of open
+                elements. */
+                array_pop($this->stack);
+
+                /* If the parser was not originally created in order to handle
+                the setting of an element's innerHTML attribute (innerHTML case),
+                and the current node is no longer a frameset element, then change
+                the insertion mode to "after frameset". */
+                $this->mode = self::AFTR_FRAME;
+            }
+
+            /* A start tag with the tag name "frame" */
+        } elseif ($token['name'] === 'frame' &&
+            $token['type'] === HTML5::STARTTAG
+        ) {
+            /* Insert an HTML element for the token. */
+            $this->insertElement($token);
+
+            /* Immediately pop the current node off the stack of open elements. */
+            array_pop($this->stack);
+
+            /* A start tag with the tag name "noframes" */
+        } elseif ($token['name'] === 'noframes' &&
+            $token['type'] === HTML5::STARTTAG
+        ) {
+            /* Process the token as if the insertion mode had been "in body". */
+            $this->inBody($token);
+
+            /* Anything else */
+        } else {
+            /* Parse error. Ignore the token. */
+        }
+    }
+
+    private function afterFrameset($token)
+    {
+        /* Handle the token as follows: */
+
+        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+        U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */
+        if ($token['type'] === HTML5::CHARACTR &&
+            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+        ) {
+            /* Append the character to the current node. */
+            $this->insertText($token['data']);
+
+            /* A comment token */
+        } elseif ($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the current node with the data
+            attribute set to the data given in the comment token. */
+            $this->insertComment($token['data']);
+
+            /* An end tag with the tag name "html" */
+        } elseif ($token['name'] === 'html' &&
+            $token['type'] === HTML5::ENDTAG
+        ) {
+            /* Switch to the trailing end phase. */
+            $this->phase = self::END_PHASE;
+
+            /* A start tag with the tag name "noframes" */
+        } elseif ($token['name'] === 'noframes' &&
+            $token['type'] === HTML5::STARTTAG
+        ) {
+            /* Process the token as if the insertion mode had been "in body". */
+            $this->inBody($token);
+
+            /* Anything else */
+        } else {
+            /* Parse error. Ignore the token. */
+        }
+    }
+
+    private function trailingEndPhase($token)
+    {
+        /* After the main phase, as each token is emitted from the tokenisation
+        stage, it must be processed as described in this section. */
+
+        /* A DOCTYPE token */
+        if ($token['type'] === HTML5::DOCTYPE) {
+            // Parse error. Ignore the token.
+
+            /* A comment token */
+        } elseif ($token['type'] === HTML5::COMMENT) {
+            /* Append a Comment node to the Document object with the data
+            attribute set to the data given in the comment token. */
+            $comment = $this->dom->createComment($token['data']);
+            $this->dom->appendChild($comment);
+
+            /* A character token that is one of one of U+0009 CHARACTER TABULATION,
+            U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+            or U+0020 SPACE */
+        } elseif ($token['type'] === HTML5::CHARACTR &&
+            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
+        ) {
+            /* Process the token as it would be processed in the main phase. */
+            $this->mainPhase($token);
+
+            /* A character token that is not one of U+0009 CHARACTER TABULATION,
+            U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
+            or U+0020 SPACE. Or a start tag token. Or an end tag token. */
+        } elseif (($token['type'] === HTML5::CHARACTR &&
+                preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) ||
+            $token['type'] === HTML5::STARTTAG || $token['type'] === HTML5::ENDTAG
+        ) {
+            /* Parse error. Switch back to the main phase and reprocess the
+            token. */
+            $this->phase = self::MAIN_PHASE;
+            return $this->mainPhase($token);
+
+            /* An end-of-file token */
+        } elseif ($token['type'] === HTML5::EOF) {
+            /* OMG DONE!! */
+        }
+    }
+
+    private function insertElement($token, $append = true, $check = false)
+    {
+        // Proprietary workaround for libxml2's limitations with tag names
+        if ($check) {
+            // Slightly modified HTML5 tag-name modification,
+            // removing anything that's not an ASCII letter, digit, or hyphen
+            $token['name'] = preg_replace('/[^a-z0-9-]/i', '', $token['name']);
+            // Remove leading hyphens and numbers
+            $token['name'] = ltrim($token['name'], '-0..9');
+            // In theory, this should ever be needed, but just in case
+            if ($token['name'] === '') {
+                $token['name'] = 'span';
+            } // arbitrary generic choice
+        }
+
+        $el = $this->dom->createElement($token['name']);
+
+        foreach ($token['attr'] as $attr) {
+            if (!$el->hasAttribute($attr['name'])) {
+                $el->setAttribute($attr['name'], $attr['value']);
+            }
+        }
+
+        $this->appendToRealParent($el);
+        $this->stack[] = $el;
+
+        return $el;
+    }
+
+    private function insertText($data)
+    {
+        $text = $this->dom->createTextNode($data);
+        $this->appendToRealParent($text);
+    }
+
+    private function insertComment($data)
+    {
+        $comment = $this->dom->createComment($data);
+        $this->appendToRealParent($comment);
+    }
+
+    private function appendToRealParent($node)
+    {
+        if ($this->foster_parent === null) {
+            end($this->stack)->appendChild($node);
+
+        } elseif ($this->foster_parent !== null) {
+            /* If the foster parent element is the parent element of the
+            last table element in the stack of open elements, then the new
+            node must be inserted immediately before the last table element
+            in the stack of open elements in the foster parent element;
+            otherwise, the new node must be appended to the foster parent
+            element. */
+            for ($n = count($this->stack) - 1; $n >= 0; $n--) {
+                if ($this->stack[$n]->nodeName === 'table' &&
+                    $this->stack[$n]->parentNode !== null
+                ) {
+                    $table = $this->stack[$n];
+                    break;
+                }
+            }
+
+            if (isset($table) && $this->foster_parent->isSameNode($table->parentNode)) {
+                $this->foster_parent->insertBefore($node, $table);
+            } else {
+                $this->foster_parent->appendChild($node);
+            }
+
+            $this->foster_parent = null;
+        }
+    }
+
+    private function elementInScope($el, $table = false)
+    {
+        if (is_array($el)) {
+            foreach ($el as $element) {
+                if ($this->elementInScope($element, $table)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        $leng = count($this->stack);
+
+        for ($n = 0; $n < $leng; $n++) {
+            /* 1. Initialise node to be the current node (the bottommost node of
+            the stack). */
+            $node = $this->stack[$leng - 1 - $n];
+
+            if ($node->tagName === $el) {
+                /* 2. If node is the target node, terminate in a match state. */
+                return true;
+
+            } elseif ($node->tagName === 'table') {
+                /* 3. Otherwise, if node is a table element, terminate in a failure
+                state. */
+                return false;
+
+            } elseif ($table === true && in_array(
+                    $node->tagName,
+                    array(
+                        'caption',
+                        'td',
+                        'th',
+                        'button',
+                        'marquee',
+                        'object'
+                    )
+                )
+            ) {
+                /* 4. Otherwise, if the algorithm is the "has an element in scope"
+                variant (rather than the "has an element in table scope" variant),
+                and node is one of the following, terminate in a failure state. */
+                return false;
+
+            } elseif ($node === $node->ownerDocument->documentElement) {
+                /* 5. Otherwise, if node is an html element (root element), terminate
+                in a failure state. (This can only happen if the node is the topmost
+                node of the    stack of open elements, and prevents the next step from
+                being invoked if there are no more elements in the stack.) */
+                return false;
+            }
+
+            /* Otherwise, set node to the previous entry in the stack of open
+            elements and return to step 2. (This will never fail, since the loop
+            will always terminate in the previous step if the top of the stack
+            is reached.) */
+        }
+    }
+
+    private function reconstructActiveFormattingElements()
+    {
+        /* 1. If there are no entries in the list of active formatting elements,
+        then there is nothing to reconstruct; stop this algorithm. */
+        $formatting_elements = count($this->a_formatting);
+
+        if ($formatting_elements === 0) {
+            return false;
+        }
+
+        /* 3. Let entry be the last (most recently added) element in the list
+        of active formatting elements. */
+        $entry = end($this->a_formatting);
+
+        /* 2. If the last (most recently added) entry in the list of active
+        formatting elements is a marker, or if it is an element that is in the
+        stack of open elements, then there is nothing to reconstruct; stop this
+        algorithm. */
+        if ($entry === self::MARKER || in_array($entry, $this->stack, true)) {
+            return false;
+        }
+
+        for ($a = $formatting_elements - 1; $a >= 0; true) {
+            /* 4. If there are no entries before entry in the list of active
+            formatting elements, then jump to step 8. */
+            if ($a === 0) {
+                $step_seven = false;
+                break;
+            }
+
+            /* 5. Let entry be the entry one earlier than entry in the list of
+            active formatting elements. */
+            $a--;
+            $entry = $this->a_formatting[$a];
+
+            /* 6. If entry is neither a marker nor an element that is also in
+            thetack of open elements, go to step 4. */
+            if ($entry === self::MARKER || in_array($entry, $this->stack, true)) {
+                break;
+            }
+        }
+
+        while (true) {
+            /* 7. Let entry be the element one later than entry in the list of
+            active formatting elements. */
+            if (isset($step_seven) && $step_seven === true) {
+                $a++;
+                $entry = $this->a_formatting[$a];
+            }
+
+            /* 8. Perform a shallow clone of the element entry to obtain clone. */
+            $clone = $entry->cloneNode();
+
+            /* 9. Append clone to the current node and push it onto the stack
+            of open elements  so that it is the new current node. */
+            end($this->stack)->appendChild($clone);
+            $this->stack[] = $clone;
+
+            /* 10. Replace the entry for entry in the list with an entry for
+            clone. */
+            $this->a_formatting[$a] = $clone;
+
+            /* 11. If the entry for clone in the list of active formatting
+            elements is not the last entry in the list, return to step 7. */
+            if (end($this->a_formatting) !== $clone) {
+                $step_seven = true;
+            } else {
+                break;
+            }
+        }
+    }
+
+    private function clearTheActiveFormattingElementsUpToTheLastMarker()
+    {
+        /* When the steps below require the UA to clear the list of active
+        formatting elements up to the last marker, the UA must perform the
+        following steps: */
+
+        while (true) {
+            /* 1. Let entry be the last (most recently added) entry in the list
+            of active formatting elements. */
+            $entry = end($this->a_formatting);
+
+            /* 2. Remove entry from the list of active formatting elements. */
+            array_pop($this->a_formatting);
+
+            /* 3. If entry was a marker, then stop the algorithm at this point.
+            The list has been cleared up to the last marker. */
+            if ($entry === self::MARKER) {
+                break;
+            }
+        }
+    }
+
+    private function generateImpliedEndTags($exclude = array())
+    {
+        /* When the steps below require the UA to generate implied end tags,
+        then, if the current node is a dd element, a dt element, an li element,
+        a p element, a td element, a th  element, or a tr element, the UA must
+        act as if an end tag with the respective tag name had been seen and
+        then generate implied end tags again. */
+        $node = end($this->stack);
+        $elements = array_diff(array('dd', 'dt', 'li', 'p', 'td', 'th', 'tr'), $exclude);
+
+        while (in_array(end($this->stack)->nodeName, $elements)) {
+            array_pop($this->stack);
+        }
+    }
+
+    private function getElementCategory($node)
+    {
+        $name = $node->tagName;
+        if (in_array($name, $this->special)) {
+            return self::SPECIAL;
+        } elseif (in_array($name, $this->scoping)) {
+            return self::SCOPING;
+        } elseif (in_array($name, $this->formatting)) {
+            return self::FORMATTING;
+        } else {
+            return self::PHRASING;
+        }
+    }
+
+    private function clearStackToTableContext($elements)
+    {
+        /* When the steps above require the UA to clear the stack back to a
+        table context, it means that the UA must, while the current node is not
+        a table element or an html element, pop elements from the stack of open
+        elements. If this causes any elements to be popped from the stack, then
+        this is a parse error. */
+        while (true) {
+            $node = end($this->stack)->nodeName;
+
+            if (in_array($node, $elements)) {
+                break;
+            } else {
+                array_pop($this->stack);
+            }
+        }
+    }
+
+    private function resetInsertionMode()
+    {
+        /* 1. Let last be false. */
+        $last = false;
+        $leng = count($this->stack);
+
+        for ($n = $leng - 1; $n >= 0; $n--) {
+            /* 2. Let node be the last node in the stack of open elements. */
+            $node = $this->stack[$n];
+
+            /* 3. If node is the first node in the stack of open elements, then
+            set last to true. If the element whose innerHTML  attribute is being
+            set is neither a td  element nor a th element, then set node to the
+            element whose innerHTML  attribute is being set. (innerHTML  case) */
+            if ($this->stack[0]->isSameNode($node)) {
+                $last = true;
+            }
+
+            /* 4. If node is a select element, then switch the insertion mode to
+            "in select" and abort these steps. (innerHTML case) */
+            if ($node->nodeName === 'select') {
+                $this->mode = self::IN_SELECT;
+                break;
+
+                /* 5. If node is a td or th element, then switch the insertion mode
+                to "in cell" and abort these steps. */
+            } elseif ($node->nodeName === 'td' || $node->nodeName === 'th') {
+                $this->mode = self::IN_CELL;
+                break;
+
+                /* 6. If node is a tr element, then switch the insertion mode to
+                "in    row" and abort these steps. */
+            } elseif ($node->nodeName === 'tr') {
+                $this->mode = self::IN_ROW;
+                break;
+
+                /* 7. If node is a tbody, thead, or tfoot element, then switch the
+                insertion mode to "in table body" and abort these steps. */
+            } elseif (in_array($node->nodeName, array('tbody', 'thead', 'tfoot'))) {
+                $this->mode = self::IN_TBODY;
+                break;
+
+                /* 8. If node is a caption element, then switch the insertion mode
+                to "in caption" and abort these steps. */
+            } elseif ($node->nodeName === 'caption') {
+                $this->mode = self::IN_CAPTION;
+                break;
+
+                /* 9. If node is a colgroup element, then switch the insertion mode
+                to "in column group" and abort these steps. (innerHTML case) */
+            } elseif ($node->nodeName === 'colgroup') {
+                $this->mode = self::IN_CGROUP;
+                break;
+
+                /* 10. If node is a table element, then switch the insertion mode
+                to "in table" and abort these steps. */
+            } elseif ($node->nodeName === 'table') {
+                $this->mode = self::IN_TABLE;
+                break;
+
+                /* 11. If node is a head element, then switch the insertion mode
+                to "in body" ("in body"! not "in head"!) and abort these steps.
+                (innerHTML case) */
+            } elseif ($node->nodeName === 'head') {
+                $this->mode = self::IN_BODY;
+                break;
+
+                /* 12. If node is a body element, then switch the insertion mode to
+                "in body" and abort these steps. */
+            } elseif ($node->nodeName === 'body') {
+                $this->mode = self::IN_BODY;
+                break;
+
+                /* 13. If node is a frameset element, then switch the insertion
+                mode to "in frameset" and abort these steps. (innerHTML case) */
+            } elseif ($node->nodeName === 'frameset') {
+                $this->mode = self::IN_FRAME;
+                break;
+
+                /* 14. If node is an html element, then: if the head element
+                pointer is null, switch the insertion mode to "before head",
+                otherwise, switch the insertion mode to "after head". In either
+                case, abort these steps. (innerHTML case) */
+            } elseif ($node->nodeName === 'html') {
+                $this->mode = ($this->head_pointer === null)
+                    ? self::BEFOR_HEAD
+                    : self::AFTER_HEAD;
+
+                break;
+
+                /* 15. If last is true, then set the insertion mode to "in body"
+                and    abort these steps. (innerHTML case) */
+            } elseif ($last) {
+                $this->mode = self::IN_BODY;
+                break;
+            }
+        }
+    }
+
+    private function closeCell()
+    {
+        /* If the stack of open elements has a td or th element in table scope,
+        then act as if an end tag token with that tag name had been seen. */
+        foreach (array('td', 'th') as $cell) {
+            if ($this->elementInScope($cell, true)) {
+                $this->inCell(
+                    array(
+                        'name' => $cell,
+                        'type' => HTML5::ENDTAG
+                    )
+                );
+
+                break;
+            }
+        }
+    }
+
+    public function save()
+    {
+        return $this->dom;
+    }
+}
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Node.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Node.php
new file mode 100644 (file)
index 0000000..3995fec
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * Abstract base node class that all others inherit from.
+ *
+ * Why do we not use the DOM extension?  (1) It is not always available,
+ * (2) it has funny constraints on the data it can represent,
+ * whereas we want a maximally flexible representation, and (3) its
+ * interface is a bit cumbersome.
+ */
+abstract class HTMLPurifier_Node
+{
+    /**
+     * Line number of the start token in the source document
+     * @type int
+     */
+    public $line;
+
+    /**
+     * Column number of the start token in the source document. Null if unknown.
+     * @type int
+     */
+    public $col;
+
+    /**
+     * Lookup array of processing that this token is exempt from.
+     * Currently, valid values are "ValidateAttributes".
+     * @type array
+     */
+    public $armor = array();
+
+    /**
+     * When true, this node should be ignored as non-existent.
+     *
+     * Who is responsible for ignoring dead nodes?  FixNesting is
+     * responsible for removing them before passing on to child
+     * validators.
+     */
+    public $dead = false;
+
+    /**
+     * Returns a pair of start and end tokens, where the end token
+     * is null if it is not necessary. Does not include children.
+     * @type array
+     */
+    abstract public function toTokenPair();
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Node/Comment.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Node/Comment.php
new file mode 100644 (file)
index 0000000..38ba193
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * Concrete comment node class.
+ */
+class HTMLPurifier_Node_Comment extends HTMLPurifier_Node
+{
+    /**
+     * Character data within comment.
+     * @type string
+     */
+    public $data;
+
+    /**
+     * @type bool
+     */
+    public $is_whitespace = true;
+
+    /**
+     * Transparent constructor.
+     *
+     * @param string $data String comment data.
+     * @param int $line
+     * @param int $col
+     */
+    public function __construct($data, $line = null, $col = null)
+    {
+        $this->data = $data;
+        $this->line = $line;
+        $this->col = $col;
+    }
+
+    public function toTokenPair() {
+        return array(new HTMLPurifier_Token_Comment($this->data, $this->line, $this->col), null);
+    }
+}
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Node/Element.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Node/Element.php
new file mode 100644 (file)
index 0000000..6cbf56d
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * Concrete element node class.
+ */
+class HTMLPurifier_Node_Element extends HTMLPurifier_Node
+{
+    /**
+     * The lower-case name of the tag, like 'a', 'b' or 'blockquote'.
+     *
+     * @note Strictly speaking, XML tags are case sensitive, so we shouldn't
+     * be lower-casing them, but these tokens cater to HTML tags, which are
+     * insensitive.
+     * @type string
+     */
+    public $name;
+
+    /**
+     * Associative array of the node's attributes.
+     * @type array
+     */
+    public $attr = array();
+
+    /**
+     * List of child elements.
+     * @type array
+     */
+    public $children = array();
+
+    /**
+     * Does this use the <a></a> form or the </a> form, i.e.
+     * is it a pair of start/end tokens or an empty token.
+     * @bool
+     */
+    public $empty = false;
+
+    public $endCol = null, $endLine = null, $endArmor = array();
+
+    public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array()) {
+        $this->name = $name;
+        $this->attr = $attr;
+        $this->line = $line;
+        $this->col = $col;
+        $this->armor = $armor;
+    }
+
+    public function toTokenPair() {
+        // XXX inefficiency here, normalization is not necessary
+        if ($this->empty) {
+            return array(new HTMLPurifier_Token_Empty($this->name, $this->attr, $this->line, $this->col, $this->armor), null);
+        } else {
+            $start = new HTMLPurifier_Token_Start($this->name, $this->attr, $this->line, $this->col, $this->armor);
+            $end = new HTMLPurifier_Token_End($this->name, array(), $this->endLine, $this->endCol, $this->endArmor);
+            //$end->start = $start;
+            return array($start, $end);
+        }
+    }
+}
+
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Node/Text.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Node/Text.php
new file mode 100644 (file)
index 0000000..aec9166
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * Concrete text token class.
+ *
+ * Text tokens comprise of regular parsed character data (PCDATA) and raw
+ * character data (from the CDATA sections). Internally, their
+ * data is parsed with all entities expanded. Surprisingly, the text token
+ * does have a "tag name" called #PCDATA, which is how the DTD represents it
+ * in permissible child nodes.
+ */
+class HTMLPurifier_Node_Text extends HTMLPurifier_Node
+{
+
+    /**
+     * PCDATA tag name compatible with DTD, see
+     * HTMLPurifier_ChildDef_Custom for details.
+     * @type string
+     */
+    public $name = '#PCDATA';
+
+    /**
+     * @type string
+     */
+    public $data;
+    /**< Parsed character data of text. */
+
+    /**
+     * @type bool
+     */
+    public $is_whitespace;
+
+    /**< Bool indicating if node is whitespace. */
+
+    /**
+     * Constructor, accepts data and determines if it is whitespace.
+     * @param string $data String parsed character data.
+     * @param int $line
+     * @param int $col
+     */
+    public function __construct($data, $is_whitespace, $line = null, $col = null)
+    {
+        $this->data = $data;
+        $this->is_whitespace = $is_whitespace;
+        $this->line = $line;
+        $this->col = $col;
+    }
+
+    public function toTokenPair() {
+        return array(new HTMLPurifier_Token_Text($this->data, $this->line, $this->col), null);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/PercentEncoder.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/PercentEncoder.php
new file mode 100644 (file)
index 0000000..18c8bbb
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * Class that handles operations involving percent-encoding in URIs.
+ *
+ * @warning
+ *      Be careful when reusing instances of PercentEncoder. The object
+ *      you use for normalize() SHOULD NOT be used for encode(), or
+ *      vice-versa.
+ */
+class HTMLPurifier_PercentEncoder
+{
+
+    /**
+     * Reserved characters to preserve when using encode().
+     * @type array
+     */
+    protected $preserve = array();
+
+    /**
+     * String of characters that should be preserved while using encode().
+     * @param bool $preserve
+     */
+    public function __construct($preserve = false)
+    {
+        // unreserved letters, ought to const-ify
+        for ($i = 48; $i <= 57; $i++) { // digits
+            $this->preserve[$i] = true;
+        }
+        for ($i = 65; $i <= 90; $i++) { // upper-case
+            $this->preserve[$i] = true;
+        }
+        for ($i = 97; $i <= 122; $i++) { // lower-case
+            $this->preserve[$i] = true;
+        }
+        $this->preserve[45] = true; // Dash         -
+        $this->preserve[46] = true; // Period       .
+        $this->preserve[95] = true; // Underscore   _
+        $this->preserve[126]= true; // Tilde        ~
+
+        // extra letters not to escape
+        if ($preserve !== false) {
+            for ($i = 0, $c = strlen($preserve); $i < $c; $i++) {
+                $this->preserve[ord($preserve[$i])] = true;
+            }
+        }
+    }
+
+    /**
+     * Our replacement for urlencode, it encodes all non-reserved characters,
+     * as well as any extra characters that were instructed to be preserved.
+     * @note
+     *      Assumes that the string has already been normalized, making any
+     *      and all percent escape sequences valid. Percents will not be
+     *      re-escaped, regardless of their status in $preserve
+     * @param string $string String to be encoded
+     * @return string Encoded string.
+     */
+    public function encode($string)
+    {
+        $ret = '';
+        for ($i = 0, $c = strlen($string); $i < $c; $i++) {
+            if ($string[$i] !== '%' && !isset($this->preserve[$int = ord($string[$i])])) {
+                $ret .= '%' . sprintf('%02X', $int);
+            } else {
+                $ret .= $string[$i];
+            }
+        }
+        return $ret;
+    }
+
+    /**
+     * Fix up percent-encoding by decoding unreserved characters and normalizing.
+     * @warning This function is affected by $preserve, even though the
+     *          usual desired behavior is for this not to preserve those
+     *          characters. Be careful when reusing instances of PercentEncoder!
+     * @param string $string String to normalize
+     * @return string
+     */
+    public function normalize($string)
+    {
+        if ($string == '') {
+            return '';
+        }
+        $parts = explode('%', $string);
+        $ret = array_shift($parts);
+        foreach ($parts as $part) {
+            $length = strlen($part);
+            if ($length < 2) {
+                $ret .= '%25' . $part;
+                continue;
+            }
+            $encoding = substr($part, 0, 2);
+            $text     = substr($part, 2);
+            if (!ctype_xdigit($encoding)) {
+                $ret .= '%25' . $part;
+                continue;
+            }
+            $int = hexdec($encoding);
+            if (isset($this->preserve[$int])) {
+                $ret .= chr($int) . $text;
+                continue;
+            }
+            $encoding = strtoupper($encoding);
+            $ret .= '%' . $encoding . $text;
+        }
+        return $ret;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer.php
new file mode 100644 (file)
index 0000000..549e4ce
--- /dev/null
@@ -0,0 +1,218 @@
+<?php
+
+// OUT OF DATE, NEEDS UPDATING!
+// USE XMLWRITER!
+
+class HTMLPurifier_Printer
+{
+
+    /**
+     * For HTML generation convenience funcs.
+     * @type HTMLPurifier_Generator
+     */
+    protected $generator;
+
+    /**
+     * For easy access.
+     * @type HTMLPurifier_Config
+     */
+    protected $config;
+
+    /**
+     * Initialize $generator.
+     */
+    public function __construct()
+    {
+    }
+
+    /**
+     * Give generator necessary configuration if possible
+     * @param HTMLPurifier_Config $config
+     */
+    public function prepareGenerator($config)
+    {
+        $all = $config->getAll();
+        $context = new HTMLPurifier_Context();
+        $this->generator = new HTMLPurifier_Generator($config, $context);
+    }
+
+    /**
+     * Main function that renders object or aspect of that object
+     * @note Parameters vary depending on printer
+     */
+    // function render() {}
+
+    /**
+     * Returns a start tag
+     * @param string $tag Tag name
+     * @param array $attr Attribute array
+     * @return string
+     */
+    protected function start($tag, $attr = array())
+    {
+        return $this->generator->generateFromToken(
+            new HTMLPurifier_Token_Start($tag, $attr ? $attr : array())
+        );
+    }
+
+    /**
+     * Returns an end tag
+     * @param string $tag Tag name
+     * @return string
+     */
+    protected function end($tag)
+    {
+        return $this->generator->generateFromToken(
+            new HTMLPurifier_Token_End($tag)
+        );
+    }
+
+    /**
+     * Prints a complete element with content inside
+     * @param string $tag Tag name
+     * @param string $contents Element contents
+     * @param array $attr Tag attributes
+     * @param bool $escape whether or not to escape contents
+     * @return string
+     */
+    protected function element($tag, $contents, $attr = array(), $escape = true)
+    {
+        return $this->start($tag, $attr) .
+            ($escape ? $this->escape($contents) : $contents) .
+            $this->end($tag);
+    }
+
+    /**
+     * @param string $tag
+     * @param array $attr
+     * @return string
+     */
+    protected function elementEmpty($tag, $attr = array())
+    {
+        return $this->generator->generateFromToken(
+            new HTMLPurifier_Token_Empty($tag, $attr)
+        );
+    }
+
+    /**
+     * @param string $text
+     * @return string
+     */
+    protected function text($text)
+    {
+        return $this->generator->generateFromToken(
+            new HTMLPurifier_Token_Text($text)
+        );
+    }
+
+    /**
+     * Prints a simple key/value row in a table.
+     * @param string $name Key
+     * @param mixed $value Value
+     * @return string
+     */
+    protected function row($name, $value)
+    {
+        if (is_bool($value)) {
+            $value = $value ? 'On' : 'Off';
+        }
+        return
+            $this->start('tr') . "\n" .
+            $this->element('th', $name) . "\n" .
+            $this->element('td', $value) . "\n" .
+            $this->end('tr');
+    }
+
+    /**
+     * Escapes a string for HTML output.
+     * @param string $string String to escape
+     * @return string
+     */
+    protected function escape($string)
+    {
+        $string = HTMLPurifier_Encoder::cleanUTF8($string);
+        $string = htmlspecialchars($string, ENT_COMPAT, 'UTF-8');
+        return $string;
+    }
+
+    /**
+     * Takes a list of strings and turns them into a single list
+     * @param string[] $array List of strings
+     * @param bool $polite Bool whether or not to add an end before the last
+     * @return string
+     */
+    protected function listify($array, $polite = false)
+    {
+        if (empty($array)) {
+            return 'None';
+        }
+        $ret = '';
+        $i = count($array);
+        foreach ($array as $value) {
+            $i--;
+            $ret .= $value;
+            if ($i > 0 && !($polite && $i == 1)) {
+                $ret .= ', ';
+            }
+            if ($polite && $i == 1) {
+                $ret .= 'and ';
+            }
+        }
+        return $ret;
+    }
+
+    /**
+     * Retrieves the class of an object without prefixes, as well as metadata
+     * @param object $obj Object to determine class of
+     * @param string $sec_prefix Further prefix to remove
+     * @return string
+     */
+    protected function getClass($obj, $sec_prefix = '')
+    {
+        static $five = null;
+        if ($five === null) {
+            $five = version_compare(PHP_VERSION, '5', '>=');
+        }
+        $prefix = 'HTMLPurifier_' . $sec_prefix;
+        if (!$five) {
+            $prefix = strtolower($prefix);
+        }
+        $class = str_replace($prefix, '', get_class($obj));
+        $lclass = strtolower($class);
+        $class .= '(';
+        switch ($lclass) {
+            case 'enum':
+                $values = array();
+                foreach ($obj->valid_values as $value => $bool) {
+                    $values[] = $value;
+                }
+                $class .= implode(', ', $values);
+                break;
+            case 'css_composite':
+                $values = array();
+                foreach ($obj->defs as $def) {
+                    $values[] = $this->getClass($def, $sec_prefix);
+                }
+                $class .= implode(', ', $values);
+                break;
+            case 'css_multiple':
+                $class .= $this->getClass($obj->single, $sec_prefix) . ', ';
+                $class .= $obj->max;
+                break;
+            case 'css_denyelementdecorator':
+                $class .= $this->getClass($obj->def, $sec_prefix) . ', ';
+                $class .= $obj->element;
+                break;
+            case 'css_importantdecorator':
+                $class .= $this->getClass($obj->def, $sec_prefix);
+                if ($obj->allow) {
+                    $class .= ', !important';
+                }
+                break;
+        }
+        $class .= ')';
+        return $class;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php
new file mode 100644 (file)
index 0000000..29505fe
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+class HTMLPurifier_Printer_CSSDefinition extends HTMLPurifier_Printer
+{
+    /**
+     * @type HTMLPurifier_CSSDefinition
+     */
+    protected $def;
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return string
+     */
+    public function render($config)
+    {
+        $this->def = $config->getCSSDefinition();
+        $ret = '';
+
+        $ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer'));
+        $ret .= $this->start('table');
+
+        $ret .= $this->element('caption', 'Properties ($info)');
+
+        $ret .= $this->start('thead');
+        $ret .= $this->start('tr');
+        $ret .= $this->element('th', 'Property', array('class' => 'heavy'));
+        $ret .= $this->element('th', 'Definition', array('class' => 'heavy', 'style' => 'width:auto;'));
+        $ret .= $this->end('tr');
+        $ret .= $this->end('thead');
+
+        ksort($this->def->info);
+        foreach ($this->def->info as $property => $obj) {
+            $name = $this->getClass($obj, 'AttrDef_');
+            $ret .= $this->row($property, $name);
+        }
+
+        $ret .= $this->end('table');
+        $ret .= $this->end('div');
+
+        return $ret;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css b/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css
new file mode 100644 (file)
index 0000000..3ff1a88
--- /dev/null
@@ -0,0 +1,10 @@
+
+.hp-config {}
+
+.hp-config tbody th {text-align:right; padding-right:0.5em;}
+.hp-config thead, .hp-config .namespace {background:#3C578C; color:#FFF;}
+.hp-config .namespace th {text-align:center;}
+.hp-config .verbose {display:none;}
+.hp-config .controls {text-align:center;}
+
+/* vim: et sw=4 sts=4 */
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js b/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js
new file mode 100644 (file)
index 0000000..cba00c9
--- /dev/null
@@ -0,0 +1,5 @@
+function toggleWriteability(id_of_patient, checked) {
+    document.getElementById(id_of_patient).disabled = checked;
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php
new file mode 100644 (file)
index 0000000..36100ce
--- /dev/null
@@ -0,0 +1,447 @@
+<?php
+
+/**
+ * @todo Rewrite to use Interchange objects
+ */
+class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
+{
+
+    /**
+     * Printers for specific fields.
+     * @type HTMLPurifier_Printer[]
+     */
+    protected $fields = array();
+
+    /**
+     * Documentation URL, can have fragment tagged on end.
+     * @type string
+     */
+    protected $docURL;
+
+    /**
+     * Name of form element to stuff config in.
+     * @type string
+     */
+    protected $name;
+
+    /**
+     * Whether or not to compress directive names, clipping them off
+     * after a certain amount of letters. False to disable or integer letters
+     * before clipping.
+     * @type bool
+     */
+    protected $compress = false;
+
+    /**
+     * @param string $name Form element name for directives to be stuffed into
+     * @param string $doc_url String documentation URL, will have fragment tagged on
+     * @param bool $compress Integer max length before compressing a directive name, set to false to turn off
+     */
+    public function __construct(
+        $name,
+        $doc_url = null,
+        $compress = false
+    ) {
+        parent::__construct();
+        $this->docURL = $doc_url;
+        $this->name = $name;
+        $this->compress = $compress;
+        // initialize sub-printers
+        $this->fields[0] = new HTMLPurifier_Printer_ConfigForm_default();
+        $this->fields[HTMLPurifier_VarParser::BOOL] = new HTMLPurifier_Printer_ConfigForm_bool();
+    }
+
+    /**
+     * Sets default column and row size for textareas in sub-printers
+     * @param $cols Integer columns of textarea, null to use default
+     * @param $rows Integer rows of textarea, null to use default
+     */
+    public function setTextareaDimensions($cols = null, $rows = null)
+    {
+        if ($cols) {
+            $this->fields['default']->cols = $cols;
+        }
+        if ($rows) {
+            $this->fields['default']->rows = $rows;
+        }
+    }
+
+    /**
+     * Retrieves styling, in case it is not accessible by webserver
+     */
+    public static function getCSS()
+    {
+        return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.css');
+    }
+
+    /**
+     * Retrieves JavaScript, in case it is not accessible by webserver
+     */
+    public static function getJavaScript()
+    {
+        return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.js');
+    }
+
+    /**
+     * Returns HTML output for a configuration form
+     * @param HTMLPurifier_Config|array $config Configuration object of current form state, or an array
+     *        where [0] has an HTML namespace and [1] is being rendered.
+     * @param array|bool $allowed Optional namespace(s) and directives to restrict form to.
+     * @param bool $render_controls
+     * @return string
+     */
+    public function render($config, $allowed = true, $render_controls = true)
+    {
+        if (is_array($config) && isset($config[0])) {
+            $gen_config = $config[0];
+            $config = $config[1];
+        } else {
+            $gen_config = $config;
+        }
+
+        $this->config = $config;
+        $this->genConfig = $gen_config;
+        $this->prepareGenerator($gen_config);
+
+        $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $config->def);
+        $all = array();
+        foreach ($allowed as $key) {
+            list($ns, $directive) = $key;
+            $all[$ns][$directive] = $config->get($ns . '.' . $directive);
+        }
+
+        $ret = '';
+        $ret .= $this->start('table', array('class' => 'hp-config'));
+        $ret .= $this->start('thead');
+        $ret .= $this->start('tr');
+        $ret .= $this->element('th', 'Directive', array('class' => 'hp-directive'));
+        $ret .= $this->element('th', 'Value', array('class' => 'hp-value'));
+        $ret .= $this->end('tr');
+        $ret .= $this->end('thead');
+        foreach ($all as $ns => $directives) {
+            $ret .= $this->renderNamespace($ns, $directives);
+        }
+        if ($render_controls) {
+            $ret .= $this->start('tbody');
+            $ret .= $this->start('tr');
+            $ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls'));
+            $ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit'));
+            $ret .= '[<a href="?">Reset</a>]';
+            $ret .= $this->end('td');
+            $ret .= $this->end('tr');
+            $ret .= $this->end('tbody');
+        }
+        $ret .= $this->end('table');
+        return $ret;
+    }
+
+    /**
+     * Renders a single namespace
+     * @param $ns String namespace name
+     * @param array $directives array of directives to values
+     * @return string
+     */
+    protected function renderNamespace($ns, $directives)
+    {
+        $ret = '';
+        $ret .= $this->start('tbody', array('class' => 'namespace'));
+        $ret .= $this->start('tr');
+        $ret .= $this->element('th', $ns, array('colspan' => 2));
+        $ret .= $this->end('tr');
+        $ret .= $this->end('tbody');
+        $ret .= $this->start('tbody');
+        foreach ($directives as $directive => $value) {
+            $ret .= $this->start('tr');
+            $ret .= $this->start('th');
+            if ($this->docURL) {
+                $url = str_replace('%s', urlencode("$ns.$directive"), $this->docURL);
+                $ret .= $this->start('a', array('href' => $url));
+            }
+            $attr = array('for' => "{$this->name}:$ns.$directive");
+
+            // crop directive name if it's too long
+            if (!$this->compress || (strlen($directive) < $this->compress)) {
+                $directive_disp = $directive;
+            } else {
+                $directive_disp = substr($directive, 0, $this->compress - 2) . '...';
+                $attr['title'] = $directive;
+            }
+
+            $ret .= $this->element(
+                'label',
+                $directive_disp,
+                // component printers must create an element with this id
+                $attr
+            );
+            if ($this->docURL) {
+                $ret .= $this->end('a');
+            }
+            $ret .= $this->end('th');
+
+            $ret .= $this->start('td');
+            $def = $this->config->def->info["$ns.$directive"];
+            if (is_int($def)) {
+                $allow_null = $def < 0;
+                $type = abs($def);
+            } else {
+                $type = $def->type;
+                $allow_null = isset($def->allow_null);
+            }
+            if (!isset($this->fields[$type])) {
+                $type = 0;
+            } // default
+            $type_obj = $this->fields[$type];
+            if ($allow_null) {
+                $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj);
+            }
+            $ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config));
+            $ret .= $this->end('td');
+            $ret .= $this->end('tr');
+        }
+        $ret .= $this->end('tbody');
+        return $ret;
+    }
+
+}
+
+/**
+ * Printer decorator for directives that accept null
+ */
+class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer
+{
+    /**
+     * Printer being decorated
+     * @type HTMLPurifier_Printer
+     */
+    protected $obj;
+
+    /**
+     * @param HTMLPurifier_Printer $obj Printer to decorate
+     */
+    public function __construct($obj)
+    {
+        parent::__construct();
+        $this->obj = $obj;
+    }
+
+    /**
+     * @param string $ns
+     * @param string $directive
+     * @param string $value
+     * @param string $name
+     * @param HTMLPurifier_Config|array $config
+     * @return string
+     */
+    public function render($ns, $directive, $value, $name, $config)
+    {
+        if (is_array($config) && isset($config[0])) {
+            $gen_config = $config[0];
+            $config = $config[1];
+        } else {
+            $gen_config = $config;
+        }
+        $this->prepareGenerator($gen_config);
+
+        $ret = '';
+        $ret .= $this->start('label', array('for' => "$name:Null_$ns.$directive"));
+        $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
+        $ret .= $this->text(' Null/Disabled');
+        $ret .= $this->end('label');
+        $attr = array(
+            'type' => 'checkbox',
+            'value' => '1',
+            'class' => 'null-toggle',
+            'name' => "$name" . "[Null_$ns.$directive]",
+            'id' => "$name:Null_$ns.$directive",
+            'onclick' => "toggleWriteability('$name:$ns.$directive',checked)" // INLINE JAVASCRIPT!!!!
+        );
+        if ($this->obj instanceof HTMLPurifier_Printer_ConfigForm_bool) {
+            // modify inline javascript slightly
+            $attr['onclick'] =
+                "toggleWriteability('$name:Yes_$ns.$directive',checked);" .
+                "toggleWriteability('$name:No_$ns.$directive',checked)";
+        }
+        if ($value === null) {
+            $attr['checked'] = 'checked';
+        }
+        $ret .= $this->elementEmpty('input', $attr);
+        $ret .= $this->text(' or ');
+        $ret .= $this->elementEmpty('br');
+        $ret .= $this->obj->render($ns, $directive, $value, $name, array($gen_config, $config));
+        return $ret;
+    }
+}
+
+/**
+ * Swiss-army knife configuration form field printer
+ */
+class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer
+{
+    /**
+     * @type int
+     */
+    public $cols = 18;
+
+    /**
+     * @type int
+     */
+    public $rows = 5;
+
+    /**
+     * @param string $ns
+     * @param string $directive
+     * @param string $value
+     * @param string $name
+     * @param HTMLPurifier_Config|array $config
+     * @return string
+     */
+    public function render($ns, $directive, $value, $name, $config)
+    {
+        if (is_array($config) && isset($config[0])) {
+            $gen_config = $config[0];
+            $config = $config[1];
+        } else {
+            $gen_config = $config;
+        }
+        $this->prepareGenerator($gen_config);
+        // this should probably be split up a little
+        $ret = '';
+        $def = $config->def->info["$ns.$directive"];
+        if (is_int($def)) {
+            $type = abs($def);
+        } else {
+            $type = $def->type;
+        }
+        if (is_array($value)) {
+            switch ($type) {
+                case HTMLPurifier_VarParser::LOOKUP:
+                    $array = $value;
+                    $value = array();
+                    foreach ($array as $val => $b) {
+                        $value[] = $val;
+                    }
+                    //TODO does this need a break?
+                case HTMLPurifier_VarParser::ALIST:
+                    $value = implode(PHP_EOL, $value);
+                    break;
+                case HTMLPurifier_VarParser::HASH:
+                    $nvalue = '';
+                    foreach ($value as $i => $v) {
+                        $nvalue .= "$i:$v" . PHP_EOL;
+                    }
+                    $value = $nvalue;
+                    break;
+                default:
+                    $value = '';
+            }
+        }
+        if ($type === HTMLPurifier_VarParser::MIXED) {
+            return 'Not supported';
+            $value = serialize($value);
+        }
+        $attr = array(
+            'name' => "$name" . "[$ns.$directive]",
+            'id' => "$name:$ns.$directive"
+        );
+        if ($value === null) {
+            $attr['disabled'] = 'disabled';
+        }
+        if (isset($def->allowed)) {
+            $ret .= $this->start('select', $attr);
+            foreach ($def->allowed as $val => $b) {
+                $attr = array();
+                if ($value == $val) {
+                    $attr['selected'] = 'selected';
+                }
+                $ret .= $this->element('option', $val, $attr);
+            }
+            $ret .= $this->end('select');
+        } elseif ($type === HTMLPurifier_VarParser::TEXT ||
+                $type === HTMLPurifier_VarParser::ITEXT ||
+                $type === HTMLPurifier_VarParser::ALIST ||
+                $type === HTMLPurifier_VarParser::HASH ||
+                $type === HTMLPurifier_VarParser::LOOKUP) {
+            $attr['cols'] = $this->cols;
+            $attr['rows'] = $this->rows;
+            $ret .= $this->start('textarea', $attr);
+            $ret .= $this->text($value);
+            $ret .= $this->end('textarea');
+        } else {
+            $attr['value'] = $value;
+            $attr['type'] = 'text';
+            $ret .= $this->elementEmpty('input', $attr);
+        }
+        return $ret;
+    }
+}
+
+/**
+ * Bool form field printer
+ */
+class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer
+{
+    /**
+     * @param string $ns
+     * @param string $directive
+     * @param string $value
+     * @param string $name
+     * @param HTMLPurifier_Config|array $config
+     * @return string
+     */
+    public function render($ns, $directive, $value, $name, $config)
+    {
+        if (is_array($config) && isset($config[0])) {
+            $gen_config = $config[0];
+            $config = $config[1];
+        } else {
+            $gen_config = $config;
+        }
+        $this->prepareGenerator($gen_config);
+        $ret = '';
+        $ret .= $this->start('div', array('id' => "$name:$ns.$directive"));
+
+        $ret .= $this->start('label', array('for' => "$name:Yes_$ns.$directive"));
+        $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
+        $ret .= $this->text(' Yes');
+        $ret .= $this->end('label');
+
+        $attr = array(
+            'type' => 'radio',
+            'name' => "$name" . "[$ns.$directive]",
+            'id' => "$name:Yes_$ns.$directive",
+            'value' => '1'
+        );
+        if ($value === true) {
+            $attr['checked'] = 'checked';
+        }
+        if ($value === null) {
+            $attr['disabled'] = 'disabled';
+        }
+        $ret .= $this->elementEmpty('input', $attr);
+
+        $ret .= $this->start('label', array('for' => "$name:No_$ns.$directive"));
+        $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
+        $ret .= $this->text(' No');
+        $ret .= $this->end('label');
+
+        $attr = array(
+            'type' => 'radio',
+            'name' => "$name" . "[$ns.$directive]",
+            'id' => "$name:No_$ns.$directive",
+            'value' => '0'
+        );
+        if ($value === false) {
+            $attr['checked'] = 'checked';
+        }
+        if ($value === null) {
+            $attr['disabled'] = 'disabled';
+        }
+        $ret .= $this->elementEmpty('input', $attr);
+
+        $ret .= $this->end('div');
+
+        return $ret;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php
new file mode 100644 (file)
index 0000000..5f2f2f8
--- /dev/null
@@ -0,0 +1,324 @@
+<?php
+
+class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
+{
+
+    /**
+     * @type HTMLPurifier_HTMLDefinition, for easy access
+     */
+    protected $def;
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return string
+     */
+    public function render($config)
+    {
+        $ret = '';
+        $this->config =& $config;
+
+        $this->def = $config->getHTMLDefinition();
+
+        $ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer'));
+
+        $ret .= $this->renderDoctype();
+        $ret .= $this->renderEnvironment();
+        $ret .= $this->renderContentSets();
+        $ret .= $this->renderInfo();
+
+        $ret .= $this->end('div');
+
+        return $ret;
+    }
+
+    /**
+     * Renders the Doctype table
+     * @return string
+     */
+    protected function renderDoctype()
+    {
+        $doctype = $this->def->doctype;
+        $ret = '';
+        $ret .= $this->start('table');
+        $ret .= $this->element('caption', 'Doctype');
+        $ret .= $this->row('Name', $doctype->name);
+        $ret .= $this->row('XML', $doctype->xml ? 'Yes' : 'No');
+        $ret .= $this->row('Default Modules', implode($doctype->modules, ', '));
+        $ret .= $this->row('Default Tidy Modules', implode($doctype->tidyModules, ', '));
+        $ret .= $this->end('table');
+        return $ret;
+    }
+
+
+    /**
+     * Renders environment table, which is miscellaneous info
+     * @return string
+     */
+    protected function renderEnvironment()
+    {
+        $def = $this->def;
+
+        $ret = '';
+
+        $ret .= $this->start('table');
+        $ret .= $this->element('caption', 'Environment');
+
+        $ret .= $this->row('Parent of fragment', $def->info_parent);
+        $ret .= $this->renderChildren($def->info_parent_def->child);
+        $ret .= $this->row('Block wrap name', $def->info_block_wrapper);
+
+        $ret .= $this->start('tr');
+        $ret .= $this->element('th', 'Global attributes');
+        $ret .= $this->element('td', $this->listifyAttr($def->info_global_attr), null, 0);
+        $ret .= $this->end('tr');
+
+        $ret .= $this->start('tr');
+        $ret .= $this->element('th', 'Tag transforms');
+        $list = array();
+        foreach ($def->info_tag_transform as $old => $new) {
+            $new = $this->getClass($new, 'TagTransform_');
+            $list[] = "<$old> with $new";
+        }
+        $ret .= $this->element('td', $this->listify($list));
+        $ret .= $this->end('tr');
+
+        $ret .= $this->start('tr');
+        $ret .= $this->element('th', 'Pre-AttrTransform');
+        $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_pre));
+        $ret .= $this->end('tr');
+
+        $ret .= $this->start('tr');
+        $ret .= $this->element('th', 'Post-AttrTransform');
+        $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_post));
+        $ret .= $this->end('tr');
+
+        $ret .= $this->end('table');
+        return $ret;
+    }
+
+    /**
+     * Renders the Content Sets table
+     * @return string
+     */
+    protected function renderContentSets()
+    {
+        $ret = '';
+        $ret .= $this->start('table');
+        $ret .= $this->element('caption', 'Content Sets');
+        foreach ($this->def->info_content_sets as $name => $lookup) {
+            $ret .= $this->heavyHeader($name);
+            $ret .= $this->start('tr');
+            $ret .= $this->element('td', $this->listifyTagLookup($lookup));
+            $ret .= $this->end('tr');
+        }
+        $ret .= $this->end('table');
+        return $ret;
+    }
+
+    /**
+     * Renders the Elements ($info) table
+     * @return string
+     */
+    protected function renderInfo()
+    {
+        $ret = '';
+        $ret .= $this->start('table');
+        $ret .= $this->element('caption', 'Elements ($info)');
+        ksort($this->def->info);
+        $ret .= $this->heavyHeader('Allowed tags', 2);
+        $ret .= $this->start('tr');
+        $ret .= $this->element('td', $this->listifyTagLookup($this->def->info), array('colspan' => 2));
+        $ret .= $this->end('tr');
+        foreach ($this->def->info as $name => $def) {
+            $ret .= $this->start('tr');
+            $ret .= $this->element('th', "<$name>", array('class' => 'heavy', 'colspan' => 2));
+            $ret .= $this->end('tr');
+            $ret .= $this->start('tr');
+            $ret .= $this->element('th', 'Inline content');
+            $ret .= $this->element('td', $def->descendants_are_inline ? 'Yes' : 'No');
+            $ret .= $this->end('tr');
+            if (!empty($def->excludes)) {
+                $ret .= $this->start('tr');
+                $ret .= $this->element('th', 'Excludes');
+                $ret .= $this->element('td', $this->listifyTagLookup($def->excludes));
+                $ret .= $this->end('tr');
+            }
+            if (!empty($def->attr_transform_pre)) {
+                $ret .= $this->start('tr');
+                $ret .= $this->element('th', 'Pre-AttrTransform');
+                $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_pre));
+                $ret .= $this->end('tr');
+            }
+            if (!empty($def->attr_transform_post)) {
+                $ret .= $this->start('tr');
+                $ret .= $this->element('th', 'Post-AttrTransform');
+                $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_post));
+                $ret .= $this->end('tr');
+            }
+            if (!empty($def->auto_close)) {
+                $ret .= $this->start('tr');
+                $ret .= $this->element('th', 'Auto closed by');
+                $ret .= $this->element('td', $this->listifyTagLookup($def->auto_close));
+                $ret .= $this->end('tr');
+            }
+            $ret .= $this->start('tr');
+            $ret .= $this->element('th', 'Allowed attributes');
+            $ret .= $this->element('td', $this->listifyAttr($def->attr), array(), 0);
+            $ret .= $this->end('tr');
+
+            if (!empty($def->required_attr)) {
+                $ret .= $this->row('Required attributes', $this->listify($def->required_attr));
+            }
+
+            $ret .= $this->renderChildren($def->child);
+        }
+        $ret .= $this->end('table');
+        return $ret;
+    }
+
+    /**
+     * Renders a row describing the allowed children of an element
+     * @param HTMLPurifier_ChildDef $def HTMLPurifier_ChildDef of pertinent element
+     * @return string
+     */
+    protected function renderChildren($def)
+    {
+        $context = new HTMLPurifier_Context();
+        $ret = '';
+        $ret .= $this->start('tr');
+        $elements = array();
+        $attr = array();
+        if (isset($def->elements)) {
+            if ($def->type == 'strictblockquote') {
+                $def->validateChildren(array(), $this->config, $context);
+            }
+            $elements = $def->elements;
+        }
+        if ($def->type == 'chameleon') {
+            $attr['rowspan'] = 2;
+        } elseif ($def->type == 'empty') {
+            $elements = array();
+        } elseif ($def->type == 'table') {
+            $elements = array_flip(
+                array(
+                    'col',
+                    'caption',
+                    'colgroup',
+                    'thead',
+                    'tfoot',
+                    'tbody',
+                    'tr'
+                )
+            );
+        }
+        $ret .= $this->element('th', 'Allowed children', $attr);
+
+        if ($def->type == 'chameleon') {
+
+            $ret .= $this->element(
+                'td',
+                '<em>Block</em>: ' .
+                $this->escape($this->listifyTagLookup($def->block->elements)),
+                null,
+                0
+            );
+            $ret .= $this->end('tr');
+            $ret .= $this->start('tr');
+            $ret .= $this->element(
+                'td',
+                '<em>Inline</em>: ' .
+                $this->escape($this->listifyTagLookup($def->inline->elements)),
+                null,
+                0
+            );
+
+        } elseif ($def->type == 'custom') {
+
+            $ret .= $this->element(
+                'td',
+                '<em>' . ucfirst($def->type) . '</em>: ' .
+                $def->dtd_regex
+            );
+
+        } else {
+            $ret .= $this->element(
+                'td',
+                '<em>' . ucfirst($def->type) . '</em>: ' .
+                $this->escape($this->listifyTagLookup($elements)),
+                null,
+                0
+            );
+        }
+        $ret .= $this->end('tr');
+        return $ret;
+    }
+
+    /**
+     * Listifies a tag lookup table.
+     * @param array $array Tag lookup array in form of array('tagname' => true)
+     * @return string
+     */
+    protected function listifyTagLookup($array)
+    {
+        ksort($array);
+        $list = array();
+        foreach ($array as $name => $discard) {
+            if ($name !== '#PCDATA' && !isset($this->def->info[$name])) {
+                continue;
+            }
+            $list[] = $name;
+        }
+        return $this->listify($list);
+    }
+
+    /**
+     * Listifies a list of objects by retrieving class names and internal state
+     * @param array $array List of objects
+     * @return string
+     * @todo Also add information about internal state
+     */
+    protected function listifyObjectList($array)
+    {
+        ksort($array);
+        $list = array();
+        foreach ($array as $obj) {
+            $list[] = $this->getClass($obj, 'AttrTransform_');
+        }
+        return $this->listify($list);
+    }
+
+    /**
+     * Listifies a hash of attributes to AttrDef classes
+     * @param array $array Array hash in form of array('attrname' => HTMLPurifier_AttrDef)
+     * @return string
+     */
+    protected function listifyAttr($array)
+    {
+        ksort($array);
+        $list = array();
+        foreach ($array as $name => $obj) {
+            if ($obj === false) {
+                continue;
+            }
+            $list[] = "$name&nbsp;=&nbsp;<i>" . $this->getClass($obj, 'AttrDef_') . '</i>';
+        }
+        return $this->listify($list);
+    }
+
+    /**
+     * Creates a heavy header row
+     * @param string $text
+     * @param int $num
+     * @return string
+     */
+    protected function heavyHeader($text, $num = 1)
+    {
+        $ret = '';
+        $ret .= $this->start('tr');
+        $ret .= $this->element('th', $text, array('colspan' => $num, 'class' => 'heavy'));
+        $ret .= $this->end('tr');
+        return $ret;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/PropertyList.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/PropertyList.php
new file mode 100644 (file)
index 0000000..189348f
--- /dev/null
@@ -0,0 +1,122 @@
+<?php
+
+/**
+ * Generic property list implementation
+ */
+class HTMLPurifier_PropertyList
+{
+    /**
+     * Internal data-structure for properties.
+     * @type array
+     */
+    protected $data = array();
+
+    /**
+     * Parent plist.
+     * @type HTMLPurifier_PropertyList
+     */
+    protected $parent;
+
+    /**
+     * Cache.
+     * @type array
+     */
+    protected $cache;
+
+    /**
+     * @param HTMLPurifier_PropertyList $parent Parent plist
+     */
+    public function __construct($parent = null)
+    {
+        $this->parent = $parent;
+    }
+
+    /**
+     * Recursively retrieves the value for a key
+     * @param string $name
+     * @throws HTMLPurifier_Exception
+     */
+    public function get($name)
+    {
+        if ($this->has($name)) {
+            return $this->data[$name];
+        }
+        // possible performance bottleneck, convert to iterative if necessary
+        if ($this->parent) {
+            return $this->parent->get($name);
+        }
+        throw new HTMLPurifier_Exception("Key '$name' not found");
+    }
+
+    /**
+     * Sets the value of a key, for this plist
+     * @param string $name
+     * @param mixed $value
+     */
+    public function set($name, $value)
+    {
+        $this->data[$name] = $value;
+    }
+
+    /**
+     * Returns true if a given key exists
+     * @param string $name
+     * @return bool
+     */
+    public function has($name)
+    {
+        return array_key_exists($name, $this->data);
+    }
+
+    /**
+     * Resets a value to the value of it's parent, usually the default. If
+     * no value is specified, the entire plist is reset.
+     * @param string $name
+     */
+    public function reset($name = null)
+    {
+        if ($name == null) {
+            $this->data = array();
+        } else {
+            unset($this->data[$name]);
+        }
+    }
+
+    /**
+     * Squashes this property list and all of its property lists into a single
+     * array, and returns the array. This value is cached by default.
+     * @param bool $force If true, ignores the cache and regenerates the array.
+     * @return array
+     */
+    public function squash($force = false)
+    {
+        if ($this->cache !== null && !$force) {
+            return $this->cache;
+        }
+        if ($this->parent) {
+            return $this->cache = array_merge($this->parent->squash($force), $this->data);
+        } else {
+            return $this->cache = $this->data;
+        }
+    }
+
+    /**
+     * Returns the parent plist.
+     * @return HTMLPurifier_PropertyList
+     */
+    public function getParent()
+    {
+        return $this->parent;
+    }
+
+    /**
+     * Sets the parent plist.
+     * @param HTMLPurifier_PropertyList $plist Parent plist
+     */
+    public function setParent($plist)
+    {
+        $this->parent = $plist;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php
new file mode 100644 (file)
index 0000000..15b330e
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * Property list iterator. Do not instantiate this class directly.
+ */
+class HTMLPurifier_PropertyListIterator extends FilterIterator
+{
+
+    /**
+     * @type int
+     */
+    protected $l;
+    /**
+     * @type string
+     */
+    protected $filter;
+
+    /**
+     * @param Iterator $iterator Array of data to iterate over
+     * @param string $filter Optional prefix to only allow values of
+     */
+    public function __construct(Iterator $iterator, $filter = null)
+    {
+        parent::__construct($iterator);
+        $this->l = strlen($filter);
+        $this->filter = $filter;
+    }
+
+    /**
+     * @return bool
+     */
+    public function accept()
+    {
+        $key = $this->getInnerIterator()->key();
+        if (strncmp($key, $this->filter, $this->l) !== 0) {
+            return false;
+        }
+        return true;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Queue.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Queue.php
new file mode 100644 (file)
index 0000000..f58db90
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * A simple array-backed queue, based off of the classic Okasaki
+ * persistent amortized queue.  The basic idea is to maintain two
+ * stacks: an input stack and an output stack.  When the output
+ * stack runs out, reverse the input stack and use it as the output
+ * stack.
+ *
+ * We don't use the SPL implementation because it's only supported
+ * on PHP 5.3 and later.
+ *
+ * Exercise: Prove that push/pop on this queue take amortized O(1) time.
+ *
+ * Exercise: Extend this queue to be a deque, while preserving amortized
+ * O(1) time.  Some care must be taken on rebalancing to avoid quadratic
+ * behaviour caused by repeatedly shuffling data from the input stack
+ * to the output stack and back.
+ */
+class HTMLPurifier_Queue {
+    private $input;
+    private $output;
+
+    public function __construct($input = array()) {
+        $this->input = $input;
+        $this->output = array();
+    }
+
+    /**
+     * Shifts an element off the front of the queue.
+     */
+    public function shift() {
+        if (empty($this->output)) {
+            $this->output = array_reverse($this->input);
+            $this->input = array();
+        }
+        if (empty($this->output)) {
+            return NULL;
+        }
+        return array_pop($this->output);
+    }
+
+    /**
+     * Pushes an element onto the front of the queue.
+     */
+    public function push($x) {
+        array_push($this->input, $x);
+    }
+
+    /**
+     * Checks if it's empty.
+     */
+    public function isEmpty() {
+        return empty($this->input) && empty($this->output);
+    }
+}
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy.php
new file mode 100644 (file)
index 0000000..e1ff3b7
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * Supertype for classes that define a strategy for modifying/purifying tokens.
+ *
+ * While HTMLPurifier's core purpose is fixing HTML into something proper,
+ * strategies provide plug points for extra configuration or even extra
+ * features, such as custom tags, custom parsing of text, etc.
+ */
+
+
+abstract class HTMLPurifier_Strategy
+{
+
+    /**
+     * Executes the strategy on the tokens.
+     *
+     * @param HTMLPurifier_Token[] $tokens Array of HTMLPurifier_Token objects to be operated on.
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return HTMLPurifier_Token[] Processed array of token objects.
+     */
+    abstract public function execute($tokens, $config, $context);
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Composite.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Composite.php
new file mode 100644 (file)
index 0000000..d7d35ce
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * Composite strategy that runs multiple strategies on tokens.
+ */
+abstract class HTMLPurifier_Strategy_Composite extends HTMLPurifier_Strategy
+{
+
+    /**
+     * List of strategies to run tokens through.
+     * @type HTMLPurifier_Strategy[]
+     */
+    protected $strategies = array();
+
+    /**
+     * @param HTMLPurifier_Token[] $tokens
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return HTMLPurifier_Token[]
+     */
+    public function execute($tokens, $config, $context)
+    {
+        foreach ($this->strategies as $strategy) {
+            $tokens = $strategy->execute($tokens, $config, $context);
+        }
+        return $tokens;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Core.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Core.php
new file mode 100644 (file)
index 0000000..4414c17
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * Core strategy composed of the big four strategies.
+ */
+class HTMLPurifier_Strategy_Core extends HTMLPurifier_Strategy_Composite
+{
+    public function __construct()
+    {
+        $this->strategies[] = new HTMLPurifier_Strategy_RemoveForeignElements();
+        $this->strategies[] = new HTMLPurifier_Strategy_MakeWellFormed();
+        $this->strategies[] = new HTMLPurifier_Strategy_FixNesting();
+        $this->strategies[] = new HTMLPurifier_Strategy_ValidateAttributes();
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php
new file mode 100644 (file)
index 0000000..6fa673d
--- /dev/null
@@ -0,0 +1,181 @@
+<?php
+
+/**
+ * Takes a well formed list of tokens and fixes their nesting.
+ *
+ * HTML elements dictate which elements are allowed to be their children,
+ * for example, you can't have a p tag in a span tag.  Other elements have
+ * much more rigorous definitions: tables, for instance, require a specific
+ * order for their elements.  There are also constraints not expressible by
+ * document type definitions, such as the chameleon nature of ins/del
+ * tags and global child exclusions.
+ *
+ * The first major objective of this strategy is to iterate through all
+ * the nodes and determine whether or not their children conform to the
+ * element's definition.  If they do not, the child definition may
+ * optionally supply an amended list of elements that is valid or
+ * require that the entire node be deleted (and the previous node
+ * rescanned).
+ *
+ * The second objective is to ensure that explicitly excluded elements of
+ * an element do not appear in its children.  Code that accomplishes this
+ * task is pervasive through the strategy, though the two are distinct tasks
+ * and could, theoretically, be seperated (although it's not recommended).
+ *
+ * @note Whether or not unrecognized children are silently dropped or
+ *       translated into text depends on the child definitions.
+ *
+ * @todo Enable nodes to be bubbled out of the structure.  This is
+ *       easier with our new algorithm.
+ */
+
+class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
+{
+
+    /**
+     * @param HTMLPurifier_Token[] $tokens
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array|HTMLPurifier_Token[]
+     */
+    public function execute($tokens, $config, $context)
+    {
+
+        //####################################################################//
+        // Pre-processing
+
+        // O(n) pass to convert to a tree, so that we can efficiently
+        // refer to substrings
+        $top_node = HTMLPurifier_Arborize::arborize($tokens, $config, $context);
+
+        // get a copy of the HTML definition
+        $definition = $config->getHTMLDefinition();
+
+        $excludes_enabled = !$config->get('Core.DisableExcludes');
+
+        // setup the context variable 'IsInline', for chameleon processing
+        // is 'false' when we are not inline, 'true' when it must always
+        // be inline, and an integer when it is inline for a certain
+        // branch of the document tree
+        $is_inline = $definition->info_parent_def->descendants_are_inline;
+        $context->register('IsInline', $is_inline);
+
+        // setup error collector
+        $e =& $context->get('ErrorCollector', true);
+
+        //####################################################################//
+        // Loop initialization
+
+        // stack that contains all elements that are excluded
+        // it is organized by parent elements, similar to $stack,
+        // but it is only populated when an element with exclusions is
+        // processed, i.e. there won't be empty exclusions.
+        $exclude_stack = array($definition->info_parent_def->excludes);
+
+        // variable that contains the start token while we are processing
+        // nodes. This enables error reporting to do its job
+        $node = $top_node;
+        // dummy token
+        list($token, $d) = $node->toTokenPair();
+        $context->register('CurrentNode', $node);
+        $context->register('CurrentToken', $token);
+
+        //####################################################################//
+        // Loop
+
+        // We need to implement a post-order traversal iteratively, to
+        // avoid running into stack space limits.  This is pretty tricky
+        // to reason about, so we just manually stack-ify the recursive
+        // variant:
+        //
+        //  function f($node) {
+        //      foreach ($node->children as $child) {
+        //          f($child);
+        //      }
+        //      validate($node);
+        //  }
+        //
+        // Thus, we will represent a stack frame as array($node,
+        // $is_inline, stack of children)
+        // e.g. array_reverse($node->children) - already processed
+        // children.
+
+        $parent_def = $definition->info_parent_def;
+        $stack = array(
+            array($top_node,
+                  $parent_def->descendants_are_inline,
+                  $parent_def->excludes, // exclusions
+                  0)
+            );
+
+        while (!empty($stack)) {
+            list($node, $is_inline, $excludes, $ix) = array_pop($stack);
+            // recursive call
+            $go = false;
+            $def = empty($stack) ? $definition->info_parent_def : $definition->info[$node->name];
+            while (isset($node->children[$ix])) {
+                $child = $node->children[$ix++];
+                if ($child instanceof HTMLPurifier_Node_Element) {
+                    $go = true;
+                    $stack[] = array($node, $is_inline, $excludes, $ix);
+                    $stack[] = array($child,
+                        // ToDo: I don't think it matters if it's def or
+                        // child_def, but double check this...
+                        $is_inline || $def->descendants_are_inline,
+                        empty($def->excludes) ? $excludes
+                                              : array_merge($excludes, $def->excludes),
+                        0);
+                    break;
+                }
+            };
+            if ($go) continue;
+            list($token, $d) = $node->toTokenPair();
+            // base case
+            if ($excludes_enabled && isset($excludes[$node->name])) {
+                $node->dead = true;
+                if ($e) $e->send(E_ERROR, 'Strategy_FixNesting: Node excluded');
+            } else {
+                // XXX I suppose it would be slightly more efficient to
+                // avoid the allocation here and have children
+                // strategies handle it
+                $children = array();
+                foreach ($node->children as $child) {
+                    if (!$child->dead) $children[] = $child;
+                }
+                $result = $def->child->validateChildren($children, $config, $context);
+                if ($result === true) {
+                    // nop
+                    $node->children = $children;
+                } elseif ($result === false) {
+                    $node->dead = true;
+                    if ($e) $e->send(E_ERROR, 'Strategy_FixNesting: Node removed');
+                } else {
+                    $node->children = $result;
+                    if ($e) {
+                        // XXX This will miss mutations of internal nodes. Perhaps defer to the child validators
+                        if (empty($result) && !empty($children)) {
+                            $e->send(E_ERROR, 'Strategy_FixNesting: Node contents removed');
+                        } else if ($result != $children) {
+                            $e->send(E_WARNING, 'Strategy_FixNesting: Node reorganized');
+                        }
+                    }
+                }
+            }
+        }
+
+        //####################################################################//
+        // Post-processing
+
+        // remove context variables
+        $context->destroy('IsInline');
+        $context->destroy('CurrentNode');
+        $context->destroy('CurrentToken');
+
+        //####################################################################//
+        // Return
+
+        return HTMLPurifier_Arborize::flatten($node, $config, $context);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php
new file mode 100644 (file)
index 0000000..e389e00
--- /dev/null
@@ -0,0 +1,600 @@
+<?php
+
+/**
+ * Takes tokens makes them well-formed (balance end tags, etc.)
+ *
+ * Specification of the armor attributes this strategy uses:
+ *
+ *      - MakeWellFormed_TagClosedError: This armor field is used to
+ *        suppress tag closed errors for certain tokens [TagClosedSuppress],
+ *        in particular, if a tag was generated automatically by HTML
+ *        Purifier, we may rely on our infrastructure to close it for us
+ *        and shouldn't report an error to the user [TagClosedAuto].
+ */
+class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
+{
+
+    /**
+     * Array stream of tokens being processed.
+     * @type HTMLPurifier_Token[]
+     */
+    protected $tokens;
+
+    /**
+     * Current token.
+     * @type HTMLPurifier_Token
+     */
+    protected $token;
+
+    /**
+     * Zipper managing the true state.
+     * @type HTMLPurifier_Zipper
+     */
+    protected $zipper;
+
+    /**
+     * Current nesting of elements.
+     * @type array
+     */
+    protected $stack;
+
+    /**
+     * Injectors active in this stream processing.
+     * @type HTMLPurifier_Injector[]
+     */
+    protected $injectors;
+
+    /**
+     * Current instance of HTMLPurifier_Config.
+     * @type HTMLPurifier_Config
+     */
+    protected $config;
+
+    /**
+     * Current instance of HTMLPurifier_Context.
+     * @type HTMLPurifier_Context
+     */
+    protected $context;
+
+    /**
+     * @param HTMLPurifier_Token[] $tokens
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return HTMLPurifier_Token[]
+     * @throws HTMLPurifier_Exception
+     */
+    public function execute($tokens, $config, $context)
+    {
+        $definition = $config->getHTMLDefinition();
+
+        // local variables
+        $generator = new HTMLPurifier_Generator($config, $context);
+        $escape_invalid_tags = $config->get('Core.EscapeInvalidTags');
+        // used for autoclose early abortion
+        $global_parent_allowed_elements = $definition->info_parent_def->child->getAllowedElements($config);
+        $e = $context->get('ErrorCollector', true);
+        $i = false; // injector index
+        list($zipper, $token) = HTMLPurifier_Zipper::fromArray($tokens);
+        if ($token === NULL) {
+            return array();
+        }
+        $reprocess = false; // whether or not to reprocess the same token
+        $stack = array();
+
+        // member variables
+        $this->stack =& $stack;
+        $this->tokens =& $tokens;
+        $this->token =& $token;
+        $this->zipper =& $zipper;
+        $this->config = $config;
+        $this->context = $context;
+
+        // context variables
+        $context->register('CurrentNesting', $stack);
+        $context->register('InputZipper', $zipper);
+        $context->register('CurrentToken', $token);
+
+        // -- begin INJECTOR --
+
+        $this->injectors = array();
+
+        $injectors = $config->getBatch('AutoFormat');
+        $def_injectors = $definition->info_injector;
+        $custom_injectors = $injectors['Custom'];
+        unset($injectors['Custom']); // special case
+        foreach ($injectors as $injector => $b) {
+            // XXX: Fix with a legitimate lookup table of enabled filters
+            if (strpos($injector, '.') !== false) {
+                continue;
+            }
+            $injector = "HTMLPurifier_Injector_$injector";
+            if (!$b) {
+                continue;
+            }
+            $this->injectors[] = new $injector;
+        }
+        foreach ($def_injectors as $injector) {
+            // assumed to be objects
+            $this->injectors[] = $injector;
+        }
+        foreach ($custom_injectors as $injector) {
+            if (!$injector) {
+                continue;
+            }
+            if (is_string($injector)) {
+                $injector = "HTMLPurifier_Injector_$injector";
+                $injector = new $injector;
+            }
+            $this->injectors[] = $injector;
+        }
+
+        // give the injectors references to the definition and context
+        // variables for performance reasons
+        foreach ($this->injectors as $ix => $injector) {
+            $error = $injector->prepare($config, $context);
+            if (!$error) {
+                continue;
+            }
+            array_splice($this->injectors, $ix, 1); // rm the injector
+            trigger_error("Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING);
+        }
+
+        // -- end INJECTOR --
+
+        // a note on reprocessing:
+        //      In order to reduce code duplication, whenever some code needs
+        //      to make HTML changes in order to make things "correct", the
+        //      new HTML gets sent through the purifier, regardless of its
+        //      status. This means that if we add a start token, because it
+        //      was totally necessary, we don't have to update nesting; we just
+        //      punt ($reprocess = true; continue;) and it does that for us.
+
+        // isset is in loop because $tokens size changes during loop exec
+        for (;;
+             // only increment if we don't need to reprocess
+             $reprocess ? $reprocess = false : $token = $zipper->next($token)) {
+
+            // check for a rewind
+            if (is_int($i)) {
+                // possibility: disable rewinding if the current token has a
+                // rewind set on it already. This would offer protection from
+                // infinite loop, but might hinder some advanced rewinding.
+                $rewind_offset = $this->injectors[$i]->getRewindOffset();
+                if (is_int($rewind_offset)) {
+                    for ($j = 0; $j < $rewind_offset; $j++) {
+                        if (empty($zipper->front)) break;
+                        $token = $zipper->prev($token);
+                        // indicate that other injectors should not process this token,
+                        // but we need to reprocess it
+                        unset($token->skip[$i]);
+                        $token->rewind = $i;
+                        if ($token instanceof HTMLPurifier_Token_Start) {
+                            array_pop($this->stack);
+                        } elseif ($token instanceof HTMLPurifier_Token_End) {
+                            $this->stack[] = $token->start;
+                        }
+                    }
+                }
+                $i = false;
+            }
+
+            // handle case of document end
+            if ($token === NULL) {
+                // kill processing if stack is empty
+                if (empty($this->stack)) {
+                    break;
+                }
+
+                // peek
+                $top_nesting = array_pop($this->stack);
+                $this->stack[] = $top_nesting;
+
+                // send error [TagClosedSuppress]
+                if ($e && !isset($top_nesting->armor['MakeWellFormed_TagClosedError'])) {
+                    $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $top_nesting);
+                }
+
+                // append, don't splice, since this is the end
+                $token = new HTMLPurifier_Token_End($top_nesting->name);
+
+                // punt!
+                $reprocess = true;
+                continue;
+            }
+
+            //echo '<br>'; printZipper($zipper, $token);//printTokens($this->stack);
+            //flush();
+
+            // quick-check: if it's not a tag, no need to process
+            if (empty($token->is_tag)) {
+                if ($token instanceof HTMLPurifier_Token_Text) {
+                    foreach ($this->injectors as $i => $injector) {
+                        if (isset($token->skip[$i])) {
+                            continue;
+                        }
+                        if ($token->rewind !== null && $token->rewind !== $i) {
+                            continue;
+                        }
+                        // XXX fuckup
+                        $r = $token;
+                        $injector->handleText($r);
+                        $token = $this->processToken($r, $i);
+                        $reprocess = true;
+                        break;
+                    }
+                }
+                // another possibility is a comment
+                continue;
+            }
+
+            if (isset($definition->info[$token->name])) {
+                $type = $definition->info[$token->name]->child->type;
+            } else {
+                $type = false; // Type is unknown, treat accordingly
+            }
+
+            // quick tag checks: anything that's *not* an end tag
+            $ok = false;
+            if ($type === 'empty' && $token instanceof HTMLPurifier_Token_Start) {
+                // claims to be a start tag but is empty
+                $token = new HTMLPurifier_Token_Empty(
+                    $token->name,
+                    $token->attr,
+                    $token->line,
+                    $token->col,
+                    $token->armor
+                );
+                $ok = true;
+            } elseif ($type && $type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) {
+                // claims to be empty but really is a start tag
+                // NB: this assignment is required
+                $old_token = $token;
+                $token = new HTMLPurifier_Token_End($token->name);
+                $token = $this->insertBefore(
+                    new HTMLPurifier_Token_Start($old_token->name, $old_token->attr, $old_token->line, $old_token->col, $old_token->armor)
+                );
+                // punt (since we had to modify the input stream in a non-trivial way)
+                $reprocess = true;
+                continue;
+            } elseif ($token instanceof HTMLPurifier_Token_Empty) {
+                // real empty token
+                $ok = true;
+            } elseif ($token instanceof HTMLPurifier_Token_Start) {
+                // start tag
+
+                // ...unless they also have to close their parent
+                if (!empty($this->stack)) {
+
+                    // Performance note: you might think that it's rather
+                    // inefficient, recalculating the autoclose information
+                    // for every tag that a token closes (since when we
+                    // do an autoclose, we push a new token into the
+                    // stream and then /process/ that, before
+                    // re-processing this token.)  But this is
+                    // necessary, because an injector can make an
+                    // arbitrary transformations to the autoclosing
+                    // tokens we introduce, so things may have changed
+                    // in the meantime.  Also, doing the inefficient thing is
+                    // "easy" to reason about (for certain perverse definitions
+                    // of "easy")
+
+                    $parent = array_pop($this->stack);
+                    $this->stack[] = $parent;
+
+                    $parent_def = null;
+                    $parent_elements = null;
+                    $autoclose = false;
+                    if (isset($definition->info[$parent->name])) {
+                        $parent_def = $definition->info[$parent->name];
+                        $parent_elements = $parent_def->child->getAllowedElements($config);
+                        $autoclose = !isset($parent_elements[$token->name]);
+                    }
+
+                    if ($autoclose && $definition->info[$token->name]->wrap) {
+                        // Check if an element can be wrapped by another
+                        // element to make it valid in a context (for
+                        // example, <ul><ul> needs a <li> in between)
+                        $wrapname = $definition->info[$token->name]->wrap;
+                        $wrapdef = $definition->info[$wrapname];
+                        $elements = $wrapdef->child->getAllowedElements($config);
+                        if (isset($elements[$token->name]) && isset($parent_elements[$wrapname])) {
+                            $newtoken = new HTMLPurifier_Token_Start($wrapname);
+                            $token = $this->insertBefore($newtoken);
+                            $reprocess = true;
+                            continue;
+                        }
+                    }
+
+                    $carryover = false;
+                    if ($autoclose && $parent_def->formatting) {
+                        $carryover = true;
+                    }
+
+                    if ($autoclose) {
+                        // check if this autoclose is doomed to fail
+                        // (this rechecks $parent, which his harmless)
+                        $autoclose_ok = isset($global_parent_allowed_elements[$token->name]);
+                        if (!$autoclose_ok) {
+                            foreach ($this->stack as $ancestor) {
+                                $elements = $definition->info[$ancestor->name]->child->getAllowedElements($config);
+                                if (isset($elements[$token->name])) {
+                                    $autoclose_ok = true;
+                                    break;
+                                }
+                                if ($definition->info[$token->name]->wrap) {
+                                    $wrapname = $definition->info[$token->name]->wrap;
+                                    $wrapdef = $definition->info[$wrapname];
+                                    $wrap_elements = $wrapdef->child->getAllowedElements($config);
+                                    if (isset($wrap_elements[$token->name]) && isset($elements[$wrapname])) {
+                                        $autoclose_ok = true;
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+                        if ($autoclose_ok) {
+                            // errors need to be updated
+                            $new_token = new HTMLPurifier_Token_End($parent->name);
+                            $new_token->start = $parent;
+                            // [TagClosedSuppress]
+                            if ($e && !isset($parent->armor['MakeWellFormed_TagClosedError'])) {
+                                if (!$carryover) {
+                                    $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag auto closed', $parent);
+                                } else {
+                                    $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag carryover', $parent);
+                                }
+                            }
+                            if ($carryover) {
+                                $element = clone $parent;
+                                // [TagClosedAuto]
+                                $element->armor['MakeWellFormed_TagClosedError'] = true;
+                                $element->carryover = true;
+                                $token = $this->processToken(array($new_token, $token, $element));
+                            } else {
+                                $token = $this->insertBefore($new_token);
+                            }
+                        } else {
+                            $token = $this->remove();
+                        }
+                        $reprocess = true;
+                        continue;
+                    }
+
+                }
+                $ok = true;
+            }
+
+            if ($ok) {
+                foreach ($this->injectors as $i => $injector) {
+                    if (isset($token->skip[$i])) {
+                        continue;
+                    }
+                    if ($token->rewind !== null && $token->rewind !== $i) {
+                        continue;
+                    }
+                    $r = $token;
+                    $injector->handleElement($r);
+                    $token = $this->processToken($r, $i);
+                    $reprocess = true;
+                    break;
+                }
+                if (!$reprocess) {
+                    // ah, nothing interesting happened; do normal processing
+                    if ($token instanceof HTMLPurifier_Token_Start) {
+                        $this->stack[] = $token;
+                    } elseif ($token instanceof HTMLPurifier_Token_End) {
+                        throw new HTMLPurifier_Exception(
+                            'Improper handling of end tag in start code; possible error in MakeWellFormed'
+                        );
+                    }
+                }
+                continue;
+            }
+
+            // sanity check: we should be dealing with a closing tag
+            if (!$token instanceof HTMLPurifier_Token_End) {
+                throw new HTMLPurifier_Exception('Unaccounted for tag token in input stream, bug in HTML Purifier');
+            }
+
+            // make sure that we have something open
+            if (empty($this->stack)) {
+                if ($escape_invalid_tags) {
+                    if ($e) {
+                        $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text');
+                    }
+                    $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token));
+                } else {
+                    if ($e) {
+                        $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed');
+                    }
+                    $token = $this->remove();
+                }
+                $reprocess = true;
+                continue;
+            }
+
+            // first, check for the simplest case: everything closes neatly.
+            // Eventually, everything passes through here; if there are problems
+            // we modify the input stream accordingly and then punt, so that
+            // the tokens get processed again.
+            $current_parent = array_pop($this->stack);
+            if ($current_parent->name == $token->name) {
+                $token->start = $current_parent;
+                foreach ($this->injectors as $i => $injector) {
+                    if (isset($token->skip[$i])) {
+                        continue;
+                    }
+                    if ($token->rewind !== null && $token->rewind !== $i) {
+                        continue;
+                    }
+                    $r = $token;
+                    $injector->handleEnd($r);
+                    $token = $this->processToken($r, $i);
+                    $this->stack[] = $current_parent;
+                    $reprocess = true;
+                    break;
+                }
+                continue;
+            }
+
+            // okay, so we're trying to close the wrong tag
+
+            // undo the pop previous pop
+            $this->stack[] = $current_parent;
+
+            // scroll back the entire nest, trying to find our tag.
+            // (feature could be to specify how far you'd like to go)
+            $size = count($this->stack);
+            // -2 because -1 is the last element, but we already checked that
+            $skipped_tags = false;
+            for ($j = $size - 2; $j >= 0; $j--) {
+                if ($this->stack[$j]->name == $token->name) {
+                    $skipped_tags = array_slice($this->stack, $j);
+                    break;
+                }
+            }
+
+            // we didn't find the tag, so remove
+            if ($skipped_tags === false) {
+                if ($escape_invalid_tags) {
+                    if ($e) {
+                        $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text');
+                    }
+                    $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token));
+                } else {
+                    if ($e) {
+                        $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed');
+                    }
+                    $token = $this->remove();
+                }
+                $reprocess = true;
+                continue;
+            }
+
+            // do errors, in REVERSE $j order: a,b,c with </a></b></c>
+            $c = count($skipped_tags);
+            if ($e) {
+                for ($j = $c - 1; $j > 0; $j--) {
+                    // notice we exclude $j == 0, i.e. the current ending tag, from
+                    // the errors... [TagClosedSuppress]
+                    if (!isset($skipped_tags[$j]->armor['MakeWellFormed_TagClosedError'])) {
+                        $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', $skipped_tags[$j]);
+                    }
+                }
+            }
+
+            // insert tags, in FORWARD $j order: c,b,a with </a></b></c>
+            $replace = array($token);
+            for ($j = 1; $j < $c; $j++) {
+                // ...as well as from the insertions
+                $new_token = new HTMLPurifier_Token_End($skipped_tags[$j]->name);
+                $new_token->start = $skipped_tags[$j];
+                array_unshift($replace, $new_token);
+                if (isset($definition->info[$new_token->name]) && $definition->info[$new_token->name]->formatting) {
+                    // [TagClosedAuto]
+                    $element = clone $skipped_tags[$j];
+                    $element->carryover = true;
+                    $element->armor['MakeWellFormed_TagClosedError'] = true;
+                    $replace[] = $element;
+                }
+            }
+            $token = $this->processToken($replace);
+            $reprocess = true;
+            continue;
+        }
+
+        $context->destroy('CurrentToken');
+        $context->destroy('CurrentNesting');
+        $context->destroy('InputZipper');
+
+        unset($this->injectors, $this->stack, $this->tokens);
+        return $zipper->toArray($token);
+    }
+
+    /**
+     * Processes arbitrary token values for complicated substitution patterns.
+     * In general:
+     *
+     * If $token is an array, it is a list of tokens to substitute for the
+     * current token. These tokens then get individually processed. If there
+     * is a leading integer in the list, that integer determines how many
+     * tokens from the stream should be removed.
+     *
+     * If $token is a regular token, it is swapped with the current token.
+     *
+     * If $token is false, the current token is deleted.
+     *
+     * If $token is an integer, that number of tokens (with the first token
+     * being the current one) will be deleted.
+     *
+     * @param HTMLPurifier_Token|array|int|bool $token Token substitution value
+     * @param HTMLPurifier_Injector|int $injector Injector that performed the substitution; default is if
+     *        this is not an injector related operation.
+     * @throws HTMLPurifier_Exception
+     */
+    protected function processToken($token, $injector = -1)
+    {
+        // normalize forms of token
+        if (is_object($token)) {
+            $token = array(1, $token);
+        }
+        if (is_int($token)) {
+            $token = array($token);
+        }
+        if ($token === false) {
+            $token = array(1);
+        }
+        if (!is_array($token)) {
+            throw new HTMLPurifier_Exception('Invalid token type from injector');
+        }
+        if (!is_int($token[0])) {
+            array_unshift($token, 1);
+        }
+        if ($token[0] === 0) {
+            throw new HTMLPurifier_Exception('Deleting zero tokens is not valid');
+        }
+
+        // $token is now an array with the following form:
+        // array(number nodes to delete, new node 1, new node 2, ...)
+
+        $delete = array_shift($token);
+        list($old, $r) = $this->zipper->splice($this->token, $delete, $token);
+
+        if ($injector > -1) {
+            // determine appropriate skips
+            $oldskip = isset($old[0]) ? $old[0]->skip : array();
+            foreach ($token as $object) {
+                $object->skip = $oldskip;
+                $object->skip[$injector] = true;
+            }
+        }
+
+        return $r;
+
+    }
+
+    /**
+     * Inserts a token before the current token. Cursor now points to
+     * this token.  You must reprocess after this.
+     * @param HTMLPurifier_Token $token
+     */
+    private function insertBefore($token)
+    {
+        // NB not $this->zipper->insertBefore(), due to positioning
+        // differences
+        $splice = $this->zipper->splice($this->token, 0, array($token));
+
+        return $splice[1];
+    }
+
+    /**
+     * Removes current token. Cursor now points to new token occupying previously
+     * occupied space.  You must reprocess after this.
+     */
+    private function remove()
+    {
+        return $this->zipper->delete();
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php
new file mode 100644 (file)
index 0000000..1a8149e
--- /dev/null
@@ -0,0 +1,207 @@
+<?php
+
+/**
+ * Removes all unrecognized tags from the list of tokens.
+ *
+ * This strategy iterates through all the tokens and removes unrecognized
+ * tokens. If a token is not recognized but a TagTransform is defined for
+ * that element, the element will be transformed accordingly.
+ */
+
+class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
+{
+
+    /**
+     * @param HTMLPurifier_Token[] $tokens
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array|HTMLPurifier_Token[]
+     */
+    public function execute($tokens, $config, $context)
+    {
+        $definition = $config->getHTMLDefinition();
+        $generator = new HTMLPurifier_Generator($config, $context);
+        $result = array();
+
+        $escape_invalid_tags = $config->get('Core.EscapeInvalidTags');
+        $remove_invalid_img = $config->get('Core.RemoveInvalidImg');
+
+        // currently only used to determine if comments should be kept
+        $trusted = $config->get('HTML.Trusted');
+        $comment_lookup = $config->get('HTML.AllowedComments');
+        $comment_regexp = $config->get('HTML.AllowedCommentsRegexp');
+        $check_comments = $comment_lookup !== array() || $comment_regexp !== null;
+
+        $remove_script_contents = $config->get('Core.RemoveScriptContents');
+        $hidden_elements = $config->get('Core.HiddenElements');
+
+        // remove script contents compatibility
+        if ($remove_script_contents === true) {
+            $hidden_elements['script'] = true;
+        } elseif ($remove_script_contents === false && isset($hidden_elements['script'])) {
+            unset($hidden_elements['script']);
+        }
+
+        $attr_validator = new HTMLPurifier_AttrValidator();
+
+        // removes tokens until it reaches a closing tag with its value
+        $remove_until = false;
+
+        // converts comments into text tokens when this is equal to a tag name
+        $textify_comments = false;
+
+        $token = false;
+        $context->register('CurrentToken', $token);
+
+        $e = false;
+        if ($config->get('Core.CollectErrors')) {
+            $e =& $context->get('ErrorCollector');
+        }
+
+        foreach ($tokens as $token) {
+            if ($remove_until) {
+                if (empty($token->is_tag) || $token->name !== $remove_until) {
+                    continue;
+                }
+            }
+            if (!empty($token->is_tag)) {
+                // DEFINITION CALL
+
+                // before any processing, try to transform the element
+                if (isset($definition->info_tag_transform[$token->name])) {
+                    $original_name = $token->name;
+                    // there is a transformation for this tag
+                    // DEFINITION CALL
+                    $token = $definition->
+                        info_tag_transform[$token->name]->transform($token, $config, $context);
+                    if ($e) {
+                        $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Tag transform', $original_name);
+                    }
+                }
+
+                if (isset($definition->info[$token->name])) {
+                    // mostly everything's good, but
+                    // we need to make sure required attributes are in order
+                    if (($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) &&
+                        $definition->info[$token->name]->required_attr &&
+                        ($token->name != 'img' || $remove_invalid_img) // ensure config option still works
+                    ) {
+                        $attr_validator->validateToken($token, $config, $context);
+                        $ok = true;
+                        foreach ($definition->info[$token->name]->required_attr as $name) {
+                            if (!isset($token->attr[$name])) {
+                                $ok = false;
+                                break;
+                            }
+                        }
+                        if (!$ok) {
+                            if ($e) {
+                                $e->send(
+                                    E_ERROR,
+                                    'Strategy_RemoveForeignElements: Missing required attribute',
+                                    $name
+                                );
+                            }
+                            continue;
+                        }
+                        $token->armor['ValidateAttributes'] = true;
+                    }
+
+                    if (isset($hidden_elements[$token->name]) && $token instanceof HTMLPurifier_Token_Start) {
+                        $textify_comments = $token->name;
+                    } elseif ($token->name === $textify_comments && $token instanceof HTMLPurifier_Token_End) {
+                        $textify_comments = false;
+                    }
+
+                } elseif ($escape_invalid_tags) {
+                    // invalid tag, generate HTML representation and insert in
+                    if ($e) {
+                        $e->send(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text');
+                    }
+                    $token = new HTMLPurifier_Token_Text(
+                        $generator->generateFromToken($token)
+                    );
+                } else {
+                    // check if we need to destroy all of the tag's children
+                    // CAN BE GENERICIZED
+                    if (isset($hidden_elements[$token->name])) {
+                        if ($token instanceof HTMLPurifier_Token_Start) {
+                            $remove_until = $token->name;
+                        } elseif ($token instanceof HTMLPurifier_Token_Empty) {
+                            // do nothing: we're still looking
+                        } else {
+                            $remove_until = false;
+                        }
+                        if ($e) {
+                            $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign meta element removed');
+                        }
+                    } else {
+                        if ($e) {
+                            $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign element removed');
+                        }
+                    }
+                    continue;
+                }
+            } elseif ($token instanceof HTMLPurifier_Token_Comment) {
+                // textify comments in script tags when they are allowed
+                if ($textify_comments !== false) {
+                    $data = $token->data;
+                    $token = new HTMLPurifier_Token_Text($data);
+                } elseif ($trusted || $check_comments) {
+                    // always cleanup comments
+                    $trailing_hyphen = false;
+                    if ($e) {
+                        // perform check whether or not there's a trailing hyphen
+                        if (substr($token->data, -1) == '-') {
+                            $trailing_hyphen = true;
+                        }
+                    }
+                    $token->data = rtrim($token->data, '-');
+                    $found_double_hyphen = false;
+                    while (strpos($token->data, '--') !== false) {
+                        $found_double_hyphen = true;
+                        $token->data = str_replace('--', '-', $token->data);
+                    }
+                    if ($trusted || !empty($comment_lookup[trim($token->data)]) ||
+                        ($comment_regexp !== null && preg_match($comment_regexp, trim($token->data)))) {
+                        // OK good
+                        if ($e) {
+                            if ($trailing_hyphen) {
+                                $e->send(
+                                    E_NOTICE,
+                                    'Strategy_RemoveForeignElements: Trailing hyphen in comment removed'
+                                );
+                            }
+                            if ($found_double_hyphen) {
+                                $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Hyphens in comment collapsed');
+                            }
+                        }
+                    } else {
+                        if ($e) {
+                            $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed');
+                        }
+                        continue;
+                    }
+                } else {
+                    // strip comments
+                    if ($e) {
+                        $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed');
+                    }
+                    continue;
+                }
+            } elseif ($token instanceof HTMLPurifier_Token_Text) {
+            } else {
+                continue;
+            }
+            $result[] = $token;
+        }
+        if ($remove_until && $e) {
+            // we removed tokens until the end, throw error
+            $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Token removed to end', $remove_until);
+        }
+        $context->destroy('CurrentToken');
+        return $result;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php
new file mode 100644 (file)
index 0000000..fbb3d27
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * Validate all attributes in the tokens.
+ */
+
+class HTMLPurifier_Strategy_ValidateAttributes extends HTMLPurifier_Strategy
+{
+
+    /**
+     * @param HTMLPurifier_Token[] $tokens
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return HTMLPurifier_Token[]
+     */
+    public function execute($tokens, $config, $context)
+    {
+        // setup validator
+        $validator = new HTMLPurifier_AttrValidator();
+
+        $token = false;
+        $context->register('CurrentToken', $token);
+
+        foreach ($tokens as $key => $token) {
+
+            // only process tokens that have attributes,
+            //   namely start and empty tags
+            if (!$token instanceof HTMLPurifier_Token_Start && !$token instanceof HTMLPurifier_Token_Empty) {
+                continue;
+            }
+
+            // skip tokens that are armored
+            if (!empty($token->armor['ValidateAttributes'])) {
+                continue;
+            }
+
+            // note that we have no facilities here for removing tokens
+            $validator->validateToken($token, $config, $context);
+        }
+        $context->destroy('CurrentToken');
+        return $tokens;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/StringHash.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/StringHash.php
new file mode 100644 (file)
index 0000000..c073701
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * This is in almost every respect equivalent to an array except
+ * that it keeps track of which keys were accessed.
+ *
+ * @warning For the sake of backwards compatibility with early versions
+ *     of PHP 5, you must not use the $hash[$key] syntax; if you do
+ *     our version of offsetGet is never called.
+ */
+class HTMLPurifier_StringHash extends ArrayObject
+{
+    /**
+     * @type array
+     */
+    protected $accessed = array();
+
+    /**
+     * Retrieves a value, and logs the access.
+     * @param mixed $index
+     * @return mixed
+     */
+    public function offsetGet($index)
+    {
+        $this->accessed[$index] = true;
+        return parent::offsetGet($index);
+    }
+
+    /**
+     * Returns a lookup array of all array indexes that have been accessed.
+     * @return array in form array($index => true).
+     */
+    public function getAccessed()
+    {
+        return $this->accessed;
+    }
+
+    /**
+     * Resets the access array.
+     */
+    public function resetAccessed()
+    {
+        $this->accessed = array();
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/StringHashParser.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/StringHashParser.php
new file mode 100644 (file)
index 0000000..7c73f80
--- /dev/null
@@ -0,0 +1,136 @@
+<?php
+
+/**
+ * Parses string hash files. File format is as such:
+ *
+ *      DefaultKeyValue
+ *      KEY: Value
+ *      KEY2: Value2
+ *      --MULTILINE-KEY--
+ *      Multiline
+ *      value.
+ *
+ * Which would output something similar to:
+ *
+ *      array(
+ *          'ID' => 'DefaultKeyValue',
+ *          'KEY' => 'Value',
+ *          'KEY2' => 'Value2',
+ *          'MULTILINE-KEY' => "Multiline\nvalue.\n",
+ *      )
+ *
+ * We use this as an easy to use file-format for configuration schema
+ * files, but the class itself is usage agnostic.
+ *
+ * You can use ---- to forcibly terminate parsing of a single string-hash;
+ * this marker is used in multi string-hashes to delimit boundaries.
+ */
+class HTMLPurifier_StringHashParser
+{
+
+    /**
+     * @type string
+     */
+    public $default = 'ID';
+
+    /**
+     * Parses a file that contains a single string-hash.
+     * @param string $file
+     * @return array
+     */
+    public function parseFile($file)
+    {
+        if (!file_exists($file)) {
+            return false;
+        }
+        $fh = fopen($file, 'r');
+        if (!$fh) {
+            return false;
+        }
+        $ret = $this->parseHandle($fh);
+        fclose($fh);
+        return $ret;
+    }
+
+    /**
+     * Parses a file that contains multiple string-hashes delimited by '----'
+     * @param string $file
+     * @return array
+     */
+    public function parseMultiFile($file)
+    {
+        if (!file_exists($file)) {
+            return false;
+        }
+        $ret = array();
+        $fh = fopen($file, 'r');
+        if (!$fh) {
+            return false;
+        }
+        while (!feof($fh)) {
+            $ret[] = $this->parseHandle($fh);
+        }
+        fclose($fh);
+        return $ret;
+    }
+
+    /**
+     * Internal parser that acepts a file handle.
+     * @note While it's possible to simulate in-memory parsing by using
+     *       custom stream wrappers, if such a use-case arises we should
+     *       factor out the file handle into its own class.
+     * @param resource $fh File handle with pointer at start of valid string-hash
+     *            block.
+     * @return array
+     */
+    protected function parseHandle($fh)
+    {
+        $state   = false;
+        $single  = false;
+        $ret     = array();
+        do {
+            $line = fgets($fh);
+            if ($line === false) {
+                break;
+            }
+            $line = rtrim($line, "\n\r");
+            if (!$state && $line === '') {
+                continue;
+            }
+            if ($line === '----') {
+                break;
+            }
+            if (strncmp('--#', $line, 3) === 0) {
+                // Comment
+                continue;
+            } elseif (strncmp('--', $line, 2) === 0) {
+                // Multiline declaration
+                $state = trim($line, '- ');
+                if (!isset($ret[$state])) {
+                    $ret[$state] = '';
+                }
+                continue;
+            } elseif (!$state) {
+                $single = true;
+                if (strpos($line, ':') !== false) {
+                    // Single-line declaration
+                    list($state, $line) = explode(':', $line, 2);
+                    $line = trim($line);
+                } else {
+                    // Use default declaration
+                    $state  = $this->default;
+                }
+            }
+            if ($single) {
+                $ret[$state] = $line;
+                $single = false;
+                $state  = false;
+            } else {
+                $ret[$state] .= "$line\n";
+            }
+        } while (!feof($fh));
+        return $ret;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform.php
new file mode 100644 (file)
index 0000000..7b8d833
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * Defines a mutation of an obsolete tag into a valid tag.
+ */
+abstract class HTMLPurifier_TagTransform
+{
+
+    /**
+     * Tag name to transform the tag to.
+     * @type string
+     */
+    public $transform_to;
+
+    /**
+     * Transforms the obsolete tag into the valid tag.
+     * @param HTMLPurifier_Token_Tag $tag Tag to be transformed.
+     * @param HTMLPurifier_Config $config Mandatory HTMLPurifier_Config object
+     * @param HTMLPurifier_Context $context Mandatory HTMLPurifier_Context object
+     */
+    abstract public function transform($tag, $config, $context);
+
+    /**
+     * Prepends CSS properties to the style attribute, creating the
+     * attribute if it doesn't exist.
+     * @warning Copied over from AttrTransform, be sure to keep in sync
+     * @param array $attr Attribute array to process (passed by reference)
+     * @param string $css CSS to prepend
+     */
+    protected function prependCSS(&$attr, $css)
+    {
+        $attr['style'] = isset($attr['style']) ? $attr['style'] : '';
+        $attr['style'] = $css . $attr['style'];
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Font.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Font.php
new file mode 100644 (file)
index 0000000..7853d90
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+
+/**
+ * Transforms FONT tags to the proper form (SPAN with CSS styling)
+ *
+ * This transformation takes the three proprietary attributes of FONT and
+ * transforms them into their corresponding CSS attributes.  These are color,
+ * face, and size.
+ *
+ * @note Size is an interesting case because it doesn't map cleanly to CSS.
+ *       Thanks to
+ *       http://style.cleverchimp.com/font_size_intervals/altintervals.html
+ *       for reasonable mappings.
+ * @warning This doesn't work completely correctly; specifically, this
+ *          TagTransform operates before well-formedness is enforced, so
+ *          the "active formatting elements" algorithm doesn't get applied.
+ */
+class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform
+{
+    /**
+     * @type string
+     */
+    public $transform_to = 'span';
+
+    /**
+     * @type array
+     */
+    protected $_size_lookup = array(
+        '0' => 'xx-small',
+        '1' => 'xx-small',
+        '2' => 'small',
+        '3' => 'medium',
+        '4' => 'large',
+        '5' => 'x-large',
+        '6' => 'xx-large',
+        '7' => '300%',
+        '-1' => 'smaller',
+        '-2' => '60%',
+        '+1' => 'larger',
+        '+2' => '150%',
+        '+3' => '200%',
+        '+4' => '300%'
+    );
+
+    /**
+     * @param HTMLPurifier_Token_Tag $tag
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return HTMLPurifier_Token_End|string
+     */
+    public function transform($tag, $config, $context)
+    {
+        if ($tag instanceof HTMLPurifier_Token_End) {
+            $new_tag = clone $tag;
+            $new_tag->name = $this->transform_to;
+            return $new_tag;
+        }
+
+        $attr = $tag->attr;
+        $prepend_style = '';
+
+        // handle color transform
+        if (isset($attr['color'])) {
+            $prepend_style .= 'color:' . $attr['color'] . ';';
+            unset($attr['color']);
+        }
+
+        // handle face transform
+        if (isset($attr['face'])) {
+            $prepend_style .= 'font-family:' . $attr['face'] . ';';
+            unset($attr['face']);
+        }
+
+        // handle size transform
+        if (isset($attr['size'])) {
+            // normalize large numbers
+            if ($attr['size'] !== '') {
+                if ($attr['size']{0} == '+' || $attr['size']{0} == '-') {
+                    $size = (int)$attr['size'];
+                    if ($size < -2) {
+                        $attr['size'] = '-2';
+                    }
+                    if ($size > 4) {
+                        $attr['size'] = '+4';
+                    }
+                } else {
+                    $size = (int)$attr['size'];
+                    if ($size > 7) {
+                        $attr['size'] = '7';
+                    }
+                }
+            }
+            if (isset($this->_size_lookup[$attr['size']])) {
+                $prepend_style .= 'font-size:' .
+                    $this->_size_lookup[$attr['size']] . ';';
+            }
+            unset($attr['size']);
+        }
+
+        if ($prepend_style) {
+            $attr['style'] = isset($attr['style']) ?
+                $prepend_style . $attr['style'] :
+                $prepend_style;
+        }
+
+        $new_tag = clone $tag;
+        $new_tag->name = $this->transform_to;
+        $new_tag->attr = $attr;
+
+        return $new_tag;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php
new file mode 100644 (file)
index 0000000..71bf10b
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * Simple transformation, just change tag name to something else,
+ * and possibly add some styling. This will cover most of the deprecated
+ * tag cases.
+ */
+class HTMLPurifier_TagTransform_Simple extends HTMLPurifier_TagTransform
+{
+    /**
+     * @type string
+     */
+    protected $style;
+
+    /**
+     * @param string $transform_to Tag name to transform to.
+     * @param string $style CSS style to add to the tag
+     */
+    public function __construct($transform_to, $style = null)
+    {
+        $this->transform_to = $transform_to;
+        $this->style = $style;
+    }
+
+    /**
+     * @param HTMLPurifier_Token_Tag $tag
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return string
+     */
+    public function transform($tag, $config, $context)
+    {
+        $new_tag = clone $tag;
+        $new_tag->name = $this->transform_to;
+        if (!is_null($this->style) &&
+            ($new_tag instanceof HTMLPurifier_Token_Start || $new_tag instanceof HTMLPurifier_Token_Empty)
+        ) {
+            $this->prependCSS($new_tag->attr, $this->style);
+        }
+        return $new_tag;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Token.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token.php
new file mode 100644 (file)
index 0000000..85b85e0
--- /dev/null
@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * Abstract base token class that all others inherit from.
+ */
+abstract class HTMLPurifier_Token
+{
+    /**
+     * Line number node was on in source document. Null if unknown.
+     * @type int
+     */
+    public $line;
+
+    /**
+     * Column of line node was on in source document. Null if unknown.
+     * @type int
+     */
+    public $col;
+
+    /**
+     * Lookup array of processing that this token is exempt from.
+     * Currently, valid values are "ValidateAttributes" and
+     * "MakeWellFormed_TagClosedError"
+     * @type array
+     */
+    public $armor = array();
+
+    /**
+     * Used during MakeWellFormed.
+     * @type
+     */
+    public $skip;
+
+    /**
+     * @type
+     */
+    public $rewind;
+
+    /**
+     * @type
+     */
+    public $carryover;
+
+    /**
+     * @param string $n
+     * @return null|string
+     */
+    public function __get($n)
+    {
+        if ($n === 'type') {
+            trigger_error('Deprecated type property called; use instanceof', E_USER_NOTICE);
+            switch (get_class($this)) {
+                case 'HTMLPurifier_Token_Start':
+                    return 'start';
+                case 'HTMLPurifier_Token_Empty':
+                    return 'empty';
+                case 'HTMLPurifier_Token_End':
+                    return 'end';
+                case 'HTMLPurifier_Token_Text':
+                    return 'text';
+                case 'HTMLPurifier_Token_Comment':
+                    return 'comment';
+                default:
+                    return null;
+            }
+        }
+    }
+
+    /**
+     * Sets the position of the token in the source document.
+     * @param int $l
+     * @param int $c
+     */
+    public function position($l = null, $c = null)
+    {
+        $this->line = $l;
+        $this->col = $c;
+    }
+
+    /**
+     * Convenience function for DirectLex settings line/col position.
+     * @param int $l
+     * @param int $c
+     */
+    public function rawPosition($l, $c)
+    {
+        if ($c === -1) {
+            $l++;
+        }
+        $this->line = $l;
+        $this->col = $c;
+    }
+
+    /**
+     * Converts a token into its corresponding node.
+     */
+    abstract public function toNode();
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Comment.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Comment.php
new file mode 100644 (file)
index 0000000..23453c7
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * Concrete comment token class. Generally will be ignored.
+ */
+class HTMLPurifier_Token_Comment extends HTMLPurifier_Token
+{
+    /**
+     * Character data within comment.
+     * @type string
+     */
+    public $data;
+
+    /**
+     * @type bool
+     */
+    public $is_whitespace = true;
+
+    /**
+     * Transparent constructor.
+     *
+     * @param string $data String comment data.
+     * @param int $line
+     * @param int $col
+     */
+    public function __construct($data, $line = null, $col = null)
+    {
+        $this->data = $data;
+        $this->line = $line;
+        $this->col = $col;
+    }
+
+    public function toNode() {
+        return new HTMLPurifier_Node_Comment($this->data, $this->line, $this->col);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Empty.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Empty.php
new file mode 100644 (file)
index 0000000..78a95f5
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+
+/**
+ * Concrete empty token class.
+ */
+class HTMLPurifier_Token_Empty extends HTMLPurifier_Token_Tag
+{
+    public function toNode() {
+        $n = parent::toNode();
+        $n->empty = true;
+        return $n;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/End.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/End.php
new file mode 100644 (file)
index 0000000..59b38fd
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * Concrete end token class.
+ *
+ * @warning This class accepts attributes even though end tags cannot. This
+ * is for optimization reasons, as under normal circumstances, the Lexers
+ * do not pass attributes.
+ */
+class HTMLPurifier_Token_End extends HTMLPurifier_Token_Tag
+{
+    /**
+     * Token that started this node.
+     * Added by MakeWellFormed. Please do not edit this!
+     * @type HTMLPurifier_Token
+     */
+    public $start;
+
+    public function toNode() {
+        throw new Exception("HTMLPurifier_Token_End->toNode not supported!");
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Start.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Start.php
new file mode 100644 (file)
index 0000000..019f317
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+
+/**
+ * Concrete start token class.
+ */
+class HTMLPurifier_Token_Start extends HTMLPurifier_Token_Tag
+{
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Tag.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Tag.php
new file mode 100644 (file)
index 0000000..d643fa6
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * Abstract class of a tag token (start, end or empty), and its behavior.
+ */
+abstract class HTMLPurifier_Token_Tag extends HTMLPurifier_Token
+{
+    /**
+     * Static bool marker that indicates the class is a tag.
+     *
+     * This allows us to check objects with <tt>!empty($obj->is_tag)</tt>
+     * without having to use a function call <tt>is_a()</tt>.
+     * @type bool
+     */
+    public $is_tag = true;
+
+    /**
+     * The lower-case name of the tag, like 'a', 'b' or 'blockquote'.
+     *
+     * @note Strictly speaking, XML tags are case sensitive, so we shouldn't
+     * be lower-casing them, but these tokens cater to HTML tags, which are
+     * insensitive.
+     * @type string
+     */
+    public $name;
+
+    /**
+     * Associative array of the tag's attributes.
+     * @type array
+     */
+    public $attr = array();
+
+    /**
+     * Non-overloaded constructor, which lower-cases passed tag name.
+     *
+     * @param string $name String name.
+     * @param array $attr Associative array of attributes.
+     * @param int $line
+     * @param int $col
+     * @param array $armor
+     */
+    public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array())
+    {
+        $this->name = ctype_lower($name) ? $name : strtolower($name);
+        foreach ($attr as $key => $value) {
+            // normalization only necessary when key is not lowercase
+            if (!ctype_lower($key)) {
+                $new_key = strtolower($key);
+                if (!isset($attr[$new_key])) {
+                    $attr[$new_key] = $attr[$key];
+                }
+                if ($new_key !== $key) {
+                    unset($attr[$key]);
+                }
+            }
+        }
+        $this->attr = $attr;
+        $this->line = $line;
+        $this->col = $col;
+        $this->armor = $armor;
+    }
+
+    public function toNode() {
+        return new HTMLPurifier_Node_Element($this->name, $this->attr, $this->line, $this->col, $this->armor);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Text.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Text.php
new file mode 100644 (file)
index 0000000..f26a1c2
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * Concrete text token class.
+ *
+ * Text tokens comprise of regular parsed character data (PCDATA) and raw
+ * character data (from the CDATA sections). Internally, their
+ * data is parsed with all entities expanded. Surprisingly, the text token
+ * does have a "tag name" called #PCDATA, which is how the DTD represents it
+ * in permissible child nodes.
+ */
+class HTMLPurifier_Token_Text extends HTMLPurifier_Token
+{
+
+    /**
+     * @type string
+     */
+    public $name = '#PCDATA';
+    /**< PCDATA tag name compatible with DTD. */
+
+    /**
+     * @type string
+     */
+    public $data;
+    /**< Parsed character data of text. */
+
+    /**
+     * @type bool
+     */
+    public $is_whitespace;
+
+    /**< Bool indicating if node is whitespace. */
+
+    /**
+     * Constructor, accepts data and determines if it is whitespace.
+     * @param string $data String parsed character data.
+     * @param int $line
+     * @param int $col
+     */
+    public function __construct($data, $line = null, $col = null)
+    {
+        $this->data = $data;
+        $this->is_whitespace = ctype_space($data);
+        $this->line = $line;
+        $this->col = $col;
+    }
+
+    public function toNode() {
+        return new HTMLPurifier_Node_Text($this->data, $this->is_whitespace, $this->line, $this->col);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/TokenFactory.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/TokenFactory.php
new file mode 100644 (file)
index 0000000..dea2446
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+
+/**
+ * Factory for token generation.
+ *
+ * @note Doing some benchmarking indicates that the new operator is much
+ *       slower than the clone operator (even discounting the cost of the
+ *       constructor).  This class is for that optimization.
+ *       Other then that, there's not much point as we don't
+ *       maintain parallel HTMLPurifier_Token hierarchies (the main reason why
+ *       you'd want to use an abstract factory).
+ * @todo Port DirectLex to use this
+ */
+class HTMLPurifier_TokenFactory
+{
+    // p stands for prototype
+
+    /**
+     * @type HTMLPurifier_Token_Start
+     */
+    private $p_start;
+
+    /**
+     * @type HTMLPurifier_Token_End
+     */
+    private $p_end;
+
+    /**
+     * @type HTMLPurifier_Token_Empty
+     */
+    private $p_empty;
+
+    /**
+     * @type HTMLPurifier_Token_Text
+     */
+    private $p_text;
+
+    /**
+     * @type HTMLPurifier_Token_Comment
+     */
+    private $p_comment;
+
+    /**
+     * Generates blank prototypes for cloning.
+     */
+    public function __construct()
+    {
+        $this->p_start = new HTMLPurifier_Token_Start('', array());
+        $this->p_end = new HTMLPurifier_Token_End('');
+        $this->p_empty = new HTMLPurifier_Token_Empty('', array());
+        $this->p_text = new HTMLPurifier_Token_Text('');
+        $this->p_comment = new HTMLPurifier_Token_Comment('');
+    }
+
+    /**
+     * Creates a HTMLPurifier_Token_Start.
+     * @param string $name Tag name
+     * @param array $attr Associative array of attributes
+     * @return HTMLPurifier_Token_Start Generated HTMLPurifier_Token_Start
+     */
+    public function createStart($name, $attr = array())
+    {
+        $p = clone $this->p_start;
+        $p->__construct($name, $attr);
+        return $p;
+    }
+
+    /**
+     * Creates a HTMLPurifier_Token_End.
+     * @param string $name Tag name
+     * @return HTMLPurifier_Token_End Generated HTMLPurifier_Token_End
+     */
+    public function createEnd($name)
+    {
+        $p = clone $this->p_end;
+        $p->__construct($name);
+        return $p;
+    }
+
+    /**
+     * Creates a HTMLPurifier_Token_Empty.
+     * @param string $name Tag name
+     * @param array $attr Associative array of attributes
+     * @return HTMLPurifier_Token_Empty Generated HTMLPurifier_Token_Empty
+     */
+    public function createEmpty($name, $attr = array())
+    {
+        $p = clone $this->p_empty;
+        $p->__construct($name, $attr);
+        return $p;
+    }
+
+    /**
+     * Creates a HTMLPurifier_Token_Text.
+     * @param string $data Data of text token
+     * @return HTMLPurifier_Token_Text Generated HTMLPurifier_Token_Text
+     */
+    public function createText($data)
+    {
+        $p = clone $this->p_text;
+        $p->__construct($data);
+        return $p;
+    }
+
+    /**
+     * Creates a HTMLPurifier_Token_Comment.
+     * @param string $data Data of comment token
+     * @return HTMLPurifier_Token_Comment Generated HTMLPurifier_Token_Comment
+     */
+    public function createComment($data)
+    {
+        $p = clone $this->p_comment;
+        $p->__construct($data);
+        return $p;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URI.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URI.php
new file mode 100644 (file)
index 0000000..a5e7ae2
--- /dev/null
@@ -0,0 +1,314 @@
+<?php
+
+/**
+ * HTML Purifier's internal representation of a URI.
+ * @note
+ *      Internal data-structures are completely escaped. If the data needs
+ *      to be used in a non-URI context (which is very unlikely), be sure
+ *      to decode it first. The URI may not necessarily be well-formed until
+ *      validate() is called.
+ */
+class HTMLPurifier_URI
+{
+    /**
+     * @type string
+     */
+    public $scheme;
+
+    /**
+     * @type string
+     */
+    public $userinfo;
+
+    /**
+     * @type string
+     */
+    public $host;
+
+    /**
+     * @type int
+     */
+    public $port;
+
+    /**
+     * @type string
+     */
+    public $path;
+
+    /**
+     * @type string
+     */
+    public $query;
+
+    /**
+     * @type string
+     */
+    public $fragment;
+
+    /**
+     * @param string $scheme
+     * @param string $userinfo
+     * @param string $host
+     * @param int $port
+     * @param string $path
+     * @param string $query
+     * @param string $fragment
+     * @note Automatically normalizes scheme and port
+     */
+    public function __construct($scheme, $userinfo, $host, $port, $path, $query, $fragment)
+    {
+        $this->scheme = is_null($scheme) || ctype_lower($scheme) ? $scheme : strtolower($scheme);
+        $this->userinfo = $userinfo;
+        $this->host = $host;
+        $this->port = is_null($port) ? $port : (int)$port;
+        $this->path = $path;
+        $this->query = $query;
+        $this->fragment = $fragment;
+    }
+
+    /**
+     * Retrieves a scheme object corresponding to the URI's scheme/default
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return HTMLPurifier_URIScheme Scheme object appropriate for validating this URI
+     */
+    public function getSchemeObj($config, $context)
+    {
+        $registry = HTMLPurifier_URISchemeRegistry::instance();
+        if ($this->scheme !== null) {
+            $scheme_obj = $registry->getScheme($this->scheme, $config, $context);
+            if (!$scheme_obj) {
+                return false;
+            } // invalid scheme, clean it out
+        } else {
+            // no scheme: retrieve the default one
+            $def = $config->getDefinition('URI');
+            $scheme_obj = $def->getDefaultScheme($config, $context);
+            if (!$scheme_obj) {
+                // something funky happened to the default scheme object
+                trigger_error(
+                    'Default scheme object "' . $def->defaultScheme . '" was not readable',
+                    E_USER_WARNING
+                );
+                return false;
+            }
+        }
+        return $scheme_obj;
+    }
+
+    /**
+     * Generic validation method applicable for all schemes. May modify
+     * this URI in order to get it into a compliant form.
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool True if validation/filtering succeeds, false if failure
+     */
+    public function validate($config, $context)
+    {
+        // ABNF definitions from RFC 3986
+        $chars_sub_delims = '!$&\'()*+,;=';
+        $chars_gen_delims = ':/?#[]@';
+        $chars_pchar = $chars_sub_delims . ':@';
+
+        // validate host
+        if (!is_null($this->host)) {
+            $host_def = new HTMLPurifier_AttrDef_URI_Host();
+            $this->host = $host_def->validate($this->host, $config, $context);
+            if ($this->host === false) {
+                $this->host = null;
+            }
+        }
+
+        // validate scheme
+        // NOTE: It's not appropriate to check whether or not this
+        // scheme is in our registry, since a URIFilter may convert a
+        // URI that we don't allow into one we do.  So instead, we just
+        // check if the scheme can be dropped because there is no host
+        // and it is our default scheme.
+        if (!is_null($this->scheme) && is_null($this->host) || $this->host === '') {
+            // support for relative paths is pretty abysmal when the
+            // scheme is present, so axe it when possible
+            $def = $config->getDefinition('URI');
+            if ($def->defaultScheme === $this->scheme) {
+                $this->scheme = null;
+            }
+        }
+
+        // validate username
+        if (!is_null($this->userinfo)) {
+            $encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . ':');
+            $this->userinfo = $encoder->encode($this->userinfo);
+        }
+
+        // validate port
+        if (!is_null($this->port)) {
+            if ($this->port < 1 || $this->port > 65535) {
+                $this->port = null;
+            }
+        }
+
+        // validate path
+        $segments_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/');
+        if (!is_null($this->host)) { // this catches $this->host === ''
+            // path-abempty (hier and relative)
+            // http://www.example.com/my/path
+            // //www.example.com/my/path (looks odd, but works, and
+            //                            recognized by most browsers)
+            // (this set is valid or invalid on a scheme by scheme
+            // basis, so we'll deal with it later)
+            // file:///my/path
+            // ///my/path
+            $this->path = $segments_encoder->encode($this->path);
+        } elseif ($this->path !== '') {
+            if ($this->path[0] === '/') {
+                // path-absolute (hier and relative)
+                // http:/my/path
+                // /my/path
+                if (strlen($this->path) >= 2 && $this->path[1] === '/') {
+                    // This could happen if both the host gets stripped
+                    // out
+                    // http://my/path
+                    // //my/path
+                    $this->path = '';
+                } else {
+                    $this->path = $segments_encoder->encode($this->path);
+                }
+            } elseif (!is_null($this->scheme)) {
+                // path-rootless (hier)
+                // http:my/path
+                // Short circuit evaluation means we don't need to check nz
+                $this->path = $segments_encoder->encode($this->path);
+            } else {
+                // path-noscheme (relative)
+                // my/path
+                // (once again, not checking nz)
+                $segment_nc_encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . '@');
+                $c = strpos($this->path, '/');
+                if ($c !== false) {
+                    $this->path =
+                        $segment_nc_encoder->encode(substr($this->path, 0, $c)) .
+                        $segments_encoder->encode(substr($this->path, $c));
+                } else {
+                    $this->path = $segment_nc_encoder->encode($this->path);
+                }
+            }
+        } else {
+            // path-empty (hier and relative)
+            $this->path = ''; // just to be safe
+        }
+
+        // qf = query and fragment
+        $qf_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/?');
+
+        if (!is_null($this->query)) {
+            $this->query = $qf_encoder->encode($this->query);
+        }
+
+        if (!is_null($this->fragment)) {
+            $this->fragment = $qf_encoder->encode($this->fragment);
+        }
+        return true;
+    }
+
+    /**
+     * Convert URI back to string
+     * @return string URI appropriate for output
+     */
+    public function toString()
+    {
+        // reconstruct authority
+        $authority = null;
+        // there is a rendering difference between a null authority
+        // (http:foo-bar) and an empty string authority
+        // (http:///foo-bar).
+        if (!is_null($this->host)) {
+            $authority = '';
+            if (!is_null($this->userinfo)) {
+                $authority .= $this->userinfo . '@';
+            }
+            $authority .= $this->host;
+            if (!is_null($this->port)) {
+                $authority .= ':' . $this->port;
+            }
+        }
+
+        // Reconstruct the result
+        // One might wonder about parsing quirks from browsers after
+        // this reconstruction.  Unfortunately, parsing behavior depends
+        // on what *scheme* was employed (file:///foo is handled *very*
+        // differently than http:///foo), so unfortunately we have to
+        // defer to the schemes to do the right thing.
+        $result = '';
+        if (!is_null($this->scheme)) {
+            $result .= $this->scheme . ':';
+        }
+        if (!is_null($authority)) {
+            $result .= '//' . $authority;
+        }
+        $result .= $this->path;
+        if (!is_null($this->query)) {
+            $result .= '?' . $this->query;
+        }
+        if (!is_null($this->fragment)) {
+            $result .= '#' . $this->fragment;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns true if this URL might be considered a 'local' URL given
+     * the current context.  This is true when the host is null, or
+     * when it matches the host supplied to the configuration.
+     *
+     * Note that this does not do any scheme checking, so it is mostly
+     * only appropriate for metadata that doesn't care about protocol
+     * security.  isBenign is probably what you actually want.
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function isLocal($config, $context)
+    {
+        if ($this->host === null) {
+            return true;
+        }
+        $uri_def = $config->getDefinition('URI');
+        if ($uri_def->host === $this->host) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if this URL should be considered a 'benign' URL,
+     * that is:
+     *
+     *      - It is a local URL (isLocal), and
+     *      - It has a equal or better level of security
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function isBenign($config, $context)
+    {
+        if (!$this->isLocal($config, $context)) {
+            return false;
+        }
+
+        $scheme_obj = $this->getSchemeObj($config, $context);
+        if (!$scheme_obj) {
+            return false;
+        } // conservative approach
+
+        $current_scheme_obj = $config->getDefinition('URI')->getDefaultScheme($config, $context);
+        if ($current_scheme_obj->secure) {
+            if (!$scheme_obj->secure) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIDefinition.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIDefinition.php
new file mode 100644 (file)
index 0000000..e0bd8bc
--- /dev/null
@@ -0,0 +1,112 @@
+<?php
+
+class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
+{
+
+    public $type = 'URI';
+    protected $filters = array();
+    protected $postFilters = array();
+    protected $registeredFilters = array();
+
+    /**
+     * HTMLPurifier_URI object of the base specified at %URI.Base
+     */
+    public $base;
+
+    /**
+     * String host to consider "home" base, derived off of $base
+     */
+    public $host;
+
+    /**
+     * Name of default scheme based on %URI.DefaultScheme and %URI.Base
+     */
+    public $defaultScheme;
+
+    public function __construct()
+    {
+        $this->registerFilter(new HTMLPurifier_URIFilter_DisableExternal());
+        $this->registerFilter(new HTMLPurifier_URIFilter_DisableExternalResources());
+        $this->registerFilter(new HTMLPurifier_URIFilter_DisableResources());
+        $this->registerFilter(new HTMLPurifier_URIFilter_HostBlacklist());
+        $this->registerFilter(new HTMLPurifier_URIFilter_SafeIframe());
+        $this->registerFilter(new HTMLPurifier_URIFilter_MakeAbsolute());
+        $this->registerFilter(new HTMLPurifier_URIFilter_Munge());
+    }
+
+    public function registerFilter($filter)
+    {
+        $this->registeredFilters[$filter->name] = $filter;
+    }
+
+    public function addFilter($filter, $config)
+    {
+        $r = $filter->prepare($config);
+        if ($r === false) return; // null is ok, for backwards compat
+        if ($filter->post) {
+            $this->postFilters[$filter->name] = $filter;
+        } else {
+            $this->filters[$filter->name] = $filter;
+        }
+    }
+
+    protected function doSetup($config)
+    {
+        $this->setupMemberVariables($config);
+        $this->setupFilters($config);
+    }
+
+    protected function setupFilters($config)
+    {
+        foreach ($this->registeredFilters as $name => $filter) {
+            if ($filter->always_load) {
+                $this->addFilter($filter, $config);
+            } else {
+                $conf = $config->get('URI.' . $name);
+                if ($conf !== false && $conf !== null) {
+                    $this->addFilter($filter, $config);
+                }
+            }
+        }
+        unset($this->registeredFilters);
+    }
+
+    protected function setupMemberVariables($config)
+    {
+        $this->host = $config->get('URI.Host');
+        $base_uri = $config->get('URI.Base');
+        if (!is_null($base_uri)) {
+            $parser = new HTMLPurifier_URIParser();
+            $this->base = $parser->parse($base_uri);
+            $this->defaultScheme = $this->base->scheme;
+            if (is_null($this->host)) $this->host = $this->base->host;
+        }
+        if (is_null($this->defaultScheme)) $this->defaultScheme = $config->get('URI.DefaultScheme');
+    }
+
+    public function getDefaultScheme($config, $context)
+    {
+        return HTMLPurifier_URISchemeRegistry::instance()->getScheme($this->defaultScheme, $config, $context);
+    }
+
+    public function filter(&$uri, $config, $context)
+    {
+        foreach ($this->filters as $name => $f) {
+            $result = $f->filter($uri, $config, $context);
+            if (!$result) return false;
+        }
+        return true;
+    }
+
+    public function postFilter(&$uri, $config, $context)
+    {
+        foreach ($this->postFilters as $name => $f) {
+            $result = $f->filter($uri, $config, $context);
+            if (!$result) return false;
+        }
+        return true;
+    }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter.php
new file mode 100644 (file)
index 0000000..09724e9
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * Chainable filters for custom URI processing.
+ *
+ * These filters can perform custom actions on a URI filter object,
+ * including transformation or blacklisting.  A filter named Foo
+ * must have a corresponding configuration directive %URI.Foo,
+ * unless always_load is specified to be true.
+ *
+ * The following contexts may be available while URIFilters are being
+ * processed:
+ *
+ *      - EmbeddedURI: true if URI is an embedded resource that will
+ *        be loaded automatically on page load
+ *      - CurrentToken: a reference to the token that is currently
+ *        being processed
+ *      - CurrentAttr: the name of the attribute that is currently being
+ *        processed
+ *      - CurrentCSSProperty: the name of the CSS property that is
+ *        currently being processed (if applicable)
+ *
+ * @warning This filter is called before scheme object validation occurs.
+ *          Make sure, if you require a specific scheme object, you
+ *          you check that it exists. This allows filters to convert
+ *          proprietary URI schemes into regular ones.
+ */
+abstract class HTMLPurifier_URIFilter
+{
+
+    /**
+     * Unique identifier of filter.
+     * @type string
+     */
+    public $name;
+
+    /**
+     * True if this filter should be run after scheme validation.
+     * @type bool
+     */
+    public $post = false;
+
+    /**
+     * True if this filter should always be loaded.
+     * This permits a filter to be named Foo without the corresponding
+     * %URI.Foo directive existing.
+     * @type bool
+     */
+    public $always_load = false;
+
+    /**
+     * Performs initialization for the filter.  If the filter returns
+     * false, this means that it shouldn't be considered active.
+     * @param HTMLPurifier_Config $config
+     * @return bool
+     */
+    public function prepare($config)
+    {
+        return true;
+    }
+
+    /**
+     * Filter a URI object
+     * @param HTMLPurifier_URI $uri Reference to URI object variable
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool Whether or not to continue processing: false indicates
+     *         URL is no good, true indicates continue processing. Note that
+     *         all changes are committed directly on the URI object
+     */
+    abstract public function filter(&$uri, $config, $context);
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternal.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternal.php
new file mode 100644 (file)
index 0000000..ced1b13
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+
+class HTMLPurifier_URIFilter_DisableExternal extends HTMLPurifier_URIFilter
+{
+    /**
+     * @type string
+     */
+    public $name = 'DisableExternal';
+
+    /**
+     * @type array
+     */
+    protected $ourHostParts = false;
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return void
+     */
+    public function prepare($config)
+    {
+        $our_host = $config->getDefinition('URI')->host;
+        if ($our_host !== null) {
+            $this->ourHostParts = array_reverse(explode('.', $our_host));
+        }
+    }
+
+    /**
+     * @param HTMLPurifier_URI $uri Reference
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function filter(&$uri, $config, $context)
+    {
+        if (is_null($uri->host)) {
+            return true;
+        }
+        if ($this->ourHostParts === false) {
+            return false;
+        }
+        $host_parts = array_reverse(explode('.', $uri->host));
+        foreach ($this->ourHostParts as $i => $x) {
+            if (!isset($host_parts[$i])) {
+                return false;
+            }
+            if ($host_parts[$i] != $this->ourHostParts[$i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php
new file mode 100644 (file)
index 0000000..c656216
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+
+class HTMLPurifier_URIFilter_DisableExternalResources extends HTMLPurifier_URIFilter_DisableExternal
+{
+    /**
+     * @type string
+     */
+    public $name = 'DisableExternalResources';
+
+    /**
+     * @param HTMLPurifier_URI $uri
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function filter(&$uri, $config, $context)
+    {
+        if (!$context->get('EmbeddedURI', true)) {
+            return true;
+        }
+        return parent::filter($uri, $config, $context);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php
new file mode 100644 (file)
index 0000000..d5c412c
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+
+class HTMLPurifier_URIFilter_DisableResources extends HTMLPurifier_URIFilter
+{
+    /**
+     * @type string
+     */
+    public $name = 'DisableResources';
+
+    /**
+     * @param HTMLPurifier_URI $uri
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function filter(&$uri, $config, $context)
+    {
+        return !$context->get('EmbeddedURI', true);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php
new file mode 100644 (file)
index 0000000..a6645c1
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+// It's not clear to me whether or not Punycode means that hostnames
+// do not have canonical forms anymore. As far as I can tell, it's
+// not a problem (punycoding should be identity when no Unicode
+// points are involved), but I'm not 100% sure
+class HTMLPurifier_URIFilter_HostBlacklist extends HTMLPurifier_URIFilter
+{
+    /**
+     * @type string
+     */
+    public $name = 'HostBlacklist';
+
+    /**
+     * @type array
+     */
+    protected $blacklist = array();
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return bool
+     */
+    public function prepare($config)
+    {
+        $this->blacklist = $config->get('URI.HostBlacklist');
+        return true;
+    }
+
+    /**
+     * @param HTMLPurifier_URI $uri
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function filter(&$uri, $config, $context)
+    {
+        foreach ($this->blacklist as $blacklisted_host_fragment) {
+            if (strpos($uri->host, $blacklisted_host_fragment) !== false) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php
new file mode 100644 (file)
index 0000000..c507bbf
--- /dev/null
@@ -0,0 +1,158 @@
+<?php
+
+// does not support network paths
+
+class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter
+{
+    /**
+     * @type string
+     */
+    public $name = 'MakeAbsolute';
+
+    /**
+     * @type
+     */
+    protected $base;
+
+    /**
+     * @type array
+     */
+    protected $basePathStack = array();
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return bool
+     */
+    public function prepare($config)
+    {
+        $def = $config->getDefinition('URI');
+        $this->base = $def->base;
+        if (is_null($this->base)) {
+            trigger_error(
+                'URI.MakeAbsolute is being ignored due to lack of ' .
+                'value for URI.Base configuration',
+                E_USER_WARNING
+            );
+            return false;
+        }
+        $this->base->fragment = null; // fragment is invalid for base URI
+        $stack = explode('/', $this->base->path);
+        array_pop($stack); // discard last segment
+        $stack = $this->_collapseStack($stack); // do pre-parsing
+        $this->basePathStack = $stack;
+        return true;
+    }
+
+    /**
+     * @param HTMLPurifier_URI $uri
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function filter(&$uri, $config, $context)
+    {
+        if (is_null($this->base)) {
+            return true;
+        } // abort early
+        if ($uri->path === '' && is_null($uri->scheme) &&
+            is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment)) {
+            // reference to current document
+            $uri = clone $this->base;
+            return true;
+        }
+        if (!is_null($uri->scheme)) {
+            // absolute URI already: don't change
+            if (!is_null($uri->host)) {
+                return true;
+            }
+            $scheme_obj = $uri->getSchemeObj($config, $context);
+            if (!$scheme_obj) {
+                // scheme not recognized
+                return false;
+            }
+            if (!$scheme_obj->hierarchical) {
+                // non-hierarchal URI with explicit scheme, don't change
+                return true;
+            }
+            // special case: had a scheme but always is hierarchical and had no authority
+        }
+        if (!is_null($uri->host)) {
+            // network path, don't bother
+            return true;
+        }
+        if ($uri->path === '') {
+            $uri->path = $this->base->path;
+        } elseif ($uri->path[0] !== '/') {
+            // relative path, needs more complicated processing
+            $stack = explode('/', $uri->path);
+            $new_stack = array_merge($this->basePathStack, $stack);
+            if ($new_stack[0] !== '' && !is_null($this->base->host)) {
+                array_unshift($new_stack, '');
+            }
+            $new_stack = $this->_collapseStack($new_stack);
+            $uri->path = implode('/', $new_stack);
+        } else {
+            // absolute path, but still we should collapse
+            $uri->path = implode('/', $this->_collapseStack(explode('/', $uri->path)));
+        }
+        // re-combine
+        $uri->scheme = $this->base->scheme;
+        if (is_null($uri->userinfo)) {
+            $uri->userinfo = $this->base->userinfo;
+        }
+        if (is_null($uri->host)) {
+            $uri->host = $this->base->host;
+        }
+        if (is_null($uri->port)) {
+            $uri->port = $this->base->port;
+        }
+        return true;
+    }
+
+    /**
+     * Resolve dots and double-dots in a path stack
+     * @param array $stack
+     * @return array
+     */
+    private function _collapseStack($stack)
+    {
+        $result = array();
+        $is_folder = false;
+        for ($i = 0; isset($stack[$i]); $i++) {
+            $is_folder = false;
+            // absorb an internally duplicated slash
+            if ($stack[$i] == '' && $i && isset($stack[$i + 1])) {
+                continue;
+            }
+            if ($stack[$i] == '..') {
+                if (!empty($result)) {
+                    $segment = array_pop($result);
+                    if ($segment === '' && empty($result)) {
+                        // error case: attempted to back out too far:
+                        // restore the leading slash
+                        $result[] = '';
+                    } elseif ($segment === '..') {
+                        $result[] = '..'; // cannot remove .. with ..
+                    }
+                } else {
+                    // relative path, preserve the double-dots
+                    $result[] = '..';
+                }
+                $is_folder = true;
+                continue;
+            }
+            if ($stack[$i] == '.') {
+                // silently absorb
+                $is_folder = true;
+                continue;
+            }
+            $result[] = $stack[$i];
+        }
+        if ($is_folder) {
+            $result[] = '';
+        }
+        return $result;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php
new file mode 100644 (file)
index 0000000..6e03315
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+
+class HTMLPurifier_URIFilter_Munge extends HTMLPurifier_URIFilter
+{
+    /**
+     * @type string
+     */
+    public $name = 'Munge';
+
+    /**
+     * @type bool
+     */
+    public $post = true;
+
+    /**
+     * @type string
+     */
+    private $target;
+
+    /**
+     * @type HTMLPurifier_URIParser
+     */
+    private $parser;
+
+    /**
+     * @type bool
+     */
+    private $doEmbed;
+
+    /**
+     * @type string
+     */
+    private $secretKey;
+
+    /**
+     * @type array
+     */
+    protected $replace = array();
+
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return bool
+     */
+    public function prepare($config)
+    {
+        $this->target = $config->get('URI.' . $this->name);
+        $this->parser = new HTMLPurifier_URIParser();
+        $this->doEmbed = $config->get('URI.MungeResources');
+        $this->secretKey = $config->get('URI.MungeSecretKey');
+        if ($this->secretKey && !function_exists('hash_hmac')) {
+            throw new Exception("Cannot use %URI.MungeSecretKey without hash_hmac support.");
+        }
+        return true;
+    }
+
+    /**
+     * @param HTMLPurifier_URI $uri
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function filter(&$uri, $config, $context)
+    {
+        if ($context->get('EmbeddedURI', true) && !$this->doEmbed) {
+            return true;
+        }
+
+        $scheme_obj = $uri->getSchemeObj($config, $context);
+        if (!$scheme_obj) {
+            return true;
+        } // ignore unknown schemes, maybe another postfilter did it
+        if (!$scheme_obj->browsable) {
+            return true;
+        } // ignore non-browseable schemes, since we can't munge those in a reasonable way
+        if ($uri->isBenign($config, $context)) {
+            return true;
+        } // don't redirect if a benign URL
+
+        $this->makeReplace($uri, $config, $context);
+        $this->replace = array_map('rawurlencode', $this->replace);
+
+        $new_uri = strtr($this->target, $this->replace);
+        $new_uri = $this->parser->parse($new_uri);
+        // don't redirect if the target host is the same as the
+        // starting host
+        if ($uri->host === $new_uri->host) {
+            return true;
+        }
+        $uri = $new_uri; // overwrite
+        return true;
+    }
+
+    /**
+     * @param HTMLPurifier_URI $uri
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     */
+    protected function makeReplace($uri, $config, $context)
+    {
+        $string = $uri->toString();
+        // always available
+        $this->replace['%s'] = $string;
+        $this->replace['%r'] = $context->get('EmbeddedURI', true);
+        $token = $context->get('CurrentToken', true);
+        $this->replace['%n'] = $token ? $token->name : null;
+        $this->replace['%m'] = $context->get('CurrentAttr', true);
+        $this->replace['%p'] = $context->get('CurrentCSSProperty', true);
+        // not always available
+        if ($this->secretKey) {
+            $this->replace['%t'] = hash_hmac("sha256", $string, $this->secretKey);
+        }
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php
new file mode 100644 (file)
index 0000000..f609c47
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * Implements safety checks for safe iframes.
+ *
+ * @warning This filter is *critical* for ensuring that %HTML.SafeIframe
+ * works safely.
+ */
+class HTMLPurifier_URIFilter_SafeIframe extends HTMLPurifier_URIFilter
+{
+    /**
+     * @type string
+     */
+    public $name = 'SafeIframe';
+
+    /**
+     * @type bool
+     */
+    public $always_load = true;
+
+    /**
+     * @type string
+     */
+    protected $regexp = null;
+
+    // XXX: The not so good bit about how this is all set up now is we
+    // can't check HTML.SafeIframe in the 'prepare' step: we have to
+    // defer till the actual filtering.
+    /**
+     * @param HTMLPurifier_Config $config
+     * @return bool
+     */
+    public function prepare($config)
+    {
+        $this->regexp = $config->get('URI.SafeIframeRegexp');
+        return true;
+    }
+
+    /**
+     * @param HTMLPurifier_URI $uri
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function filter(&$uri, $config, $context)
+    {
+        // check if filter not applicable
+        if (!$config->get('HTML.SafeIframe')) {
+            return true;
+        }
+        // check if the filter should actually trigger
+        if (!$context->get('EmbeddedURI', true)) {
+            return true;
+        }
+        $token = $context->get('CurrentToken', true);
+        if (!($token && $token->name == 'iframe')) {
+            return true;
+        }
+        // check if we actually have some whitelists enabled
+        if ($this->regexp === null) {
+            return false;
+        }
+        // actually check the whitelists
+        return preg_match($this->regexp, $uri->toString());
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIParser.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIParser.php
new file mode 100644 (file)
index 0000000..0e7381a
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * Parses a URI into the components and fragment identifier as specified
+ * by RFC 3986.
+ */
+class HTMLPurifier_URIParser
+{
+
+    /**
+     * Instance of HTMLPurifier_PercentEncoder to do normalization with.
+     */
+    protected $percentEncoder;
+
+    public function __construct()
+    {
+        $this->percentEncoder = new HTMLPurifier_PercentEncoder();
+    }
+
+    /**
+     * Parses a URI.
+     * @param $uri string URI to parse
+     * @return HTMLPurifier_URI representation of URI. This representation has
+     *         not been validated yet and may not conform to RFC.
+     */
+    public function parse($uri)
+    {
+        $uri = $this->percentEncoder->normalize($uri);
+
+        // Regexp is as per Appendix B.
+        // Note that ["<>] are an addition to the RFC's recommended
+        // characters, because they represent external delimeters.
+        $r_URI = '!'.
+            '(([a-zA-Z0-9\.\+\-]+):)?'. // 2. Scheme
+            '(//([^/?#"<>]*))?'. // 4. Authority
+            '([^?#"<>]*)'.       // 5. Path
+            '(\?([^#"<>]*))?'.   // 7. Query
+            '(#([^"<>]*))?'.     // 8. Fragment
+            '!';
+
+        $matches = array();
+        $result = preg_match($r_URI, $uri, $matches);
+
+        if (!$result) return false; // *really* invalid URI
+
+        // seperate out parts
+        $scheme     = !empty($matches[1]) ? $matches[2] : null;
+        $authority  = !empty($matches[3]) ? $matches[4] : null;
+        $path       = $matches[5]; // always present, can be empty
+        $query      = !empty($matches[6]) ? $matches[7] : null;
+        $fragment   = !empty($matches[8]) ? $matches[9] : null;
+
+        // further parse authority
+        if ($authority !== null) {
+            $r_authority = "/^((.+?)@)?(\[[^\]]+\]|[^:]*)(:(\d*))?/";
+            $matches = array();
+            preg_match($r_authority, $authority, $matches);
+            $userinfo   = !empty($matches[1]) ? $matches[2] : null;
+            $host       = !empty($matches[3]) ? $matches[3] : '';
+            $port       = !empty($matches[4]) ? (int) $matches[5] : null;
+        } else {
+            $port = $host = $userinfo = null;
+        }
+
+        return new HTMLPurifier_URI(
+            $scheme, $userinfo, $host, $port, $path, $query, $fragment);
+    }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme.php
new file mode 100644 (file)
index 0000000..fe9e82c
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * Validator for the components of a URI for a specific scheme
+ */
+abstract class HTMLPurifier_URIScheme
+{
+
+    /**
+     * Scheme's default port (integer). If an explicit port number is
+     * specified that coincides with the default port, it will be
+     * elided.
+     * @type int
+     */
+    public $default_port = null;
+
+    /**
+     * Whether or not URIs of this scheme are locatable by a browser
+     * http and ftp are accessible, while mailto and news are not.
+     * @type bool
+     */
+    public $browsable = false;
+
+    /**
+     * Whether or not data transmitted over this scheme is encrypted.
+     * https is secure, http is not.
+     * @type bool
+     */
+    public $secure = false;
+
+    /**
+     * Whether or not the URI always uses <hier_part>, resolves edge cases
+     * with making relative URIs absolute
+     * @type bool
+     */
+    public $hierarchical = false;
+
+    /**
+     * Whether or not the URI may omit a hostname when the scheme is
+     * explicitly specified, ala file:///path/to/file. As of writing,
+     * 'file' is the only scheme that browsers support his properly.
+     * @type bool
+     */
+    public $may_omit_host = false;
+
+    /**
+     * Validates the components of a URI for a specific scheme.
+     * @param HTMLPurifier_URI $uri Reference to a HTMLPurifier_URI object
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool success or failure
+     */
+    abstract public function doValidate(&$uri, $config, $context);
+
+    /**
+     * Public interface for validating components of a URI.  Performs a
+     * bunch of default actions. Don't overload this method.
+     * @param HTMLPurifier_URI $uri Reference to a HTMLPurifier_URI object
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool success or failure
+     */
+    public function validate(&$uri, $config, $context)
+    {
+        if ($this->default_port == $uri->port) {
+            $uri->port = null;
+        }
+        // kludge: browsers do funny things when the scheme but not the
+        // authority is set
+        if (!$this->may_omit_host &&
+            // if the scheme is present, a missing host is always in error
+            (!is_null($uri->scheme) && ($uri->host === '' || is_null($uri->host))) ||
+            // if the scheme is not present, a *blank* host is in error,
+            // since this translates into '///path' which most browsers
+            // interpret as being 'http://path'.
+            (is_null($uri->scheme) && $uri->host === '')
+        ) {
+            do {
+                if (is_null($uri->scheme)) {
+                    if (substr($uri->path, 0, 2) != '//') {
+                        $uri->host = null;
+                        break;
+                    }
+                    // URI is '////path', so we cannot nullify the
+                    // host to preserve semantics.  Try expanding the
+                    // hostname instead (fall through)
+                }
+                // first see if we can manually insert a hostname
+                $host = $config->get('URI.Host');
+                if (!is_null($host)) {
+                    $uri->host = $host;
+                } else {
+                    // we can't do anything sensible, reject the URL.
+                    return false;
+                }
+            } while (false);
+        }
+        return $this->doValidate($uri, $config, $context);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/data.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/data.php
new file mode 100644 (file)
index 0000000..6ebca49
--- /dev/null
@@ -0,0 +1,127 @@
+<?php
+
+/**
+ * Implements data: URI for base64 encoded images supported by GD.
+ */
+class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme
+{
+    /**
+     * @type bool
+     */
+    public $browsable = true;
+
+    /**
+     * @type array
+     */
+    public $allowed_types = array(
+        // you better write validation code for other types if you
+        // decide to allow them
+        'image/jpeg' => true,
+        'image/gif' => true,
+        'image/png' => true,
+    );
+    // this is actually irrelevant since we only write out the path
+    // component
+    /**
+     * @type bool
+     */
+    public $may_omit_host = true;
+
+    /**
+     * @param HTMLPurifier_URI $uri
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function doValidate(&$uri, $config, $context)
+    {
+        $result = explode(',', $uri->path, 2);
+        $is_base64 = false;
+        $charset = null;
+        $content_type = null;
+        if (count($result) == 2) {
+            list($metadata, $data) = $result;
+            // do some legwork on the metadata
+            $metas = explode(';', $metadata);
+            while (!empty($metas)) {
+                $cur = array_shift($metas);
+                if ($cur == 'base64') {
+                    $is_base64 = true;
+                    break;
+                }
+                if (substr($cur, 0, 8) == 'charset=') {
+                    // doesn't match if there are arbitrary spaces, but
+                    // whatever dude
+                    if ($charset !== null) {
+                        continue;
+                    } // garbage
+                    $charset = substr($cur, 8); // not used
+                } else {
+                    if ($content_type !== null) {
+                        continue;
+                    } // garbage
+                    $content_type = $cur;
+                }
+            }
+        } else {
+            $data = $result[0];
+        }
+        if ($content_type !== null && empty($this->allowed_types[$content_type])) {
+            return false;
+        }
+        if ($charset !== null) {
+            // error; we don't allow plaintext stuff
+            $charset = null;
+        }
+        $data = rawurldecode($data);
+        if ($is_base64) {
+            $raw_data = base64_decode($data);
+        } else {
+            $raw_data = $data;
+        }
+        // XXX probably want to refactor this into a general mechanism
+        // for filtering arbitrary content types
+        $file = tempnam("/tmp", "");
+        file_put_contents($file, $raw_data);
+        if (function_exists('exif_imagetype')) {
+            $image_code = exif_imagetype($file);
+            unlink($file);
+        } elseif (function_exists('getimagesize')) {
+            set_error_handler(array($this, 'muteErrorHandler'));
+            $info = getimagesize($file);
+            restore_error_handler();
+            unlink($file);
+            if ($info == false) {
+                return false;
+            }
+            $image_code = $info[2];
+        } else {
+            trigger_error("could not find exif_imagetype or getimagesize functions", E_USER_ERROR);
+        }
+        $real_content_type = image_type_to_mime_type($image_code);
+        if ($real_content_type != $content_type) {
+            // we're nice guys; if the content type is something else we
+            // support, change it over
+            if (empty($this->allowed_types[$real_content_type])) {
+                return false;
+            }
+            $content_type = $real_content_type;
+        }
+        // ok, it's kosher, rewrite what we need
+        $uri->userinfo = null;
+        $uri->host = null;
+        $uri->port = null;
+        $uri->fragment = null;
+        $uri->query = null;
+        $uri->path = "$content_type;base64," . base64_encode($raw_data);
+        return true;
+    }
+
+    /**
+     * @param int $errno
+     * @param string $errstr
+     */
+    public function muteErrorHandler($errno, $errstr)
+    {
+    }
+}
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/file.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/file.php
new file mode 100644 (file)
index 0000000..215be4b
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * Validates file as defined by RFC 1630 and RFC 1738.
+ */
+class HTMLPurifier_URIScheme_file extends HTMLPurifier_URIScheme
+{
+    /**
+     * Generally file:// URLs are not accessible from most
+     * machines, so placing them as an img src is incorrect.
+     * @type bool
+     */
+    public $browsable = false;
+
+    /**
+     * Basically the *only* URI scheme for which this is true, since
+     * accessing files on the local machine is very common.  In fact,
+     * browsers on some operating systems don't understand the
+     * authority, though I hear it is used on Windows to refer to
+     * network shares.
+     * @type bool
+     */
+    public $may_omit_host = true;
+
+    /**
+     * @param HTMLPurifier_URI $uri
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function doValidate(&$uri, $config, $context)
+    {
+        // Authentication method is not supported
+        $uri->userinfo = null;
+        // file:// makes no provisions for accessing the resource
+        $uri->port = null;
+        // While it seems to work on Firefox, the querystring has
+        // no possible effect and is thus stripped.
+        $uri->query = null;
+        return true;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php
new file mode 100644 (file)
index 0000000..1eb43ee
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * Validates ftp (File Transfer Protocol) URIs as defined by generic RFC 1738.
+ */
+class HTMLPurifier_URIScheme_ftp extends HTMLPurifier_URIScheme
+{
+    /**
+     * @type int
+     */
+    public $default_port = 21;
+
+    /**
+     * @type bool
+     */
+    public $browsable = true; // usually
+
+    /**
+     * @type bool
+     */
+    public $hierarchical = true;
+
+    /**
+     * @param HTMLPurifier_URI $uri
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function doValidate(&$uri, $config, $context)
+    {
+        $uri->query = null;
+
+        // typecode check
+        $semicolon_pos = strrpos($uri->path, ';'); // reverse
+        if ($semicolon_pos !== false) {
+            $type = substr($uri->path, $semicolon_pos + 1); // no semicolon
+            $uri->path = substr($uri->path, 0, $semicolon_pos);
+            $type_ret = '';
+            if (strpos($type, '=') !== false) {
+                // figure out whether or not the declaration is correct
+                list($key, $typecode) = explode('=', $type, 2);
+                if ($key !== 'type') {
+                    // invalid key, tack it back on encoded
+                    $uri->path .= '%3B' . $type;
+                } elseif ($typecode === 'a' || $typecode === 'i' || $typecode === 'd') {
+                    $type_ret = ";type=$typecode";
+                }
+            } else {
+                $uri->path .= '%3B' . $type;
+            }
+            $uri->path = str_replace(';', '%3B', $uri->path);
+            $uri->path .= $type_ret;
+        }
+        return true;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/http.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/http.php
new file mode 100644 (file)
index 0000000..ce69ec4
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * Validates http (HyperText Transfer Protocol) as defined by RFC 2616
+ */
+class HTMLPurifier_URIScheme_http extends HTMLPurifier_URIScheme
+{
+    /**
+     * @type int
+     */
+    public $default_port = 80;
+
+    /**
+     * @type bool
+     */
+    public $browsable = true;
+
+    /**
+     * @type bool
+     */
+    public $hierarchical = true;
+
+    /**
+     * @param HTMLPurifier_URI $uri
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function doValidate(&$uri, $config, $context)
+    {
+        $uri->userinfo = null;
+        return true;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/https.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/https.php
new file mode 100644 (file)
index 0000000..0e96882
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * Validates https (Secure HTTP) according to http scheme.
+ */
+class HTMLPurifier_URIScheme_https extends HTMLPurifier_URIScheme_http
+{
+    /**
+     * @type int
+     */
+    public $default_port = 443;
+    /**
+     * @type bool
+     */
+    public $secure = true;
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/mailto.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/mailto.php
new file mode 100644 (file)
index 0000000..c3a6b60
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+// VERY RELAXED! Shouldn't cause problems, not even Firefox checks if the
+// email is valid, but be careful!
+
+/**
+ * Validates mailto (for E-mail) according to RFC 2368
+ * @todo Validate the email address
+ * @todo Filter allowed query parameters
+ */
+
+class HTMLPurifier_URIScheme_mailto extends HTMLPurifier_URIScheme
+{
+    /**
+     * @type bool
+     */
+    public $browsable = false;
+
+    /**
+     * @type bool
+     */
+    public $may_omit_host = true;
+
+    /**
+     * @param HTMLPurifier_URI $uri
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function doValidate(&$uri, $config, $context)
+    {
+        $uri->userinfo = null;
+        $uri->host     = null;
+        $uri->port     = null;
+        // we need to validate path against RFC 2368's addr-spec
+        return true;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/news.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/news.php
new file mode 100644 (file)
index 0000000..7490927
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * Validates news (Usenet) as defined by generic RFC 1738
+ */
+class HTMLPurifier_URIScheme_news extends HTMLPurifier_URIScheme
+{
+    /**
+     * @type bool
+     */
+    public $browsable = false;
+
+    /**
+     * @type bool
+     */
+    public $may_omit_host = true;
+
+    /**
+     * @param HTMLPurifier_URI $uri
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function doValidate(&$uri, $config, $context)
+    {
+        $uri->userinfo = null;
+        $uri->host = null;
+        $uri->port = null;
+        $uri->query = null;
+        // typecode check needed on path
+        return true;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php
new file mode 100644 (file)
index 0000000..f211d71
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * Validates nntp (Network News Transfer Protocol) as defined by generic RFC 1738
+ */
+class HTMLPurifier_URIScheme_nntp extends HTMLPurifier_URIScheme
+{
+    /**
+     * @type int
+     */
+    public $default_port = 119;
+
+    /**
+     * @type bool
+     */
+    public $browsable = false;
+
+    /**
+     * @param HTMLPurifier_URI $uri
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function doValidate(&$uri, $config, $context)
+    {
+        $uri->userinfo = null;
+        $uri->query = null;
+        return true;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php
new file mode 100644 (file)
index 0000000..4ac8a0b
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * Registry for retrieving specific URI scheme validator objects.
+ */
+class HTMLPurifier_URISchemeRegistry
+{
+
+    /**
+     * Retrieve sole instance of the registry.
+     * @param HTMLPurifier_URISchemeRegistry $prototype Optional prototype to overload sole instance with,
+     *                   or bool true to reset to default registry.
+     * @return HTMLPurifier_URISchemeRegistry
+     * @note Pass a registry object $prototype with a compatible interface and
+     *       the function will copy it and return it all further times.
+     */
+    public static function instance($prototype = null)
+    {
+        static $instance = null;
+        if ($prototype !== null) {
+            $instance = $prototype;
+        } elseif ($instance === null || $prototype == true) {
+            $instance = new HTMLPurifier_URISchemeRegistry();
+        }
+        return $instance;
+    }
+
+    /**
+     * Cache of retrieved schemes.
+     * @type HTMLPurifier_URIScheme[]
+     */
+    protected $schemes = array();
+
+    /**
+     * Retrieves a scheme validator object
+     * @param string $scheme String scheme name like http or mailto
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return HTMLPurifier_URIScheme
+     */
+    public function getScheme($scheme, $config, $context)
+    {
+        if (!$config) {
+            $config = HTMLPurifier_Config::createDefault();
+        }
+
+        // important, otherwise attacker could include arbitrary file
+        $allowed_schemes = $config->get('URI.AllowedSchemes');
+        if (!$config->get('URI.OverrideAllowedSchemes') &&
+            !isset($allowed_schemes[$scheme])
+        ) {
+            return;
+        }
+
+        if (isset($this->schemes[$scheme])) {
+            return $this->schemes[$scheme];
+        }
+        if (!isset($allowed_schemes[$scheme])) {
+            return;
+        }
+
+        $class = 'HTMLPurifier_URIScheme_' . $scheme;
+        if (!class_exists($class)) {
+            return;
+        }
+        $this->schemes[$scheme] = new $class();
+        return $this->schemes[$scheme];
+    }
+
+    /**
+     * Registers a custom scheme to the cache, bypassing reflection.
+     * @param string $scheme Scheme name
+     * @param HTMLPurifier_URIScheme $scheme_obj
+     */
+    public function register($scheme, $scheme_obj)
+    {
+        $this->schemes[$scheme] = $scheme_obj;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/UnitConverter.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/UnitConverter.php
new file mode 100644 (file)
index 0000000..166f3bf
--- /dev/null
@@ -0,0 +1,307 @@
+<?php
+
+/**
+ * Class for converting between different unit-lengths as specified by
+ * CSS.
+ */
+class HTMLPurifier_UnitConverter
+{
+
+    const ENGLISH = 1;
+    const METRIC = 2;
+    const DIGITAL = 3;
+
+    /**
+     * Units information array. Units are grouped into measuring systems
+     * (English, Metric), and are assigned an integer representing
+     * the conversion factor between that unit and the smallest unit in
+     * the system. Numeric indexes are actually magical constants that
+     * encode conversion data from one system to the next, with a O(n^2)
+     * constraint on memory (this is generally not a problem, since
+     * the number of measuring systems is small.)
+     */
+    protected static $units = array(
+        self::ENGLISH => array(
+            'px' => 3, // This is as per CSS 2.1 and Firefox. Your mileage may vary
+            'pt' => 4,
+            'pc' => 48,
+            'in' => 288,
+            self::METRIC => array('pt', '0.352777778', 'mm'),
+        ),
+        self::METRIC => array(
+            'mm' => 1,
+            'cm' => 10,
+            self::ENGLISH => array('mm', '2.83464567', 'pt'),
+        ),
+    );
+
+    /**
+     * Minimum bcmath precision for output.
+     * @type int
+     */
+    protected $outputPrecision;
+
+    /**
+     * Bcmath precision for internal calculations.
+     * @type int
+     */
+    protected $internalPrecision;
+
+    /**
+     * Whether or not BCMath is available.
+     * @type bool
+     */
+    private $bcmath;
+
+    public function __construct($output_precision = 4, $internal_precision = 10, $force_no_bcmath = false)
+    {
+        $this->outputPrecision = $output_precision;
+        $this->internalPrecision = $internal_precision;
+        $this->bcmath = !$force_no_bcmath && function_exists('bcmul');
+    }
+
+    /**
+     * Converts a length object of one unit into another unit.
+     * @param HTMLPurifier_Length $length
+     *      Instance of HTMLPurifier_Length to convert. You must validate()
+     *      it before passing it here!
+     * @param string $to_unit
+     *      Unit to convert to.
+     * @return HTMLPurifier_Length|bool
+     * @note
+     *      About precision: This conversion function pays very special
+     *      attention to the incoming precision of values and attempts
+     *      to maintain a number of significant figure. Results are
+     *      fairly accurate up to nine digits. Some caveats:
+     *          - If a number is zero-padded as a result of this significant
+     *            figure tracking, the zeroes will be eliminated.
+     *          - If a number contains less than four sigfigs ($outputPrecision)
+     *            and this causes some decimals to be excluded, those
+     *            decimals will be added on.
+     */
+    public function convert($length, $to_unit)
+    {
+        if (!$length->isValid()) {
+            return false;
+        }
+
+        $n = $length->getN();
+        $unit = $length->getUnit();
+
+        if ($n === '0' || $unit === false) {
+            return new HTMLPurifier_Length('0', false);
+        }
+
+        $state = $dest_state = false;
+        foreach (self::$units as $k => $x) {
+            if (isset($x[$unit])) {
+                $state = $k;
+            }
+            if (isset($x[$to_unit])) {
+                $dest_state = $k;
+            }
+        }
+        if (!$state || !$dest_state) {
+            return false;
+        }
+
+        // Some calculations about the initial precision of the number;
+        // this will be useful when we need to do final rounding.
+        $sigfigs = $this->getSigFigs($n);
+        if ($sigfigs < $this->outputPrecision) {
+            $sigfigs = $this->outputPrecision;
+        }
+
+        // BCMath's internal precision deals only with decimals. Use
+        // our default if the initial number has no decimals, or increase
+        // it by how ever many decimals, thus, the number of guard digits
+        // will always be greater than or equal to internalPrecision.
+        $log = (int)floor(log(abs($n), 10));
+        $cp = ($log < 0) ? $this->internalPrecision - $log : $this->internalPrecision; // internal precision
+
+        for ($i = 0; $i < 2; $i++) {
+
+            // Determine what unit IN THIS SYSTEM we need to convert to
+            if ($dest_state === $state) {
+                // Simple conversion
+                $dest_unit = $to_unit;
+            } else {
+                // Convert to the smallest unit, pending a system shift
+                $dest_unit = self::$units[$state][$dest_state][0];
+            }
+
+            // Do the conversion if necessary
+            if ($dest_unit !== $unit) {
+                $factor = $this->div(self::$units[$state][$unit], self::$units[$state][$dest_unit], $cp);
+                $n = $this->mul($n, $factor, $cp);
+                $unit = $dest_unit;
+            }
+
+            // Output was zero, so bail out early. Shouldn't ever happen.
+            if ($n === '') {
+                $n = '0';
+                $unit = $to_unit;
+                break;
+            }
+
+            // It was a simple conversion, so bail out
+            if ($dest_state === $state) {
+                break;
+            }
+
+            if ($i !== 0) {
+                // Conversion failed! Apparently, the system we forwarded
+                // to didn't have this unit. This should never happen!
+                return false;
+            }
+
+            // Pre-condition: $i == 0
+
+            // Perform conversion to next system of units
+            $n = $this->mul($n, self::$units[$state][$dest_state][1], $cp);
+            $unit = self::$units[$state][$dest_state][2];
+            $state = $dest_state;
+
+            // One more loop around to convert the unit in the new system.
+
+        }
+
+        // Post-condition: $unit == $to_unit
+        if ($unit !== $to_unit) {
+            return false;
+        }
+
+        // Useful for debugging:
+        //echo "<pre>n";
+        //echo "$n\nsigfigs = $sigfigs\nnew_log = $new_log\nlog = $log\nrp = $rp\n</pre>\n";
+
+        $n = $this->round($n, $sigfigs);
+        if (strpos($n, '.') !== false) {
+            $n = rtrim($n, '0');
+        }
+        $n = rtrim($n, '.');
+
+        return new HTMLPurifier_Length($n, $unit);
+    }
+
+    /**
+     * Returns the number of significant figures in a string number.
+     * @param string $n Decimal number
+     * @return int number of sigfigs
+     */
+    public function getSigFigs($n)
+    {
+        $n = ltrim($n, '0+-');
+        $dp = strpos($n, '.'); // decimal position
+        if ($dp === false) {
+            $sigfigs = strlen(rtrim($n, '0'));
+        } else {
+            $sigfigs = strlen(ltrim($n, '0.')); // eliminate extra decimal character
+            if ($dp !== 0) {
+                $sigfigs--;
+            }
+        }
+        return $sigfigs;
+    }
+
+    /**
+     * Adds two numbers, using arbitrary precision when available.
+     * @param string $s1
+     * @param string $s2
+     * @param int $scale
+     * @return string
+     */
+    private function add($s1, $s2, $scale)
+    {
+        if ($this->bcmath) {
+            return bcadd($s1, $s2, $scale);
+        } else {
+            return $this->scale((float)$s1 + (float)$s2, $scale);
+        }
+    }
+
+    /**
+     * Multiples two numbers, using arbitrary precision when available.
+     * @param string $s1
+     * @param string $s2
+     * @param int $scale
+     * @return string
+     */
+    private function mul($s1, $s2, $scale)
+    {
+        if ($this->bcmath) {
+            return bcmul($s1, $s2, $scale);
+        } else {
+            return $this->scale((float)$s1 * (float)$s2, $scale);
+        }
+    }
+
+    /**
+     * Divides two numbers, using arbitrary precision when available.
+     * @param string $s1
+     * @param string $s2
+     * @param int $scale
+     * @return string
+     */
+    private function div($s1, $s2, $scale)
+    {
+        if ($this->bcmath) {
+            return bcdiv($s1, $s2, $scale);
+        } else {
+            return $this->scale((float)$s1 / (float)$s2, $scale);
+        }
+    }
+
+    /**
+     * Rounds a number according to the number of sigfigs it should have,
+     * using arbitrary precision when available.
+     * @param float $n
+     * @param int $sigfigs
+     * @return string
+     */
+    private function round($n, $sigfigs)
+    {
+        $new_log = (int)floor(log(abs($n), 10)); // Number of digits left of decimal - 1
+        $rp = $sigfigs - $new_log - 1; // Number of decimal places needed
+        $neg = $n < 0 ? '-' : ''; // Negative sign
+        if ($this->bcmath) {
+            if ($rp >= 0) {
+                $n = bcadd($n, $neg . '0.' . str_repeat('0', $rp) . '5', $rp + 1);
+                $n = bcdiv($n, '1', $rp);
+            } else {
+                // This algorithm partially depends on the standardized
+                // form of numbers that comes out of bcmath.
+                $n = bcadd($n, $neg . '5' . str_repeat('0', $new_log - $sigfigs), 0);
+                $n = substr($n, 0, $sigfigs + strlen($neg)) . str_repeat('0', $new_log - $sigfigs + 1);
+            }
+            return $n;
+        } else {
+            return $this->scale(round($n, $sigfigs - $new_log - 1), $rp + 1);
+        }
+    }
+
+    /**
+     * Scales a float to $scale digits right of decimal point, like BCMath.
+     * @param float $r
+     * @param int $scale
+     * @return string
+     */
+    private function scale($r, $scale)
+    {
+        if ($scale < 0) {
+            // The f sprintf type doesn't support negative numbers, so we
+            // need to cludge things manually. First get the string.
+            $r = sprintf('%.0f', (float)$r);
+            // Due to floating point precision loss, $r will more than likely
+            // look something like 4652999999999.9234. We grab one more digit
+            // than we need to precise from $r and then use that to round
+            // appropriately.
+            $precise = (string)round(substr($r, 0, strlen($r) + $scale), -1);
+            // Now we return it, truncating the zero that was rounded off.
+            return substr($precise, 0, -1) . str_repeat('0', -$scale + 1);
+        }
+        return sprintf('%.' . $scale . 'f', (float)$r);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser.php
new file mode 100644 (file)
index 0000000..50cba69
--- /dev/null
@@ -0,0 +1,198 @@
+<?php
+
+/**
+ * Parses string representations into their corresponding native PHP
+ * variable type. The base implementation does a simple type-check.
+ */
+class HTMLPurifier_VarParser
+{
+
+    const STRING = 1;
+    const ISTRING = 2;
+    const TEXT = 3;
+    const ITEXT = 4;
+    const INT = 5;
+    const FLOAT = 6;
+    const BOOL = 7;
+    const LOOKUP = 8;
+    const ALIST = 9;
+    const HASH = 10;
+    const MIXED = 11;
+
+    /**
+     * Lookup table of allowed types. Mainly for backwards compatibility, but
+     * also convenient for transforming string type names to the integer constants.
+     */
+    public static $types = array(
+        'string' => self::STRING,
+        'istring' => self::ISTRING,
+        'text' => self::TEXT,
+        'itext' => self::ITEXT,
+        'int' => self::INT,
+        'float' => self::FLOAT,
+        'bool' => self::BOOL,
+        'lookup' => self::LOOKUP,
+        'list' => self::ALIST,
+        'hash' => self::HASH,
+        'mixed' => self::MIXED
+    );
+
+    /**
+     * Lookup table of types that are string, and can have aliases or
+     * allowed value lists.
+     */
+    public static $stringTypes = array(
+        self::STRING => true,
+        self::ISTRING => true,
+        self::TEXT => true,
+        self::ITEXT => true,
+    );
+
+    /**
+     * Validate a variable according to type.
+     * It may return NULL as a valid type if $allow_null is true.
+     *
+     * @param mixed $var Variable to validate
+     * @param int $type Type of variable, see HTMLPurifier_VarParser->types
+     * @param bool $allow_null Whether or not to permit null as a value
+     * @return string Validated and type-coerced variable
+     * @throws HTMLPurifier_VarParserException
+     */
+    final public function parse($var, $type, $allow_null = false)
+    {
+        if (is_string($type)) {
+            if (!isset(HTMLPurifier_VarParser::$types[$type])) {
+                throw new HTMLPurifier_VarParserException("Invalid type '$type'");
+            } else {
+                $type = HTMLPurifier_VarParser::$types[$type];
+            }
+        }
+        $var = $this->parseImplementation($var, $type, $allow_null);
+        if ($allow_null && $var === null) {
+            return null;
+        }
+        // These are basic checks, to make sure nothing horribly wrong
+        // happened in our implementations.
+        switch ($type) {
+            case (self::STRING):
+            case (self::ISTRING):
+            case (self::TEXT):
+            case (self::ITEXT):
+                if (!is_string($var)) {
+                    break;
+                }
+                if ($type == self::ISTRING || $type == self::ITEXT) {
+                    $var = strtolower($var);
+                }
+                return $var;
+            case (self::INT):
+                if (!is_int($var)) {
+                    break;
+                }
+                return $var;
+            case (self::FLOAT):
+                if (!is_float($var)) {
+                    break;
+                }
+                return $var;
+            case (self::BOOL):
+                if (!is_bool($var)) {
+                    break;
+                }
+                return $var;
+            case (self::LOOKUP):
+            case (self::ALIST):
+            case (self::HASH):
+                if (!is_array($var)) {
+                    break;
+                }
+                if ($type === self::LOOKUP) {
+                    foreach ($var as $k) {
+                        if ($k !== true) {
+                            $this->error('Lookup table contains value other than true');
+                        }
+                    }
+                } elseif ($type === self::ALIST) {
+                    $keys = array_keys($var);
+                    if (array_keys($keys) !== $keys) {
+                        $this->error('Indices for list are not uniform');
+                    }
+                }
+                return $var;
+            case (self::MIXED):
+                return $var;
+            default:
+                $this->errorInconsistent(get_class($this), $type);
+        }
+        $this->errorGeneric($var, $type);
+    }
+
+    /**
+     * Actually implements the parsing. Base implementation does not
+     * do anything to $var. Subclasses should overload this!
+     * @param mixed $var
+     * @param int $type
+     * @param bool $allow_null
+     * @return string
+     */
+    protected function parseImplementation($var, $type, $allow_null)
+    {
+        return $var;
+    }
+
+    /**
+     * Throws an exception.
+     * @throws HTMLPurifier_VarParserException
+     */
+    protected function error($msg)
+    {
+        throw new HTMLPurifier_VarParserException($msg);
+    }
+
+    /**
+     * Throws an inconsistency exception.
+     * @note This should not ever be called. It would be called if we
+     *       extend the allowed values of HTMLPurifier_VarParser without
+     *       updating subclasses.
+     * @param string $class
+     * @param int $type
+     * @throws HTMLPurifier_Exception
+     */
+    protected function errorInconsistent($class, $type)
+    {
+        throw new HTMLPurifier_Exception(
+            "Inconsistency in $class: " . HTMLPurifier_VarParser::getTypeName($type) .
+            " not implemented"
+        );
+    }
+
+    /**
+     * Generic error for if a type didn't work.
+     * @param mixed $var
+     * @param int $type
+     */
+    protected function errorGeneric($var, $type)
+    {
+        $vtype = gettype($var);
+        $this->error("Expected type " . HTMLPurifier_VarParser::getTypeName($type) . ", got $vtype");
+    }
+
+    /**
+     * @param int $type
+     * @return string
+     */
+    public static function getTypeName($type)
+    {
+        static $lookup;
+        if (!$lookup) {
+            // Lazy load the alternative lookup table
+            $lookup = array_flip(HTMLPurifier_VarParser::$types);
+        }
+        if (!isset($lookup[$type])) {
+            return 'unknown';
+        }
+        return $lookup[$type];
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php
new file mode 100644 (file)
index 0000000..b15016c
--- /dev/null
@@ -0,0 +1,130 @@
+<?php
+
+/**
+ * Performs safe variable parsing based on types which can be used by
+ * users. This may not be able to represent all possible data inputs,
+ * however.
+ */
+class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser
+{
+    /**
+     * @param mixed $var
+     * @param int $type
+     * @param bool $allow_null
+     * @return array|bool|float|int|mixed|null|string
+     * @throws HTMLPurifier_VarParserException
+     */
+    protected function parseImplementation($var, $type, $allow_null)
+    {
+        if ($allow_null && $var === null) {
+            return null;
+        }
+        switch ($type) {
+            // Note: if code "breaks" from the switch, it triggers a generic
+            // exception to be thrown. Specific errors can be specifically
+            // done here.
+            case self::MIXED:
+            case self::ISTRING:
+            case self::STRING:
+            case self::TEXT:
+            case self::ITEXT:
+                return $var;
+            case self::INT:
+                if (is_string($var) && ctype_digit($var)) {
+                    $var = (int)$var;
+                }
+                return $var;
+            case self::FLOAT:
+                if ((is_string($var) && is_numeric($var)) || is_int($var)) {
+                    $var = (float)$var;
+                }
+                return $var;
+            case self::BOOL:
+                if (is_int($var) && ($var === 0 || $var === 1)) {
+                    $var = (bool)$var;
+                } elseif (is_string($var)) {
+                    if ($var == 'on' || $var == 'true' || $var == '1') {
+                        $var = true;
+                    } elseif ($var == 'off' || $var == 'false' || $var == '0') {
+                        $var = false;
+                    } else {
+                        throw new HTMLPurifier_VarParserException("Unrecognized value '$var' for $type");
+                    }
+                }
+                return $var;
+            case self::ALIST:
+            case self::HASH:
+            case self::LOOKUP:
+                if (is_string($var)) {
+                    // special case: technically, this is an array with
+                    // a single empty string item, but having an empty
+                    // array is more intuitive
+                    if ($var == '') {
+                        return array();
+                    }
+                    if (strpos($var, "\n") === false && strpos($var, "\r") === false) {
+                        // simplistic string to array method that only works
+                        // for simple lists of tag names or alphanumeric characters
+                        $var = explode(',', $var);
+                    } else {
+                        $var = preg_split('/(,|[\n\r]+)/', $var);
+                    }
+                    // remove spaces
+                    foreach ($var as $i => $j) {
+                        $var[$i] = trim($j);
+                    }
+                    if ($type === self::HASH) {
+                        // key:value,key2:value2
+                        $nvar = array();
+                        foreach ($var as $keypair) {
+                            $c = explode(':', $keypair, 2);
+                            if (!isset($c[1])) {
+                                continue;
+                            }
+                            $nvar[trim($c[0])] = trim($c[1]);
+                        }
+                        $var = $nvar;
+                    }
+                }
+                if (!is_array($var)) {
+                    break;
+                }
+                $keys = array_keys($var);
+                if ($keys === array_keys($keys)) {
+                    if ($type == self::ALIST) {
+                        return $var;
+                    } elseif ($type == self::LOOKUP) {
+                        $new = array();
+                        foreach ($var as $key) {
+                            $new[$key] = true;
+                        }
+                        return $new;
+                    } else {
+                        break;
+                    }
+                }
+                if ($type === self::ALIST) {
+                    trigger_error("Array list did not have consecutive integer indexes", E_USER_WARNING);
+                    return array_values($var);
+                }
+                if ($type === self::LOOKUP) {
+                    foreach ($var as $key => $value) {
+                        if ($value !== true) {
+                            trigger_error(
+                                "Lookup array has non-true value at key '$key'; " .
+                                "maybe your input array was not indexed numerically",
+                                E_USER_WARNING
+                            );
+                        }
+                        $var[$key] = true;
+                    }
+                }
+                return $var;
+            default:
+                $this->errorInconsistent(__CLASS__, $type);
+        }
+        $this->errorGeneric($var, $type);
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php
new file mode 100644 (file)
index 0000000..f11c318
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * This variable parser uses PHP's internal code engine. Because it does
+ * this, it can represent all inputs; however, it is dangerous and cannot
+ * be used by users.
+ */
+class HTMLPurifier_VarParser_Native extends HTMLPurifier_VarParser
+{
+
+    /**
+     * @param mixed $var
+     * @param int $type
+     * @param bool $allow_null
+     * @return null|string
+     */
+    protected function parseImplementation($var, $type, $allow_null)
+    {
+        return $this->evalExpression($var);
+    }
+
+    /**
+     * @param string $expr
+     * @return mixed
+     * @throws HTMLPurifier_VarParserException
+     */
+    protected function evalExpression($expr)
+    {
+        $var = null;
+        $result = eval("\$var = $expr;");
+        if ($result === false) {
+            throw new HTMLPurifier_VarParserException("Fatal error in evaluated code");
+        }
+        return $var;
+    }
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/VarParserException.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/VarParserException.php
new file mode 100644 (file)
index 0000000..5df3414
--- /dev/null
@@ -0,0 +1,11 @@
+<?php
+
+/**
+ * Exception type for HTMLPurifier_VarParser
+ */
+class HTMLPurifier_VarParserException extends HTMLPurifier_Exception
+{
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Zipper.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Zipper.php
new file mode 100644 (file)
index 0000000..6e21ea0
--- /dev/null
@@ -0,0 +1,157 @@
+<?php
+
+/**
+ * A zipper is a purely-functional data structure which contains
+ * a focus that can be efficiently manipulated.  It is known as
+ * a "one-hole context".  This mutable variant implements a zipper
+ * for a list as a pair of two arrays, laid out as follows:
+ *
+ *      Base list: 1 2 3 4 [ ] 6 7 8 9
+ *      Front list: 1 2 3 4
+ *      Back list: 9 8 7 6
+ *
+ * User is expected to keep track of the "current element" and properly
+ * fill it back in as necessary.  (ToDo: Maybe it's more user friendly
+ * to implicitly track the current element?)
+ *
+ * Nota bene: the current class gets confused if you try to store NULLs
+ * in the list.
+ */
+
+class HTMLPurifier_Zipper
+{
+    public $front, $back;
+
+    public function __construct($front, $back) {
+        $this->front = $front;
+        $this->back = $back;
+    }
+
+    /**
+     * Creates a zipper from an array, with a hole in the
+     * 0-index position.
+     * @param Array to zipper-ify.
+     * @return Tuple of zipper and element of first position.
+     */
+    static public function fromArray($array) {
+        $z = new self(array(), array_reverse($array));
+        $t = $z->delete(); // delete the "dummy hole"
+        return array($z, $t);
+    }
+
+    /**
+     * Convert zipper back into a normal array, optionally filling in
+     * the hole with a value. (Usually you should supply a $t, unless you
+     * are at the end of the array.)
+     */
+    public function toArray($t = NULL) {
+        $a = $this->front;
+        if ($t !== NULL) $a[] = $t;
+        for ($i = count($this->back)-1; $i >= 0; $i--) {
+            $a[] = $this->back[$i];
+        }
+        return $a;
+    }
+
+    /**
+     * Move hole to the next element.
+     * @param $t Element to fill hole with
+     * @return Original contents of new hole.
+     */
+    public function next($t) {
+        if ($t !== NULL) array_push($this->front, $t);
+        return empty($this->back) ? NULL : array_pop($this->back);
+    }
+
+    /**
+     * Iterated hole advancement.
+     * @param $t Element to fill hole with
+     * @param $i How many forward to advance hole
+     * @return Original contents of new hole, i away
+     */
+    public function advance($t, $n) {
+        for ($i = 0; $i < $n; $i++) {
+            $t = $this->next($t);
+        }
+        return $t;
+    }
+
+    /**
+     * Move hole to the previous element
+     * @param $t Element to fill hole with
+     * @return Original contents of new hole.
+     */
+    public function prev($t) {
+        if ($t !== NULL) array_push($this->back, $t);
+        return empty($this->front) ? NULL : array_pop($this->front);
+    }
+
+    /**
+     * Delete contents of current hole, shifting hole to
+     * next element.
+     * @return Original contents of new hole.
+     */
+    public function delete() {
+        return empty($this->back) ? NULL : array_pop($this->back);
+    }
+
+    /**
+     * Returns true if we are at the end of the list.
+     * @return bool
+     */
+    public function done() {
+        return empty($this->back);
+    }
+
+    /**
+     * Insert element before hole.
+     * @param Element to insert
+     */
+    public function insertBefore($t) {
+        if ($t !== NULL) array_push($this->front, $t);
+    }
+
+    /**
+     * Insert element after hole.
+     * @param Element to insert
+     */
+    public function insertAfter($t) {
+        if ($t !== NULL) array_push($this->back, $t);
+    }
+
+    /**
+     * Splice in multiple elements at hole.  Functional specification
+     * in terms of array_splice:
+     *
+     *      $arr1 = $arr;
+     *      $old1 = array_splice($arr1, $i, $delete, $replacement);
+     *
+     *      list($z, $t) = HTMLPurifier_Zipper::fromArray($arr);
+     *      $t = $z->advance($t, $i);
+     *      list($old2, $t) = $z->splice($t, $delete, $replacement);
+     *      $arr2 = $z->toArray($t);
+     *
+     *      assert($old1 === $old2);
+     *      assert($arr1 === $arr2);
+     *
+     * NB: the absolute index location after this operation is
+     * *unchanged!*
+     *
+     * @param Current contents of hole.
+     */
+    public function splice($t, $delete, $replacement) {
+        // delete
+        $old = array();
+        $r = $t;
+        for ($i = $delete; $i > 0; $i--) {
+            $old[] = $r;
+            $r = $this->delete();
+        }
+        // insert
+        for ($i = count($replacement)-1; $i >= 0; $i--) {
+            $this->insertAfter($r);
+            $r = $replacement[$i];
+        }
+        return array($old, $r);
+    }
+}
diff --git a/library/ezyang/htmlpurifier/package.php b/library/ezyang/htmlpurifier/package.php
new file mode 100644 (file)
index 0000000..bfef936
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+set_time_limit(0);
+
+require_once 'PEAR/PackageFileManager2.php';
+require_once 'PEAR/PackageFileManager/File.php';
+PEAR::setErrorHandling(PEAR_ERROR_PRINT);
+$pkg = new PEAR_PackageFileManager2;
+
+$pkg->setOptions(
+    array(
+        'baseinstalldir' => '/',
+        'packagefile' => 'package.xml',
+        'packagedirectory' => realpath(dirname(__FILE__) . '/library'),
+        'filelistgenerator' => 'file',
+        'include' => array('*'),
+        'dir_roles' => array('/' => 'php'), // hack to put *.ser files in the right place
+        'ignore' => array(
+            'HTMLPurifier.standalone.php',
+            'HTMLPurifier.path.php',
+            '*.tar.gz',
+            '*.tgz',
+            'standalone/'
+        ),
+    )
+);
+
+$pkg->setPackage('HTMLPurifier');
+$pkg->setLicense('LGPL', 'http://www.gnu.org/licenses/lgpl.html');
+$pkg->setSummary('Standards-compliant HTML filter');
+$pkg->setDescription(
+    'HTML Purifier is an HTML filter that will remove all malicious code
+    (better known as XSS) with a thoroughly audited, secure yet permissive
+    whitelist and will also make sure your documents are standards
+    compliant.'
+);
+
+$pkg->addMaintainer('lead', 'ezyang', 'Edward Z. Yang', 'admin@htmlpurifier.org', 'yes');
+
+$version = trim(file_get_contents('VERSION'));
+$api_version = substr($version, 0, strrpos($version, '.'));
+
+$pkg->setChannel('htmlpurifier.org');
+$pkg->setAPIVersion($api_version);
+$pkg->setAPIStability('stable');
+$pkg->setReleaseVersion($version);
+$pkg->setReleaseStability('stable');
+
+$pkg->addRelease();
+
+$pkg->setNotes(file_get_contents('WHATSNEW'));
+$pkg->setPackageType('php');
+
+$pkg->setPhpDep('5.0.0');
+$pkg->setPearinstallerDep('1.4.3');
+
+$pkg->generateContents();
+
+$pkg->writePackageFile();
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/phpdoc.ini b/library/ezyang/htmlpurifier/phpdoc.ini
new file mode 100644 (file)
index 0000000..c4c3723
--- /dev/null
@@ -0,0 +1,102 @@
+;; phpDocumentor parse configuration file
+;;
+;; This file is designed to cut down on repetitive typing on the command-line or web interface
+;; You can copy this file to create a number of configuration files that can be used with the
+;; command-line switch -c, as in phpdoc -c default.ini or phpdoc -c myini.ini.  The web
+;; interface will automatically generate a list of .ini files that can be used.
+;;
+;; default.ini is used to generate the online manual at http://www.phpdoc.org/docs
+;;
+;; ALL .ini files must be in the user subdirectory of phpDocumentor with an extension of .ini
+;;
+;; Copyright 2002, Greg Beaver <cellog@users.sourceforge.net>
+;;
+;; WARNING: do not change the name of any command-line parameters, phpDocumentor will ignore them
+
+[Parse Data]
+;; title of all the documentation
+;; legal values: any string
+title = HTML Purifier API Documentation
+
+;; parse files that start with a . like .bash_profile
+;; legal values: true, false
+hidden = false
+
+;; show elements marked @access private in documentation by setting this to on
+;; legal values: on, off
+parseprivate = off
+
+;; parse with javadoc-like description (first sentence is always the short description)
+;; legal values: on, off
+javadocdesc = on
+
+;; add any custom @tags separated by commas here
+;; legal values: any legal tagname separated by commas.
+;customtags = mytag1,mytag2
+
+;; This is only used by the XML:DocBook/peardoc2 converter
+defaultcategoryname = Documentation
+
+;; what is the main package?
+;; legal values: alphanumeric string plus - and _
+defaultpackagename = HTMLPurifier
+
+;; output any parsing information?  set to on for cron jobs
+;; legal values: on
+;quiet = on
+
+;; parse a PEAR-style repository.  Do not turn this on if your project does
+;; not have a parent directory named "pear"
+;; legal values: on/off
+;pear = on
+
+;; where should the documentation be written?
+;; legal values: a legal path
+target = docs/phpdoc
+
+;; Which files should be parsed out as special documentation files, such as README,
+;; INSTALL and CHANGELOG?  This overrides the default files found in
+;; phpDocumentor.ini (this file is not a user .ini file, but the global file)
+readmeinstallchangelog = README, INSTALL, NEWS, WYSIWYG, SLOW, LICENSE, CREDITS
+
+;; limit output to the specified packages, even if others are parsed
+;; legal values: package names separated by commas
+;packageoutput = package1,package2
+
+;; comma-separated list of files to parse
+;; legal values: paths separated by commas
+;filename = /path/to/file1,/path/to/file2,fileincurrentdirectory
+
+;; comma-separated list of directories to parse
+;; legal values: directory paths separated by commas
+;directory = /path1,/path2,.,..,subdirectory
+;directory = /home/jeichorn/cvs/pear
+directory = .
+
+;; template base directory (the equivalent directory of <installdir>/phpDocumentor)
+;templatebase = /path/to/my/templates
+
+;; directory to find any example files in through @example and {@example} tags
+;examplesdir = /path/to/my/templates
+
+;; comma-separated list of files, directories or wildcards ? and * (any wildcard) to ignore
+;; legal values: any wildcard strings separated by commas
+;ignore = /path/to/ignore*,*list.php,myfile.php,subdirectory/
+ignore = *tests*,*benchmarks*,*docs*,*test-settings.php,*configdoc*,*maintenance*,*smoketests*,*standalone*,*.svn*,*conf*
+
+sourcecode = on
+
+;; comma-separated list of Converters to use in outputformat:Convertername:templatedirectory format
+;; legal values: HTML:frames:default,HTML:frames:l0l33t,HTML:frames:phpdoc.de,HTML:frames:phphtmllib,
+;;               HTML:frames:earthli,
+;;               HTML:frames:DOM/default,HTML:frames:DOM/l0l33t,HTML:frames:DOM/phpdoc.de,
+;;               HTML:frames:DOM/phphtmllib,HTML:frames:DOM/earthli
+;;               HTML:Smarty:default,HTML:Smarty:PHP,HTML:Smarty:HandS
+;;               PDF:default:default,CHM:default:default,XML:DocBook/peardoc2:default
+output=HTML:frames:default
+
+;; turn this option on if you want highlighted source code for every file
+;; legal values: on/off
+sourcecode = on
+
+; vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/plugins/modx.txt b/library/ezyang/htmlpurifier/plugins/modx.txt
new file mode 100644 (file)
index 0000000..0763821
--- /dev/null
@@ -0,0 +1,112 @@
+
+MODx Plugin
+
+MODx <http://www.modxcms.com/> is an open source PHP application framework.
+I first came across them in my referrer logs when tillda asked if anyone
+could implement an HTML Purifier plugin.  This forum thread
+<http://modxcms.com/forums/index.php/topic,6604.0.html> eventually resulted
+in the fruition of this plugin that davidm says, "is on top of my favorite
+list."  HTML Purifier goes great with WYSIWYG editors!
+
+
+
+1. Credits
+
+PaulGregory wrote the overall structure of the code.  I added the
+slashes hack.
+
+
+
+2. Install
+
+First, you need to place HTML Purifier library somewhere.  The code here
+assumes that you've placed in MODx's assets/plugins/htmlpurifier (no version
+number).
+
+Log into the manager, and navigate:
+
+Resources > Manage Resources > Plugins tab > New Plugin
+
+Type in a name (probably HTML Purifier), and copy paste this code into the
+textarea:
+
+--------------------------------------------------------------------------------
+$e = &$modx->Event;
+if ($e->name == 'OnBeforeDocFormSave') {
+    global $content;
+
+    include_once '../assets/plugins/htmlpurifier/library/HTMLPurifier.auto.php';
+    $purifier = new HTMLPurifier();
+
+    static $magic_quotes = null;
+    if ($magic_quotes === null) {
+        // this is an ugly hack because this hook hasn't
+        // had the backslashes removed yet when magic_quotes_gpc is on,
+        // but HTMLPurifier must not have the quotes slashed.
+        $magic_quotes = get_magic_quotes_gpc();
+    }
+
+    if ($magic_quotes) $content = stripslashes($content);
+    $content = $purifier->purify($content);
+    if ($magic_quotes) $content = addslashes($content);
+}
+--------------------------------------------------------------------------------
+
+Then navigate to the System Events tab and check "OnBeforeDocFormSave".
+Save the plugin.  HTML Purifier now is integrated!
+
+
+
+3. Making sure it works
+
+You can test HTML Purifier by deliberately putting in crappy HTML and seeing
+whether or not it gets fixed.  A better way is to put in something like this:
+
+<p lang="fr">Il est bon</p>
+
+...and seeing whether or not the content comes out as:
+
+<p lang="fr" xml:lang="fr">Il est bon</p>
+
+(lang to xml:lang synchronization is one of the many features HTML Purifier
+has).
+
+
+
+4. Caveat Emptor
+
+This code does not intercept save requests from the QuickEdit plugin, this may
+be added in a later version.  It also modifies things on save, so there's a
+slight chance that HTML Purifier may make a boo-boo and accidently mess things
+up (the original version is not saved).
+
+Finally, make sure that MODx is using UTF-8.  If you are using, say, a French
+localisation, you may be using Latin-1, if that's the case, configure
+HTML Purifier properly like this:
+
+$config = HTMLPurifier_Config::createDefault();
+$config->set('Core', 'Encoding', 'ISO-8859-1'); // or whatever encoding
+$purifier = new HTMLPurifier($config);
+
+
+
+5. Known Bugs
+
+'rn' characters sometimes mysteriously appear after purification. We are
+currently investigating this issue. See: <http://htmlpurifier.org/phorum/read.php?3,1866>
+
+
+
+6. See Also
+
+A modified version of Jot 1.1.3 is available, which integrates with HTML
+Purifier. You can check it out here: <http://modxcms.com/forums/index.php/topic,25621.msg161970.html>
+
+
+X. Changelog
+
+2008-06-16
+- Updated code to work with 3.1.0 and later
+- Add Known Bugs and See Also section
+
+    vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/plugins/phorum/.gitignore b/library/ezyang/htmlpurifier/plugins/phorum/.gitignore
new file mode 100644 (file)
index 0000000..8325e09
--- /dev/null
@@ -0,0 +1,2 @@
+migrate.php
+htmlpurifier/*
diff --git a/library/ezyang/htmlpurifier/plugins/phorum/Changelog b/library/ezyang/htmlpurifier/plugins/phorum/Changelog
new file mode 100644 (file)
index 0000000..9f939e5
--- /dev/null
@@ -0,0 +1,27 @@
+Changelog                                         HTMLPurifier : Phorum Mod
+|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+
+= KEY ====================
+    # Breaks back-compat
+    ! Feature
+    - Bugfix
+      + Sub-comment
+    . Internal change
+==========================
+
+Version 4.0.0 for Phorum 5.2, released July 9, 2009
+# Works only with HTML Purifier 4.0.0
+! Better installation documentation
+- Fixed double encoded quotes
+- Fixed fatal error when migrate.php is blank
+
+Version 3.0.0 for Phorum 5.2, released January 12, 2008
+# WYSIWYG and suppress_message options are now configurable via web
+  interface.
+- Module now compatible with Phorum 5.2, primary bugs were in migration
+  code as well as signature and edit message handling. This module is NOT
+  compatible with Phorum 5.1.
+- Buggy WYSIWYG mode refined
+. AutoFormatParam added to list of default configuration namespaces
+
+    vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/plugins/phorum/INSTALL b/library/ezyang/htmlpurifier/plugins/phorum/INSTALL
new file mode 100644 (file)
index 0000000..23c76fc
--- /dev/null
@@ -0,0 +1,84 @@
+
+Install
+    How to install the Phorum HTML Purifier plugin
+
+0. PREREQUISITES
+----------------
+This Phorum module only works on PHP5 and with HTML Purifier 4.0.0
+or later.
+
+1. UNZIP
+--------
+Unzip phorum-htmlpurifier-x.y.z, producing an htmlpurifier folder.
+You've already done this step if you're reading this!
+
+2. MOVE
+-------
+Move the htmlpurifier folder to the mods/ folder of your Phorum
+installation, so the directory structure looks like:
+
+phorum/
+    mods/
+        htmlpurifier/
+            INSTALL - this install file
+            info.txt, ... - the module files
+            htmlpurifier/
+
+3. INSTALL HTML PURIFIER
+------------------------
+Download and unzip HTML Purifier <htmlpurifier.org>. Place the contents of
+the library/ folder in the htmlpurifier/htmlpurifier folder. Your directory
+structure will look like:
+
+phorum/
+    mods/
+        htmlpurifier/
+            htmlpurifier/
+                HTMLPurifier.auto.php
+                ... - other files
+                HTMLPurifier/
+
+Advanced users:
+    If you have HTML Purifier installed elsewhere on your server,
+    all you need is an HTMLPurifier.auto.php file in the library folder which
+    includes the HTMLPurifier.auto.php file in your install.
+
+4. MIGRATE
+----------
+If you're setting up a new Phorum installation, all you need to do is create
+a blank migrate.php file in the htmlpurifier module folder (NOT the library
+folder.
+
+If you have an old Phorum installation and was using BBCode,
+copy migrate.bbcode.php to migrate.php. If you were using a different input
+format, follow the instructions in migrate.bbcode.php to create your own custom
+migrate.php file.
+
+Your directory structure should now look like this:
+
+phorum/
+    mods/
+        htmlpurifier/
+            migrate.php
+
+5. ENABLE
+---------
+Navigate to your Phorum admin panel at http://example.com/phorum/admin.php,
+click on Global Settings > Modules, scroll to "HTML Purifier Phorum Mod" and
+turn it On.
+
+6. MIGRATE SIGNATURES
+---------------------
+If you're setting up a new Phorum installation, skip this step.
+
+If you allowed your users to make signatures, navigate to the module settings
+page of HTML Purifier (Global Settings > Modules > HTML Purifier Phorum Mod >
+Configure), type in "yes" in the "Confirm" box, and press "Migrate."
+
+ONLY DO THIS ONCE! BE SURE TO BACK UP YOUR DATABASE!
+
+7. CONFIGURE
+------------
+Configure using Edit settings. See that page for more information.
+
+    vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/plugins/phorum/README b/library/ezyang/htmlpurifier/plugins/phorum/README
new file mode 100644 (file)
index 0000000..0524ed3
--- /dev/null
@@ -0,0 +1,45 @@
+
+HTML Purifier Phorum Mod - Filter your HTML the Standards-Compliant Way!
+
+This Phorum mod enables HTML posting on Phorum.  Under normal circumstances,
+this would cause a huge security risk, but because we are running
+HTML through HTML Purifier, output is guaranteed to be XSS free and
+standards-compliant.
+
+This mod requires HTML input, and previous markup languages need to be
+converted accordingly.  Thus, it is vital that you create a 'migrate.php'
+file that works with your installation. If you're using the built-in
+BBCode formatting, simply move migrate.bbcode.php to that place; for
+other markup languages, consult said file for instructions on how
+to adapt it to your needs.
+
+    -- NOTE -------------------------------------------------
+    You can also run this module in parallel with another
+    formatting module; this module attempts to place itself
+    at the end of the filtering chain. However, if any
+    previous modules produce insecure HTML (for instance,
+    a JavaScript email obfuscator) they will get cleaned.
+
+This module will not work if 'migrate.php' is not created, and an improperly
+made migration file may *CORRUPT* Phorum, so please take your time to
+do this correctly. It should go without saying to *BACKUP YOUR DATABASE*
+before attempting anything here. If no migration is necessary, you can
+simply create a blank migrate.php file. HTML Purifier is smart and will
+not re-migrate already processed messages. However, the original code
+is irretrievably lost (we may change this in the future.)
+
+This module will not automatically migrate user signatures, because this
+process may take a long time. After installing the HTML Purifier module and
+then configuring 'migrate.php', navigate to Settings and click 'Migrate
+Signatures' to migrate all user signatures to HTML.
+
+All of HTML Purifier's usual functions are configurable via the mod settings
+page. If you require custom configuration, create config.php file in
+the mod directory that edits a $config variable. Be sure, also, to
+set $PHORUM['mod_htmlpurifier']['wysiwyg'] to TRUE if you are using a
+WYSIWYG editor (you can do this through a common hook or the web
+configuration form).
+
+Visit HTML Purifier at <http://htmlpurifier.org/>.
+
+    vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/plugins/phorum/config.default.php b/library/ezyang/htmlpurifier/plugins/phorum/config.default.php
new file mode 100644 (file)
index 0000000..e047c0b
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+
+if(!defined("PHORUM")) exit;
+
+// default HTML Purifier configuration settings
+$config->set('HTML.Allowed',
+  // alphabetically sorted
+'a[href|title]
+abbr[title]
+acronym[title]
+b
+blockquote[cite]
+br
+caption
+cite
+code
+dd
+del
+dfn
+div
+dl
+dt
+em
+i
+img[src|alt|title|class]
+ins
+kbd
+li
+ol
+p
+pre
+s
+strike
+strong
+sub
+sup
+table
+tbody
+td
+tfoot
+th
+thead
+tr
+tt
+u
+ul
+var');
+$config->set('AutoFormat.AutoParagraph', true);
+$config->set('AutoFormat.Linkify', true);
+$config->set('HTML.Doctype', 'XHTML 1.0 Transitional');
+$config->set('Core.AggressivelyFixLt', true);
+$config->set('Core.Encoding', $GLOBALS['PHORUM']['DATA']['CHARSET']); // we'll change this eventually
+if (strtolower($GLOBALS['PHORUM']['DATA']['CHARSET']) !== 'utf-8') {
+  $config->set('Core.EscapeNonASCIICharacters', true);
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/plugins/phorum/htmlpurifier.php b/library/ezyang/htmlpurifier/plugins/phorum/htmlpurifier.php
new file mode 100644 (file)
index 0000000..f66d8c3
--- /dev/null
@@ -0,0 +1,316 @@
+<?php
+
+/**
+ * HTML Purifier Phorum Mod. Filter your HTML the Standards-Compliant Way!
+ *
+ * This Phorum mod enables users to post raw HTML into Phorum.  But never
+ * fear: with the help of HTML Purifier, this HTML will be beat into
+ * de-XSSed and standards-compliant form, safe for general consumption.
+ * It is not recommended, but possible to run this mod in parallel
+ * with other formatters (in short, please DISABLE the BBcode mod).
+ *
+ * For help migrating from your previous markup language to pure HTML
+ * please check the migrate.bbcode.php file.
+ *
+ * If you'd like to use this with a WYSIWYG editor, make sure that
+ * editor sets $PHORUM['mod_htmlpurifier']['wysiwyg'] to true. Otherwise,
+ * administrators who need to edit other people's comments may be at
+ * risk for some nasty attacks.
+ *
+ * Tested with Phorum 5.2.11.
+ */
+
+// Note: Cache data is base64 encoded because Phorum insists on flinging
+// to the user and expecting it to come back unharmed, newlines and
+// all, which ain't happening. It's slower, it takes up more space, but
+// at least it won't get mutilated
+
+/**
+ * Purifies a data array
+ */
+function phorum_htmlpurifier_format($data)
+{
+    $PHORUM = $GLOBALS["PHORUM"];
+
+    $purifier =& HTMLPurifier::getInstance();
+    $cache_serial = $PHORUM['mod_htmlpurifier']['body_cache_serial'];
+
+    foreach($data as $message_id => $message){
+        if(isset($message['body'])) {
+
+            if ($message_id) {
+                // we're dealing with a real message, not a fake, so
+                // there a number of shortcuts that can be taken
+
+                if (isset($message['meta']['htmlpurifier_light'])) {
+                    // format hook was called outside of Phorum's normal
+                    // functions, do the abridged purification
+                    $data[$message_id]['body'] = $purifier->purify($message['body']);
+                    continue;
+                }
+
+                if (!empty($PHORUM['args']['purge'])) {
+                    // purge the cache, must be below the following if
+                    unset($message['meta']['body_cache']);
+                }
+
+                if (
+                    isset($message['meta']['body_cache']) &&
+                    isset($message['meta']['body_cache_serial']) &&
+                    $message['meta']['body_cache_serial'] == $cache_serial
+                ) {
+                    // cached version is present, bail out early
+                    $data[$message_id]['body'] = base64_decode($message['meta']['body_cache']);
+                    continue;
+                }
+            }
+
+            // migration might edit this array, that's why it's defined
+            // so early
+            $updated_message = array();
+
+            // create the $body variable
+            if (
+                $message_id && // message must be real to migrate
+                !isset($message['meta']['body_cache_serial'])
+            ) {
+                // perform migration
+                $fake_data = array();
+                list($signature, $edit_message) = phorum_htmlpurifier_remove_sig_and_editmessage($message);
+                $fake_data[$message_id] = $message;
+                $fake_data = phorum_htmlpurifier_migrate($fake_data);
+                $body = $fake_data[$message_id]['body'];
+                $body = str_replace("<phorum break>\n", "\n", $body);
+                $updated_message['body'] = $body; // save it in
+                $body .= $signature . $edit_message; // add it back in
+            } else {
+                // reverse Phorum's pre-processing
+                $body = $message['body'];
+                // order is important
+                $body = str_replace("<phorum break>\n", "\n", $body);
+                $body = str_replace(array('&lt;','&gt;','&amp;', '&quot;'), array('<','>','&','"'), $body);
+                if (!$message_id && defined('PHORUM_CONTROL_CENTER')) {
+                    // we're in control.php, so it was double-escaped
+                    $body = str_replace(array('&lt;','&gt;','&amp;', '&quot;'), array('<','>','&','"'), $body);
+                }
+            }
+
+            $body = $purifier->purify($body);
+
+            // dynamically update the cache (MUST BE DONE HERE!)
+            // this is inefficient because it's one db call per
+            // cache miss, but once the cache is in place things are
+            // a lot zippier.
+
+            if ($message_id) { // make sure it's not a fake id
+                $updated_message['meta'] = $message['meta'];
+                $updated_message['meta']['body_cache'] = base64_encode($body);
+                $updated_message['meta']['body_cache_serial'] = $cache_serial;
+                phorum_db_update_message($message_id, $updated_message);
+            }
+
+            // must not get overloaded until after we cache it, otherwise
+            // we'll inadvertently change the original text
+            $data[$message_id]['body'] = $body;
+
+        }
+    }
+
+    return $data;
+}
+
+// -----------------------------------------------------------------------
+// This is fragile code, copied from read.php:596 (Phorum 5.2.6). Please
+// keep this code in-sync with Phorum
+
+/**
+ * Generates a signature based on a message array
+ */
+function phorum_htmlpurifier_generate_sig($row)
+{
+    $phorum_sig = '';
+    if(isset($row["user"]["signature"])
+       && isset($row['meta']['show_signature']) && $row['meta']['show_signature']==1){
+           $phorum_sig=trim($row["user"]["signature"]);
+           if(!empty($phorum_sig)){
+               $phorum_sig="\n\n$phorum_sig";
+           }
+    }
+    return $phorum_sig;
+}
+
+/**
+ * Generates an edit message based on a message array
+ */
+function phorum_htmlpurifier_generate_editmessage($row)
+{
+    $PHORUM = $GLOBALS['PHORUM'];
+    $editmessage = '';
+    if(isset($row['meta']['edit_count']) && $row['meta']['edit_count'] > 0) {
+        $editmessage = str_replace ("%count%", $row['meta']['edit_count'], $PHORUM["DATA"]["LANG"]["EditedMessage"]);
+        $editmessage = str_replace ("%lastedit%", phorum_date($PHORUM["short_date_time"],$row['meta']['edit_date']),  $editmessage);
+        $editmessage = str_replace ("%lastuser%", $row['meta']['edit_username'],  $editmessage);
+        $editmessage = "\n\n\n\n$editmessage";
+    }
+    return $editmessage;
+}
+
+// End fragile code
+// -----------------------------------------------------------------------
+
+/**
+ * Removes the signature and edit message from a message
+ * @param $row Message passed by reference
+ */
+function phorum_htmlpurifier_remove_sig_and_editmessage(&$row)
+{
+    $signature = phorum_htmlpurifier_generate_sig($row);
+    $editmessage = phorum_htmlpurifier_generate_editmessage($row);
+    $replacements = array();
+    // we need to remove add <phorum break> as that is the form these
+    // extra bits are in.
+    if ($signature) $replacements[str_replace("\n", "<phorum break>\n", $signature)] = '';
+    if ($editmessage) $replacements[str_replace("\n", "<phorum break>\n", $editmessage)] = '';
+    $row['body'] = strtr($row['body'], $replacements);
+    return array($signature, $editmessage);
+}
+
+/**
+ * Indicate that data is fully HTML and not from migration, invalidate
+ * previous caches
+ * @note This function could generate the actual cache entries, but
+ *       since there's data missing that must be deferred to the first read
+ */
+function phorum_htmlpurifier_posting($message)
+{
+    $PHORUM = $GLOBALS["PHORUM"];
+    unset($message['meta']['body_cache']); // invalidate the cache
+    $message['meta']['body_cache_serial'] = $PHORUM['mod_htmlpurifier']['body_cache_serial'];
+    return $message;
+}
+
+/**
+ * Overload quoting mechanism to prevent default, mail-style quote from happening
+ */
+function phorum_htmlpurifier_quote($array)
+{
+    $PHORUM = $GLOBALS["PHORUM"];
+    $purifier =& HTMLPurifier::getInstance();
+    $text = $purifier->purify($array[1]);
+    $source = htmlspecialchars($array[0]);
+    return "<blockquote cite=\"$source\">\n$text\n</blockquote>";
+}
+
+/**
+ * Ensure that our format hook is processed last. Also, loads the library.
+ * @credits <http://secretsauce.phorum.org/snippets/make_bbcode_last_formatter.php.txt>
+ */
+function phorum_htmlpurifier_common()
+{
+    require_once(dirname(__FILE__).'/htmlpurifier/HTMLPurifier.auto.php');
+    require(dirname(__FILE__).'/init-config.php');
+
+    $config = phorum_htmlpurifier_get_config();
+    HTMLPurifier::getInstance($config);
+
+    // increment revision.txt if you want to invalidate the cache
+    $GLOBALS['PHORUM']['mod_htmlpurifier']['body_cache_serial'] = $config->getSerial();
+
+    // load migration
+    if (file_exists(dirname(__FILE__) . '/migrate.php')) {
+        include(dirname(__FILE__) . '/migrate.php');
+    } else {
+        echo '<strong>Error:</strong> No migration path specified for HTML Purifier, please check
+        <tt>modes/htmlpurifier/migrate.bbcode.php</tt> for instructions on
+        how to migrate from your previous markup language.';
+        exit;
+    }
+
+    if (!function_exists('phorum_htmlpurifier_migrate')) {
+        // Dummy function
+        function phorum_htmlpurifier_migrate($data) {return $data;}
+    }
+
+}
+
+/**
+ * Pre-emptively performs purification if it looks like a WYSIWYG editor
+ * is being used
+ */
+function phorum_htmlpurifier_before_editor($message)
+{
+    if (!empty($GLOBALS['PHORUM']['mod_htmlpurifier']['wysiwyg'])) {
+        if (!empty($message['body'])) {
+            $body = $message['body'];
+            // de-entity-ize contents
+            $body = str_replace(array('&lt;','&gt;','&amp;'), array('<','>','&'), $body);
+            $purifier =& HTMLPurifier::getInstance();
+            $body = $purifier->purify($body);
+            // re-entity-ize contents
+            $body = htmlspecialchars($body, ENT_QUOTES, $GLOBALS['PHORUM']['DATA']['CHARSET']);
+            $message['body'] = $body;
+        }
+    }
+    return $message;
+}
+
+function phorum_htmlpurifier_editor_after_subject()
+{
+    // don't show this message if it's a WYSIWYG editor, since it will
+    // then be handled automatically
+    if (!empty($GLOBALS['PHORUM']['mod_htmlpurifier']['wysiwyg'])) {
+        $i = $GLOBALS['PHORUM']['DATA']['MODE'];
+        if ($i == 'quote' || $i == 'edit' || $i == 'moderation') {
+          ?>
+          <div>
+            <p>
+              <strong>Notice:</strong> HTML has been scrubbed for your safety.
+              If you would like to see the original, turn off WYSIWYG mode
+              (consult your administrator for details.)
+            </p>
+          </div>
+          <?php
+        }
+        return;
+    }
+    if (!empty($GLOBALS['PHORUM']['mod_htmlpurifier']['suppress_message'])) return;
+    ?><div class="htmlpurifier-help">
+    <p>
+        <strong>HTML input</strong> is enabled. Make sure you escape all HTML and
+        angled brackets with <code>&amp;lt;</code> and <code>&amp;gt;</code>.
+    </p><?php
+            $purifier =& HTMLPurifier::getInstance();
+            $config = $purifier->config;
+            if ($config->get('AutoFormat.AutoParagraph')) {
+                ?><p>
+                    <strong>Auto-paragraphing</strong> is enabled. Double
+                    newlines will be converted to paragraphs; for single
+                    newlines, use the <code>pre</code> tag.
+                </p><?php
+            }
+            $html_definition = $config->getDefinition('HTML');
+            $allowed = array();
+            foreach ($html_definition->info as $name => $x) $allowed[] = "<code>$name</code>";
+            sort($allowed);
+            $allowed_text = implode(', ', $allowed);
+            ?><p><strong>Allowed tags:</strong> <?php
+            echo $allowed_text;
+            ?>.</p><?php
+        ?>
+    </p>
+    <p>
+        For inputting literal code such as HTML and PHP for display, use
+        CDATA tags to auto-escape your angled brackets, and <code>pre</code>
+        to preserve newlines:
+    </p>
+    <pre>&lt;pre&gt;&lt;![CDATA[
+<em>Place code here</em>
+]]&gt;&lt;/pre&gt;</pre>
+    <p>
+        Power users, you can hide this notice with:
+        <pre>.htmlpurifier-help {display:none;}</pre>
+    </p>
+    </div><?php
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/plugins/phorum/info.txt b/library/ezyang/htmlpurifier/plugins/phorum/info.txt
new file mode 100644 (file)
index 0000000..7234654
--- /dev/null
@@ -0,0 +1,18 @@
+title:   HTML Purifier Phorum Mod
+desc:    This module enables standards-compliant HTML filtering on Phorum. Please check migrate.bbcode.php before enabling this mod.
+author:  Edward Z. Yang
+url:     http://htmlpurifier.org/
+version: 4.0.0
+
+hook:  format|phorum_htmlpurifier_format
+hook:  quote|phorum_htmlpurifier_quote
+hook:  posting_custom_action|phorum_htmlpurifier_posting
+hook:  common|phorum_htmlpurifier_common
+hook:  before_editor|phorum_htmlpurifier_before_editor
+hook:  tpl_editor_after_subject|phorum_htmlpurifier_editor_after_subject
+
+# This module is meant to be a drop-in for bbcode, so make it run last.
+priority: run module after *
+priority: run hook format after *
+
+    vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/plugins/phorum/init-config.php b/library/ezyang/htmlpurifier/plugins/phorum/init-config.php
new file mode 100644 (file)
index 0000000..e19787b
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * Initializes the appropriate configuration from either a PHP file
+ * or a module configuration value
+ * @return Instance of HTMLPurifier_Config
+ */
+function phorum_htmlpurifier_get_config($default = false)
+{
+    global $PHORUM;
+    $config_exists = phorum_htmlpurifier_config_file_exists();
+    if ($default || $config_exists || !isset($PHORUM['mod_htmlpurifier']['config'])) {
+        $config = HTMLPurifier_Config::createDefault();
+        include(dirname(__FILE__) . '/config.default.php');
+        if ($config_exists) {
+            include(dirname(__FILE__) . '/config.php');
+        }
+        unset($PHORUM['mod_htmlpurifier']['config']); // unnecessary
+    } else {
+        $config = HTMLPurifier_Config::create($PHORUM['mod_htmlpurifier']['config']);
+    }
+    return $config;
+}
+
+function phorum_htmlpurifier_config_file_exists()
+{
+    return file_exists(dirname(__FILE__) . '/config.php');
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/plugins/phorum/migrate.bbcode.php b/library/ezyang/htmlpurifier/plugins/phorum/migrate.bbcode.php
new file mode 100644 (file)
index 0000000..0d09194
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * This file is responsible for migrating from a specific markup language
+ * like BBCode or Markdown to HTML. WARNING: THIS PROCESS IS NOT REVERSIBLE
+ *
+ * Copy this file to 'migrate.php' and it will automatically work for
+ * BBCode; you may need to tweak this a little to get it to work for other
+ * languages (usually, just replace the include name and the function name).
+ *
+ * If you do NOT want to have any migration performed (for instance, you
+ * are installing the module on a new forum with no posts), simply remove
+ * phorum_htmlpurifier_migrate() function. You still need migrate.php
+ * present, otherwise the module won't work. This ensures that the user
+ * explicitly says, "No, I do not need to migrate."
+ */
+
+if(!defined("PHORUM")) exit;
+
+require_once(dirname(__FILE__) . "/../bbcode/bbcode.php");
+
+/**
+ * 'format' hook style function that will be called to convert
+ * legacy markup into HTML.
+ */
+function phorum_htmlpurifier_migrate($data)
+{
+    return phorum_mod_bbcode_format($data); // bbcode's 'format' hook
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/plugins/phorum/settings.php b/library/ezyang/htmlpurifier/plugins/phorum/settings.php
new file mode 100644 (file)
index 0000000..8158f02
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+
+// based off of BBCode's settings file
+
+/**
+ * HTML Purifier Phorum mod settings configuration. This provides
+ * a convenient web-interface for editing the most common HTML Purifier
+ * configuration directives. You can also specify custom configuration
+ * by creating a 'config.php' file.
+ */
+
+if(!defined("PHORUM_ADMIN")) exit;
+
+// error reporting is good!
+error_reporting(E_ALL ^ E_NOTICE);
+
+// load library and other paraphenalia
+require_once './include/admin/PhorumInputForm.php';
+require_once (dirname(__FILE__) . '/htmlpurifier/HTMLPurifier.auto.php');
+require_once (dirname(__FILE__) . '/init-config.php');
+require_once (dirname(__FILE__) . '/settings/migrate-sigs-form.php');
+require_once (dirname(__FILE__) . '/settings/migrate-sigs.php');
+require_once (dirname(__FILE__) . '/settings/form.php');
+require_once (dirname(__FILE__) . '/settings/save.php');
+
+// define friendly configuration directives. you can expand this array
+// to get more web-definable directives
+$PHORUM['mod_htmlpurifier']['directives'] = array(
+    'URI.Host', // auto-detectable
+    'URI.DisableExternal',
+    'URI.DisableExternalResources',
+    'URI.DisableResources',
+    'URI.Munge',
+    'URI.HostBlacklist',
+    'URI.Disable',
+    'HTML.TidyLevel',
+    'HTML.Doctype', // auto-detectable
+    'HTML.Allowed',
+    'AutoFormat',
+    '-AutoFormat.Custom',
+    'AutoFormatParam',
+    'Output.TidyFormat',
+);
+
+// lower this setting if you're getting time outs/out of memory
+$PHORUM['mod_htmlpurifier']['migrate-sigs-increment'] = 100;
+
+if (isset($_POST['reset'])) {
+    unset($PHORUM['mod_htmlpurifier']['config']);
+}
+
+if ($offset = phorum_htmlpurifier_migrate_sigs_check()) {
+    // migrate signatures
+    phorum_htmlpurifier_migrate_sigs($offset);
+} elseif(!empty($_POST)){
+    // save settings
+    phorum_htmlpurifier_save_settings();
+}
+
+phorum_htmlpurifier_show_migrate_sigs_form();
+echo '<br />';
+phorum_htmlpurifier_show_form();
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/plugins/phorum/settings/form.php b/library/ezyang/htmlpurifier/plugins/phorum/settings/form.php
new file mode 100644 (file)
index 0000000..9b6ad5f
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+
+function phorum_htmlpurifier_show_form()
+{
+    if (phorum_htmlpurifier_config_file_exists()) {
+        phorum_htmlpurifier_show_config_info();
+        return;
+    }
+
+    global $PHORUM;
+
+    $config = phorum_htmlpurifier_get_config();
+
+    $frm = new PhorumInputForm ("", "post", "Save");
+    $frm->hidden("module", "modsettings");
+    $frm->hidden("mod", "htmlpurifier"); // this is the directory name that the Settings file lives in
+
+    if (!empty($error)){
+        echo "$error<br />";
+    }
+
+    $frm->addbreak("Edit settings for the HTML Purifier module");
+
+    $frm->addMessage('<p>The box below sets <code>$PHORUM[\'mod_htmlpurifier\'][\'wysiwyg\']</code>.
+    When checked, contents sent for edit are now purified and the
+    informative message is disabled. If your WYSIWYG editor is disabled for
+    admin edits, you can safely keep this unchecked.</p>');
+    $frm->addRow('Use WYSIWYG?', $frm->checkbox('wysiwyg', '1', '', $PHORUM['mod_htmlpurifier']['wysiwyg']));
+
+    $frm->addMessage('<p>The box below sets <code>$PHORUM[\'mod_htmlpurifier\'][\'suppress_message\']</code>,
+    which removes the big how-to use
+    HTML Purifier message.</p>');
+    $frm->addRow('Suppress information?', $frm->checkbox('suppress_message', '1', '', $PHORUM['mod_htmlpurifier']['suppress_message']));
+
+    $frm->addMessage('<p>Click on directive links to read what each option does
+    (links do not open in new windows).</p>
+    <p>For more flexibility (for instance, you want to edit the full
+    range of configuration directives), you can create a <tt>config.php</tt>
+    file in your <tt>mods/htmlpurifier/</tt> directory. Doing so will,
+    however, make the web configuration interface unavailable.</p>');
+
+    require_once 'HTMLPurifier/Printer/ConfigForm.php';
+    $htmlpurifier_form = new HTMLPurifier_Printer_ConfigForm('config', 'http://htmlpurifier.org/live/configdoc/plain.html#%s');
+    $htmlpurifier_form->setTextareaDimensions(23, 7); // widen a little, since we have space
+
+    $frm->addMessage($htmlpurifier_form->render(
+        $config, $PHORUM['mod_htmlpurifier']['directives'], false));
+
+    $frm->addMessage("<strong>Warning: Changing HTML Purifier's configuration will invalidate
+      the cache. Expect to see a flurry of database activity after you change
+      any of these settings.</strong>");
+
+    $frm->addrow('Reset to defaults:', $frm->checkbox("reset", "1", "", false));
+
+    // hack to include extra styling
+    echo '<style type="text/css">' . $htmlpurifier_form->getCSS() . '
+    .hp-config {margin-left:auto;margin-right:auto;}
+    </style>';
+    $js = $htmlpurifier_form->getJavaScript();
+    echo '<script type="text/javascript">'."<!--\n$js\n//-->".'</script>';
+
+    $frm->show();
+}
+
+function phorum_htmlpurifier_show_config_info()
+{
+    global $PHORUM;
+
+    // update mod_htmlpurifier for housekeeping
+    phorum_htmlpurifier_commit_settings();
+
+    // politely tell user how to edit settings manually
+?>
+        <div class="input-form-td-break">How to edit settings for HTML Purifier module</div>
+        <p>
+          A <tt>config.php</tt> file exists in your <tt>mods/htmlpurifier/</tt>
+          directory. This file contains your custom configuration: in order to
+          change it, please navigate to that file and edit it accordingly.
+          You can also set <code>$GLOBALS['PHORUM']['mod_htmlpurifier']['wysiwyg']</code>
+          or <code>$GLOBALS['PHORUM']['mod_htmlpurifier']['suppress_message']</code>
+        </p>
+        <p>
+          To use the web interface, delete <tt>config.php</tt> (or rename it to
+          <tt>config.php.bak</tt>).
+        </p>
+        <p>
+          <strong>Warning: Changing HTML Purifier's configuration will invalidate
+          the cache. Expect to see a flurry of database activity after you change
+          any of these settings.</strong>
+        </p>
+<?php
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/plugins/phorum/settings/migrate-sigs-form.php b/library/ezyang/htmlpurifier/plugins/phorum/settings/migrate-sigs-form.php
new file mode 100644 (file)
index 0000000..abea3b5
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+
+function phorum_htmlpurifier_show_migrate_sigs_form()
+{
+    $frm = new PhorumInputForm ('', "post", "Migrate");
+    $frm->hidden("module", "modsettings");
+    $frm->hidden("mod", "htmlpurifier");
+    $frm->hidden("migrate-sigs", "1");
+    $frm->addbreak("Migrate user signatures to HTML");
+    $frm->addMessage('This operation will migrate your users signatures
+        to HTML. <strong>This process is irreversible and must only be performed once.</strong>
+        Type in yes in the confirmation field to migrate.');
+    if (!file_exists(dirname(__FILE__) . '/../migrate.php')) {
+        $frm->addMessage('Migration file does not exist, cannot migrate signatures.
+            Please check <tt>migrate.bbcode.php</tt> on how to create an appropriate file.');
+    } else {
+        $frm->addrow('Confirm:', $frm->text_box("confirmation", ""));
+    }
+    $frm->show();
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/plugins/phorum/settings/migrate-sigs.php b/library/ezyang/htmlpurifier/plugins/phorum/settings/migrate-sigs.php
new file mode 100644 (file)
index 0000000..5ea9cd0
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+
+function phorum_htmlpurifier_migrate_sigs_check()
+{
+    global $PHORUM;
+    $offset = 0;
+    if (!empty($_POST['migrate-sigs'])) {
+        if (!isset($_POST['confirmation']) || strtolower($_POST['confirmation']) !== 'yes') {
+            echo 'Invalid confirmation code.';
+            exit;
+        }
+        $PHORUM['mod_htmlpurifier']['migrate-sigs'] = true;
+        phorum_db_update_settings(array("mod_htmlpurifier"=>$PHORUM["mod_htmlpurifier"]));
+        $offset = 1;
+    } elseif (!empty($_GET['migrate-sigs']) && $PHORUM['mod_htmlpurifier']['migrate-sigs']) {
+        $offset = (int) $_GET['migrate-sigs'];
+    }
+    return $offset;
+}
+
+function phorum_htmlpurifier_migrate_sigs($offset)
+{
+    global $PHORUM;
+
+    if(!$offset) return; // bail out quick if $offset == 0
+
+    // theoretically, we could get rid of this multi-request
+    // doo-hickery if safe mode is off
+    @set_time_limit(0); // attempt to let this run
+    $increment = $PHORUM['mod_htmlpurifier']['migrate-sigs-increment'];
+
+    require_once(dirname(__FILE__) . '/../migrate.php');
+    // migrate signatures
+    // do this in batches so we don't run out of time/space
+    $end = $offset + $increment;
+    $user_ids = array();
+    for ($i = $offset; $i < $end; $i++) {
+        $user_ids[] = $i;
+    }
+    $userinfos = phorum_db_user_get_fields($user_ids, 'signature');
+    foreach ($userinfos as $i => $user) {
+        if (empty($user['signature'])) continue;
+        $sig = $user['signature'];
+        // perform standard Phorum processing on the sig
+        $sig = str_replace(array("&","<",">"), array("&amp;","&lt;","&gt;"), $sig);
+        $sig = preg_replace("/<((http|https|ftp):\/\/[a-z0-9;\/\?:@=\&\$\-_\.\+!*'\(\),~%]+?)>/i", "$1", $sig);
+        // prepare fake data to pass to migration function
+        $fake_data = array(array("author"=>"", "email"=>"", "subject"=>"", 'body' => $sig));
+        list($fake_message) = phorum_htmlpurifier_migrate($fake_data);
+        $user['signature'] = $fake_message['body'];
+        if (!phorum_api_user_save($user)) {
+            exit('Error while saving user data');
+        }
+    }
+    unset($userinfos); // free up memory
+
+    // query for highest ID in database
+    $type = $PHORUM['DBCONFIG']['type'];
+    $sql = "select MAX(user_id) from {$PHORUM['user_table']}";
+    $row = phorum_db_interact(DB_RETURN_ROW, $sql);
+    $top_id = (int) $row[0];
+
+    $offset += $increment;
+    if ($offset > $top_id) { // test for end condition
+        echo 'Migration finished';
+        $PHORUM['mod_htmlpurifier']['migrate-sigs'] = false;
+        phorum_htmlpurifier_commit_settings();
+        return true;
+    }
+    $host  = $_SERVER['HTTP_HOST'];
+    $uri   = rtrim(dirname($_SERVER['PHP_SELF']), '/\\');
+    $extra = 'admin.php?module=modsettings&mod=htmlpurifier&migrate-sigs=' . $offset;
+    // relies on output buffering to work
+    header("Location: http://$host$uri/$extra");
+    exit;
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/plugins/phorum/settings/save.php b/library/ezyang/htmlpurifier/plugins/phorum/settings/save.php
new file mode 100644 (file)
index 0000000..2aefaf8
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+function phorum_htmlpurifier_save_settings()
+{
+    global $PHORUM;
+    if (phorum_htmlpurifier_config_file_exists()) {
+        echo "Cannot update settings, <code>mods/htmlpurifier/config.php</code> already exists. To change
+        settings, edit that file. To use the web form, delete that file.<br />";
+    } else {
+        $config = phorum_htmlpurifier_get_config(true);
+        if (!isset($_POST['reset'])) $config->mergeArrayFromForm($_POST, 'config', $PHORUM['mod_htmlpurifier']['directives']);
+        $PHORUM['mod_htmlpurifier']['config'] = $config->getAll();
+    }
+    $PHORUM['mod_htmlpurifier']['wysiwyg'] = !empty($_POST['wysiwyg']);
+    $PHORUM['mod_htmlpurifier']['suppress_message'] = !empty($_POST['suppress_message']);
+    if(!phorum_htmlpurifier_commit_settings()){
+        $error="Database error while updating settings.";
+    } else {
+        echo "Settings Updated<br />";
+    }
+}
+
+function phorum_htmlpurifier_commit_settings()
+{
+    global $PHORUM;
+    return phorum_db_update_settings(array("mod_htmlpurifier"=>$PHORUM["mod_htmlpurifier"]));
+}
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/release1-update.php b/library/ezyang/htmlpurifier/release1-update.php
new file mode 100644 (file)
index 0000000..834d385
--- /dev/null
@@ -0,0 +1,110 @@
+<?php
+
+// release script
+// PHP 5.0 only
+
+if (php_sapi_name() != 'cli') {
+    echo 'Release script cannot be called from web-browser.';
+    exit;
+}
+
+if (!isset($argv[1])) {
+    echo
+'php release.php [version]
+    HTML Purifier release script
+';
+    exit;
+}
+
+$version = trim($argv[1]);
+
+// Bump version numbers:
+
+// ...in VERSION
+file_put_contents('VERSION', $version);
+
+// ...in NEWS
+if ($is_dev = (strpos($version, 'dev') === false)) {
+  $date = date('Y-m-d');
+  $news_c = str_replace(
+      $l = "$version, unknown release date",
+      "$version, released $date",
+      file_get_contents('NEWS'),
+      $c
+  );
+  if (!$c) {
+      echo 'Could not update NEWS, missing ' . $l . PHP_EOL;
+      exit;
+  } elseif ($c > 1) {
+      echo 'More than one release declaration in NEWS replaced' . PHP_EOL;
+      exit;
+  }
+  file_put_contents('NEWS', $news_c);
+}
+
+// ...in Doxyfile
+$doxyfile_c = preg_replace(
+    '/(?<=PROJECT_NUMBER {9}= )[^\s]+/m', // brittle
+    $version,
+    file_get_contents('Doxyfile'),
+    1, $c
+);
+if (!$c) {
+    echo 'Could not update Doxyfile, missing PROJECT_NUMBER.' . PHP_EOL;
+    exit;
+}
+file_put_contents('Doxyfile', $doxyfile_c);
+
+// ...in HTMLPurifier.php
+$htmlpurifier_c = file_get_contents('library/HTMLPurifier.php');
+$htmlpurifier_c = preg_replace(
+    '/HTML Purifier .+? - /',
+    "HTML Purifier $version - ",
+    $htmlpurifier_c,
+    1, $c
+);
+if (!$c) {
+    echo 'Could not update HTMLPurifier.php, missing HTML Purifier [version] header.' . PHP_EOL;
+    exit;
+}
+$htmlpurifier_c = preg_replace(
+    '/public \$version = \'.+?\';/',
+    "public \$version = '$version';",
+    $htmlpurifier_c,
+    1, $c
+);
+if (!$c) {
+    echo 'Could not update HTMLPurifier.php, missing public $version.' . PHP_EOL;
+    exit;
+}
+$htmlpurifier_c = preg_replace(
+    '/const VERSION = \'.+?\';/',
+    "const VERSION = '$version';",
+    $htmlpurifier_c,
+    1, $c
+);
+if (!$c) {
+    echo 'Could not update HTMLPurifier.php, missing const $version.' . PHP_EOL;
+    exit;
+}
+file_put_contents('library/HTMLPurifier.php', $htmlpurifier_c);
+
+$config_c = file_get_contents('library/HTMLPurifier/Config.php');
+$config_c = preg_replace(
+    '/public \$version = \'.+?\';/',
+    "public \$version = '$version';",
+    $config_c,
+    1, $c
+);
+if (!$c) {
+    echo 'Could not update Config.php, missing public $version.' . PHP_EOL;
+    exit;
+}
+file_put_contents('library/HTMLPurifier/Config.php', $config_c);
+
+passthru('php maintenance/flush.php');
+
+if ($is_dev) echo "Review changes, write something in WHATSNEW and FOCUS, and then commit with log 'Release $version.'" . PHP_EOL;
+else echo "Numbers updated to dev, no other modifications necessary!";
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/release2-tag.php b/library/ezyang/htmlpurifier/release2-tag.php
new file mode 100644 (file)
index 0000000..25e5300
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+
+// Tags releases
+
+if (php_sapi_name() != 'cli') {
+    echo 'Release script cannot be called from web-browser.';
+    exit;
+}
+
+require 'svn.php';
+
+$svn_info = my_svn_info('.');
+
+$version = trim(file_get_contents('VERSION'));
+
+$trunk_url  = $svn_info['Repository Root'] . '/htmlpurifier/trunk';
+$trunk_tag_url  = $svn_info['Repository Root'] . '/htmlpurifier/tags/' . $version;
+
+echo "Tagging trunk to tags/$version...";
+passthru("svn copy --message \"Tag $version release.\" $trunk_url $trunk_tag_url");
+
+// vim: et sw=4 sts=4
diff --git a/library/ezyang/htmlpurifier/test-settings.sample.php b/library/ezyang/htmlpurifier/test-settings.sample.php
new file mode 100644 (file)
index 0000000..886b974
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+
+// ATTENTION! DO NOT EDIT THIS FILE!
+// This file is necessary to run the unit tests and profiling scripts.
+// Please copy it to 'test-settings.php' and make the necessary edits.
+
+// Note: The only external library you *need* is SimpleTest; everything else
+//       is optional.
+
+// We've got a lot of tests, so we recommend turning the limit off.
+set_time_limit(0);
+
+// Turning off output buffering will prevent mysterious errors from core dumps.
+$data = @ob_get_clean();
+if ($data !== false && $data !== '') {
+    echo "Output buffer contains data [".urlencode($data)."]\n";
+    exit;
+}
+
+// -----------------------------------------------------------------------------
+// REQUIRED SETTINGS
+
+// Note on running SimpleTest:
+//      Because HTML Purifier is PHP5-only and E_STRICT compliant, SimpleTest
+//      1.0.1 will not work; you need to run SimpleTest off its trunk using:
+//
+//        $ svn co https://simpletest.svn.sourceforge.net/svnroot/simpletest/simpletest/trunk simpletest
+//
+//      If SimpleTest is borked with HTML Purifier, please contact me or
+//      the SimpleTest devs; I am a developer for SimpleTest so I should be
+//      able to quickly assess a fix. SimpleTest's problem is my problem!
+
+// Where is SimpleTest located? Remember to include a trailing slash!
+$simpletest_location = '/path/to/simpletest/';
+
+// -----------------------------------------------------------------------------
+// OPTIONAL SETTINGS
+
+// Note on running PHPT:
+//      Vanilla PHPT from https://github.com/tswicegood/PHPT_Core should
+//      work fine on Linux w/o multitest.
+//
+//      To do multitest or Windows testing, you'll need some more
+//      patches at https://github.com/ezyang/PHPT_Core
+//
+//      I haven't tested the Windows setup in a while so I don't know if
+//      it still works.
+
+// Should PHPT tests be enabled?
+$GLOBALS['HTMLPurifierTest']['PHPT'] = false;
+
+// If PHPT isn't in your Path via PEAR, set that here:
+// set_include_path('/path/to/phpt/Core/src' . PATH_SEPARATOR . get_include_path());
+
+// Where is CSSTidy located? (Include trailing slash. Leave false to disable.)
+$csstidy_location    = false;
+
+// For tests/multitest.php, which versions to test?
+$versions_to_test    = array();
+
+// Stable PHP binary to use when invoking maintenance scripts.
+$php = 'php';
+
+// For tests/multitest.php, what is the multi-version executable? It must
+// accept an extra parameter (version number) before all other arguments
+$phpv = false;
+
+// Should PEAR tests be run? If you've got a valid PEAR installation, set this
+// to true (or, if it's not in the include path, to its install directory).
+$GLOBALS['HTMLPurifierTest']['PEAR'] = false;
+
+// If PEAR is enabled, what PEAR tests should be run? (Note: you will
+// need to ensure these libraries are installed)
+$GLOBALS['HTMLPurifierTest']['Net_IDNA2'] = true;
+
+// vim: et sw=4 sts=4
diff --git a/library/simplepie/LICENSE.txt b/library/simplepie/LICENSE.txt
deleted file mode 100644 (file)
index a822a4b..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-Copyright (c) 2004-2007, Ryan Parman and Geoffrey Sneddon.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are 
-permitted provided that the following conditions are met:
-
-       * Redistributions of source code must retain the above copyright notice, this list of 
-         conditions and the following disclaimer.
-
-       * Redistributions in binary form must reproduce the above copyright notice, this list 
-         of conditions and the following disclaimer in the documentation and/or other materials 
-         provided with the distribution.
-
-       * Neither the name of the SimplePie Team nor the names of its contributors may be used 
-         to endorse or promote products derived from this software without specific prior 
-         written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 
-OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 
-AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS 
-AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
-OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
-POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/library/simplepie/README.markdown b/library/simplepie/README.markdown
deleted file mode 100644 (file)
index e5ca021..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-# SimplePie
-
-## Authors and contributors
-
-* [Ryan Parman](http://ryanparman.com)
-* [Geoffrey Sneddon](http://gsnedders.com)
-* [Ryan McCue](http://ryanmccue.info)
-* [Michael Shipley](http://michaelpshipley.com)
-* [Steve Minutillo](http://minutillo.com/steve/)
-
-
-## License
-
-[New BSD license](http://www.opensource.org/licenses/bsd-license.php)
-
-
-## Project status
-
-SimplePie is currently maintained by Ryan McCue.
-
-At the moment, there isn't a lot of active development happening. If the community decides that SimplePie is a valuable tool, then the community will come together to maintain it into the future.
-
-If you're interested in getting involved with SimplePie, please get in touch with Ryan McCue.
-
-
-## What comes in the package?
-
-1. `simplepie.inc` - The SimplePie library.  This is all that's required for your pages.
-2. `README.markdown` - This document.
-3. `LICENSE.txt` - A copy of the BSD license.
-4. `compatibility_test/` - The SimplePie compatibility test that checks your server for required settings.
-5. `demo/` - A basic feed reader demo that shows off some of SimplePie's more noticable features.
-6. `idn/` - A third-party library that SimplePie can optionally use to understand Internationalized Domain Names (IDNs).
-7. `test/` - SimplePie's unit test suite.
-
-
-## To start the demo
-
-1. Upload this package to your webserver.
-2. Make sure that the cache folder inside of the demo folder is server-writable.
-3. Navigate your browser to the demo folder.
-
-
-## Need support?
-
-For further setup and install documentation, function references, etc., visit:
-[http://simplepie.org/wiki/](http://simplepie.org/wiki/)
-
-For bug reports and feature requests, visit:
-[http://github.com/rmccue/simplepie/issues](http://github.com/rmccue/simplepie/issues)
-
-Support mailing list -- powered by users, for users.
-[http://tech.groups.yahoo.com/group/simplepie-support/](http://tech.groups.yahoo.com/group/simplepie-support/)
diff --git a/library/simplepie/compatibility_test/COMPATIBILITY README.txt b/library/simplepie/compatibility_test/COMPATIBILITY README.txt
deleted file mode 100644 (file)
index 5b24989..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-SIMPLEPIE COMPATIBILITY TEST
-
-1) Upload sp_compatibility_test.php to the web-accessible root of your website.
-For example, if your website is www.example.com, upload it so that you can get 
-to it at www.example.com/sp_compatibility_test.php
-
-2) Open your web browser and go to the page you just uploaded.
\ No newline at end of file
diff --git a/library/simplepie/compatibility_test/sp_compatibility_test.php b/library/simplepie/compatibility_test/sp_compatibility_test.php
deleted file mode 100644 (file)
index a7a7f5f..0000000
+++ /dev/null
@@ -1,330 +0,0 @@
-<?php
-if (isset($_GET['logopng']))
-{
-       $data='iVBORw0KGgoAAAANSUhEUgAAAZAAAAAtCAYAAACAnD3TAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABZ0RVh0Q3JlYXRpb24gVGltZQAwMi8wMy8wNnKU/JIAAAAfdEVYdFNvZnR3YXJlAE1hY3JvbWVkaWEgRmlyZXdvcmtzIDi1aNJ4AAAR6ElEQVR4nO1dzYrrSJb+TtP79Buk5gnSRbvp1ZC6q4GB4bqWvbq+m1kMDDdr0dCLGcoJDTXUpnxp6Fle52pWQ/syMNAwUEoaLhTtppxPUPITtL0vV8wiTthy6IQUkkOynKkPhDOl+DmKv3Pi/IRIKYUePXr06NGjKn5+bgK6DiKKAEwAbAAslFLpGcm5KBBRDCAGkEK33eac9PQIAyKaAIgArJRSi0BlRqg4z4howHkGnGcVgpYeFaCUepEX9KCL+Ro60gyhB7TiKz033SXvNIZerFXmis9Ey8SiY37mtolMf5+7ny75AjC3+nXC9wcAEuvZ1LNM5zwDMLWeqUx9K6u+6Nzt89KuF7UDIaIhgDvohfbKegYAa+hJMFVaArqz0l0T0VB1V9K5A3AdqjCW8O4cjzfQE3il5J3F1Pp/HIouX2TonyDTLtzXjwDuOtyXnQPvEt5Yt8fQTGUC4LZm0UXz7EtHnjGAG+HerCYNwVAyb3yQKqXmgchpFEcMhP46+hbAR/WLZdBOoL+OIhwkwCG09HDDvwYrAFv+TQAk6hfLYCoP3nZ/KEl2DT1B3hDRZ0yzjYFw77liAvcE3oOIHnBgugY2I7tCi2BhYSHQYXALzeRaZ2xdREaFtIdSamoli4SsA+u3DorKrZ0no0I1aGthHsJj3hTgEZopN4oQ7XO8A/lJb/HpL6Mb9cvl29qE/WWUVQ+9htzZNob8ewvgHZeTAHgAsFC/rM9MiGiMcuZh4yUxChd82+ANgDERxV2Q6FkCLGIeBn0fH5Ag317T9skIA2aI31q3W1mYLwGh2sdmIAYT+m40BPC5+tUy9Sbqu9EEWqJ7XYWIAsR8fUPfjd4DmKlf1WIkVXdUZieUq0spldSo/yXgCsCCVQ8b6DbM7joeW6RlhoCqvBcCn/aS5l4SoO4686yMlqg+OS8CUYhCfnb03+7oGmKHH+jTaE6fRs7K6NNoSJ9GM/o0+ht2+IAdXlvlhLgG2OFLpqeSbpG3adLk2AJ4D+AVX5/z/2tog+8GWorN4n2Vup8xnhz3r3FQg9ht14pu2qGnN9hCMzJDf9I8Rc8HvLtcZ25tEUairzPP7DxPz0i4S89NgC+OdyA7MY22Cfx5lOJgZB5Aq5xsO0bTGAD4hv48eg3grfp7r91R7LovqFsWAO5YBQKl1JwNrmMAiVLq7Aa6DuBeKTXlNkqRt21MoJnFHfQuLoZmyEHcPT0wddx/gDaab4A9o+lRHTF03w4AzFQAt/Y680wplRLRKxxcf6en0hEQK2ih1IatMoKU7pIYoQ8DMYj4qutpERIxgO8pGb1V8bLOwvRYpKvPehWxUWleo45nDaXUhojmYHtVBjfmOTQjaZvpSkbxR6XUJHsjxML3EpHxTgxd7hwV5xkvtEloWk4Fj/3Evs9M0k6bS3dJOFZh/XhR1wA/4o/0f6NJjfcelifp4YFUuskeUK2D1ZWSt9e0XUp69HgZsHcgK1ze4vqB/jSC+ofl3PFcMrZdscdQEpIQVotEADauHQ4vcoA7fkLKY1yfK+XzLDtGAb0lcOWppdY8kRZAVleu6/QzEUVVdyms1hsCbsky05epb/mZcYUq+WyaiuhqGuYdLl3ibhuZvq8073kuBV0rJNgMpNHKGsQH+t9Rqv5xmQjPpHuAlkrjsoLZBTjLVOdmAnNsSczXtZUP0Hr3KbRaZQIr8ImItmC7S7ajedCM+cqpDInoCVr/PC+jX8hbFFwnxXPUAtczRsbbw44raIAWSfhJfDMT0TRLC9Nh+ihHC0/SMXT/230LcLAi05XzTuQ0H7nsVea+aTtTth30uoZW98yscRMz/REcquZM2xo7xkRKx2mnmX9TtlVETNcge99RxIDLuDPvkHnniUW7c54V0Gfa1SDh3xiyl1Fkv5OQLrGZnEBbo/Ek3I93yI+XNfRYydVNRGYeSeNwC902c2iBOoZf++TaIlevUofDFOl/Rt/CY1HtKDYAPlP/lDesE1EK2RPriyKDHRHNkNfxv1JKJUS0QbjguEelVMx1TuEfhPSQ1e0TUYL8wvHKDAJemBLkI3iz2EIf7ZI6aLk3jIAHusswOLPrUUrtlcBVaSlIswcRrYTyCvu4Ii17xwsiWiCcu/oWWjrf8GI1h9/YemKaNryw/1Chzkfod/Yda4/QC9TKou1RKRVXHLeA9poaAqXzLHfaq1KKuJ3+aD26h16/TrHTvldK7W08jgDko3nnC9e7WGmk+pw0eo7dLGz3+iLs57oLthvvqgEX3LauAXbOhnc1wrTEG6dInRcysvo2YzeIK+R7Y0kMZZijfKBdwd+YWdQ+ZfWEpsVVp486bOFJS9ZhI6T34RUOO4Eh/MfWTYamqGKdtzXyRAg37m94sQTqqc1DqNold3S73EhIU1fFWgheA3wCnt9lVOE5Qa0EQU+EsBnIpgOM4JQrpv8e5bxweMsnBbLZi8I5IXkP+WDvdlwEHnC+EvOtp5urq97CCdYQLS4UqmV5EfOVWK8zi15o1O3/28xiUhVV8iU16yhC3XcOhalwz2YgEqNqhIGgmsfihH+bbMOkLIHNQJIOMIFTL3srbDCB3r7ZuOEtdAh8Aa2+eRCebTPPJcmniAk8cL574dkV/AaR5HppypXo8SkzFu6tPQx3wWlxeX55GORD0mLyfeF4bgJXpfFR1P9POAS7SpgIumoTNGkuF4qeZ/PXWTSfAPwdgM8gz724RpllWOE4WDQLu01WOA6MBLSDTZT5P9cvTTgC8Pi1BZktdJ9L42nMeVw7irc4BEm/hR5zJnjat33SMrptI3pphgtATP81GqpfL48GPOv07yBvEd8R0eLUgWF07Wy4sqOhV5nnM4GOoi353Pi8E5GkZ/bZztsS/xZsvGc1mK1PLtzVOAY84CepBqWlQpoj8EIhbf+nPF6kfooLisz20zf2w4zeOkV+fBSpIfZjk4gekW930/9voXdcic3EOWbHrnOesWdJ+vnYKiO205Rg/10PthnZ9V/57J6rINPGMfL2uZXwTolAV4TD4mm3dVNH8kiCycwE4PLONztGruAe84+Cof3of9/2KcPRDkT9erl+BmosYCerR7hRPzraYhF6MBcgPSGvNIALGYhj4mdd/KQdg5QH0CqzBMD3jufzFmk5FVK524zRPhWe2xJqZZzo5ZYI90zw5lwp5fpw1yl1hoCr/hC2jFMg7axiYG+g9kkfArFwL8n8Xbarz2LY1lr2s9ydH5F0IEjw1CsueOcJ8ttWoFv2kNAIqce9gttm8Oixi2tTp1yGSLi3cvxdlq/HZULq44h/2xyrdeuSBOLW1rI8A3kedpDY9cIsnU0cj29ZzXVpKDMCS9JIFYnGB0/ws5u0QYsv4qKHTQdhhcQJhvS2kJybAAkOgccs5pHwrCkGkrNleI4/l/32ltWWjUJiIIsOMIDTrwLwoJEM0kC5a+8loo6aoMri+REcj9ABWtrAudUuThDRgIjmRJQSkeIrweXGd7UBWy1sbA2RdX/bxPduXA4gHtjwWiY5ZQDazb9RgTj3SVv1z8s1/edohQ5PkhDgE2XHyBsvr6A9cy5xJ+KCJPUP2WA9RLXt8xoHfXYCbSitMqlC0tIEUo80bdnKKqEgqKwLB6B2GStYbcRCpD0WmxqH0nja8pwYMB25PszMuzscTke38Q0RbZqKnJe/ib7DDJd9Aq1vR08gG4MnuCwGUscz5DXqRVLvPXcCoi4tTSA9NwF1wFHbU1QLKuuhkSAfCR8hv7AnLdBicAXPyH72Xowhf14BAD4Q0aqJ3VNehQUYNdYle2N5NRQ3qLT9O9nT5hngXIZtCbVpeWH9OBHuGd9+KQ6jTcRnrr8I0vgaIq/C6tKcOIrjYPVxDHc/J014ZokMRL1bbrHDrAOMoO5VxQPBlTaqUMZzwxrdmSxetBR4f0UV64s90qQVy2wczCil897G7NvvMraeFV04nZfdqm3PzAHy7dmVOQEI6xYLxBNH+is0MAZkFRZg1Fh36Ki+twCp+s3SFeshoUuDoi7SGnnMYXorHIzUR0eFk/ABnIZQSssJiAKUYSNtoMxTsIbjPbuwQF8IVjhmGJH1fH2Gj5DdQ8+H/RpV1p9KqQURfQEgF8gKPjsv5Hs4GYj6zXJL/zGaoqOSSwGmFdN3zcOnDJJBNK1RTtKALaMuQtGyRl5qjFHTnneCd0yTiIV7acs01IHUlk1FdddBgmM7nO2S3rqgWXdOKKVm7CAkrRVDBBwvsg3EEPLb5Xv8hBV+Ai7kStRvly6XNhfiiunPhgIdZlqSNQlLyUlIGiw7Fe6NC9qtbFGom69JSDSljrRdWqAj4V6Xdv82LbYxujFaG9olugT/oEJRIQMBAOwwuRCD+gY7OZCNiGbSKaq8sEwdb96lwW0QO+6X0SrtspxBfw0HpTVJSyLcu4I2IEaZMo3bsERL5PjbwOewyEbA47XKkfWdcN8tOHMsaZWQAngs4mXPT0XO+M27iBw41qeMEbQyRt02EIb69+UTfTmaoNvHfGwAxOp+6fJAeAfsP9aUQEtsA+iFSzI8rtk1LjihNWGkTmlArT3c86TnN5n2APRiOeQ6ronos+pkeqEWLZ4uiK40NwB+EPpTCia9Jtp/zjYWnifCvaZR1P+AnptR6Eoz7XAqJJf4rTkosEVEJc+lgyoNmhYoc7Eo0EHNwIEZxNDz4jV08O6YAwU30GrgNJN3UlCPC1EVggEPBgIA6n75kf5tNEE3Y0M08/jdUjqe2JZgr5E/eVPC/HSygmLOJ7jWkuI4RkD6EtmXcPuaN+I80SQtbECs8sU1V7lzxymtwHkEqXcFpx+v+eRgUeLkU3CLvHP25SAvTM34K48x9DhL/EkGoA/ejCHT3bRtNRXuXfMJyxvo9rCPwZcWcUB/PbFpiX4h1H2D/MnUBmbs3uHwCeYt9DsM4Z4DCf+mwrOy9smhXIXFUL9bPmCHYcfUWQvsELmYByP2fUfGFt1zHLiCO0DMl9Z5GFKCYN5g2VX6bgM5DugWMjN7OoPUbOCSjGfAPg5AOiT0NfS7SDvtJPN3WpC3ztcLAffBm2s0PMcKdk7vcGiPyHqWOPK0oc5eoF6sTrZfTXu7mMeDYYQ12ycHbwYCAOqr5RN2iHD+Axc32GGivlp+rr5yqq0MqkgOW2i/+UvxzHqoEF06RbUB2qQH0hTN0TKD/LEcCWlFWiYV6GgDa+t779MTyipjjNEJZWfR5hwrc+ePrP9dc6lxBsILehWmOqzoJfiEvCqxavvkUImBAID6erlVXy9fsXE9PQPjmGKHSH3t523FE+y9R9JH6AMBk4I0W7gZUtmilZ74XKpP0i1nsT+3KhOpKkmpNh7h3iWkPsQ5aEFgWnLIlF3WH/f8/YwUuh2LmMgWwOclzHqL4rYpe1eftrDrO7KJ8HlHvl6IDzheGOcobjPXu6f8m6CcET9Bz7GyBblonhV5lqXW/9MSmo7o4LEgpW+KgRz1Obvt+qxV5gNsK/j193vIh51OUaF9JJBSuQ+RVQLdjd4wIdFJBRVjBc2dF2pWuuMQwZ4gMQ4G2gH0gEtR/UDAxsC6d3vb/xbHBw0uoM+kqiXFsXeHKS/bDivojzuldcrtGi0soY1xOJDOlLuwy+XxMUbeUH1SW1cFOxPY6rP30LQb2lbQX6tLHWWY945wmJeJ+XUJSezlNYGeJ0bHnnKeuSf9pj+zbW7KEOttEtyvExyrss0YyNEjzT+lVKveNJmxGOEw5xNwUKFNtzDON9DvmEK/p3PsVm2fXP5TGci+oH8d3WYIiQIUmUBP3oX6/bKqdHaxcDCQV31E8cuAg4Hcdyjo81lDmH+PVT/z+pLg5YXlA/X7pfkQO+hfRjc4SJWGg8ZCthTHW2DNYf+w7FIAVI8ePV4ObLtCJzQTXUUwBpKF+sPyCVrfWTUqvEePHj1agR3nwsHGtgdT0iJJF4dGGEiPHj16dBlsN/ieiMxBngPkvwlyjmDHi0LPQHr06PESEfPvLdwxNj3zKEFlN94ePXr0eAYoO91gi9Pial4EegbSo0ePl4g53DEQJtgxbY2aC0Wvwuoe7DNxHnEZ33voEQYJdGCjMeY+ofcECg4+P8zETwyhQw9M/MS8Zx5+CBYH0qNHjx49Xhb+H6JWCt7+7okIAAAAAElFTkSuQmCC';
-       header('Content-type: image/png');
-       echo base64_decode($data);
-       exit;
-}
-else if (isset($_GET['background']))
-{
-       $data='R0lGODlhMAEeAeYAAP///8ni6cTf5+72+PD3+c3k6+nz9ufy9ev099Pn7bnZ48bg6LfY4uHv8/r8/f3+/v7//7bX4cjh6fj7/Mvj6vz9/rva4+z19/X6+/f7/Pn8/fb6+7jZ4vv9/bra473b5Lzb5LXX4b7c5b/c5e31+NTo7tvs8dfq7/H3+bjY4tnq79Hm7c/l69nr8PL4+t7t8sDd5cLe5uPw9Nvr8Mri6fP4+tLn7er099Hm7O/2+dDm7OTw9OXx9Nbp7tbp7+Lv8+Du8szj6sXg57bY4d3s8djq7+jz9tzs8cPe58fh6M7k68Pf5+by9fz+/sDd5vT5+vT5+97t8bbY4trr8P7+/v7+/+Pw8+Xx9dXo7sHe5vH4+fP5+sHd5t/u8s/l7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAAwAR4BAAf/gCGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp+goaKNEaWmp6ipqqusra6vsLGys7S1tre4ubq7vL2+v8DBwsPExbBDyMnKy8zNzs/Q0dLT1NXW19jZ2tvc3d7KUuHi4+Tl5ufo6err7O3u7/Dx8vP09fb34wz6+/z9/v8AAwocSLCgwYMIEypcyLChw4cQI0qcSLGixYsYM2osmKKjx48gQ4ocSbKkyZMoU6pcybKly5cwY8qc+ZGDzZs4c+rcybOnz59AgwodSrSo0aNIkypdyrSp06dQo0qdSrWq1aAKsmrdyrWr169gw4odS7as2bNo06pdy7at27dw/+PKnUu3rt27ePOS9cC3r9+/gAMLHky4sOHDiBMrXsy4sePHkCNLnky5suXLmDNr3sz5sIXPoEOLHk26tOnTqFOrXs26tevXsGPLnk27tu3buHPr3s27t+/fqkEIH068uPHjyJMrX868ufPn0KNLn069uvXr2LNr3869u/fv4MOLb/6hvPnz6NOrX8++vfv38OPLn0+/vv37+PPr38+/v///AAYo4IAEFgifCAgmqOCCDDbo4IMQRijhhBRWaOGFGGao4YYcdujhhyCGKOKIJJZo4okoTjjCiiy26OKLMMYo44w01mjjjTjmqOOOPPbo449ABinkkEQWaeSRSCap5P+SNsLg5JNQRinllFRWaeWVWGap5ZZcdunll2CGKeaYZELpxJlopqnmmmy26eabcMYp55x01mnnnXjmqeeefPaZJheABirooIQWauihiCaq6KKMNuroo5BGKumklFZqqaBZZKrpppx26umnoIYq6qiklmrqqaimquqqrLbq6qubxiDrrLTWauutuOaq66689urrr8AGK+ywxBZr7LHI0orEssw26+yz0EYr7bTUVmvttdhmq+223Hbr7bfghtvsEuSWa+656Kar7rrstuvuu/DGK++89NZr77345quvuQL06++/AAcs8MAEF2zwwQgnrPDCDDfs8MMQRyzxxBRXbPH/xRhnrPHGHHeMsBAghyzyyCSXbPLJKKes8sost+zyyzDHLPPMNNdss8gL5Kzzzjz37PPPQAct9NBEF2300UgnrfTSTDft9NNQRy311FRXbfXVWGdNdBJcd+3112CHLfbYZJdt9tlop6322my37fbbcMctt9cS1G333XjnrffefPft99+ABy744IQXbvjhiCeu+OKMN+7445BHLvnklFcOeACYZ6755px37vnnoIcu+uikl2766ainrvrqrLfu+uuwxy777LTXbvvtuI9Ow+689+7778AHL/zwxBdv/PHIJ6/88sw37/zz0EffOwXUV2/99dhnr/323Hfv/ffghy/+//jkl2/++einr/767Lfv/vvwxy///PR/H8T9+Oev//789+///wAMoAAHSMACGvCACEygAhfIwAbmrwAQjKAEJ0jBClrwghjMoAY3yMEOevCDIAyhCEdIwhKa8IQoTKEKV8jCFrrwhTDcoBJmSMMa2vCGOMyhDnfIwx768IdADKIQh0jEIhrxiEhMYg1ZwMQmOvGJUIyiFKdIxSpa8YpYzKIWt8jFLnrxi2AMoxid6IUymvGMaEyjGtfIxja68Y1wjKMc50jHOtrxjnjMox73eEYd+PGPgAykIAdJyEIa8pCITKQiF8nIRjrykZCMpCQnSUlA4uCSmMykJjfJyU568v+ToAylKEdJylKa8pSoTKUqV8nKVmZyBbCMpSxnScta2vKWuMylLnfJy1768pfADKYwh0nMYhpTljZIpjKXycxmOvOZ0IymNKdJzWpa85rYzKY2t8nNbnrzm8tMgDjHSc5ymvOc6EynOtfJzna6853wjKc850nPetrznvjMpz73yc9++vOfAA2oQNtZgoIa9KAITahCF8rQhjr0oRCNqEQnStGKWvSiGM2oRjd6UCx49KMgDalIR0rSkpr0pChNqUpXytKWuvSlMI2pTGdKU5D24KY4zalOd8rTnvr0p0ANqlCHStSiGvWoSE2qUpfK1Kbm1AdQjapUp0rVqlr1qlj/zapWt8rVrnr1q2ANq1jHStaymlWqJ0irWtfK1ra69a1wjatc50rXutr1rnjNq173yte++vWvay2CYAdL2MIa9rCITaxiF8vYxjr2sZCNrGQnS9nKWvaymCWsCjbL2c569rOgDa1oR0va0pr2tKhNrWpXy9rWuva1sI1tZ1tA29ra9ra4za1ud8vb3vr2t8ANrnCHS9ziGve4yE2ucm07heY697nQja50p0vd6lr3utjNrna3y93ueve74A2veMf73BmY97zoTa9618ve9rr3vfCNr3znS9/62ve++M2vfvfLX/Sa4L8ADrCAB0zgAhv4wAhOsIIXzOAGO/jBEI6w/4QnTOEKB/gIGM6whjfM4Q57+MMgDrGIR0ziEpv4xChOsYpXzOIWu1jDRIixjGdM4xrb+MY4zrGOd8zjHvv4x0AOspCHTOQiG/nIM46CkpfM5CY7+clQjrKUp0zlKlv5yljOspa3zOUue/nLYGbyC8ZM5jKb+cxoTrOa18zmNrv5zXCOs5znTOc62/nOeM5zmbvA5z77+c+ADrSgB03oQhv60IhOtKIXzehGO/rRkI60pP0MhEpb+tKYzrSmN83pTnv606AOtahHTepSm/rUqE61qld96Qa4+tWwjrWsZ03rWtv61rjOta53zete+/rXwA62sIdN7GIb+9jITrayl//N7GY7O9c/iLa0p03talv72tjOtra3ze1ue/vb4A63uMdN7nKb+9zTtoK6183udrv73fCOt7znTe962/ve+M63vvfN7377+98AZ7cMBk7wghv84AhPuMIXzvCGO/zhEI+4xCdO8Ypb/OIYz3jBd8Dxjnv84yAPuchHTvKSm/zkKE+5ylfO8pa7/OUwj7nMPc6Dmtv85jjPuc53zvOe+/znQA+60IdO9KIb/ehIT7rSl37zKzj96VCPutSnTvWqW/3qWM+61rfO9a57/etgD7vYx052qDPh7GhPu9rXzva2u/3tcI+73OdO97rb/e54z7ve9873vqf9AIAPvOAHT/j/whv+8IhPvOIXz/jGO/7xkI+85CdP+cpb/vKYz7zmN8/5znv+86BfvBFGT/rSm/70qE+96lfP+ta7/vWwj73sZ0/72tv+9rjPfekNwPve+/73wA++8IdP/OIb//jIT77yl8/85jv/+dCPvvSnT/3qW//62M++9rfP/ePf4PvgD7/4x0/+8pv//OhPv/rXz/72u//98I+//OdP//qHHwH4z7/+98///vv//wAYgAI4gARYgAZ4gAiYgAq4gAzYgA74gBAYgRI4gRRYgRZ4gRg4gBewgRzYgR74gSAYgiI4giRYgiZ4giiYgiq4gizYgi74gjAYgzI4gzRYgzZ4gziY/4M6uIMmSAI++INAGIRCOIREWIRGeIRImIRKuIRM2IRO+IRQGIVSOIVUCIQDcIVYmIVauIVc2IVe+IVgGIZiOIZkWIZmeIZomIZquIZs2IZu+IZwGIdyOId0WId2eIdimAN6uId82Id++IeAGIiCOIiEWIiGeIiImIiKuIiM2IiO+IiQyIcEMImUWImWeImYmImauImc2Ime+ImgGIqiOIqkWIqmeIqomIqquIqs2Iqu+IqwGIuyOIueiAK2eIu4mIu6uIu82Iu++IvAGIzCOIzEWIzGeIzImIzKuIzMiIta8IzQGI3SOI3UWI3WeI3YmI3auI3c2I3e+I3gGI7iOP+O5FiO0egC6JiO6riO7NiO7viO8BiP8jiP9FiP9niP+JiP+riP/NiP/qiONRCQAjmQBFmQBnmQCJmQCrmQDNmQDvmQEBmREjmRFFmRFnmRA7kFGrmRHNmRHvmRIBmSIjmSJFmSJnmSKJmSKrmSLNmSLvmSMMmRTzCTNFmTNnmTOJmTOrmTPNmTPvmTQBmUQjmURFmURnmUSJmUNQkFTNmUTvmUUBmVUjmVVFmVVnmVWJmVWrmVXNmVXvmVYBmWYumUGFCWZnmWaJmWarmWbNmWbvmWcBmXcjmXdFmXdnmXeJmXermXfNmXfvmXgBmYgjmYhFmYcLkBiJmYirmYjNn/mI75mJAZmZI5mZRZmZZ5mZiZmZq5mZzZmZ75maAZmqI5mqRZmqZ5mqg5mRmwmqzZmq75mrAZm7I5m7RZm7Z5m7iZm7q5m7zZm775m8AZnMI5nMRZnMZ5nMiZnMq5nLY5Ac75nNAZndI5ndRZndZ5ndiZndq5ndzZnd75neAZnuI5nuRZnuZ5nuiZnuq5nuzZnu6ZnRoQn/I5n/RZn/Z5n/iZn/q5n/zZn/75nwAaoAI6oARaoAZ6oAiaoAq6oAzaoA76oBAaofzpABRaoRZ6oRiaoRq6oRzaoR76oSAaoiI6oiRaoiZ6oiiaoiq6oizaoi76ojAaozI6ozT6oR1w/6M4mqM6uqM82qM++qNAGqRCOqREWqRGeqRImqRKuqRM2qRO+qRQGqVSOqVUWqVWeqVCWgFauqVc2qVe+qVgGqZiOqZkWqZmeqZomqZquqZs2qZu+qZwGqdyOqd0Wqd2eqd4mqd6WqZN0Kd++qeAGqiCOqiEWqiGeqiImqiKuqiM2qiO+qiQGqmSOql/+gCWeqmYmqmauqmc2qme+qmgGqqiOqqkWqqmeqqomqqquqqs2qqu+qqwGquyOqu0Wqu2GqpUkKu6uqu82qu++qvAGqzCOqzEWqzGeqzImqzKuqzM2qzO+qy7WgXSOq3UWq3Weq3Ymq3auq3c2q3e+q3gGv+u4jqu5Fqu5nqu6EqtELCu7Nqu7vqu8Bqv8jqv9Fqv9nqv+Jqv+rqv/Nqv/vqvABuwAjuwBFuwBnuwCJuwCruw9goADvuwEBuxEjuxFFuxFnuxGJuxGruxHNuxHvuxIBuyIjuyJFuyJnuyKJuyKruyLNuyLvuyMBuzMjuzNFuzNnuzOJuzOruzPNuzPvuzQBu0Qju0RFu0Rnu0SJu0Sru0TNu0Tvu0UBu1Uju1VFu1Vnu1WJu1Wru1XNu1Xvu1YBu2Yju2ZFu2Znu2aJu2aru2bNu2bvu2cBu3cju3dFu3dnu3eJu3eru3fNu3fvu3gBu4gju4hFu4hnu4iJu4irv/uIzbuI77uJAbuZI7uZRbuZZ7uZibuZq7uZzbuZ77uaAbuqI7uqRbuqZ7uqibuqq7uqzbuq77urAbu7I7u7Rbu7Z7u7ibu7q7u7zbu777u8AbvMI7vMRbvMZ7vMibvMq7vMzbvM77vNAbvdI7vdRbvdZ7vdibvdq7vdzbvd77veAbvuI7vuRbvuZ7vuibvuq7vuzbvu77vvAbv/I7v/Rbv/Z7v/ibv/q7v/zbv/77vwAcwAI8wARcwAZ8wAicwAq8wAzcwA78wBAcwRI8wRRcwRZ8wRicwRq8wRzcwR78wSAcwiI8wiRcwiZ8wiicwiq8wizcwi78wjAcwzI8wzRcFcM2fMM4nMM6vMM83MM+/MNArLmBAAA7';
-       header('Content-type: image/gif');
-       echo base64_decode($data);
-       exit;
-}
-
-$php_ok = (function_exists('version_compare') && version_compare(phpversion(), '4.3.0', '>='));
-$pcre_ok = extension_loaded('pcre');
-$curl_ok = function_exists('curl_exec');
-$zlib_ok = extension_loaded('zlib');
-$mbstring_ok = extension_loaded('mbstring');
-$iconv_ok = extension_loaded('iconv');
-if (extension_loaded('xmlreader'))
-{
-       $xml_ok = true;
-}
-elseif (extension_loaded('xml'))
-{
-       $parser_check = xml_parser_create();
-       xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
-       xml_parser_free($parser_check);
-       $xml_ok = isset($values[0]['value']);
-}
-else
-{
-       $xml_ok = false;
-}
-
-header('Content-type: text/html; charset=UTF-8');
-
-?><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
-
-<html lang="en">
-<head>
-<title>SimplePie: Server Compatibility Test 1.2</title>
-
-<style type="text/css">
-body {
-       font:14px/1.4em "Lucida Grande", Verdana, Arial, Helvetica, Clean, Sans, sans-serif;
-       letter-spacing:0px;
-       color:#333;
-       margin:0;
-       padding:0;
-       background:#fff url(<?php echo pathinfo(__FILE__, PATHINFO_BASENAME); ?>?background) repeat-x top left;
-}
-
-div#site {
-       width:550px;
-       margin:20px auto 0 auto;
-}
-
-a {
-       color:#000;
-       text-decoration:underline;
-       padding:0 1px;
-}
-
-a:hover {
-       color:#fff;
-       background-color:#333;
-       text-decoration:none;
-       padding:0 1px;
-}
-
-p {
-       margin:0;
-       padding:5px 0;
-}
-
-em {
-       font-style:normal;
-       background-color:#ffc;
-}
-
-ul, ol {
-       margin:10px 0 10px 20px;
-       padding:0 0 0 15px;
-}
-
-ul li, ol li {
-       margin:0 0 7px 0;
-       padding:0 0 0 3px;
-}
-
-h2 {
-       font-size:18px;
-       padding:0;
-       margin:30px 0 10px 0;
-}
-
-h3 {
-       font-size:16px;
-       padding:0;
-       margin:20px 0 5px 0;
-}
-
-h4 {
-       font-size:14px;
-       padding:0;
-       margin:15px 0 5px 0;
-}
-
-code {
-       font-size:1.1em;
-       background-color:#f3f3ff;
-       color:#000;
-}
-
-em strong {
-    text-transform: uppercase;
-}
-
-table#chart {
-       border-collapse:collapse;
-}
-
-table#chart th {
-       background-color:#eee;
-       padding:2px 3px;
-       border:1px solid #fff;
-}
-
-table#chart td {
-       text-align:center;
-       padding:2px 3px;
-       border:1px solid #eee;
-}
-
-table#chart tr.enabled td {
-       /* Leave this alone */
-}
-
-table#chart tr.disabled td, 
-table#chart tr.disabled td a {
-       color:#999;
-       font-style:italic;
-}
-
-table#chart tr.disabled td a {
-       text-decoration:underline;
-}
-
-div.chunk {
-       margin:20px 0 0 0;
-       padding:0 0 10px 0;
-       border-bottom:1px solid #ccc;
-}
-
-.footnote,
-.footnote a {
-       font:10px/12px verdana, sans-serif;
-       color:#aaa;
-}
-
-.footnote em {
-       background-color:transparent;
-       font-style:italic;
-}
-</style>
-
-<script type="text/javascript">
-// Sleight - Alpha transparency PNG's in Internet Explorer 5.5/6.0
-// (c) 2001, Aaron Boodman; http://www.youngpup.net
-
-if (navigator.platform == "Win32" && navigator.appName == "Microsoft Internet Explorer" && window.attachEvent) {
-       document.writeln('<style type="text/css">img, input.image { visibility:hidden; } </style>');
-       window.attachEvent("onload", fnLoadPngs);
-}
-
-function fnLoadPngs() {
-       var rslt = navigator.appVersion.match(/MSIE (\d+\.\d+)/, '');
-       var itsAllGood = (rslt != null && Number(rslt[1]) >= 5.5);
-
-       for (var i = document.images.length - 1, img = null; (img = document.images[i]); i--) {
-               if (itsAllGood && img.src.match(/\png$/i) != null) {
-                       var src = img.src;
-                       var div = document.createElement("DIV");
-                       div.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizing='scale')";
-                       div.style.width = img.width + "px";
-                       div.style.height = img.height + "px";
-                       img.replaceNode(div);
-               }
-               img.style.visibility = "visible";
-       }
-}
-</script>
-
-</head>
-
-<body>
-
-<div id="site">
-       <div id="content">
-
-               <div class="chunk">
-                       <h2 style="text-align:center;"><img src="<?php echo pathinfo(__FILE__, PATHINFO_BASENAME); ?>?logopng" alt="SimplePie Compatibility Test" title="SimplePie Compatibility Test" /></h2>
-                       <table cellpadding="0" cellspacing="0" border="0" width="100%" id="chart">
-                               <thead>
-                                       <tr>
-                                               <th>Test</th>
-                                               <th>Should Be</th>
-                                               <th>What You Have</th>
-                                       </tr>
-                               </thead>
-                               <tbody>
-                                       <tr class="<?php echo ($php_ok) ? 'enabled' : 'disabled'; ?>">
-                                               <td>PHP&sup1;</td>
-                                               <td>4.3.0 or higher</td>
-                                               <td><?php echo phpversion(); ?></td>
-                                       </tr>
-                                       <tr class="<?php echo ($xml_ok) ? 'enabled, and sane' : 'disabled, or broken'; ?>">
-                                               <td><a href="http://php.net/xml">XML</a></td>
-                                               <td>Enabled</td>
-                                               <td><?php echo ($xml_ok) ? 'Enabled, and sane' : 'Disabled, or broken'; ?></td>
-                                       </tr>
-                                       <tr class="<?php echo ($pcre_ok) ? 'enabled' : 'disabled'; ?>">
-                                               <td><a href="http://php.net/pcre">PCRE</a>&sup2;</td>
-                                               <td>Enabled</td>
-                                               <td><?php echo ($pcre_ok) ? 'Enabled' : 'Disabled'; ?></td>
-                                       </tr>
-                                       <tr class="<?php echo ($curl_ok) ? 'enabled' : 'disabled'; ?>">
-                                               <td><a href="http://php.net/curl">cURL</a></td>
-                                               <td>Enabled</td>
-                                               <td><?php echo (extension_loaded('curl')) ? 'Enabled' : 'Disabled'; ?></td>
-                                       </tr>
-                                       <tr class="<?php echo ($zlib_ok) ? 'enabled' : 'disabled'; ?>">
-                                               <td><a href="http://php.net/zlib">Zlib</a></td>
-                                               <td>Enabled</td>
-                                               <td><?php echo ($zlib_ok) ? 'Enabled' : 'Disabled'; ?></td>
-                                       </tr>
-                                       <tr class="<?php echo ($mbstring_ok) ? 'enabled' : 'disabled'; ?>">
-                                               <td><a href="http://php.net/mbstring">mbstring</a></td>
-                                               <td>Enabled</td>
-                                               <td><?php echo ($mbstring_ok) ? 'Enabled' : 'Disabled'; ?></td>
-                                       </tr>
-                                       <tr class="<?php echo ($iconv_ok) ? 'enabled' : 'disabled'; ?>">
-                                               <td><a href="http://php.net/iconv">iconv</a></td>
-                                               <td>Enabled</td>
-                                               <td><?php echo ($iconv_ok) ? 'Enabled' : 'Disabled'; ?></td>
-                                       </tr>
-                               </tbody>
-                       </table>
-               </div>
-
-               <div class="chunk">
-                       <h3>What does this mean?</h3>
-                       <ol>
-                               <?php if ($php_ok && $xml_ok && $pcre_ok && $mbstring_ok && $iconv_ok && $curl_ok && $zlib_ok): ?>
-                               <li><em>You have everything you need to run SimplePie properly!  Congratulations!</em></li>
-                               <?php else: ?>
-                                       <?php if ($php_ok): ?>
-                                               <li><strong>PHP:</strong> You are running a supported version of PHP.  <em>No problems here.</em></li>
-                                               <?php if ($xml_ok): ?>
-                                                       <li><strong>XML:</strong> You have XMLReader support or a version of XML support that isn't broken installed.  <em>No problems here.</em></li>
-                                                       <?php if ($pcre_ok): ?>
-                                                               <li><strong>PCRE:</strong> You have PCRE support installed. <em>No problems here.</em></li>
-                                                               <?php if ($curl_ok): ?>
-                                                                       <li><strong>cURL:</strong> You have <code>cURL</code> support installed.  <em>No problems here.</em></li>
-                                                               <?php else: ?>
-                                                                       <li><strong>cURL:</strong> The <code>cURL</code> extension is not available.  SimplePie will use <code>fsockopen()</code> instead.</li>
-                                                               <?php endif; ?>
-       
-                                                               <?php if ($zlib_ok): ?>
-                                                                       <li><strong>Zlib:</strong> You have <code>Zlib</code> enabled.  This allows SimplePie to support GZIP-encoded feeds.  <em>No problems here.</em></li>
-                                                               <?php else: ?>
-                                                                       <li><strong>Zlib:</strong> The <code>Zlib</code> extension is not available.  SimplePie will ignore any GZIP-encoding, and instead handle feeds as uncompressed text.</li>
-                                                               <?php endif; ?>
-       
-                                                               <?php if ($mbstring_ok && $iconv_ok): ?>
-                                                                       <li><strong>mbstring and iconv:</strong> You have both <code>mbstring</code> and <code>iconv</code> installed!  This will allow SimplePie to handle the greatest number of languages.  Check the <a href="http://simplepie.org/wiki/faq/supported_character_encodings">Supported Character Encodings</a> chart to see what's supported on your webhost.</li>
-                                                               <?php elseif ($mbstring_ok): ?>
-                                                                       <li><strong>mbstring:</strong> <code>mbstring</code> is installed, but <code>iconv</code> is not.  Check the <a href="http://simplepie.org/wiki/faq/supported_character_encodings">Supported Character Encodings</a> chart to see what's supported on your webhost.</li>
-                                                               <?php elseif ($iconv_ok): ?>
-                                                                       <li><strong>iconv:</strong> <code>iconv</code> is installed, but <code>mbstring</code> is not.  Check the <a href="http://simplepie.org/wiki/faq/supported_character_encodings">Supported Character Encodings</a> chart to see what's supported on your webhost.</li>
-                                                               <?php else: ?>
-                                                                       <li><strong>mbstring and iconv:</strong> <em>You do not have either of the extensions installed.</em> This will significantly impair your ability to read non-English feeds, as well as even some English ones.  Check the <a href="http://simplepie.org/wiki/faq/supported_character_encodings">Supported Character Encodings</a> chart to see what's supported on your webhost.</li>
-                                                               <?php endif; ?>
-                                                       <?php else: ?>
-                                                               <li><strong>PCRE:</strong> Your PHP installation doesn't support Perl-Compatible Regular Expressions.  <em>SimplePie is a no-go at the moment.</em></li>
-                                                       <?php endif; ?>
-                                               <?php else: ?>
-                                                       <li><strong>XML:</strong> Your PHP installation doesn't support XML parsing.  <em>SimplePie is a no-go at the moment.</em></li>
-                                               <?php endif; ?>
-                                       <?php else: ?>
-                                               <li><strong>PHP:</strong> You are running an unsupported version of PHP.  <em>SimplePie is a no-go at the moment.</em></li>
-                                       <?php endif; ?>
-                               <?php endif; ?>
-                       </ol>
-               </div>
-
-               <div class="chunk">
-                       <?php if ($php_ok && $xml_ok && $pcre_ok && $mbstring_ok && $iconv_ok) { ?>
-                               <h3>Bottom Line: Yes, you can!</h3>
-                               <p><em>Your webhost has its act together!</em></p>
-                               <p>You can download the latest version of SimplePie from <a href="http://simplepie.org/downloads/">SimplePie.org</a> and install it by <a href="http://simplepie.org/wiki/setup/start">following the instructions</a>.  You can find example uses with <a href="http://simplepie.org/ideas/">SimplePie Ideas</a>.</p>
-                               <p>Take the time to read <a href="http://simplepie.org/wiki/setup/start">Requirements and Getting Started</a> to make sure you're prepared to use SimplePie. No seriously, read them.</p>
-                               <p class="footnote"><em><strong>Note</strong></em>: Passing this test does not guarantee that SimplePie will run on your webhost &mdash; it only ensures that the basic requirements have been addressed.</p>
-                       <?php } else if ($php_ok && $xml_ok && $pcre_ok) { ?>
-                               <h3>Bottom Line: Yes, you can!</h3>
-                               <p><em>For most feeds, it'll run with no problems.</em>  There are <a href="http://simplepie.org/wiki/faq/supported_character_encodings">certain languages</a> that you might have a hard time with though.</p>
-                               <p>You can download the latest version of SimplePie from <a href="http://simplepie.org/downloads/">SimplePie.org</a> and install it by <a href="http://simplepie.org/wiki/setup/start">following the instructions</a>.  You can find example uses with <a href="http://simplepie.org/ideas/">SimplePie Ideas</a>.</p>
-                               <p>Take the time to read <a href="http://simplepie.org/wiki/setup/start">Requirements and Getting Started</a> to make sure you're prepared to use SimplePie. No seriously, read them.</p>
-                               <p class="footnote"><em><strong>Note</strong></em>: Passing this test does not guarantee that SimplePie will run on your webhost &mdash; it only ensures that the basic requirements have been addressed.</p>
-                       <?php } else { ?>
-                               <h3>Bottom Line: We're sorry…</h3>
-                               <p><em>Your webhost does not support the minimum requirements for SimplePie.</em>  It may be a good idea to contact your webhost, and ask them to install a more recent version of PHP as well as the <code>xmlreader</code>, <code>xml</code>, <code>mbstring</code>, <code>iconv</code>, <code>curl</code>, and <code>zlib</code> extensions.</p>
-                       <?php } ?>
-               </div>
-
-               <div class="chunk">
-                       <p class="footnote">&sup1; &mdash; SimplePie 2 will not support PHP 4.x. The core PHP team has discontinued PHP 4.x patches and support. <a href="http://simplepie.org/blog/2007/07/13/simplepie-is-going-php5-only/">Read the announcement.</a></p>
-                       <p class="footnote">&sup2; &mdash; Some recent versions of the PCRE (PERL-Compatible Regular Expression) engine compiled into PHP have been buggy, and are the source of PHP segmentation faults (e.g. crashes) which cause random things like blank, white screens. Check the <a href="http://simplepie.org/support/">Support Forums</a> for the latest information on patches and ongoing fixes.</p>
-               </div>
-
-       </div>
-
-</div>
-
-</body>
-</html>
\ No newline at end of file
diff --git a/library/simplepie/create.php b/library/simplepie/create.php
deleted file mode 100644 (file)
index 908ed18..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-<?php
-
-require_once 'simplepie.inc';
-
-function normalize_character_set($charset)
-{
-       return strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\1', $charset));
-}
-
-function build_character_set_list()
-{
-       $file = new SimplePie_File('http://www.iana.org/assignments/character-sets');
-       if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
-       {
-               return false;
-       }
-       else
-       {
-               $data = explode("\n", $file->body);
-               unset($file);
-               
-               foreach ($data as $line)
-               {
-                       // New character set
-                       if (substr($line, 0, 5) === 'Name:')
-                       {
-                               // If we already have one, push it on to the array
-                               if (isset($aliases))
-                               {
-                                       for ($i = 0, $count = count($aliases); $i < $count; $i++)
-                                       {
-                                               $aliases[$i] = normalize_character_set($aliases[$i]);
-                                       }
-                                       $charsets[$preferred] = array_unique($aliases);
-                                       natsort($charsets[$preferred]);
-                               }
-                               
-                               $start = 5 + strspn($line, "\x09\x0A\x0B\xC\x0D\x20", 5);
-                               $chars = strcspn($line, "\x09\x0A\x0B\xC\x0D\x20", $start);
-                               $aliases = array(substr($line, $start, $chars));
-                               $preferred = end($aliases);
-                       }
-                       // Another alias
-                       elseif(substr($line, 0, 6) === 'Alias:')
-                       {
-                               $start = 7 + strspn($line, "\x09\x0A\x0B\xC\x0D\x20", 7);
-                               $chars = strcspn($line, "\x09\x0A\x0B\xC\x0D\x20", $start);
-                               $aliases[] = substr($line, $start, $chars);
-                               
-                               if (end($aliases) === 'None')
-                               {
-                                       array_pop($aliases);
-                               }
-                               elseif (substr($line, 7 + $chars + 1, 21) === '(preferred MIME name)')
-                               {
-                                       $preferred = end($aliases);
-                               }
-                       }
-               }
-               
-               // Compatibility replacements
-               $compat = array(
-                       'EUC-KR' => 'windows-949',
-                       'GB2312' => 'GBK',
-                       'GB_2312-80' => 'GBK',
-                       'ISO-8859-1' => 'windows-1252',
-                       'ISO-8859-9' => 'windows-1254',
-                       'ISO-8859-11' => 'windows-874',
-                       'KS_C_5601-1987' => 'windows-949',
-                       'TIS-620' => 'windows-874',
-                       //'US-ASCII' => 'windows-1252',
-                       'x-x-big5' => 'Big5',
-               );
-               
-               foreach ($compat as $real => $replace)
-               {
-                       if (isset($charsets[$real]) && isset($charsets[$replace]))
-                       {
-                               $charsets[$replace] = array_merge($charsets[$replace], $charsets[$real]);
-                               unset($charsets[$real]);
-                       }
-                       elseif (isset($charsets[$real]))
-                       {
-                               $charsets[$replace] = $charsets[$real];
-                               $charsets[$replace][] = normalize_character_set($replace);
-                               unset($charsets[$real]);
-                       }
-                       else
-                       {
-                               $charsets[$replace][] = normalize_character_set($real);
-                       }
-                       $charsets[$replace] = array_unique($charsets[$replace]);
-                       natsort($charsets[$replace]);
-               }
-               
-               // Sort it
-               uksort($charsets, 'strnatcasecmp');
-               
-               // Check that nothing matches more than one
-               $all = call_user_func_array('array_merge', $charsets);
-               $all_count = array_count_values($all);
-               if (max($all_count) > 1)
-               {
-                       echo "Duplicated charsets:\n";
-                       foreach ($all_count as $charset => $count)
-                       {
-                               if ($count > 1)
-                               {
-                                       echo "$charset\n";
-                               }
-                       }
-               }
-               
-               // And we're done!
-               return $charsets;
-       }
-}
-
-function charset($charset)
-{
-       $normalized_charset = normalize_character_set($charset);
-       if ($charsets = build_character_set_list())
-       {
-               foreach ($charsets as $preferred => $aliases)
-               {
-                       if (in_array($normalized_charset, $aliases))
-                       {
-                               return $preferred;
-                       }
-               }
-               return $charset;
-       }
-       else
-       {
-               return false;
-       }
-}
-
-function build_function()
-{
-       if ($charsets = build_character_set_list())
-       {
-               $return = <<<EOF
-function charset(\$charset)
-{
-       // Normalization from UTS #22
-       switch (strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\\1', \$charset)))
-       {
-
-EOF;
-               foreach ($charsets as $preferred => $aliases)
-               {
-                       foreach ($aliases as $alias)
-                       {
-                               $return .= "\t\tcase " . var_export($alias, true) . ":\n";
-                       }
-                       $return .= "\t\t\treturn " . var_export($preferred, true) . ";\n\n";
-               }
-               $return .= <<<EOF
-               default:
-                       return \$charset;
-       }
-}
-EOF;
-               return $return;
-       }
-       else
-       {
-               return false;
-       }
-}
-
-if (php_sapi_name() === 'cli' && realpath($_SERVER['argv'][0]) === __FILE__)
-{
-       echo build_function();
-}
-
-?>
\ No newline at end of file
diff --git a/library/simplepie/db.sql b/library/simplepie/db.sql
deleted file mode 100644 (file)
index 13f504c..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/* SQLite */
-CREATE TABLE cache_data (
-       id TEXT NOT NULL,
-       items SMALLINT NOT NULL DEFAULT 0,
-       data BLOB NOT NULL,
-       mtime INTEGER UNSIGNED NOT NULL
-);
-CREATE UNIQUE INDEX id ON cache_data(id);
-
-CREATE TABLE items (
-       feed_id TEXT NOT NULL,
-       id TEXT NOT NULL,
-       data TEXT NOT NULL,
-       posted INTEGER UNSIGNED NOT NULL
-);
-CREATE INDEX feed_id ON items(feed_id);
-
-
-/* MySQL */
-CREATE TABLE `cache_data` (
-       `id` TEXT CHARACTER SET utf8 NOT NULL,
-       `items` SMALLINT NOT NULL DEFAULT 0,
-       `data` BLOB NOT NULL,
-       `mtime` INT UNSIGNED NOT NULL,
-       UNIQUE (
-               `id`(125)
-       )
-);
-
-CREATE TABLE `items` (
-       `feed_id` TEXT CHARACTER SET utf8 NOT NULL,
-       `id` TEXT CHARACTER SET utf8 NOT NULL,
-       `data` TEXT CHARACTER SET utf8 NOT NULL,
-       `posted` INT UNSIGNED NOT NULL,
-       INDEX `feed_id` (
-               `feed_id`(125)
-       )
-);
\ No newline at end of file
diff --git a/library/simplepie/demo/cli_test.php b/library/simplepie/demo/cli_test.php
deleted file mode 100644 (file)
index ec933c5..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/php
-<?php
-include_once('../simplepie.inc');
-
-// Parse it
-$feed = new SimplePie();
-if (isset($argv[1]) && $argv[1] !== '')
-{
-       $feed->set_feed_url($argv[1]);
-       $feed->enable_cache(false);
-       $feed->init();
-}
-
-$items = $feed->get_items();
-
-foreach ($items as $item)
-{
-       echo $item->get_title() . "\n";
-}
-
-var_dump($feed->get_item_quantity());
-
-?>
\ No newline at end of file
diff --git a/library/simplepie/demo/for_the_demo/alternate_favicon.png b/library/simplepie/demo/for_the_demo/alternate_favicon.png
deleted file mode 100644 (file)
index 063fb28..0000000
Binary files a/library/simplepie/demo/for_the_demo/alternate_favicon.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/background_blockquote.png b/library/simplepie/demo/for_the_demo/background_blockquote.png
deleted file mode 100644 (file)
index 8267e23..0000000
Binary files a/library/simplepie/demo/for_the_demo/background_blockquote.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/background_menuitem.gif b/library/simplepie/demo/for_the_demo/background_menuitem.gif
deleted file mode 100644 (file)
index fa765d6..0000000
Binary files a/library/simplepie/demo/for_the_demo/background_menuitem.gif and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/background_menuitem_off.gif b/library/simplepie/demo/for_the_demo/background_menuitem_off.gif
deleted file mode 100644 (file)
index 236cf40..0000000
Binary files a/library/simplepie/demo/for_the_demo/background_menuitem_off.gif and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/background_menuitem_shadow.gif b/library/simplepie/demo/for_the_demo/background_menuitem_shadow.gif
deleted file mode 100644 (file)
index 95cfb82..0000000
Binary files a/library/simplepie/demo/for_the_demo/background_menuitem_shadow.gif and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/favicons/alternate.png b/library/simplepie/demo/for_the_demo/favicons/alternate.png
deleted file mode 100644 (file)
index 063fb28..0000000
Binary files a/library/simplepie/demo/for_the_demo/favicons/alternate.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/favicons/blinklist.png b/library/simplepie/demo/for_the_demo/favicons/blinklist.png
deleted file mode 100644 (file)
index 53200b3..0000000
Binary files a/library/simplepie/demo/for_the_demo/favicons/blinklist.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/favicons/blogmarks.png b/library/simplepie/demo/for_the_demo/favicons/blogmarks.png
deleted file mode 100644 (file)
index c537261..0000000
Binary files a/library/simplepie/demo/for_the_demo/favicons/blogmarks.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/favicons/delicious.png b/library/simplepie/demo/for_the_demo/favicons/delicious.png
deleted file mode 100644 (file)
index 2e6021d..0000000
Binary files a/library/simplepie/demo/for_the_demo/favicons/delicious.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/favicons/digg.png b/library/simplepie/demo/for_the_demo/favicons/digg.png
deleted file mode 100644 (file)
index 3aa9677..0000000
Binary files a/library/simplepie/demo/for_the_demo/favicons/digg.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/favicons/magnolia.png b/library/simplepie/demo/for_the_demo/favicons/magnolia.png
deleted file mode 100644 (file)
index da519f5..0000000
Binary files a/library/simplepie/demo/for_the_demo/favicons/magnolia.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/favicons/myweb2.png b/library/simplepie/demo/for_the_demo/favicons/myweb2.png
deleted file mode 100644 (file)
index 2a12968..0000000
Binary files a/library/simplepie/demo/for_the_demo/favicons/myweb2.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/favicons/newsvine.png b/library/simplepie/demo/for_the_demo/favicons/newsvine.png
deleted file mode 100644 (file)
index 5cdbb31..0000000
Binary files a/library/simplepie/demo/for_the_demo/favicons/newsvine.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/favicons/reddit.png b/library/simplepie/demo/for_the_demo/favicons/reddit.png
deleted file mode 100644 (file)
index 65c3886..0000000
Binary files a/library/simplepie/demo/for_the_demo/favicons/reddit.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/favicons/segnalo.png b/library/simplepie/demo/for_the_demo/favicons/segnalo.png
deleted file mode 100644 (file)
index 748149b..0000000
Binary files a/library/simplepie/demo/for_the_demo/favicons/segnalo.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/favicons/simpy.png b/library/simplepie/demo/for_the_demo/favicons/simpy.png
deleted file mode 100644 (file)
index 30b23c1..0000000
Binary files a/library/simplepie/demo/for_the_demo/favicons/simpy.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/favicons/spurl.png b/library/simplepie/demo/for_the_demo/favicons/spurl.png
deleted file mode 100644 (file)
index f5be396..0000000
Binary files a/library/simplepie/demo/for_the_demo/favicons/spurl.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/favicons/technorati.png b/library/simplepie/demo/for_the_demo/favicons/technorati.png
deleted file mode 100644 (file)
index 0f19e82..0000000
Binary files a/library/simplepie/demo/for_the_demo/favicons/technorati.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/favicons/wists.png b/library/simplepie/demo/for_the_demo/favicons/wists.png
deleted file mode 100644 (file)
index 2e2d294..0000000
Binary files a/library/simplepie/demo/for_the_demo/favicons/wists.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/feed.png b/library/simplepie/demo/for_the_demo/feed.png
deleted file mode 100644 (file)
index e23c50c..0000000
Binary files a/library/simplepie/demo/for_the_demo/feed.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/logo_simplepie_demo.png b/library/simplepie/demo/for_the_demo/logo_simplepie_demo.png
deleted file mode 100644 (file)
index eda2d86..0000000
Binary files a/library/simplepie/demo/for_the_demo/logo_simplepie_demo.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/lucida-grande-bold.swf b/library/simplepie/demo/for_the_demo/lucida-grande-bold.swf
deleted file mode 100644 (file)
index 0a41e15..0000000
Binary files a/library/simplepie/demo/for_the_demo/lucida-grande-bold.swf and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/mediaplayer.swf b/library/simplepie/demo/for_the_demo/mediaplayer.swf
deleted file mode 100644 (file)
index bf78fd9..0000000
Binary files a/library/simplepie/demo/for_the_demo/mediaplayer.swf and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/mediaplayer_readme.htm b/library/simplepie/demo/for_the_demo/mediaplayer_readme.htm
deleted file mode 100644 (file)
index 56e12c3..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<head>
-<meta http-equiv="refresh" content="0;url=http://www.jeroenwijering.com/extras/readme.html">
-</head>
-</html>
\ No newline at end of file
diff --git a/library/simplepie/demo/for_the_demo/mini_podcast.png b/library/simplepie/demo/for_the_demo/mini_podcast.png
deleted file mode 100644 (file)
index fd6faf2..0000000
Binary files a/library/simplepie/demo/for_the_demo/mini_podcast.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/place_audio.png b/library/simplepie/demo/for_the_demo/place_audio.png
deleted file mode 100644 (file)
index 560ea00..0000000
Binary files a/library/simplepie/demo/for_the_demo/place_audio.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/place_video.png b/library/simplepie/demo/for_the_demo/place_video.png
deleted file mode 100644 (file)
index be5ec82..0000000
Binary files a/library/simplepie/demo/for_the_demo/place_video.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/sIFR-print.css b/library/simplepie/demo/for_the_demo/sIFR-print.css
deleted file mode 100644 (file)
index ec89b19..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*=:project
-    scalable Inman Flash Replacement (sIFR) version 3.
-
-  =:file
-    Copyright: 2006 Mark Wubben.
-    Author: Mark Wubben, <http://novemberborn.net/>
-
-  =:history
-    * IFR: Shaun Inman
-    * sIFR 1: Mike Davidson, Shaun Inman and Tomas Jogin
-    * sIFR 2: Mike Davidson, Shaun Inman, Tomas Jogin and Mark Wubben
-
-  =:license
-    This software is licensed and provided under the CC-GNU LGPL.
-    See <http://creativecommons.org/licenses/LGPL/2.1/>    
-*/
-
-
-/* This is the print stylesheet to hide the Flash headlines from the browser... regular browser text headlines will now print as normal */
-
-.sIFR-flash {
-       display: none !important;
-       height: 0;
-       width: 0;
-       position: absolute;
-       overflow: hidden;
-}
-
-.sIFR-alternate {
-       visibility: visible !important;
-       display: block !important;
-       position: static !important;
-       left: auto !important;
-       top: auto !important;
-}
\ No newline at end of file
diff --git a/library/simplepie/demo/for_the_demo/sIFR-screen.css b/library/simplepie/demo/for_the_demo/sIFR-screen.css
deleted file mode 100644 (file)
index 778e09d..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*=:project
-    scalable Inman Flash Replacement (sIFR) version 3.
-
-  =:file
-    Copyright: 2006 Mark Wubben.
-    Author: Mark Wubben, <http://novemberborn.net/>
-
-  =:history
-    * IFR: Shaun Inman
-    * sIFR 1: Mike Davidson, Shaun Inman and Tomas Jogin
-    * sIFR 2: Mike Davidson, Shaun Inman, Tomas Jogin and Mark Wubben
-
-  =:license
-    This software is licensed and provided under the CC-GNU LGPL.
-    See <http://creativecommons.org/licenses/LGPL/2.1/>    
-*/
-
-/*---- sIFR ---*/
-.sIFR-flash {
-       visibility: visible !important;
-       margin: 0;
-       padding: 0;
-}
-
-.sIFR-replaced {
-       visibility: visible !important;
-}
-
-.sIFR-alternate {
-       position: absolute;
-       left: 0;
-       top: 0;
-       width: 0;
-       height: 0;
-       display: block;
-       overflow: hidden;
-}
-
-/*---- Header styling ---*/
diff --git a/library/simplepie/demo/for_the_demo/sifr-config.js b/library/simplepie/demo/for_the_demo/sifr-config.js
deleted file mode 100644 (file)
index e7066b3..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-var yanone_kaffeesatz = {
-       src: './for_the_demo/yanone-kaffeesatz-bold.swf'
-};
-
-var lucida_grande = {
-       src: './for_the_demo/lucida-grande-bold.swf'
-};
-
-sIFR.activate(yanone_kaffeesatz);
-//sIFR.activate(lucida_grande);
-
-sIFR.replace(yanone_kaffeesatz, {
-//sIFR.replace(lucida_grande, {
-
-       selector: 'h3.header',
-       wmode: 'transparent',
-       css: {
-               '.sIFR-root': {
-                       'text-align': 'center',
-                       'color': '#000000',
-                       'font-weight': 'bold',
-                       'background-color': '#EEFFEE',
-
-                       'font-size': '50px', // For Yanone Kaffeesatz
-                       //'font-size': '40px', // For Lucida Grande
-
-                       'letter-spacing': '0' // For Yanone Kaffeesatz
-                       //'letter-spacing': '-4' // For Lucida Grande
-
-               },
-               'a': {
-                       'text-decoration': 'none',
-                       'color': '#000000'
-               },
-               'a:hover': {
-                       'text-decoration': 'none',
-                       'color': '#666666'
-               }
-       }
-});
diff --git a/library/simplepie/demo/for_the_demo/sifr.js b/library/simplepie/demo/for_the_demo/sifr.js
deleted file mode 100644 (file)
index 0a8b1b6..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/*=:project
-    scalable Inman Flash Replacement (sIFR) version 3, revision 245
-
-  =:file
-    Copyright: 2006 Mark Wubben.
-    Author: Mark Wubben, <http://novemberborn.net/>
-
-  =:history
-    * IFR: Shaun Inman
-    * sIFR 1: Mike Davidson, Shaun Inman and Tomas Jogin
-    * sIFR 2: Mike Davidson, Shaun Inman, Tomas Jogin and Mark Wubben
-
-  =:license
-    This software is licensed and provided under the CC-GNU LGPL.
-    See <http://creativecommons.org/licenses/LGPL/2.1/>    
-*/
-
-var parseSelector=(function(){var _1=/\s*,\s*/;var _2=/\s*([\s>+~(),]|^|$)\s*/g;var _3=/([\s>+~,]|[^(]\+|^)([#.:@])/g;var _4=/^[^\s>+~]/;var _5=/[\s#.:>+~()@]|[^\s#.:>+~()@]+/g;function parseSelector(_6,_7){_7=_7||document.documentElement;var _8=_6.split(_1),_9=[];for(var i=0;i<_8.length;i++){var _b=[_7],_c=toStream(_8[i]);for(var j=0;j<_c.length;){var _e=_c[j++],_f=_c[j++],_10="";if(_c[j]=="("){while(_c[j++]!=")"&&j<_c.length){_10+=_c[j]}_10=_10.slice(0,-1)}_b=select(_b,_e,_f,_10)}_9=_9.concat(_b)}return _9}function toStream(_11){var _12=_11.replace(_2,"$1").replace(_3,"$1*$2");if(_4.test(_12)){_12=" "+_12}return _12.match(_5)||[]}function select(_13,_14,_15,_16){return (_17[_14])?_17[_14](_13,_15,_16):[]}var _18={toArray:function(_19){var a=[];for(var i=0;i<_19.length;i++){a.push(_19[i])}return a}};var dom={isTag:function(_1d,tag){return (tag=="*")||(tag.toLowerCase()==_1d.nodeName.toLowerCase())},previousSiblingElement:function(_1f){do{_1f=_1f.previousSibling}while(_1f&&_1f.nodeType!=1);return _1f},nextSiblingElement:function(_20){do{_20=_20.nextSibling}while(_20&&_20.nodeType!=1);return _20},hasClass:function(_21,_22){return (_22.className||"").match("(^|\\s)"+_21+"(\\s|$)")},getByTag:function(tag,_24){return _24.getElementsByTagName(tag)}};var _17={"#":function(_25,_26){for(var i=0;i<_25.length;i++){if(_25[i].getAttribute("id")==_26){return [_25[i]]}}return []}," ":function(_28,_29){var _2a=[];for(var i=0;i<_28.length;i++){_2a=_2a.concat(_18.toArray(dom.getByTag(_29,_28[i])))}return _2a},">":function(_2c,_2d){var _2e=[];for(var i=0,_30;i<_2c.length;i++){_30=_2c[i];for(var j=0,_32;j<_30.childNodes.length;j++){_32=_30.childNodes[j];if(_32.nodeType==1&&dom.isTag(_32,_2d)){_2e.push(_32)}}}return _2e},".":function(_33,_34){var _35=[];for(var i=0,_37;i<_33.length;i++){_37=_33[i];if(dom.hasClass([_34],_37)){_35.push(_37)}}return _35},":":function(_38,_39,_3a){return (pseudoClasses[_39])?pseudoClasses[_39](_38,_3a):[]}};parseSelector.selectors=_17;parseSelector.pseudoClasses={};parseSelector.util=_18;parseSelector.dom=dom;return parseSelector})();
-var sIFR=new function(){var _3b=this;var _3c="sIFR-active";var _3d="sIFR-replaced";var _3e="sIFR-replacing";var _3f="sIFR-flash";var _40="sIFR-ignore";var _41="sIFR-alternate";var _42="sIFR-class";var _43="sIFR-layout";var _44=6;var _45=126;var _46=8;var _47="SIFR-PREFETCHED";var _48=[10,1.55,19,1.45,32,1.35,71,1.3,1.25];var _49=5;this.isActive=false;this.isEnabled=true;this.hideElements=true;this.preserveSingleWhitespace=false;this.fixWrap=true;this.fixHover=true;this.registerEvents=true;this.setPrefetchCookie=true;this.cookiePath="/";this.domains=[];this.fromLocal=true;this.forceClear=false;this.forceWidth=false;this.fitExactly=false;this.forceTextTransform=true;this.useDomContentLoaded=true;this.debugMode=false;this.hasFlashClassSet=false;this.delayCss=false;this.callbacks=[];var _4a=0;var _4b=false,_4c=false;var dom=new function(){var _4e="http://www.w3.org/1999/xhtml";this.getBody=function(){var _4f=document.getElementsByTagName("body");if(_4f.length==1){return _4f[0]}return null};this.addClass=function(_50,_51){if(_51){_51.className=((_51.className||"")==""?"":_51.className+" ")+_50}};this.removeClass=function(_52,_53){if(_53){_53.className=_53.className.replace(new RegExp("(^|\\s)"+_52+"(\\s|$)"),"").replace(/^\s+|(\s)\s+/g,"$1")}};this.hasClass=function(_54,_55){return new RegExp("(^|\\s)"+_54+"(\\s|$)").test(_55.className)};this.hasOneOfClassses=function(_56,_57){for(var i=0;i<_56.length;i++){if(this.hasClass(_56[i],_57)){return true}}return false};this.create=function(_59){if(document.createElementNS){return document.createElementNS(_4e,_59)}return document.createElement(_59)};this.setInnerHtml=function(_5a,_5b){if(ua.innerHtmlSupport){_5a.innerHTML=_5b}else{if(ua.xhtmlSupport){_5b=["<root xmlns=\"",_4e,"\">",_5b,"</root>"].join("");var xml=(new DOMParser()).parseFromString(_5b,"text/xml");xml=document.importNode(xml.documentElement,true);while(_5a.firstChild){_5a.removeChild(_5a.firstChild)}while(xml.firstChild){_5a.appendChild(xml.firstChild)}}}};this.nodeFromHtml=function(_5d){var _5e=this.create("div");_5e.innerHTML=_5d;return _5e.firstChild};this.getComputedStyle=function(_5f,_60){var _61;if(document.defaultView&&document.defaultView.getComputedStyle){_61=document.defaultView.getComputedStyle(_5f,null)[_60]}else{if(_5f.currentStyle){_61=_5f.currentStyle[_60]}}return _61||""};this.getStyleAsInt=function(_62,_63,_64){var _65=this.getComputedStyle(_62,_63);if(_64&&!/px$/.test(_65)){return 0}_65=parseInt(_65);return isNaN(_65)?0:_65};this.getZoom=function(){return _66.zoom.getLatest()}};this.dom=dom;var ua=new function(){var ua=navigator.userAgent.toLowerCase();var _69=(navigator.product||"").toLowerCase();this.macintosh=ua.indexOf("mac")>-1;this.windows=ua.indexOf("windows")>-1;this.quicktime=false;this.opera=ua.indexOf("opera")>-1;this.konqueror=_69.indexOf("konqueror")>-1;this.ie=false/*@cc_on || true @*/;this.ieSupported=this.ie&&!/ppc|smartphone|iemobile|msie\s5\.5/.test(ua)/*@cc_on && @_jscript_version >= 5.5 @*/;this.ieWin=this.ie&&this.windows/*@cc_on && @_jscript_version >= 5.1 @*/;this.windows=this.windows&&(!this.ie||this.ieWin);this.ieMac=this.ie&&this.macintosh/*@cc_on && @_jscript_version < 5.1 @*/;this.macintosh=this.macintosh&&(!this.ie||this.ieMac);this.safari=ua.indexOf("safari")>-1;this.webkit=ua.indexOf("applewebkit")>-1&&!this.konqueror;this.khtml=this.webkit||this.konqueror;this.gecko=!this.webkit&&_69=="gecko";this.operaVersion=this.opera&&/.*opera(\s|\/)(\d+\.\d+)/.exec(ua)?parseInt(RegExp.$2):0;this.webkitVersion=this.webkit&&/.*applewebkit\/(\d+).*/.exec(ua)?parseInt(RegExp.$1):0;this.geckoBuildDate=this.gecko&&/.*gecko\/(\d{8}).*/.exec(ua)?parseInt(RegExp.$1):0;this.konquerorVersion=this.konqueror&&/.*konqueror\/(\d\.\d).*/.exec(ua)?parseInt(RegExp.$1):0;this.flashVersion=0;if(this.ieWin){var axo;var _6b=false;try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7")}catch(e){try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");this.flashVersion=6;axo.AllowScriptAccess="always"}catch(e){_6b=this.flashVersion==6}if(!_6b){try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash")}catch(e){}}}if(!_6b&&axo){this.flashVersion=parseFloat(/([\d,?]+)/.exec(axo.GetVariable("$version"))[1].replace(/,/g,"."))}}else{if(navigator.plugins&&navigator.plugins["Shockwave Flash"]){var _6c=navigator.plugins["Shockwave Flash"];this.flashVersion=parseFloat(/(\d+\.?\d*)/.exec(_6c.description)[1]);var i=0;while(this.flashVersion>=_46&&i<navigator.mimeTypes.length){var _6e=navigator.mimeTypes[i];if(_6e.type=="application/x-shockwave-flash"&&_6e.enabledPlugin.description.toLowerCase().indexOf("quicktime")>-1){this.flashVersion=0;this.quicktime=true}i++}}}this.flash=this.flashVersion>=_46;this.transparencySupport=this.macintosh||this.windows;this.computedStyleSupport=this.ie||document.defaultView&&document.defaultView.getComputedStyle&&(!this.gecko||this.geckoBuildDate>=20030624);this.css=true;if(this.computedStyleSupport){try{var _6f=document.getElementsByTagName("head")[0];_6f.style.backgroundColor="#FF0000";var _70=dom.getComputedStyle(_6f,"backgroundColor");this.css=!_70||/\#F{2}0{4}|rgb\(255,\s?0,\s?0\)/i.test(_70);_6f.style.backgroundColor="";_6f=null}catch(e){}}this.xhtmlSupport=!!window.DOMParser&&!!document.importNode;try{var n=dom.create("span");if(!this.ieMac){n.innerHTML="x"}this.innerHtmlSupport=n.innerHTML=="x"}catch(e){this.innerHtmlSupport=false}this.zoomSupport=!!(this.opera&&document.documentElement);this.geckoXml=this.gecko&&(document.contentType||"").indexOf("xml")>-1;this.requiresPrefetch=this.ieWin||this.khtml;this.verifiedKonqueror=false;this.supported=this.flash&&this.css&&(!this.ie||this.ieSupported)&&(!this.opera||this.operaVersion>=8)&&(!this.webkit||this.webkitVersion>=412)&&(!this.konqueror||this.konquerorVersion>3.5)&&this.computedStyleSupport&&(this.innerHtmlSupport||!this.khtml&&this.xhtmlSupport)};this.ua=ua;var _72=new function(){var _73={leading:true,"margin-left":true,"margin-right":true,"text-indent":true};var _74=" ";function capitalize($){return $.toUpperCase()}this.normalize=function(str){if(_3b.preserveSingleWhitespace){return str.replace(/\s/g,_74)}return str.replace(/(\s)\s+/g,"$1").replace(/\xA0/,_74)};this.textTransform=function(_77,str){switch(_77){case "uppercase":str=str.toUpperCase();break;case "lowercase":str=str.toLowerCase();break;case "capitalize":var _79=str;str=str.replace(/^\w|\s\w/g,capitalize);if(str.indexOf("function capitalize")!=-1){var _7a=_79.replace(/(^|\s)(\w)/g,"$1$1$2$2").split(/^\w|\s\w/g);str="";for(var i=0;i<_7a.length;i++){str+=_7a[i].charAt(0).toUpperCase()+_7a[i].substring(1)}}break}return str};this.toHexString=function(str){if(typeof (str)!="string"||!str.charAt(0)=="#"||str.length!=4&&str.length!=7){return str}str=str.replace(/#/,"");if(str.length==3){str=str.replace(/(.)(.)(.)/,"$1$1$2$2$3$3")}return "0x"+str};this.toJson=function(obj){var _7e="";switch(typeof (obj)){case "string":_7e="\""+obj+"\"";break;case "number":case "boolean":_7e=obj.toString();break;case "object":_7e=[];for(var _7f in obj){if(obj[_7f]==Object.prototype[_7f]){continue}_7e.push("\""+_7f+"\":"+_72.toJson(obj[_7f]))}_7e="{"+_7e.join(",")+"}";break}return _7e};this.convertCssArg=function(arg){if(!arg){return {}}if(typeof (arg)=="object"){if(arg.constructor==Array){arg=arg.join("")}else{return arg}}var obj={};var _82=arg.split("}");for(var i=0;i<_82.length;i++){var $=_82[i].match(/([^\s{]+)\s*\{(.+)\s*;?\s*/);if(!$||$.length!=3){continue}if(!obj[$[1]]){obj[$[1]]={}}var _85=$[2].split(";");for(var j=0;j<_85.length;j++){var $2=_85[j].match(/\s*([^:\s]+)\s*\:\s*([^\s;]+)/);if(!$2||$2.length!=3){continue}obj[$[1]][$2[1]]=$2[2]}}return obj};this.extractFromCss=function(css,_89,_8a,_8b){var _8c=null;if(css&&css[_89]&&css[_89][_8a]){_8c=css[_89][_8a];if(_8b){delete css[_89][_8a]}}return _8c};this.cssToString=function(arg){var css=[];for(var _8f in arg){var _90=arg[_8f];if(_90==Object.prototype[_8f]){continue}css.push(_8f,"{");for(var _91 in _90){if(_90[_91]==Object.prototype[_91]){continue}var _92=_90[_91];if(_73[_91]){_92=parseInt(_92,10)}css.push(_91,":",_92,";")}css.push("}")}return escape(css.join(""))};this.bind=function(_93,_94){return function(){_93[_94].apply(_93,arguments)}}};this.util=_72;var _66={};_66.fragmentIdentifier=new function(){this.fix=true;var _95;this.cache=function(){_95=document.title};function doFix(){document.title=_95}this.restore=function(){if(this.fix){setTimeout(doFix,0)}}};_66.synchronizer=new function(){this.isBlocked=false;this.block=function(){this.isBlocked=true};this.unblock=function(){this.isBlocked=false;_96.replaceAll()}};_66.zoom=new function(){var _97=100;this.getLatest=function(){return _97};if(ua.zoomSupport&&ua.opera){var _98=document.createElement("div");_98.style.position="fixed";_98.style.left="-65536px";_98.style.top="0";_98.style.height="100%";_98.style.width="1px";_98.style.zIndex="-32";document.documentElement.appendChild(_98);function updateZoom(){if(!_98){return}var _99=window.innerHeight/_98.offsetHeight;var _9a=Math.round(_99*100)%10;if(_9a>5){_99=Math.round(_99*100)+10-_9a}else{_99=Math.round(_99*100)-_9a}_97=isNaN(_99)?100:_99;_66.synchronizer.unblock();document.documentElement.removeChild(_98);_98=null}_66.synchronizer.block();setTimeout(updateZoom,54)}};this.hacks=_66;var _9b={kwargs:[],replaceAll:function(){for(var i=0;i<this.kwargs.length;i++){_3b.replace(this.kwargs[i])}this.kwargs=[]}};var _96={kwargs:[],replaceAll:_9b.replaceAll};function isValidDomain(){if(_3b.domains.length==0){return true}var _9d="";try{_9d=document.domain}catch(e){}if(_3b.fromLocal&&sIFR.domains[0]!="localhost"){sIFR.domains.unshift("localhost")}for(var i=0;i<_3b.domains.length;i++){var _9f=_3b.domains[i];if(_9f=="*"||_9f==_9d){return true}var _a0=_9f.lastIndexOf("*");if(_a0>-1){_9f=_9f.substr(_a0+1);var _a1=_9d.lastIndexOf(_9f);if(_a1>-1&&(_a1+_9f.length)==_9d.length){return true}}}return false}this.activate=function(){if(!ua.supported||!this.isEnabled||this.isActive||!isValidDomain()){return}if(arguments.length>0){this.prefetch.apply(this,arguments)}this.isActive=true;if(this.hideElements){this.setFlashClass()}if(ua.ieWin&&_66.fragmentIdentifier.fix&&window.location.hash!=""){_66.fragmentIdentifier.cache()}else{_66.fragmentIdentifier.fix=false}if(!this.registerEvents){return}function handler(evt){_3b.initialize();if(evt&&evt.type=="load"){if(document.removeEventListener){document.removeEventListener("DOMContentLoaded",handler,false)}if(window.removeEventListener){window.removeEventListener("load",handler,false)}}}if(window.addEventListener){if(_3b.useDomContentLoaded&&ua.gecko){document.addEventListener("DOMContentLoaded",handler,false)}window.addEventListener("load",handler,false)}else{if(ua.ieWin){if(_3b.useDomContentLoaded){document.write("<scr"+"ipt id=__sifr_ie_onload defer src=//:></script>");document.getElementById("__sifr_ie_onload").onreadystatechange=function(){if(this.readyState=="complete"){handler();this.removeNode()}}}window.attachEvent("onload",handler)}}};this.setFlashClass=function(){if(this.hasFlashClassSet){return}dom.addClass(_3c,dom.getBody()||document.documentElement);this.hasFlashClassSet=true};this.removeFlashClass=function(){if(!this.hasFlashClassSet){return}dom.removeClass(_3c,dom.getBody());dom.removeClass(_3c,document.documentElement);this.hasFlashClassSet=false};this.initialize=function(){if(_4c||!this.isActive||!this.isEnabled){return}_4c=true;_9b.replaceAll();clearPrefetch()};function getSource(src){if(typeof (src)!="string"){if(src.src){src=src.src}if(typeof (src)!="string"){var _a4=[];for(var _a5 in src){if(src[_a5]!=Object.prototype[_a5]){_a4.push(_a5)}}_a4.sort().reverse();var _a6="";var i=-1;while(!_a6&&++i<_a4.length){if(parseFloat(_a4[i])<=ua.flashVersion){_a6=src[_a4[i]]}}src=_a6}}if(!src&&_3b.debugMode){throw new Error("sIFR: Could not determine appropriate source")}if(ua.ie&&src.charAt(0)=="/"){src=window.location.toString().replace(/([^:]+)(:\/?\/?)([^\/]+).*/,"$1$2$3")+src}return src}this.prefetch=function(){if(!ua.requiresPrefetch||!ua.supported||!this.isEnabled||!isValidDomain()){return}if(this.setPrefetchCookie&&new RegExp(";?"+_47+"=true;?").test(document.cookie)){return}try{_4b=true;if(ua.ieWin){prefetchIexplore(arguments)}else{prefetchLight(arguments)}if(this.setPrefetchCookie){document.cookie=_47+"=true;path="+this.cookiePath}}catch(e){if(_3b.debugMode){throw e}}};function prefetchIexplore(_a8){for(var i=0;i<_a8.length;i++){document.write("<script defer type=\"sifr/prefetch\" src=\""+getSource(_a8[i])+"\"></script>")}}function prefetchLight(_aa){for(var i=0;i<_aa.length;i++){new Image().src=getSource(_aa[i])}}function clearPrefetch(){if(!ua.ieWin||!_4b){return}try{var _ac=document.getElementsByTagName("script");for(var i=_ac.length-1;i>=0;i--){var _ae=_ac[i];if(_ae.type=="sifr/prefetch"){_ae.parentNode.removeChild(_ae)}}}catch(e){}}function getRatio(_af,_b0){for(var i=0;i<_b0.length;i+=2){if(_af<=_b0[i]){return _b0[i+1]}}return _b0[_b0.length-1]}function getFilters(obj){var _b3=[];for(var _b4 in obj){if(obj[_b4]==Object.prototype[_b4]){continue}var _b5=obj[_b4];_b4=[_b4.replace(/filter/i,"")+"Filter"];for(var _b6 in _b5){if(_b5[_b6]==Object.prototype[_b6]){continue}_b4.push(_b6+":"+escape(_72.toJson(_72.toHexString(_b5[_b6]))))}_b3.push(_b4.join(","))}return _b3.join(";")}function calculate(_b7){var _b8,_b9;if(!ua.ie){_b8=dom.getStyleAsInt(_b7,"lineHeight");_b9=Math.floor(dom.getStyleAsInt(_b7,"height")/_b8)}else{if(ua.ie){var _ba=_b7.innerHTML;_b7.style.visibility="visible";_b7.style.overflow="visible";_b7.style.position="static";_b7.style.zoom="normal";_b7.style.writingMode="lr-tb";_b7.style.width=_b7.style.height="auto";_b7.style.maxWidth=_b7.style.maxHeight=_b7.style.styleFloat="none";var _bb=_b7;var _bc=_b7.currentStyle.hasLayout;if(_bc){dom.setInnerHtml(_b7,"<div class=\""+_43+"\">X<br />X<br />X</div>");_bb=_b7.firstChild}else{dom.setInnerHtml(_b7,"X<br />X<br />X")}var _bd=_bb.getClientRects();_b8=_bd[1].bottom-_bd[1].top;_b8=Math.ceil(_b8*0.8);if(_bc){dom.setInnerHtml(_b7,"<div class=\""+_43+"\">"+_ba+"</div>");_bb=_b7.firstChild}else{dom.setInnerHtml(_b7,_ba)}_bd=_bb.getClientRects();_b9=_bd.length;if(_bc){dom.setInnerHtml(_b7,_ba)}_b7.style.visibility=_b7.style.width=_b7.style.height=_b7.style.maxWidth=_b7.style.maxHeight=_b7.style.overflow=_b7.style.styleFloat=_b7.style.position=_b7.style.zoom=_b7.style.writingMode=""}}return {lineHeight:_b8,lines:_b9}}this.replace=function(_be,_bf){if(!ua.supported){return}if(_bf){for(var _c0 in _be){if(typeof (_bf[_c0])=="undefined"){_bf[_c0]=_be[_c0]}}_be=_bf}if(!_4c){return _9b.kwargs.push(_be)}if(_66.synchronizer.isBlocked){return _96.kwargs.push(_be)}var _c1=_be.elements;if(!_c1&&parseSelector){_c1=parseSelector(_be.selector)}if(_c1.length==0){return}this.setFlashClass();var src=getSource(_be.src);var css=_72.convertCssArg(_be.css);var _c4=getFilters(_be.filters);var _c5=(_be.forceClear==null)?_3b.forceClear:_be.forceClear;var _c6=(_be.fitExactly==null)?_3b.fitExactly:_be.fitExactly;var _c7=_c6||(_be.forceWidth==null?_3b.forceWidth:_be.forceWidth);var _c8=parseInt(_72.extractFromCss(css,".sIFR-root","leading"))||0;var _c9=_72.extractFromCss(css,".sIFR-root","font-size",true)||0;var _ca=_72.extractFromCss(css,".sIFR-root","background-color",true)||"#FFFFFF";var _cb=_72.extractFromCss(css,".sIFR-root","kerning",true)||"";var _cc=_be.gridFitType||_72.extractFromCss(css,".sIFR-root","text-align")=="right"?"subpixel":"pixel";var _cd=_3b.forceTextTransform?_72.extractFromCss(css,".sIFR-root","text-transform",true)||"none":"none";var _ce=_72.extractFromCss(css,".sIFR-root","opacity",true)||"100";var _cf=_be.pixelFont||false;var _d0=_be.ratios||_48;if(parseInt(_c9).toString()!=_c9&&_c9.indexOf("px")==-1){_c9=0}else{_c9=parseInt(_c9)}if(parseFloat(_ce)<1){_ce=100*parseFloat(_ce)}var _d1=null;var _d2="";if(_c6){_72.extractFromCss(css,".sIFR-root","text-align",true)}if(!_be.modifyCss){_d2=_72.cssToString(css);_d1=_3b.fixHover&&_d2.indexOf("%3Ahover")>-1}var _d3=!ua.opera&&_3b.delayCss;var _d4=_be.wmode||"";if(!_d4){if(_be.transparent){_d4="transparent"}else{if(_be.opaque){_d4="opaque"}}}if(_d4=="transparent"){if(!ua.transparencySupport){_d4="opaque"}else{_ca="transparent"}}for(var i=0;i<_c1.length;i++){var _d6=_c1[i];if(!ua.verifiedKonqueror){if(dom.getComputedStyle(_d6,"lineHeight").match(/e\+08px/)){ua.supported=_3b.isEnabled=false;this.removeFlashClass();return}ua.verifiedKonqueror=true}if(dom.hasOneOfClassses([_3d,_3e,_40,_41],_d6)){continue}var _d7=_d6.offsetHeight;var _d8=_d6.offsetWidth;var _d9=dom.getComputedStyle(_d6,"display");if(!_d7||!_d8||_d9==null||_d9=="none"){continue}if(_c5&&ua.gecko){_d6.style.clear="both"}var _da=null;if(_3b.fixWrap&&ua.ie&&_d9=="block"){_da=_d6.innerHTML;dom.setInnerHtml(_d6,"X")}_d8=dom.getStyleAsInt(_d6,"width",ua.ie);if(_d8==0){var _db=dom.getStyleAsInt(_d6,"paddingRight",true);var _dc=dom.getStyleAsInt(_d6,"paddingLeft",true);var _dd=dom.getStyleAsInt(_d6,"borderRightWidth",true);var _de=dom.getStyleAsInt(_d6,"borderLeftWidth",true);_d8=_d6.offsetWidth-_dc-_db-_de-_dd}if(_da&&_3b.fixWrap&&ua.ie){dom.setInnerHtml(_d6,_da)}var _df,_e0;if(!_c9){var _e1=calculate(_d6);_df=Math.min(_45,Math.max(_44,_e1.lineHeight));if(_cf){_df=Math.max(8,8*Math.round(_df/8))}_e0=_e1.lines;if(isNaN(_e0)||!isFinite(_e0)||_e0==0){_e0=1}if(_e0>1&&_c8){_d7+=Math.round((_e0-1)*_c8)}}else{_df=_c9;_e0=1}_d7=Math.round(_e0*_df);if(_c5&&ua.gecko){_d6.style.clear=""}var _e2=dom.create("span");_e2.className=_41;var _e3=_d6.cloneNode(true);for(var j=0,l=_e3.childNodes.length;j<l;j++){_e2.appendChild(_e3.childNodes[j].cloneNode(true))}if(_be.modifyContent){_be.modifyContent(_e3,_be.selector)}if(_be.modifyCss){_d2=_be.modifyCss(css,_e3,_be.selector)}if(_d1==null){_d1=_3b.fixHover&&_d2.indexOf("%3Ahover")>-1}var _e6=handleContent(_e3,_cd);if(_be.modifyContentString){_e6=_be.modifyContentString(_e6,_be.selector)}if(_e6==""){continue}var _e7=["content="+_e6,"width="+_d8,"height="+_d7,"fitexactly="+(_c6?"true":""),"tunewidth="+(_be.tuneWidth||""),"tuneheight="+(_be.tuneHeight||""),"offsetleft="+(_be.offsetLeft||""),"offsettop="+(_be.offsetTop||""),"thickness="+(_be.thickness||""),"sharpness="+(_be.sharpness||""),"kerning="+_cb,"gridfittype="+_cc,"zoomsupport="+ua.zoomSupport,"flashfilters="+_c4,"opacity="+_ce,"blendmode="+(_be.blendMode||""),"size="+_df,"zoom="+dom.getZoom(),"css="+_d2,"selectable="+(_be.selectable==null?"true":_be.selectable),"lines="+_e0];var _e8=encodeURI(_e7.join("&amp;"));var _e9="sIFR_callback_"+_4a++;var _ea=new CallbackInfo(_e9,_e7,_be.onReplacement,_d1);window[_e9+"_DoFSCommand"]=(function(_eb){return function(_ec,arg){_eb.handle(_ec,arg)}})(_ea);_d7=Math.round(_e0*getRatio(_df,_d0)*_df)+_49;var _ee=_c7?_d8:"100%";var _ef;if(ua.ie){_ef=["<object classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" id=\"",_e9,"\" sifr=\"true\" width=\"",_ee,"\" height=\"",_d7,"\" class=\"",_3f,"\">","<param name=\"movie\" value=\"",src,"\"></param>","<param name=\"flashvars\" value=\"",_e8,"\"></param>","<param name=\"allowScriptAccess\" value=\"always\"></param>","<param name=\"quality\" value=\"best\"></param>","<param name=\"wmode\" value=\"",_d4,"\"></param>","<param name=\"bgcolor\" value=\"",_ca,"\"></param>","<param name=\"name\" value=\"",_e9,"\"></param>","</object>","<scr","ipt event=FSCommand(info,args) for=",_e9,">",_e9,"_DoFSCommand(info, args);","</","script>"].join("")}else{_ef=["<embed type=\"application/x-shockwave-flash\"",(_d3?" class=\""+_3f+"\"":"")," src=\"",src,"\" quality=\"best\" flashvars=\"",_e8,"\" width=\"",_ee,"\" height=\"",_d7,"\" wmode=\"",_d4,"\" bgcolor=\"",_ca,"\" name=\"",_e9,"\" id=\"",_e9,"\" allowScriptAccess=\"always\" sifr=\"true\"></embed>"].join("")}dom.setInnerHtml(_d6,_ef);_ea.flashNode=_d6.firstChild;_ea.html=_ef;_3b.callbacks.push(_ea);if(_be.selector){if(!_3b.callbacks[_be.selector]){_3b.callbacks[_be.selector]=[_ea]}else{_3b.callbacks[_be.selector].push(_ea)}}_d6.appendChild(_e2);dom.addClass(_d3?_3e:_3d,_d6);_ea.setupFixHover()}_66.fragmentIdentifier.restore()};this.getCallbackByFlashElement=function(_f0){for(var i=0;i<_3b.callbacks.length;i++){if(_3b.callbacks[i].id==_f0.getAttribute("id")){return _3b.callbacks[i]}}};function handleContent(_f2,_f3){var _f4=[],_f5=[];var _f6=_f2.childNodes;var i=0;while(i<_f6.length){var _f8=_f6[i];if(_f8.nodeType==3){var _f9=_72.normalize(_f8.nodeValue);_f9=_72.textTransform(_f3,_f9);_f5.push(_f9.replace(/\%/g,"%25").replace(/\&/g,"%26").replace(/\,/g,"%2C").replace(/\+/g,"%2B"))}if(_f8.nodeType==1){var _fa=[];var _fb=_f8.nodeName.toLowerCase();var _fc=_f8.className||"";if(/\s+/.test(_fc)){if(_fc.indexOf(_42)>-1){_fc=_fc.match("(\\s|^)"+_42+"-([^\\s$]*)(\\s|$)")[2]}else{_fc=_fc.match(/^([^\s]+)/)[1]}}if(_fc!=""){_fa.push("class=\""+_fc+"\"")}if(_fb=="a"){var _fd=_f8.getAttribute("href")||"";var _fe=_f8.getAttribute("target")||"";_fa.push("href=\""+_fd+"\"","target=\""+_fe+"\"")}_f5.push("<"+_fb+(_fa.length>0?" ":"")+escape(_fa.join(" "))+">");if(_f8.hasChildNodes()){_f4.push(i);i=0;_f6=_f8.childNodes;continue}else{if(!/^(br|img)$/i.test(_f8.nodeName)){_f5.push("</",_f8.nodeName.toLowerCase(),">")}}}if(_f4.length>0&&!_f8.nextSibling){do{i=_f4.pop();_f6=_f8.parentNode.parentNode.childNodes;_f8=_f6[i];if(_f8){_f5.push("</",_f8.nodeName.toLowerCase(),">")}}while(i==_f6.length-1&&_f4.length>0)}i++}return _f5.join("").replace(/\n|\r/g,"")}function CallbackInfo(id,vars,_101,_102){this.id=id;this.vars=vars;this._replacementHandler=_101;this._firedReplacementEvent=!(this._replacementHandler!=null);this._fixHover=_102;this._setClasses=!_3b.delayCss;this.html="";this._pings=0}CallbackInfo.prototype.getFlashElement=function(){return document.getElementById(this.id)};CallbackInfo.prototype.handle=function(info,arg){if(/(FSCommand\:)?resize/.test(info)){var _105=this.getFlashElement();var $=arg.split(/\:|,/);_105.setAttribute($[0],$[1]);if($.length>2){_105.setAttribute($[2],$[3])}if(!this._setClasses){if(!ua.ie&&!ua.opera){dom.addClass(_3f,_105)}dom.removeClass(_3e,_105.parentNode);dom.addClass(_3d,_105.parentNode);this._setClasses=true}if(ua.khtml){var _107=_105.offsetHeight}if(!this._firedReplacementEvent){this._replacementHandler(this);this._firedReplacementEvent=true}}else{if(/(FSCommand\:)?resetmovie/.test(info)){this.resetMovie()}else{if(/(FSCommand\:)?ping/.test(info)){if(this._pings>0){this.setupFixHover()}this._pings++}else{if(this.debugHandler&&/(FSCommand\:)?debug/.test(info)){this.debugHandler(info,arg)}}}}};CallbackInfo.prototype.call=function(type,_109){var _10a=this.getFlashElement();if(!_10a){return}_10a.SetVariable("callbackType",type);_10a.SetVariable("callbackValue",_109);_10a.SetVariable("callbackTrigger",true)};CallbackInfo.prototype.replaceText=function(_10b){_10b=escape(_10b);this.call("replacetext",_10b);this.vars[0]="content="+_10b;this.html=this.html.replace(/(flashvars(=|\"\svalue=)\")[^\"]+/,"$1"+encodeURI(this.vars.join("&amp;")))};CallbackInfo.prototype.resetMovie=function(){var _10c=this.getFlashElement();var node=_10c.parentNode;node.replaceChild(dom.nodeFromHtml(this.html),_10c);this.setupFixHover()};CallbackInfo.prototype.setupFixHover=function(){var _10e=this.getFlashElement();if(!this._fixHover||!_10e){return}var node=_10e.parentNode;if(node.addEventListener){node.addEventListener("mouseout",_72.bind(this,"fixHover"),false)}else{if(node.attachEvent){node.attachEvent("onmouseout",_72.bind(this,"fixHover"))}}};CallbackInfo.prototype.fixHover=function(){this.call("resettext")}};
\ No newline at end of file
diff --git a/library/simplepie/demo/for_the_demo/simplepie.css b/library/simplepie/demo/for_the_demo/simplepie.css
deleted file mode 100644 (file)
index 3753cb9..0000000
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
-Theme Name: SimplePie
-Theme URI: http://simplepie.org
-Description: A simple, yet beautiful theme inspired by several cleanly designed websites.
-Version: 1.4
-Author: Ryan Parman
-Author URI: http://skyzyx.com
-Updated: 21 June 2007
-*/
-
-
-/*********************************************
-HYPERLINK STYLES
-*********************************************/
-a {
-       color:#369;
-       text-decoration:underline;
-       padding:0 1px;
-}
-
-a:hover {
-       color:#fff !important;
-       background-color:#333;
-       text-decoration:none;
-       padding:0 1px;
-}
-
-a.nohover {
-       text-decoration:none;
-       border:none;
-}
-
-a.nohover:hover {
-       background-color:transparent;
-       border:none;
-}
-
-a.namelink {
-       padding:0;
-       margin:0;
-       overflow:hidden;
-       height:1px;
-}
-
-h4 a,
-.sample_feeds a {
-       color:#000;
-}
-
-
-/*********************************************
-GENERAL STYLES
-*********************************************/
-body {
-       /*font:12px/18px Verdana, sans-serif;*/
-       font:14px/1.5em "Lucida Grande", Tahoma, sans-serif;
-       letter-spacing:0px;
-       color:#333;
-       background-color:#fff;
-       margin:0;
-       padding:0;
-}
-
-div#site {
-       width:600px;
-       margin:50px auto 0 auto;
-}
-
-h1#logo {
-       margin:0;
-       padding:0;
-       text-align:center;
-}
-
-h1#logo a, 
-h1#logo a:hover {
-       background-color:transparent;
-       text-decoration:none;
-       padding:0;
-}
-
-h2.image {
-       margin:0;
-       padding:0;
-       text-align:center;
-}
-
-h3 {
-       margin:20px 0 0 0;
-       padding:0;
-       font-size:1.5em;
-}
-
-h4 {
-       margin:20px 0 0 0;
-       padding:0;
-       font-size:1.2em;
-       letter-spacing:-1px;
-}
-
-h5 {
-       margin:10px 0 0 0;
-       padding:0;
-       font-size:1em;
-       font-weight:bold;
-}
-
-em {
-       font-style:normal;
-       background-color:#ffc;
-}
-
-p {
-       margin:0;
-       padding:5px 0;
-}
-
-ul, ol {
-       margin:10px 0 10px 20px;
-       padding:0 0 0 15px;
-}
-
-ul li, ol li {
-       margin:0 0 7px 0;
-       padding:0 0 0 3px;
-}
-
-form {
-       margin:0;
-       padding:0;
-}
-
-code {
-       font-size:1em;
-       background-color:#f3f3ff;
-       color:#000;
-}
-
-div#site pre {
-       background-color:#f3f3ff;
-       color:#000080;
-       border:1px dotted #000080;
-       overflow:auto;
-       padding:3px 5px;
-}
-
-blockquote {
-       font-size:1em;
-       color:#666;
-       border-left:4px solid #666;
-       margin:10px 0 10px 30px;
-       padding:0 5px 0 10px;
-       background:#f3f3f3 url(background_blockquote.png) repeat top left;
-}
-
-input, select, textarea {
-       font-size:12px;
-       line-height:1.2em;
-       padding:2px;
-}
-
-input[type=text], select, textarea {
-       background-color:#e9f5ff;
-       border:1px solid #333;
-}
-
-input[type=text]:focus, select:focus, textarea:focus {
-       background-color:#ffe;
-}
-
-.clearLeft {clear:left;}
-.clearRight {clear:right;}
-.clearBoth {clear:both;}
-.hide {display:none;}
-
-
-/*********************************************
-NAVIGATION STYLES
-*********************************************/
-div#header {
-       background:#fff url(top_gradient.gif) repeat-x top left;
-       margin:0;
-       padding:0;
-}
-
-div#header form {
-       margin:0;
-       padding:0;
-}
-
-div#header div#headerInner {
-       margin:0;
-       padding:0;
-}
-
-div#header div#headerInner div#logoContainer {}
-
-div#header div#headerInner div#logoContainerInner {
-       width:550px;
-       margin:0 auto;
-       padding:20px;
-}
-
-div#header div#headerInner div#logoContainer div#logo {
-       float:left;
-       width:200px;
-}
-
-div#header div#headerInner div#logoContainer div#logo a,
-div#header div#headerInner div#logoContainer div#logo a:hover {
-       border:none;
-       background:none;
-}
-
-div#header div#headerInner div#logoContainer div#feed {
-       float:right;
-       width:300px;
-       text-align:right;
-       padding:10px 0 0 0;
-}
-
-div#header div#headerInner div#logoContainer div#feed input.text {
-       width:60%;
-}
-
-div#header div#headerInner div#menu {
-       background:#eee url(background_menuitem_shadow.gif) repeat-x top left;
-       border-top:2px solid #ccc;
-       border-bottom:1px solid #ddd;
-       text-align:center;
-}
-
-div#header div#headerInner div#menu table {
-       width:auto;
-       margin:0 auto;
-}
-
-div#header div#headerInner div#menu ul {
-       display:block;
-       width:100%;
-       margin:0 auto;
-       padding:0;
-       font-size:12px;
-}
-
-div#header div#headerInner div#menu ul li {
-       display:block;
-       float:left;
-}
-
-div#header div#headerInner div#menu ul li a {
-       display:block;
-       margin:-2px 0 0 0;
-       padding:5px 7px 8px 7px;
-       text-decoration:none;
-       color:#666 !important;
-       background-color:transparent;
-}
-
-div#header div#headerInner div#menu ul li a:hover {
-       display:block;
-       margin:-2px 0 0 0;
-       padding:5px 7px 8px 7px;
-       text-decoration:none;
-       color:#666;
-       background:#fff url(background_menuitem_off.gif) no-repeat bottom right;
-}
-
-body#bodydemo div#header div#headerInner div#menu ul li#demo a {
-       display:block;
-       margin:-2px 0 0 0;
-       padding:5px 7px 8px 7px;
-       text-decoration:none;
-       color:#333;
-       font-weight:bold;
-       background:#fff url(background_menuitem.gif) no-repeat bottom right;
-}
-
-
-/*********************************************
-CONTENT STYLES
-*********************************************/
-div.chunk {
-       margin:20px 0 0 0;
-       padding:0 0 10px 0;
-       border-bottom:1px solid #ccc;
-}
-
-div.topchunk {
-       margin:0 !important;
-}
-
-.footnote,
-.footnote a {
-       font-size:12px;
-       line-height:1.3em;
-       color:#aaa;
-}
-
-.footnote em {
-       background-color:transparent;
-       font-style:italic;
-}
-
-.footnote code {
-       background-color:transparent;
-       font:11px/14px monospace;
-       color:#aaa;
-}
-
-p.subscribe {
-       background-color:#f3f3f3;
-       font-size:12px;
-       text-align:center;
-}
-
-p.highlight {
-       background-color:#ffc;
-       font-size:12px;
-       text-align:center;
-}
-
-p.sample_feeds {
-       font-size:12px;
-       line-height:1.2em;
-}
-
-div.sp_errors {
-       background-color:#eee;
-       padding:5px;
-       text-align:center;
-       font-size:12px;
-}
-
-.noborder {
-       border:none !important;
-}
-
-img.favicon {
-       margin:0 4px -2px 0;
-       width:16px;
-       height:16px;
-}
-
-p.favicons a,
-p.favicons a:hover {
-       border:none;
-       background-color:transparent;
-}
-
-p.favicons img {
-       border:none;
-}
-
-
-/*********************************************
-DEMO STYLES
-*********************************************/
-div#sp_input {
-       background-color:#ffc;
-       border:2px solid #f90;
-       padding:5px;
-       text-align:center;
-}
-
-div#sp_input input.text {
-       border:1px solid #999;
-       background:#e9f5ff url(feed.png) no-repeat 4px 50%;
-       width:75%;
-       padding:2px 2px 2px 28px;
-       font:18px/22px "Lucida Grande", Verdana, sans-serif;
-       font-weight:bold;
-       letter-spacing:-1px;
-}
-
-form#sp_form {
-       margin:15px 0;
-}
-
-div.focus {
-       margin:0;
-       padding:10px 20px;
-       background-color:#efe;
-}
-
-p.sample_feeds {
-       text-align:justify;
-}
-
-
-/*********************************************
-SIFR STYLES
-*********************************************/
-.sIFR-active h3.header {
-       visibility:hidden;
-       line-height:1em;
-}
diff --git a/library/simplepie/demo/for_the_demo/sleight.js b/library/simplepie/demo/for_the_demo/sleight.js
deleted file mode 100644 (file)
index 4b5058e..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/**********************************************************
-Sleight
-(c) 2001, Aaron Boodman
-http://www.youngpup.net
-**********************************************************/
-
-if (navigator.platform == "Win32" && navigator.appName == "Microsoft Internet Explorer" && window.attachEvent)
-{
-       document.writeln('<style type="text/css">img { visibility:hidden; } </style>');
-       window.attachEvent("onload", fnLoadPngs);
-}
-
-function fnLoadPngs()
-{
-       var rslt = navigator.appVersion.match(/MSIE (\d+\.\d+)/, '');
-       var itsAllGood = (rslt != null && Number(rslt[1]) >= 5.5);
-
-       for (var i = document.images.length - 1, img = null; (img = document.images[i]); i--)
-       {
-               if (itsAllGood && img.src.match(/\.png$/i) != null)
-               {
-                       var src = img.src;
-                       var div = document.createElement("DIV");
-                       div.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizing='scale')"
-                       div.style.width = img.width + "px";
-                       div.style.height = img.height + "px";
-                       img.replaceNode(div);
-               }
-               img.style.visibility = "visible";
-       }
-}
diff --git a/library/simplepie/demo/for_the_demo/source_files/place_audio_fireworksfile.png b/library/simplepie/demo/for_the_demo/source_files/place_audio_fireworksfile.png
deleted file mode 100644 (file)
index 2bfd87d..0000000
Binary files a/library/simplepie/demo/for_the_demo/source_files/place_audio_fireworksfile.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/source_files/place_video_fireworksfile.png b/library/simplepie/demo/for_the_demo/source_files/place_video_fireworksfile.png
deleted file mode 100644 (file)
index d062976..0000000
Binary files a/library/simplepie/demo/for_the_demo/source_files/place_video_fireworksfile.png and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/SifrStyleSheet.as b/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/SifrStyleSheet.as
deleted file mode 100644 (file)
index 6a98ca5..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*=:project
-    scalable Inman Flash Replacement (sIFR) version 3.
-
-  =:file
-    Copyright: 2006 Mark Wubben.
-    Author: Mark Wubben, <http://novemberborn.net/>
-
-  =:history
-    * IFR: Shaun Inman
-    * sIFR 1: Mike Davidson, Shaun Inman and Tomas Jogin
-    * sIFR 2: Mike Davidson, Shaun Inman, Tomas Jogin and Mark Wubben
-
-  =:license
-    This software is licensed and provided under the CC-GNU LGPL.
-    See <http://creativecommons.org/licenses/LGPL/2.1/>    
-*/
-
-import TextField.StyleSheet;
-
-class SifrStyleSheet extends TextField.StyleSheet {
-  public var fontSize;
-  public var latestLeading = 0;
-  
-  public function parseCSS(cssText:String) {
-    var native = new TextField.StyleSheet();
-    var parsed = native.parseCSS(cssText);
-    
-    if(!parsed) return false;
-    
-    var selectors = native.getStyleNames();
-    for(var i = selectors.length - 1; i >= 0; i--) {
-      var selector = selectors[i];
-      var nativeStyle = native.getStyle(selector);
-      var style = this.getStyle(selector) || nativeStyle;
-      if(style != nativeStyle) {
-        for(var property in nativeStyle) style[property] = nativeStyle[property];
-      }
-      this.setStyle(selector, style);
-    }
-    
-    return true;
-  }
-  
-  // Apply leading to the textFormat. Much thanks to <http://www.blog.lessrain.com/?p=98>.
-  private function applyLeading(format, leading) {
-    this.latestLeading = leading;
-    
-    if(leading >= 0) {
-        format.leading = leading;
-        return format;
-    }
-
-    // Workaround for negative leading, which is ignored otherwise.
-    var newFormat = new TextFormat(null, null, null, null, null, null, null, null, null, null, null, null, leading);
-    for(var property in format) if(property != 'leading') newFormat[property] = format[property];
-
-    return newFormat;
-  }
-  
-  public function transform(style) {
-    var format = super.transform(style);
-    if(style.leading) format = applyLeading(format, style.leading);
-    if(style.letterSpacing) format.letterSpacing = style.letterSpacing;
-    // Support font sizes relative to the size of .sIFR-root.
-    if(this.fontSize && style.fontSize && style.fontSize.indexOf('%')) {
-      format.size = this.fontSize * parseInt(style.fontSize) / 100;
-    }
-    format.kerning = _root.kerning == 'true' || !(_root.kerning == 'false') || sIFR.defaultKerning;
-    return format;
-  }
-}
\ No newline at end of file
diff --git a/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/_README_.txt b/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/_README_.txt
deleted file mode 100644 (file)
index 2b9d32d..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-This is a pre-release nightly of sIFR 3 (r245 to be exact).  We (the SimplePie team) will be updating the 
-sIFR code and font files from time to time as new releases of sIFR 3 are made available.
-
-In this folder you'll find a few Flash 8 files.  The only one of you might want to mess with is sifr.fla.
-  * Open it up
-  * Double-click the rectangle in the middle
-  * Select all
-  * Change the font
-
-More information about sIFR 3 can be found here:
-  * http://dev.novemberborn.net/sifr3/
-  * http://wiki.novemberborn.net/sifr3/
\ No newline at end of file
diff --git a/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/options.as b/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/options.as
deleted file mode 100644 (file)
index 4d37195..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-// MTASC only parses as-files with class definitions, so here goes...
-class Options {
-       public static function apply() {
-               sIFR.fromLocal = true;
-               sIFR.domains   = ['*'];
-               
-               // Parsing `p.foo` might not work, see: <http://livedocs.macromedia.com/flash/mx2004/main_7_2/wwhelp/wwhimpl/common/html/wwhelp.htm?context=Flash_MX_2004&file=00001766.html>
-               // Appearantly you have to use hex color codes as well, names are not supported!
-
-               sIFR.styles.parseCSS('.foo { text-decoration: underline; }'); 
-       }
-}
diff --git a/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/sIFR.as b/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/sIFR.as
deleted file mode 100644 (file)
index 4902e00..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-/*=:project
-    scalable Inman Flash Replacement (sIFR) version 3.
-
-  =:file
-    Copyright: 2006 Mark Wubben.
-    Author: Mark Wubben, <http://novemberborn.net/>
-
-  =:history
-    * IFR: Shaun Inman
-    * sIFR 1: Mike Davidson, Shaun Inman and Tomas Jogin
-    * sIFR 2: Mike Davidson, Shaun Inman, Tomas Jogin and Mark Wubben
-
-  =:license
-    This software is licensed and provided under the CC-GNU LGPL.
-    See <http://creativecommons.org/licenses/LGPL/2.1/>    
-*/
-
-import SifrStyleSheet;
-
-class sIFR {
-  public static var DEFAULT_TEXT                 = 'Rendered with sIFR 3, revision 245';
-  public static var CSS_ROOT_CLASS               = 'sIFR-root';
-  public static var DEFAULT_WIDTH                = 300;
-  public static var DEFAULT_HEIGHT               = 100;
-  public static var DEFAULT_ANTI_ALIAS_TYPE      = 'advanced';
-  public static var MARGIN_LEFT                  = -3;
-  public static var PADDING_BOTTOM               = 5; // Extra padding to make sure the movie is high enough in most cases.
-  public static var LEADING_REMAINDER            = 2; // Flash uses the specified leading minus 2 as the applied leading.
-
-  public static var MAX_FONT_SIZE                = 126;
-  public static var ALIASING_MAX_FONT_SIZE       = 48;
-  
-  //= Holds CSS properties and other rendering properties for the Flash movie.
-  //  *Don't overwrite!*
-  public static var styles:SifrStyleSheet        = new SifrStyleSheet();
-  //= Allow sIFR to be run from localhost
-  public static var fromLocal:Boolean            = true;
-  //= Array containing domains for which sIFR may render text. Used to prevent
-  //  hotlinking. Use `*` to allow all domains.
-  public static var domains:Array                = [];
-  //= Whether kerning is enabled by default. This can be overriden from the client side.
-  //  See also <http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002811.html>.
-  public static var defaultKerning:Boolean       = true;
-  //= Default value which can be overriden from the client side.
-  //  See also <http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002788.html>.
-  public static var defaultSharpness:Number      = 0;
-  //= Default value which can be overriden from the client side.
-  //  See also <http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002787.html>.
-  public static var defaultThickness:Number      = 0;
-  //= Default value which can be overriden from the client side.
-  //  See also <http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002732.html>.
-  public static var defaultOpacity:Number        = -1; // Use client settings
-  //= Default value which can be overriden from the client side.
-  //  See also <http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002788.html>.
-  public static var defaultBlendMode:Number      = -1; // Use cliest settings
-  //= Overrides the grid fit type as defined on the client side.
-  //  See also <http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002444.html>.
-  public static var enforcedGridFitType:String   = null;
-  //= If `true` sIFR won't override the anti aliasing set in the Flash IDE when exporting.
-  //  Thickness and sharpness won't be affected either.
-  public static var preserveAntiAlias:Boolean    = false;
-  //= If `true` sIFR will disable anti-aliasing if the font size is larger than `ALIASING_MAX_FONT_SIZE`.
-  //  This setting is *independent* from `preserveAntiAlias`.
-  public static var conditionalAntiAlias:Boolean = true;
-  //= Sets the anti alias type. By default it's `DEFAULT_ANTI_ALIAS_TYPE`.
-  //  See also <http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002733.html>.
-  public static var antiAliasType:String         = null;
-  //= Flash filters can be added to this array and will be applied to the text field.
-  public static var filters:Array                = [];
-  //= A mapping from the names of the filters to their actual objecs, used when transforming
-  //  filters defined on the client. You can add additional filters here so they'll be supported
-  //  when defined on the client.
-  public static var filterMap:Object             = {
-    DisplacementMapFilter : flash.filters.DisplacementMapFilter,
-    ColorMatrixFilter     : flash.filters.ColorMatrixFilter,
-    ConvolutionFilter     : flash.filters.ConvolutionFilter,
-    GradientBevelFilter   : flash.filters.GradientBevelFilter,
-    GradientGlowFilter    : flash.filters.GradientGlowFilter,
-    BevelFilter           : flash.filters.BevelFilter,
-    GlowFilter            : flash.filters.GlowFilter,
-    BlurFilter            : flash.filters.BlurFilter,
-    DropShadowFilter      : flash.filters.DropShadowFilter
-  };
-
-  private static var instance;
-  
-  private var textField;
-  private var content;
-  private var realHeight;
-  private var originalHeight;
-  private var currentHeight;
-  private var fontSize;
-  private var tuneWidth;
-  private var tuneHeight;
-  
-
-  
-  //= Sets the default styles for `sIFR.styles`. This method is called
-  //  directly in `sifr.fla`, before options are applied.
-  public static function setDefaultStyles() {
-    sIFR.styles.parseCSS([
-      '.', CSS_ROOT_CLASS, ' { color: #000000; }',
-      'strong { display: inline; font-weight: bold; } ',
-      'em { display: inline; font-style: italic; }',
-      'a { color: #0000FF; text-decoration: underline; }',
-      'a:hover { color: #0000FF; text-decoration: none; }'
-    ].join(''));
-  }
-  
-  //= Validates the domain sIFR is being used on.
-  //  Returns `true` if the domain is valid, `false` otherwise.  
-  public static function checkDomain():Boolean {
-    if(sIFR.domains.length == 0) return true;
-
-    var domain = (new LocalConnection()).domain();
-    if(sIFR.fromLocal) sIFR.domains.push('localhost');
-    
-    for(var i = 0; i < sIFR.domains.length; i++) {
-      var match = sIFR.domains[i];
-      if(match == '*' || match == domain) return true;
-
-      var wildcard = match.lastIndexOf('*');
-      if(wildcard > -1) {
-        match = match.substr(wildcard + 1);
-        var matchPosition = domain.lastIndexOf(match);
-        if(matchPosition > -1 && (matchPosition + match.length) == domain.length) return true;
-      }
-    }
-    
-    return false;
-  }
-  
-  //= Runs sIFR. Called automatically.
-  public static function run() {
-    var holder  = _root.holder;
-    var content = checkDomain() ? unescape(_root.content) : DEFAULT_TEXT
-    if(content == 'undefined' || content == '') {
-      content = DEFAULT_TEXT;
-      fscommand('resetmovie', '');
-    } else fscommand('ping', '');
-
-    // Sets stage parameters
-    Stage.scaleMode = 'noscale';
-    Stage.align = 'TL';
-    Stage.showMenu = false;
-    
-    // Other parameters
-    var opacity = parseInt(_root.opacity);
-    if(!isNaN(opacity)) holder._alpha = sIFR.defaultOpacity == -1 ? opacity : sIFR.defaultOpacity;
-    else holder._alpha = 100;
-    _root.blendMode = sIFR.defaultBlendMode == -1 ? _root.blendmode : sIFR.defaultBlendMode;
-
-    sIFR.instance = new sIFR(holder.txtF, content);
-    // This should ignore resizes from the callback. Disabled for now.
-/*    if(_root.zoomsupport == 'true') Stage.addListener({onResize: function() { sIFR.instance.scale() }});*/
-
-    // Setup callbacks
-    _root.watch('callbackTrigger', function() { 
-      sIFR.callback();
-      return false;
-    });
-  }
-  
-  private static function eval(str) {
-    var as;
-    
-    if(str.charAt(0) == '{') { // Ah, we need to create an object
-      as = {};
-      str = str.substring(1, str.length - 1);
-      var $ = str.split(',');
-      for(var i = 0; i < $.length; i++) {
-        var $1 = $[i].split(':');
-        as[$1[0]] = sIFR.eval($1[1]);
-      }
-    } else if(str.charAt(0) == '"') { // String
-      as = str.substring(1, str.length - 1);
-    } else if(str == 'true' || str == 'false') { // Boolean
-      as = str == 'true';
-    } else { // Float
-      as = parseFloat(str);
-    }
-    
-    return as;
-  }
-  
-  private function applyFilters() {
-    var $filters = this.textField.filters;
-    $filters = $filters.concat(sIFR.filters);
-    
-    var $ = _root.flashfilters.split(';'); // name,prop:value,...;
-    for(var i = 0; i < $.length; i++) {
-      var $1 = $[i].split(',');
-      
-      var newFilter = new sIFR.filterMap[$1[0]]();
-      for(var j = 1; j < $1.length; j++) {
-        var $2 = $1[j].split(':');
-        newFilter[$2[0]] = sIFR.eval(unescape($2[1]));
-      }
-      
-      $filters.push(newFilter);
-    }
-
-    this.textField.filters = $filters;
-  }
-  
-  private function sIFR(textField, content) {
-    this.textField = textField;
-    this.content   = content;
-
-    var offsetLeft = parseInt(_root.offsetleft);
-    textField._x = MARGIN_LEFT + (isNaN(offsetLeft) ? 0 : offsetLeft);
-    var offsetTop = parseInt(_root.offsettop);
-    if(!isNaN(offsetTop)) textField._y += offsetTop;
-    
-    tuneWidth = parseInt(_root.tunewidth);
-    if(isNaN(tuneWidth)) tuneWidth = 0;
-    tuneHeight = parseInt(_root.tuneheight);
-    if(isNaN(tuneHeight)) tuneHeight = 0;
-
-    textField._width = tuneWidth + (isNaN(parseInt(_root.width)) ? DEFAULT_WIDTH : parseInt(_root.width));
-    textField._height = tuneHeight + (isNaN(parseInt(_root.height)) ? DEFAULT_HEIGHT : parseInt(_root.height));
-    textField.wordWrap = true;
-    textField.selectable = _root.selectable == 'true';
-    textField.gridFitType = sIFR.enforcedGridFitType || _root.gridfittype;
-    this.applyFilters();
-
-    // Determine font-size and the number of lines
-    this.fontSize = parseInt(_root.size);
-    if(isNaN(this.fontSize)) this.fontSize = 26;
-    styles.fontSize = this.fontSize;
-    
-    if(!sIFR.preserveAntiAlias && (sIFR.conditionalAntiAlias && this.fontSize < ALIASING_MAX_FONT_SIZE
-    || !sIFR.conditionalAntiAlias)) {
-      textField.antiAliasType = sIFR.antiAliasType || DEFAULT_ANTI_ALIAS_TYPE;      
-    }
-
-    if(!sIFR.preserveAntiAlias || !isNaN(parseInt(_root.sharpness))) {
-      textField.sharpness = parseInt(_root.sharpness);
-    }
-    if(isNaN(textField.sharpness)) textField.sharpness = sIFR.defaultSharpness;
-
-    if(!sIFR.preserveAntiAlias || !isNaN(parseInt(_root.thickness))) {
-      textField.thickness = parseInt(_root.thickness);
-    }
-    if(isNaN(textField.thickness)) textField.thickness = sIFR.defaultThickness;
-    
-    // Set font-size and other styles
-    sIFR.styles.parseCSS(unescape(_root.css));
-    
-    var rootStyle = styles.getStyle('.sIFR-root') || {};
-    rootStyle.fontSize = this.fontSize; // won't go higher than 126!
-    styles.setStyle('.sIFR-root', rootStyle);
-    textField.styleSheet = styles;
-    
-    this.write(content);
-    this.repaint();
-  }
-  
-  private function repaint() {
-    var leadingFix = this.isSingleLine() ? sIFR.styles.latestLeading : 0;
-    if(leadingFix > 0) leadingFix -= LEADING_REMAINDER;
-
-    // Flash wants to scroll the movie by one line, by adding the fontSize to the
-    // textField height this is no longer happens. We also add the absolute tuneHeight,
-    // to prevent a negative value from triggering the bug. We won't send the fake
-    // value to the JavaScript side, though.
-    textField._height = textField.textHeight + PADDING_BOTTOM + this.fontSize + Math.abs(tuneHeight) + tuneHeight - leadingFix;
-    this.realHeight = textField._height - this.fontSize - Math.abs(tuneHeight);
-    var arg = 'height:' + this.realHeight;
-    if(_root.fitexactly == 'true') arg += ',width:' + (textField.textWidth + tuneWidth);
-    fscommand('resize', arg);
-        
-    this.originalHeight = textField._height;
-    this.currentHeight = Stage.height;
-
-    textField._xscale = textField._yscale = parseInt(_root.zoom);
-  }
-  
-  private function write(content) {
-    this.textField.htmlText = ['<p class="', CSS_ROOT_CLASS, '">', 
-                                content, '</p>'
-                              ].join('');
-  }
-  
-  private function isSingleLine() {
-    return Math.round((this.textField.textHeight - sIFR.styles.latestLeading) / this.fontSize) == 1;
-  }
-
-  //= Scales the text field to the new scale of the Flash movie itself.
-  public function scale() {
-    this.currentHeight = Stage.height;
-    var scale = 100 * Math.round(this.currentHeight / this.originalHeight);
-    textField._xscale = textField._yscale = scale;
-  }
-  
-  private function calculateRatios() {
-    var strings = ['X', 'X<br>X', 'X<br>X<br>X', 'X<br>X<br>X<br>X'];
-    var results = {};
-
-    for(var i = 1; i <= strings.length; i++) {
-      var size = 6;
-    
-      this.write(strings[i - 1]);
-      while(size < MAX_FONT_SIZE) {
-        var rootStyle = sIFR.styles.getStyle('.sIFR-root') || {};
-        rootStyle.fontSize = size;
-        sIFR.styles.setStyle('.sIFR-root', rootStyle);
-        this.textField.styleSheet = sIFR.styles;
-        this.repaint();
-        var ratio = (this.realHeight - PADDING_BOTTOM) / i / size;
-        if(!results[size]) results[size] = ratio;
-        else results[size] = ((i - 1) * results[size] + ratio) / i;
-        size++;
-      }
-    }
-
-    var sizes = [], ratios = [];
-    var ratiosToSizes = {}, sizesToRatios = {};
-
-    for(var size in results) {
-      if(results[size] == Object.prototype[size]) continue;
-      var ratio = results[size];
-      ratiosToSizes[ratio] = Math.max(ratio, parseInt(size));
-    }
-
-    for(var ratio in ratiosToSizes) {
-      if(ratiosToSizes[ratio] == Object.prototype[ratio]) continue;
-      sizesToRatios[ratiosToSizes[ratio]] = roundDecimals(ratio, 2);
-      sizes.push(ratiosToSizes[ratio]);
-    }
-
-    sizes.sort(function(a, b) { return a - b; });
-    for(var j = 0; j < sizes.length - 1; j++) ratios.push(sizes[j], sizesToRatios[sizes[j]]);
-    ratios.push(sizesToRatios[sizes[sizes.length - 1]]);
-
-    fscommand('debug:ratios', '[' + ratios.join(',') + ']');
-  }
-  
-  private function roundDecimals(value, decimals) {
-    return Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals);
-  }
-  
-  public static function callback() {
-    switch(_root.callbackType) {
-      case 'replacetext':
-        sIFR.instance.content = _root.callbackValue;
-        sIFR.instance.write(_root.callbackValue);
-        sIFR.instance.repaint();
-        break;
-      case 'resettext':
-        sIFR.instance.write('');
-        sIFR.instance.write(sIFR.instance.content);
-        break;
-      case 'ratios':
-        sIFR.instance.calculateRatios();
-        break;
-    }
-  }
-}
diff --git a/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/sifr.fla b/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/sifr.fla
deleted file mode 100644 (file)
index 2aa3f64..0000000
Binary files a/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/sifr.fla and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/top_gradient.gif b/library/simplepie/demo/for_the_demo/top_gradient.gif
deleted file mode 100644 (file)
index f77bd38..0000000
Binary files a/library/simplepie/demo/for_the_demo/top_gradient.gif and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/verdana.swf b/library/simplepie/demo/for_the_demo/verdana.swf
deleted file mode 100644 (file)
index baf0350..0000000
Binary files a/library/simplepie/demo/for_the_demo/verdana.swf and /dev/null differ
diff --git a/library/simplepie/demo/for_the_demo/yanone-kaffeesatz-bold.swf b/library/simplepie/demo/for_the_demo/yanone-kaffeesatz-bold.swf
deleted file mode 100644 (file)
index c812a79..0000000
Binary files a/library/simplepie/demo/for_the_demo/yanone-kaffeesatz-bold.swf and /dev/null differ
diff --git a/library/simplepie/demo/handler_image.php b/library/simplepie/demo/handler_image.php
deleted file mode 100644 (file)
index 49c3ec8..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-<?php
-// This should be modifed as your own use warrants.
-
-require_once('../simplepie.inc');
-SimplePie_Misc::display_cached_file($_GET['i'], './cache', 'spi');
-?>
diff --git a/library/simplepie/demo/index.php b/library/simplepie/demo/index.php
deleted file mode 100644 (file)
index 1481ba9..0000000
+++ /dev/null
@@ -1,295 +0,0 @@
-<?php
-// Start counting time for the page load
-$starttime = explode(' ', microtime());
-$starttime = $starttime[1] + $starttime[0];
-
-// Include SimplePie
-// Located in the parent directory
-include_once('../simplepie.inc');
-include_once('../idn/idna_convert.class.php');
-
-// Create a new instance of the SimplePie object
-$feed = new SimplePie();
-
-//$feed->force_fsockopen(true);
-
-// Make sure that page is getting passed a URL
-if (isset($_GET['feed']) && $_GET['feed'] !== '')
-{
-       // Strip slashes if magic quotes is enabled (which automatically escapes certain characters)
-       if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc())
-       {
-               $_GET['feed'] = stripslashes($_GET['feed']);
-       }
-       
-       // Use the URL that was passed to the page in SimplePie
-       $feed->set_feed_url($_GET['feed']);
-       
-       // XML dump
-       $feed->enable_xml_dump(isset($_GET['xmldump']) ? true : false);
-}
-
-// Allow us to change the input encoding from the URL string if we want to. (optional)
-if (!empty($_GET['input']))
-{
-       $feed->set_input_encoding($_GET['input']);
-}
-
-// Allow us to choose to not re-order the items by date. (optional)
-if (!empty($_GET['orderbydate']) && $_GET['orderbydate'] == 'false')
-{
-       $feed->enable_order_by_date(false);
-}
-
-// Allow us to cache images in feeds.  This will also bypass any hotlink blocking put in place by the website.
-if (!empty($_GET['image']) && $_GET['image'] == 'true')
-{
-       $feed->set_image_handler('./handler_image.php');
-}
-
-// We'll enable the discovering and caching of favicons.
-$feed->set_favicon_handler('./handler_image.php');
-
-// Initialize the whole SimplePie object.  Read the feed, process it, parse it, cache it, and 
-// all that other good stuff.  The feed's information will not be available to SimplePie before 
-// this is called.
-$success = $feed->init();
-
-// We'll make sure that the right content type and character encoding gets set automatically.
-// This function will grab the proper character encoding, as well as set the content type to text/html.
-$feed->handle_content_type();
-
-// When we end our PHP block, we want to make sure our DOCTYPE is on the top line to make 
-// sure that the browser snaps into Standards Mode.
-?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
-<head>
-<title>SimplePie: Demo</title>
-
-<link rel="stylesheet" href="./for_the_demo/sIFR-screen.css" type="text/css" media="screen">
-<link rel="stylesheet" href="./for_the_demo/sIFR-print.css" type="text/css" media="print">
-<link rel="stylesheet" href="./for_the_demo/simplepie.css" type="text/css" media="screen, projector" />
-
-<script type="text/javascript" src="./for_the_demo/sifr.js"></script>
-<script type="text/javascript" src="./for_the_demo/sifr-config.js"></script>
-<script type="text/javascript" src="./for_the_demo/sleight.js"></script>
-
-</head>
-
-<body id="bodydemo">
-
-<div id="header">
-       <div id="headerInner">
-               <div id="logoContainer">
-                       <div id="logoContainerInner">
-                               <div align="center"><a href="http://simplepie.org"><img src="./for_the_demo/logo_simplepie_demo.png" alt="SimplePie Demo: PHP-based RSS and Atom feed handling" title="SimplePie Demo: PHP-based RSS and Atom feed handling" border="0" /></a></div>
-                               <div class="clearLeft"></div>
-                       </div>
-
-               </div>
-               <div id="menu">
-               <!-- I know, I know, I know... tables for layout, I know.  If a web standards evangelist (like me) has to resort 
-               to using tables for something, it's because no other possible solution could be found.  This issue?  No way to 
-               do centered floats purely with CSS. The table box model allows for a dynamic width while centered, while the 
-               CSS box model for DIVs doesn't allow for it. :( -->
-               <table cellpadding="0" cellspacing="0" border="0"><tbody><tr><td>
-<ul><li id="demo"><a href="./">SimplePie Demo</a></li><li><a href="http://simplepie.org/wiki/faq/start">FAQ/Troubleshooting</a></li><li><a href="http://simplepie.org/support/">Support Forums</a></li><li><a href="http://simplepie.org/wiki/reference/start">API Reference</a></li><li><a href="http://simplepie.org/blog/">Weblog</a></li><li><a href="../test/test.php">Unit Tests</a></li></ul>
-
-                       <div class="clearLeft"></div>
-               </td></tr></tbody></table>
-               </div>
-       </div>
-</div>
-
-<div id="site">
-
-       <div id="content">
-
-               <div class="chunk">
-                       <form action="" method="get" name="sp_form" id="sp_form">
-                               <div id="sp_input">
-
-
-                                       <!-- If a feed has already been passed through the form, then make sure that the URL remains in the form field. -->
-                                       <p><input type="text" name="feed" value="<?php if ($feed->subscribe_url()) echo $feed->subscribe_url(); ?>" class="text" id="feed_input" />&nbsp;<input type="submit" value="Read" class="button" /></p>
-
-
-                               </div>
-                       </form>
-
-
-                       <?php
-                       // Check to see if there are more than zero errors (i.e. if there are any errors at all)
-                       if ($feed->error())
-                       {
-                               // If so, start a <div> element with a classname so we can style it.
-                               echo '<div class="sp_errors">' . "\r\n";
-
-                                       // ... and display it.
-                                       echo '<p>' . htmlspecialchars($feed->error()) . "</p>\r\n";
-
-                               // Close the <div> element we opened.
-                               echo '</div>' . "\r\n";
-                       }
-                       ?>
-
-                       <!-- Here are some sample feeds. -->
-                       <p class="sample_feeds"><strong>Or try one of the following:</strong> 
-                       <a href="?feed=http://www.詹姆斯.com/atomtests/iri/everything.atom" title="Test: International Domain Name support">詹姆斯.com</a>, 
-                       <a href="?feed=http://www.adultswim.com/williams/podcast/tools/xml/video_rss.xml" title="Humor from the people who make [adult swim] cartoons.">adult swim</a>, 
-                       <a href="?feed=http://afterdawn.com/news/afterdawn_rss.xml" title="Ripping, Burning, DRM, and the Dark Side of Consumer Electronics Media">Afterdawn</a>, 
-                       <a href="?feed=http://feeds.feedburner.com/ajaxian" title="AJAX and Scripting News">Ajaxian</a>, 
-                       <a href="?feed=http://www.andybudd.com/index.rdf&amp;image=true" title="Test: Bypass Image Hotlink Blocking">Andy Budd</a>, 
-                       <a href="?feed=http://feeds.feedburner.com/AskANinja" title="Test: Embedded Enclosures">Ask a Ninja</a>, 
-                       <a href="?feed=http://www.atomenabled.org/atom.xml" title="Test: Atom 1.0 Support">AtomEnabled.org</a>, 
-                       <a href="?feed=http://newsrss.bbc.co.uk/rss/newsonline_world_edition/front_page/rss.xml" title="World News">BBC News</a>, 
-                       <a href="?feed=http://newsrss.bbc.co.uk/rss/arabic/news/rss.xml" title="Test: Windows-1256 Encoding">BBC Arabic</a>, 
-                       <a href="?feed=http://newsrss.bbc.co.uk/rss/chinese/simp/news/rss.xml" title="Test: GB2312 Encoding">BBC China</a>, 
-                       <a href="?feed=http://newsrss.bbc.co.uk/rss/russian/news/rss.xml" title="Test: Windows-1251 Encoding">BBC Russia</a>, 
-                       <a href="?feed=http://inessential.com/xml/rss.xml" title="Developer of NetNewsWire">Brent Simmons</a>, 
-                       <a href="?feed=http://www.channelfrederator.com/rss" title="Test: Embedded Enclosures">Channel Frederator</a>, 
-                       <a href="?feed=http://rss.cnn.com/rss/cnn_topstories.rss" title="World News">CNN</a>, 
-                       <a href="?feed=http://digg.com/rss/index.xml" title="Tech news. Better than Slashdot.">Digg</a>, 
-                       <a href="?feed=http://revision3.com/diggnation/feed/quicktime-large" title="Tech and industry videocast.">Diggnation</a>, 
-                       <a href="?feed=http://www.flickr.com/services/feeds/photos_public.gne?format=rss2" title="Flickr Photos">Flickr</a>, 
-                       <a href="?feed=http://news.google.com/?output=rss" title="World News">Google News</a>, 
-                       <a href="?feed=http://video.google.com/videofeed?type=top100new&num=20&output=rss" title="Test: Media RSS Support">Google Video</a>, 
-                       <a href="?feed=http://blogs.law.harvard.edu/home/feed/rdf/" title="Test: Tag Stripping">Harvard Law</a>, 
-                       <a href="?feed=http://hagada.org.il/hagada/html/backend.php" title="Test: Window-1255 Encoding">Hebrew Language</a>, 
-                       <a href="?feed=http://www.infoworld.com/rss/news.xml" title="Test: Ad Stripping">InfoWorld</a>, 
-                       <a href="?feed=http://phobos.apple.com/WebObjects/MZStore.woa/wpa/MRSS/topsongs/limit=10/rss.xml&orderbydate=false" title="Test: Tag Stripping">iTunes</a>, 
-                       <a href="?feed=http://blog.japan.cnet.com/lessig/index.rdf" title="Test: EUC-JP Encoding">Japanese Language</a>, 
-                       <a href="?feed=http://nurapt.kaist.ac.kr/~jamaica/htmls/blog/rss.php&amp;input=EUC-KR" title="Test: EUC-KR Encoding">Korean Language</a>, 
-                       <a href="?feed=http://mir.aculo.us/xml/rss/feed.xml" title="Weblog for the developer of Scriptaculous">mir.aculo.us</a>, 
-                       <a href="?feed=http://images.apple.com/trailers/rss/newtrailers.rss" title="Apple's QuickTime movie trailer site">Movie Trailers</a>, 
-                       <a href="?feed=http://www.newspond.com/rss/main.xml" title="Tech and Science News">Newspond</a>, 
-                       <a href="?feed=http://nick.typepad.com/blog/index.rss" title="Developer of TopStyle and FeedDemon">Nick Bradbury</a>, 
-                       <a href="?feed=http://feeds.feedburner.com/ok-cancel" title="Usability comics and commentary">OK/Cancel</a>, 
-                       <a href="?feed=http://osnews.com/files/recent.rdf" title="News about every OS ever">OS News</a>, 
-                       <a href="?feed=http://weblog.philringnalda.com/feed/" title="Test: Atom 1.0 Support">Phil Ringnalda</a>, 
-                       <a href="?feed=http://kabili.libsyn.com/rss" title="Test: Improved enclosure type sniffing">Photoshop Videocast</a>, 
-                       <a href="?feed=http://www.pariurisportive.com/blog/xmlsrv/rss2.php?blog=2" title="Test: ISO-8859-1 Encoding">Romanian Language</a>, 
-                       <a href="?feed=http://www.erased.info/rss2.php" title="Test: KOI8-R Encoding">Russian Language</a>, 
-                       <a href="?feed=http://www.upsaid.com/isis/index.rdf" title="Test: BIG5 Encoding">Traditional Chinese Language</a>, 
-                       <a href="?feed=http://technorati.com/watchlists/rss.html?wid=29290" title="Technorati watch for SimplePie">Technorati</a>, 
-                       <a href="?feed=http://www.tbray.org/ongoing/ongoing.atom" title="Test: Atom 1.0 Support">Tim Bray</a>, 
-                       <a href="?feed=http://tuaw.com/rss.xml" title="Apple News">TUAW</a>, 
-                       <a href="?feed=http://www.tvgasm.com/atom.xml&amp;image=true" title="Test: Bypass Image Hotlink Blocking">TVgasm</a>, 
-                       <a href="?feed=http://uneasysilence.com/feed/" title="Interesting tech randomness">UNEASYsilence</a>, 
-                       <a href="?feed=http://feeds.feedburner.com/web20Show" title="Test: Embedded Enclosures">Web 2.0 Show</a>, 
-                       <a href="?feed=http://windowsvistablog.com/blogs/MainFeed.aspx" title="Test: Tag Stripping">Windows Vista Blog</a>, 
-                       <a href="?feed=http://xkcd.com/rss.xml" title="Test: LightHTTPd and GZipping">XKCD</a>, 
-                       <a href="?feed=http://rss.news.yahoo.com/rss/topstories" title="World News">Yahoo! News</a>, 
-                       <a href="?feed=http://youtube.com/rss/global/top_favorites.rss" title="Funny user-submitted videos">You Tube</a>, 
-                       <a href="?feed=http://zeldman.com/rss/" title="The father of the web standards movement">Zeldman</a></p>
-
-               </div>
-
-               <div id="sp_results">
-
-                       <!-- As long as the feed has data to work with... -->
-                       <?php if ($success): ?>
-                               <div class="chunk focus" align="center">
-
-                                       <!-- If the feed has a link back to the site that publishes it (which 99% of them do), link the feed's title to it. -->
-                                       <h3 class="header"><?php if ($feed->get_link()) echo '<a href="' . $feed->get_link() . '">'; echo $feed->get_title(); if ($feed->get_link()) echo '</a>'; ?></h3>
-
-                                       <!-- If the feed has a description, display it. -->
-                                       <?php echo $feed->get_description(); ?>
-
-                               </div>
-
-                               <!-- Add subscribe links for several different aggregation services -->
-                               <p class="subscribe"><strong>Subscribe:</strong> <a href="<?php echo $feed->subscribe_bloglines(); ?>">Bloglines</a>, <a href="<?php echo $feed->subscribe_google(); ?>">Google Reader</a>, <a href="<?php echo $feed->subscribe_msn(); ?>">My MSN</a>, <a href="<?php echo $feed->subscribe_netvibes(); ?>">Netvibes</a>, <a href="<?php echo $feed->subscribe_newsburst(); ?>">Newsburst</a><br /><a href="<?php echo $feed->subscribe_newsgator(); ?>">Newsgator</a>, <a href="<?php echo $feed->subscribe_odeo(); ?>">Odeo</a>, <a href="<?php echo $feed->subscribe_podnova(); ?>">Podnova</a>, <a href="<?php echo $feed->subscribe_rojo(); ?>">Rojo</a>, <a href="<?php echo $feed->subscribe_yahoo(); ?>">My Yahoo!</a>, <a href="<?php echo $feed->subscribe_feed(); ?>">Desktop Reader</a></p>
-
-
-                               <!-- Let's begin looping through each individual news item in the feed. -->
-                               <?php foreach($feed->get_items() as $item): ?>
-                                       <div class="chunk">
-
-                                               <?php
-                                               // Let's add a favicon for each item. If one doesn't exist, we'll use an alternate one.
-                                               if (!$favicon = $feed->get_favicon())
-                                               {
-                                                       $favicon = './for_the_demo/favicons/alternate.png';
-                                               }
-                                               ?>
-
-                                               <!-- If the item has a permalink back to the original post (which 99% of them do), link the item's title to it. -->
-                                               <h4><img src="<?php echo $favicon; ?>" alt="Favicon" class="favicon" /><?php if ($item->get_permalink()) echo '<a href="' . $item->get_permalink() . '">'; echo $item->get_title(); if ($item->get_permalink()) echo '</a>'; ?>&nbsp;<span class="footnote"><?php echo $item->get_date('j M Y, g:i a'); ?></span></h4>
-
-                                               <!-- Display the item's primary content. -->
-                                               <?php echo $item->get_content(); ?>
-
-                                               <?php
-                                               // Check for enclosures.  If an item has any, set the first one to the $enclosure variable.
-                                               if ($enclosure = $item->get_enclosure(0))
-                                               {
-                                                       // Use the embed() method to embed the enclosure into the page inline.
-                                                       echo '<div align="center">';
-                                                       echo '<p>' . $enclosure->embed(array(
-                                                               'audio' => './for_the_demo/place_audio.png',
-                                                               'video' => './for_the_demo/place_video.png',
-                                                               'mediaplayer' => './for_the_demo/mediaplayer.swf',
-                                                               'altclass' => 'download'
-                                                       )) . '</p>';
-
-                                                       if ($enclosure->get_link() && $enclosure->get_type())
-                                                       {
-                                                               echo '<p class="footnote" align="center">(' . $enclosure->get_type();
-                                                               if ($enclosure->get_size())
-                                                               {
-                                                                       echo '; ' . $enclosure->get_size() . ' MB';                                                             
-                                                               }
-                                                               echo ')</p>';
-                                                       }
-                                                       if ($enclosure->get_thumbnail())
-                                                       {
-                                                               echo '<div><img src="' . $enclosure->get_thumbnail() . '" alt="" /></div>';
-                                                       }
-                                                       echo '</div>';
-                                               }
-                                               ?>
-
-                                               <!-- Add links to add this post to one of a handful of services. -->
-                                               <p class="footnote favicons" align="center">
-                                                       <a href="<?php echo $item->add_to_blinklist(); ?>" title="Add post to Blinklist"><img src="./for_the_demo/favicons/blinklist.png" alt="Blinklist" /></a>
-                                                       <a href="<?php echo $item->add_to_blogmarks(); ?>" title="Add post to Blogmarks"><img src="./for_the_demo/favicons/blogmarks.png" alt="Blogmarks" /></a>
-                                                       <a href="<?php echo $item->add_to_delicious(); ?>" title="Add post to del.icio.us"><img src="./for_the_demo/favicons/delicious.png" alt="del.icio.us" /></a>
-                                                       <a href="<?php echo $item->add_to_digg(); ?>" title="Digg this!"><img src="./for_the_demo/favicons/digg.png" alt="Digg" /></a>
-                                                       <a href="<?php echo $item->add_to_magnolia(); ?>" title="Add post to Ma.gnolia"><img src="./for_the_demo/favicons/magnolia.png" alt="Ma.gnolia" /></a>
-                                                       <a href="<?php echo $item->add_to_myweb20(); ?>" title="Add post to My Web 2.0"><img src="./for_the_demo/favicons/myweb2.png" alt="My Web 2.0" /></a>
-                                                       <a href="<?php echo $item->add_to_newsvine(); ?>" title="Add post to Newsvine"><img src="./for_the_demo/favicons/newsvine.png" alt="Newsvine" /></a>
-                                                       <a href="<?php echo $item->add_to_reddit(); ?>" title="Add post to Reddit"><img src="./for_the_demo/favicons/reddit.png" alt="Reddit" /></a>
-                                                       <a href="<?php echo $item->add_to_segnalo(); ?>" title="Add post to Segnalo"><img src="./for_the_demo/favicons/segnalo.png" alt="Segnalo" /></a>
-                                                       <a href="<?php echo $item->add_to_simpy(); ?>" title="Add post to Simpy"><img src="./for_the_demo/favicons/simpy.png" alt="Simpy" /></a>
-                                                       <a href="<?php echo $item->add_to_spurl(); ?>" title="Add post to Spurl"><img src="./for_the_demo/favicons/spurl.png" alt="Spurl" /></a>
-                                                       <a href="<?php echo $item->add_to_wists(); ?>" title="Add post to Wists"><img src="./for_the_demo/favicons/wists.png" alt="Wists" /></a>
-                                                       <a href="<?php echo $item->search_technorati(); ?>" title="Who's linking to this post?"><img src="./for_the_demo/favicons/technorati.png" alt="Technorati" /></a>
-                                               </p>
-
-                                       </div>
-
-                               <!-- Stop looping through each item once we've gone through all of them. -->
-                               <?php endforeach; ?>
-
-                       <!-- From here on, we're no longer using data from the feed. -->
-                       <?php endif; ?>
-
-               </div>
-
-               <div>
-                       <!-- Display how fast the page was rendered. -->
-                       <p class="footnote">Page processed in <?php $mtime = explode(' ', microtime()); echo round($mtime[0] + $mtime[1] - $starttime, 3); ?> seconds.</p>
-
-                       <!-- Display the version of SimplePie being loaded. -->
-                       <p class="footnote">Powered by <a href="<?php echo SIMPLEPIE_URL; ?>"><?php echo SIMPLEPIE_NAME . ' ' . SIMPLEPIE_VERSION . ', Build ' . SIMPLEPIE_BUILD; ?></a>.  Run the <a href="../compatibility_test/sp_compatibility_test.php">SimplePie Compatibility Test</a>.  SimplePie is &copy; 2004&ndash;<?php echo date('Y'); ?>, Ryan Parman and Geoffrey Sneddon, and licensed under the <a href="http://www.opensource.org/licenses/bsd-license.php">BSD License</a>.</p>
-               </div>
-
-       </div>
-
-</div>
-
-</body>
-</html>
diff --git a/library/simplepie/demo/minimalistic.php b/library/simplepie/demo/minimalistic.php
deleted file mode 100644 (file)
index 56509c0..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-<?php
-
-function microtime_float()
-{
-       if (version_compare(phpversion(), '5.0.0', '>='))
-       {
-               return microtime(true);
-       }
-       else
-       {
-               list($usec, $sec) = explode(' ', microtime());
-               return ((float) $usec + (float) $sec);
-       }
-}
-
-$start = microtime_float();
-
-include('../simplepie.inc');
-
-// Parse it
-$feed = new SimplePie();
-if (!empty($_GET['feed']))
-{
-       if (get_magic_quotes_gpc())
-       {
-               $_GET['feed'] = stripslashes($_GET['feed']);
-       }
-       $feed->set_feed_url($_GET['feed']);
-       $feed->init();
-}
-$feed->handle_content_type();
-
-?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<title><?php echo (empty($_GET['feed'])) ? 'SimplePie' : 'SimplePie: ' . $feed->get_title(); ?></title>
-
-<!-- META HTTP-EQUIV -->
-<meta http-equiv="content-type" content="text/html; charset=<?php echo ($feed->get_encoding()) ? $feed->get_encoding() : 'UTF-8'; ?>" />
-<meta http-equiv="imagetoolbar" content="false" />
-
-<style type="text/css">
-html, body {
-       height:100%;
-       margin:0;
-       padding:0;
-}
-
-h1 {
-       background-color:#333;
-       color:#fff;
-       font-size:3em;
-       margin:0;
-       padding:5px 15px;
-       text-align:center;
-}
-
-div#footer {
-       padding:5px 0;
-}
-
-div#footer,
-div#footer a {
-       text-align:center;
-       font-size:0.7em;
-}
-
-div#footer a {
-       text-decoration:underline;
-}
-
-code {
-       background-color:#f3f3ff;
-       color:#000;
-}
-
-pre {
-       background-color:#f3f3ff;
-       color:#000080;
-       border:1px dotted #000080;
-       padding:3px 5px;
-}
-
-form {
-       margin:0;
-       padding:0;
-}
-
-div.chunk {
-       border-bottom:1px solid #ccc;
-}
-
-form#sp_form {
-       text-align:center;
-       margin:0;
-       padding:0;
-}
-
-form#sp_form input.text {
-       width:85%;
-}
-</style>
-
-</head>
-
-<body>
-       <h1><?php echo (empty($_GET['feed'])) ? 'SimplePie' : 'SimplePie: ' . $feed->get_title(); ?></h1>
-
-       <form action="" method="get" name="sp_form" id="sp_form">
-               <p><input type="text" name="feed" value="<?php echo ($feed->subscribe_url()) ? htmlspecialchars($feed->subscribe_url()) : 'http://'; ?>" class="text" id="feed_input" />&nbsp;<input type="submit" value="Read" class="button" /></p>
-       </form>
-
-       <div id="sp_results">
-               <?php if ($feed->data): ?>
-                       <?php $items = $feed->get_items(); ?>
-                       <p align="center"><span style="background-color:#ffc;">Displaying <?php echo $feed->get_item_quantity(); ?> most recent entries.</span></p>
-                       <?php foreach($items as $item): ?>
-                               <div class="chunk" style="padding:0 5px;">
-                                       <h4><a href="<?php echo $item->get_permalink(); ?>"><?php echo $item->get_title(); ?></a> <?php echo $item->get_date('j M Y'); ?></h4>
-                                       <?php echo $item->get_content(); ?>
-                                       <?php
-                                       if ($enclosure = $item->get_enclosure(0))
-                                               echo '<p><a href="' . $enclosure->get_link() . '" class="download"><img src="./for_the_demo/mini_podcast.png" alt="Podcast" title="Download the Podcast" border="0" /></a></p>';
-                                       ?>
-                               </div>
-                       <?php endforeach; ?>
-                       </div>
-               <?php endif; ?>
-       </div>
-
-       <div id="footer">
-               Powered by <?php echo SIMPLEPIE_LINKBACK; ?>, a product of <a href="http://www.skyzyx.com">Skyzyx Technologies</a>.<br />
-               Page created in <?php echo round(microtime_float()-$start, 3); ?> seconds.
-       </div>
-</body>
-</html>
diff --git a/library/simplepie/demo/multifeeds.php b/library/simplepie/demo/multifeeds.php
deleted file mode 100644 (file)
index b23d792..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php
-/********************************************************************
-MULTIFEEDS TEST PAGE
-
-Nothing too exciting here.  Just a sample page that demos integrated 
-Multifeeds support as well as cached favicons and perhaps a few other 
-things.
-
-Lots of this code is commented to help explain some of the new stuff.  
-Code was tested in PHP 5.2.2, but *should* also work with earlier 
-versions of PHP, as supported by SimplePie (PHP 4.1).
-
-********************************************************************/
-
-// Include the SimplePie library, and the one that handles internationalized domain names.
-require_once('../simplepie.inc');
-require_once('../idn/idna_convert.class.php');
-
-// Initialize some feeds for use.
-$feed = new SimplePie();
-$feed->set_feed_url(array(
-       'http://rss.news.yahoo.com/rss/topstories',
-       'http://news.google.com/?output=atom',
-       'http://rss.cnn.com/rss/cnn_topstories.rss'
-));
-
-// When we set these, we need to make sure that the handler_image.php file is also trying to read from the same cache directory that we are.
-$feed->set_favicon_handler('./handler_image.php');
-$feed->set_image_handler('./handler_image.php');
-
-// Initialize the feed.
-$feed->init();
-
-// Make sure the page is being served with the UTF-8 headers.
-$feed->handle_content_type();
-
-// Begin the (X)HTML page.
-?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">    
-<head>
-       <title>Multifeeds Test page</title>
-       <link rel="stylesheet" href="../demo/for_the_demo/simplepie.css" type="text/css" media="screen" title="SimplePie Styles" charset="utf-8" />
-       <style type="text/css">
-       div#site {
-               width:600px;
-       }
-       span.footnote {
-               white-space:nowrap;
-       }
-       h1 {
-               line-height:1.4em;
-       }
-       h4 {
-               padding-left:20px;
-               background-color:transparent;
-               background-repeat:no-repeat;
-               background-position:0 1px;
-       }
-       .clearBoth {
-               clear:both;
-       }
-       </style>
-</head>
-<body>
-<div id="site">
-
-       <?php if ($feed->error): ?>
-               <p><?=$feed->error()?></p>
-       <?php endif ?>
-
-       <div class="chunk">
-               <h1>Quick-n-Dirty Multifeeds Demo</a></h1>
-       </div>
-
-       <?php
-       // Let's loop through each item in the feed.
-       foreach($feed->get_items() as $item):
-
-       // Let's give ourselves a reference to the parent $feed object for this particular item.
-       $feed = $item->get_feed();
-       ?>
-
-               <div class="chunk">
-                       <h4 style="background-image:url(<?php echo $feed->get_favicon(); ?>);"><a href="<?php echo $item->get_permalink(); ?>"><?php echo html_entity_decode($item->get_title(), ENT_QUOTES, 'UTF-8'); ?></a></h4>
-
-                       <!-- get_content() prefers full content over summaries -->
-                       <?php echo $item->get_content(); ?>
-
-                       <?php if ($enclosure = $item->get_enclosure()): ?>
-                               <div>
-                               <?php echo $enclosure->native_embed(array(
-                                       // New 'mediaplayer' attribute shows off Flash-based MP3 and FLV playback.
-                                       'mediaplayer' => '../demo/for_the_demo/mediaplayer.swf'
-                               )); ?>
-                               </div>
-                       <?php endif; ?>
-
-                       <p class="footnote">Source: <a href="<?php echo $feed->get_permalink(); ?>"><?php echo $feed->get_title(); ?></a> | <?php echo $item->get_date('j M Y | g:i a'); ?></p>
-               </div>
-
-       <?php endforeach ?>
-
-       <p class="footnote">This is a test of the emergency broadcast system.  This is only a test&hellip; beeeeeeeeeeeeeeeeeeeeeeeeeep!</p>
-
-</div>
-</body>
-</html>
\ No newline at end of file
diff --git a/library/simplepie/demo/test.php b/library/simplepie/demo/test.php
deleted file mode 100644 (file)
index 5b9943a..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-include_once('../simplepie.inc');
-include_once('../idn/idna_convert.class.php');
-
-// Parse it
-$feed = new SimplePie();
-if (isset($_GET['feed']) && $_GET['feed'] !== '')
-{
-       if (get_magic_quotes_gpc())
-       {
-               $_GET['feed'] = stripslashes($_GET['feed']);
-       }
-       $feed->set_feed_url($_GET['feed']);
-       $feed->enable_cache(false);
-       $starttime = explode(' ', microtime());
-       $starttime = $starttime[1] + $starttime[0];
-       $feed->init();
-       $endtime = explode(' ', microtime());
-       $endtime = $endtime[1] + $endtime[0];
-       $time = $endtime - $starttime;
-}
-else
-{
-       $time = 'null';
-}
-
-$feed->handle_content_type();
-
-?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
-<title>SimplePie Test</title>
-<pre>
-<?php
-
-// memory_get_peak_usage() only exists on PHP 5.2 and higher if PHP is compiled with the --enable-memory-limit configuration option or on PHP 5.2.1 and higher (which runs as if --enable-memory-limit was on, with no option)
-if (function_exists('memory_get_peak_usage'))
-{
-       var_dump($time, memory_get_usage(), memory_get_peak_usage());
-}
-// memory_get_usage() only exists if PHP is compiled with the --enable-memory-limit configuration option or on PHP 5.2.1 and higher (which runs as if --enable-memory-limit was on, with no option)
-else if (function_exists('memory_get_usage'))
-{
-       var_dump($time, memory_get_usage());
-}
-else
-{
-       var_dump($time);
-}
-
-// Output buffer
-function callable_htmlspecialchars($string)
-{
-       return htmlspecialchars($string);
-}
-ob_start('callable_htmlspecialchars');
-
-// Output
-print_r($feed);
-ob_end_flush();
-
-?>
-</pre>
\ No newline at end of file
diff --git a/library/simplepie/idn/LICENCE b/library/simplepie/idn/LICENCE
deleted file mode 100644 (file)
index 25a1d22..0000000
+++ /dev/null
@@ -1,502 +0,0 @@
-                  GNU LESSER GENERAL PUBLIC LICENSE
-                       Version 2.1, February 1999
-
- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
-     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-[This is the first released version of the Lesser GPL.  It also counts
- as the successor of the GNU Library Public License, version 2, hence
- the version number 2.1.]
-
-                            Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-Licenses are intended to guarantee your freedom to share and change
-free software--to make sure the software is free for all its users.
-
-  This license, the Lesser General Public License, applies to some
-specially designated software packages--typically libraries--of the
-Free Software Foundation and other authors who decide to use it.  You
-can use it too, but we suggest you first think carefully about whether
-this license or the ordinary General Public License is the better
-strategy to use in any particular case, based on the explanations below.
-
-  When we speak of free software, we are referring to freedom of use,
-not price.  Our General Public Licenses are designed to make sure that
-you have the freedom to distribute copies of free software (and charge
-for this service if you wish); that you receive source code or can get
-it if you want it; that you can change the software and use pieces of
-it in new free programs; and that you are informed that you can do
-these things.
-
-  To protect your rights, we need to make restrictions that forbid
-distributors to deny you these rights or to ask you to surrender these
-rights.  These restrictions translate to certain responsibilities for
-you if you distribute copies of the library or if you modify it.
-
-  For example, if you distribute copies of the library, whether gratis
-or for a fee, you must give the recipients all the rights that we gave
-you.  You must make sure that they, too, receive or can get the source
-code.  If you link other code with the library, you must provide
-complete object files to the recipients, so that they can relink them
-with the library after making changes to the library and recompiling
-it.  And you must show them these terms so they know their rights.
-
-  We protect your rights with a two-step method: (1) we copyright the
-library, and (2) we offer you this license, which gives you legal
-permission to copy, distribute and/or modify the library.
-
-  To protect each distributor, we want to make it very clear that
-there is no warranty for the free library.  Also, if the library is
-modified by someone else and passed on, the recipients should know
-that what they have is not the original version, so that the original
-author's reputation will not be affected by problems that might be
-introduced by others.
-
-  Finally, software patents pose a constant threat to the existence of
-any free program.  We wish to make sure that a company cannot
-effectively restrict the users of a free program by obtaining a
-restrictive license from a patent holder.  Therefore, we insist that
-any patent license obtained for a version of the library must be
-consistent with the full freedom of use specified in this license.
-
-  Most GNU software, including some libraries, is covered by the
-ordinary GNU General Public License.  This license, the GNU Lesser
-General Public License, applies to certain designated libraries, and
-is quite different from the ordinary General Public License.  We use
-this license for certain libraries in order to permit linking those
-libraries into non-free programs.
-
-  When a program is linked with a library, whether statically or using
-a shared library, the combination of the two is legally speaking a
-combined work, a derivative of the original library.  The ordinary
-General Public License therefore permits such linking only if the
-entire combination fits its criteria of freedom.  The Lesser General
-Public License permits more lax criteria for linking other code with
-the library.
-
-  We call this license the "Lesser" General Public License because it
-does Less to protect the user's freedom than the ordinary General
-Public License.  It also provides other free software developers Less
-of an advantage over competing non-free programs.  These disadvantages
-are the reason we use the ordinary General Public License for many
-libraries.  However, the Lesser license provides advantages in certain
-special circumstances.
-
-  For example, on rare occasions, there may be a special need to
-encourage the widest possible use of a certain library, so that it becomes
-a de-facto standard.  To achieve this, non-free programs must be
-allowed to use the library.  A more frequent case is that a free
-library does the same job as widely used non-free libraries.  In this
-case, there is little to gain by limiting the free library to free
-software only, so we use the Lesser General Public License.
-
-  In other cases, permission to use a particular library in non-free
-programs enables a greater number of people to use a large body of
-free software.  For example, permission to use the GNU C Library in
-non-free programs enables many more people to use the whole GNU
-operating system, as well as its variant, the GNU/Linux operating
-system.
-
-  Although the Lesser General Public License is Less protective of the
-users' freedom, it does ensure that the user of a program that is
-linked with the Library has the freedom and the wherewithal to run
-that program using a modified version of the Library.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.  Pay close attention to the difference between a
-"work based on the library" and a "work that uses the library".  The
-former contains code derived from the library, whereas the latter must
-be combined with the library in order to run.
-
-                  GNU LESSER GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License Agreement applies to any software library or other
-program which contains a notice placed by the copyright holder or
-other authorized party saying it may be distributed under the terms of
-this Lesser General Public License (also called "this License").
-Each licensee is addressed as "you".
-
-  A "library" means a collection of software functions and/or data
-prepared so as to be conveniently linked with application programs
-(which use some of those functions and data) to form executables.
-
-  The "Library", below, refers to any such software library or work
-which has been distributed under these terms.  A "work based on the
-Library" means either the Library or any derivative work under
-copyright law: that is to say, a work containing the Library or a
-portion of it, either verbatim or with modifications and/or translated
-straightforwardly into another language.  (Hereinafter, translation is
-included without limitation in the term "modification".)
-
-  "Source code" for a work means the preferred form of the work for
-making modifications to it.  For a library, complete source code means
-all the source code for all modules it contains, plus any associated
-interface definition files, plus the scripts used to control compilation
-and installation of the library.
-
-  Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running a program using the Library is not restricted, and output from
-such a program is covered only if its contents constitute a work based
-on the Library (independent of the use of the Library in a tool for
-writing it).  Whether that is true depends on what the Library does
-and what the program that uses the Library does.
-
-  1. You may copy and distribute verbatim copies of the Library's
-complete source code as you receive it, in any medium, provided that
-you conspicuously and appropriately publish on each copy an
-appropriate copyright notice and disclaimer of warranty; keep intact
-all the notices that refer to this License and to the absence of any
-warranty; and distribute a copy of this License along with the
-Library.
-
-  You may charge a fee for the physical act of transferring a copy,
-and you may at your option offer warranty protection in exchange for a
-fee.
-
-  2. You may modify your copy or copies of the Library or any portion
-of it, thus forming a work based on the Library, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) The modified work must itself be a software library.
-
-    b) You must cause the files modified to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    c) You must cause the whole of the work to be licensed at no
-    charge to all third parties under the terms of this License.
-
-    d) If a facility in the modified Library refers to a function or a
-    table of data to be supplied by an application program that uses
-    the facility, other than as an argument passed when the facility
-    is invoked, then you must make a good faith effort to ensure that,
-    in the event an application does not supply such function or
-    table, the facility still operates, and performs whatever part of
-    its purpose remains meaningful.
-
-    (For example, a function in a library to compute square roots has
-    a purpose that is entirely well-defined independent of the
-    application.  Therefore, Subsection 2d requires that any
-    application-supplied function or table used by this function must
-    be optional: if the application does not supply it, the square
-    root function must still compute square roots.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Library,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Library, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote
-it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Library.
-
-In addition, mere aggregation of another work not based on the Library
-with the Library (or with a work based on the Library) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may opt to apply the terms of the ordinary GNU General Public
-License instead of this License to a given copy of the Library.  To do
-this, you must alter all the notices that refer to this License, so
-that they refer to the ordinary GNU General Public License, version 2,
-instead of to this License.  (If a newer version than version 2 of the
-ordinary GNU General Public License has appeared, then you can specify
-that version instead if you wish.)  Do not make any other change in
-these notices.
-
-  Once this change is made in a given copy, it is irreversible for
-that copy, so the ordinary GNU General Public License applies to all
-subsequent copies and derivative works made from that copy.
-
-  This option is useful when you wish to copy part of the code of
-the Library into a program that is not a library.
-
-  4. You may copy and distribute the Library (or a portion or
-derivative of it, under Section 2) in object code or executable form
-under the terms of Sections 1 and 2 above provided that you accompany
-it with the complete corresponding machine-readable source code, which
-must be distributed under the terms of Sections 1 and 2 above on a
-medium customarily used for software interchange.
-
-  If distribution of object code is made by offering access to copy
-from a designated place, then offering equivalent access to copy the
-source code from the same place satisfies the requirement to
-distribute the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  5. A program that contains no derivative of any portion of the
-Library, but is designed to work with the Library by being compiled or
-linked with it, is called a "work that uses the Library".  Such a
-work, in isolation, is not a derivative work of the Library, and
-therefore falls outside the scope of this License.
-
-  However, linking a "work that uses the Library" with the Library
-creates an executable that is a derivative of the Library (because it
-contains portions of the Library), rather than a "work that uses the
-library".  The executable is therefore covered by this License.
-Section 6 states terms for distribution of such executables.
-
-  When a "work that uses the Library" uses material from a header file
-that is part of the Library, the object code for the work may be a
-derivative work of the Library even though the source code is not.
-Whether this is true is especially significant if the work can be
-linked without the Library, or if the work is itself a library.  The
-threshold for this to be true is not precisely defined by law.
-
-  If such an object file uses only numerical parameters, data
-structure layouts and accessors, and small macros and small inline
-functions (ten lines or less in length), then the use of the object
-file is unrestricted, regardless of whether it is legally a derivative
-work.  (Executables containing this object code plus portions of the
-Library will still fall under Section 6.)
-
-  Otherwise, if the work is a derivative of the Library, you may
-distribute the object code for the work under the terms of Section 6.
-Any executables containing that work also fall under Section 6,
-whether or not they are linked directly with the Library itself.
-
-  6. As an exception to the Sections above, you may also combine or
-link a "work that uses the Library" with the Library to produce a
-work containing portions of the Library, and distribute that work
-under terms of your choice, provided that the terms permit
-modification of the work for the customer's own use and reverse
-engineering for debugging such modifications.
-
-  You must give prominent notice with each copy of the work that the
-Library is used in it and that the Library and its use are covered by
-this License.  You must supply a copy of this License.  If the work
-during execution displays copyright notices, you must include the
-copyright notice for the Library among them, as well as a reference
-directing the user to the copy of this License.  Also, you must do one
-of these things:
-
-    a) Accompany the work with the complete corresponding
-    machine-readable source code for the Library including whatever
-    changes were used in the work (which must be distributed under
-    Sections 1 and 2 above); and, if the work is an executable linked
-    with the Library, with the complete machine-readable "work that
-    uses the Library", as object code and/or source code, so that the
-    user can modify the Library and then relink to produce a modified
-    executable containing the modified Library.  (It is understood
-    that the user who changes the contents of definitions files in the
-    Library will not necessarily be able to recompile the application
-    to use the modified definitions.)
-
-    b) Use a suitable shared library mechanism for linking with the
-    Library.  A suitable mechanism is one that (1) uses at run time a
-    copy of the library already present on the user's computer system,
-    rather than copying library functions into the executable, and (2)
-    will operate properly with a modified version of the library, if
-    the user installs one, as long as the modified version is
-    interface-compatible with the version that the work was made with.
-
-    c) Accompany the work with a written offer, valid for at
-    least three years, to give the same user the materials
-    specified in Subsection 6a, above, for a charge no more
-    than the cost of performing this distribution.
-
-    d) If distribution of the work is made by offering access to copy
-    from a designated place, offer equivalent access to copy the above
-    specified materials from the same place.
-
-    e) Verify that the user has already received a copy of these
-    materials or that you have already sent this user a copy.
-
-  For an executable, the required form of the "work that uses the
-Library" must include any data and utility programs needed for
-reproducing the executable from it.  However, as a special exception,
-the materials to be distributed need not include anything that is
-normally distributed (in either source or binary form) with the major
-components (compiler, kernel, and so on) of the operating system on
-which the executable runs, unless that component itself accompanies
-the executable.
-
-  It may happen that this requirement contradicts the license
-restrictions of other proprietary libraries that do not normally
-accompany the operating system.  Such a contradiction means you cannot
-use both them and the Library together in an executable that you
-distribute.
-
-  7. You may place library facilities that are a work based on the
-Library side-by-side in a single library together with other library
-facilities not covered by this License, and distribute such a combined
-library, provided that the separate distribution of the work based on
-the Library and of the other library facilities is otherwise
-permitted, and provided that you do these two things:
-
-    a) Accompany the combined library with a copy of the same work
-    based on the Library, uncombined with any other library
-    facilities.  This must be distributed under the terms of the
-    Sections above.
-
-    b) Give prominent notice with the combined library of the fact
-    that part of it is a work based on the Library, and explaining
-    where to find the accompanying uncombined form of the same work.
-
-  8. You may not copy, modify, sublicense, link with, or distribute
-the Library except as expressly provided under this License.  Any
-attempt otherwise to copy, modify, sublicense, link with, or
-distribute the Library is void, and will automatically terminate your
-rights under this License.  However, parties who have received copies,
-or rights, from you under this License will not have their licenses
-terminated so long as such parties remain in full compliance.
-
-  9. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Library or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Library (or any work based on the
-Library), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Library or works based on it.
-
-  10. Each time you redistribute the Library (or any work based on the
-Library), the recipient automatically receives a license from the
-original licensor to copy, distribute, link with or modify the Library
-subject to these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties with
-this License.
-
-  11. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Library at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Library by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Library.
-
-If any portion of this section is held invalid or unenforceable under any
-particular circumstance, the balance of the section is intended to apply,
-and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  12. If the distribution and/or use of the Library is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Library under this License may add
-an explicit geographical distribution limitation excluding those countries,
-so that distribution is permitted only in or among countries not thus
-excluded.  In such case, this License incorporates the limitation as if
-written in the body of this License.
-
-  13. The Free Software Foundation may publish revised and/or new
-versions of the Lesser General Public License from time to time.
-Such new versions will be similar in spirit to the present version,
-but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Library
-specifies a version number of this License which applies to it and
-"any later version", you have the option of following the terms and
-conditions either of that version or of any later version published by
-the Free Software Foundation.  If the Library does not specify a
-license version number, you may choose any version ever published by
-the Free Software Foundation.
-
-  14. If you wish to incorporate parts of the Library into other free
-programs whose distribution conditions are incompatible with these,
-write to the author to ask for permission.  For software which is
-copyrighted by the Free Software Foundation, write to the Free
-Software Foundation; we sometimes make exceptions for this.  Our
-decision will be guided by the two goals of preserving the free status
-of all derivatives of our free software and of promoting the sharing
-and reuse of software generally.
-
-                            NO WARRANTY
-
-  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
-KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
-LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
-WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
-AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
-FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
-RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
-FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
-SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGES.
-
-                     END OF TERMS AND CONDITIONS
-
-           How to Apply These Terms to Your New Libraries
-
-  If you develop a new library, and you want it to be of the greatest
-possible use to the public, we recommend making it free software that
-everyone can redistribute and change.  You can do so by permitting
-redistribution under these terms (or, alternatively, under the terms of the
-ordinary General Public License).
-
-  To apply these terms, attach the following notices to the library.  It is
-safest to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least the
-"copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the library's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-Also add information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the library, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the
-  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
-
-  <signature of Ty Coon>, 1 April 1990
-  Ty Coon, President of Vice
-
-That's all there is to it!
diff --git a/library/simplepie/idn/ReadMe.txt b/library/simplepie/idn/ReadMe.txt
deleted file mode 100644 (file)
index 7ca8c7e..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-*******************************************************************************
-*                                                                             *
-*                    IDNA Convert (idna_convert.class.php)                    *
-*                                                                             *
-* http://idnaconv.phlymail.de                     mailto:phlymail@phlylabs.de *
-*******************************************************************************
-* (c) 2004-2007 phlyLabs, Berlin                                              *
-* This file is encoded in UTF-8                                               *
-*******************************************************************************
-
-Introduction
-------------
-
-The class idna_convert allows to convert internationalized domain names
-(see RFC 3490, 3491, 3492 and 3454 for detials) as they can be used with various
-registries worldwide to be translated between their original (localized) form
-and their encoded form as it will be used in the DNS (Domain Name System).
-
-The class provides two public methods, encode() and decode(), which do exactly
-what you would expect them to do. You are allowed to use complete domain names,
-simple strings and complete email addresses as well. That means, that you might
-use any of the following notations:
-
-- www.nörgler.com
-- xn--nrgler-wxa
-- xn--brse-5qa.xn--knrz-1ra.info
-
-Errors, incorrectly encoded or invalid strings will lead to either a FALSE
-response (when in strict mode) or to only partially converted strings.
-You can query the occured error by calling the method get_last_error().
-
-Unicode strings are expected to be either UTF-8 strings, UCS-4 strings or UCS-4
-arrays. The default format is UTF-8. For setting different encodings, you can
-call the method setParams() - please see the inline documentation for details.
-ACE strings (the Punycode form) are always 7bit ASCII strings.
-
-ATTENTION: We no longer supply the PHP5 version of the class. It is not
-necessary for achieving a successfull conversion, since the supplied PHP code is
-compatible with both PHP4 and PHP5. We expect to see no compatibility issues
-with the upcoming PHP6, too.
-
-
-Files
------
-
-idna_convert.class.php         - The actual class
-idna_convert.create.npdata.php - Useful for (re)creating the NPData file
-npdata.ser                     - Serialized data for NamePrep
-example.php                    - An example web page for converting
-ReadMe.txt                     - This file
-LICENCE                        - The LGPL licence file
-
-The class is contained in idna_convert.class.php.
-MAKE SURE to copy the npdata.ser file into the same folder as the class file
-itself!
-
-
-Examples
---------
-
-1. Say we wish to encode the domain name nörgler.com:
-
-// Include the class
-include_once('idna_convert.class.php');
-// Instantiate it *
-$IDN = new idna_convert();
-// The input string, if input is not UTF-8 or UCS-4, it must be converted before
-$input = utf8_encode('nörgler.com');
-// Encode it to its punycode presentation
-$output = $IDN->encode($input);
-// Output, what we got now
-echo $output; // This will read: xn--nrgler-wxa.com
-
-
-2. We received an email from a punycoded domain and are willing to learn, how
-   the domain name reads originally
-
-// Include the class
-include_once('idna_convert.class.php');
-// Instantiate it (depending on the version you are using) with
-$IDN = new idna_convert();
-// The input string
-$input = 'andre@xn--brse-5qa.xn--knrz-1ra.info';
-// Encode it to its punycode presentation
-$output = $IDN->decode($input);
-// Output, what we got now, if output should be in a format different to UTF-8
-// or UCS-4, you will have to convert it before outputting it
-echo utf8_decode($output); // This will read: andre@börse.knörz.info
-
-
-3. The input is read from a UCS-4 coded file and encoded line by line. By
-   appending the optional second parameter we tell enode() about the input
-   format to be used
-
-// Include the class
-include_once('idna_convert.class.php');
-// Instantiate it
-$IDN = new dinca_convert();
-// Iterate through the input file line by line
-foreach (file('ucs4-domains.txt') as $line) {
-    echo $IDN->encode(trim($line), 'ucs4_string');
-    echo "\n";
-}
-
-
-NPData
-------
-
-Should you need to recreate the npdata.ser file, which holds all necessary translation
-tables in a serialized format, you can run the file idna_convert.create.npdata.php, which
-creates the file for you and stores it in the same folder, where it is placed.
-Should you need to do changes to the tables you can do so, but beware of the consequences.
-
-
-Contact us
-----------
-
-In case of errors, bugs, questions, wishes, please don't hesitate to contact us
-under the email address above.
-
-The team of phlyLabs
-http://phlylabs.de
-mailto:phlymail@phlylabs.de
\ No newline at end of file
diff --git a/library/simplepie/idn/idna_convert.class.php b/library/simplepie/idn/idna_convert.class.php
deleted file mode 100644 (file)
index ed2bae2..0000000
+++ /dev/null
@@ -1,969 +0,0 @@
-<?php
-// {{{ license
-
-/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
-//
-// +----------------------------------------------------------------------+
-// | 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 |
-// | USA.                                                                 |
-// +----------------------------------------------------------------------+
-//
-
-// }}}
-
-/**
- * Encode/decode Internationalized Domain Names.
- *
- * The class allows to convert internationalized domain names
- * (see RFC 3490 for details) as they can be used with various registries worldwide
- * to be translated between their original (localized) form and their encoded form
- * as it will be used in the DNS (Domain Name System).
- *
- * The class provides two public methods, encode() and decode(), which do exactly
- * what you would expect them to do. You are allowed to use complete domain names,
- * simple strings and complete email addresses as well. That means, that you might
- * use any of the following notations:
- *
- * - www.nörgler.com
- * - xn--nrgler-wxa
- * - xn--brse-5qa.xn--knrz-1ra.info
- *
- * Unicode input might be given as either UTF-8 string, UCS-4 string or UCS-4
- * array. Unicode output is available in the same formats.
- * You can select your preferred format via {@link set_paramter()}.
- *
- * ACE input and output is always expected to be ASCII.
- *
- * @author  Matthias Sommerfeld <mso@phlylabs.de>
- * @copyright 2004-2007 phlyLabs Berlin, http://phlylabs.de
- * @version 0.5.1
- *
- */
-class idna_convert
-{
-    /**
-     * Holds all relevant mapping tables, loaded from a seperate file on construct
-     * See RFC3454 for details
-     *
-     * @var array
-     * @access private
-     */
-    var $NP = array();
-
-    // Internal settings, do not mess with them
-    var $_punycode_prefix = 'xn--';
-    var $_invalid_ucs =     0x80000000;
-    var $_max_ucs =         0x10FFFF;
-    var $_base =            36;
-    var $_tmin =            1;
-    var $_tmax =            26;
-    var $_skew =            38;
-    var $_damp =            700;
-    var $_initial_bias =    72;
-    var $_initial_n =       0x80;
-    var $_sbase =           0xAC00;
-    var $_lbase =           0x1100;
-    var $_vbase =           0x1161;
-    var $_tbase =           0x11A7;
-    var $_lcount =          19;
-    var $_vcount =          21;
-    var $_tcount =          28;
-    var $_ncount =          588;   // _vcount * _tcount
-    var $_scount =          11172; // _lcount * _tcount * _vcount
-    var $_error =           false;
-
-    // See {@link set_paramter()} for details of how to change the following
-    // settings from within your script / application
-    var $_api_encoding   =  'utf8'; // Default input charset is UTF-8
-    var $_allow_overlong =  false;  // Overlong UTF-8 encodings are forbidden
-    var $_strict_mode    =  false;  // Behave strict or not
-
-    // The constructor
-    function idna_convert($options = false)
-    {
-        $this->slast = $this->_sbase + $this->_lcount * $this->_vcount * $this->_tcount;
-        if (function_exists('file_get_contents')) {
-            $this->NP = unserialize(file_get_contents(dirname(__FILE__).'/npdata.ser'));
-        } else {
-            $this->NP = unserialize(join('', file(dirname(__FILE__).'/npdata.ser')));
-        }
-        // If parameters are given, pass these to the respective method
-        if (is_array($options)) {
-            return $this->set_parameter($options);
-        }
-        return true;
-    }
-
-    /**
-     * Sets a new option value. Available options and values:
-     * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8,
-     *         'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8]
-     * [overlong - Unicode does not allow unnecessarily long encodings of chars,
-     *             to allow this, set this parameter to true, else to false;
-     *             default is false.]
-     * [strict - true: strict mode, good for registration purposes - Causes errors
-     *           on failures; false: loose mode, ideal for "wildlife" applications
-     *           by silently ignoring errors and returning the original input instead
-     *
-     * @param    mixed     Parameter to set (string: single parameter; array of Parameter => Value pairs)
-     * @param    string    Value to use (if parameter 1 is a string)
-     * @return   boolean   true on success, false otherwise
-     * @access   public
-     */
-    function set_parameter($option, $value = false)
-    {
-        if (!is_array($option)) {
-            $option = array($option => $value);
-        }
-        foreach ($option as $k => $v) {
-            switch ($k) {
-            case 'encoding':
-                switch ($v) {
-                case 'utf8':
-                case 'ucs4_string':
-                case 'ucs4_array':
-                    $this->_api_encoding = $v;
-                    break;
-                default:
-                    $this->_error('Set Parameter: Unknown parameter '.$v.' for option '.$k);
-                    return false;
-                }
-                break;
-            case 'overlong':
-                $this->_allow_overlong = ($v) ? true : false;
-                break;
-            case 'strict':
-                $this->_strict_mode = ($v) ? true : false;
-                break;
-            default:
-                $this->_error('Set Parameter: Unknown option '.$k);
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Decode a given ACE domain name
-     * @param    string   Domain name (ACE string)
-     * [@param    string   Desired output encoding, see {@link set_parameter}]
-     * @return   string   Decoded Domain name (UTF-8 or UCS-4)
-     * @access   public
-     */
-    function decode($input, $one_time_encoding = false)
-    {
-        // Optionally set
-        if ($one_time_encoding) {
-            switch ($one_time_encoding) {
-            case 'utf8':
-            case 'ucs4_string':
-            case 'ucs4_array':
-                break;
-            default:
-                $this->_error('Unknown encoding '.$one_time_encoding);
-                return false;
-            }
-        }
-        // Make sure to drop any newline characters around
-        $input = trim($input);
-
-        // Negotiate input and try to determine, whether it is a plain string,
-        // an email address or something like a complete URL
-        if (strpos($input, '@')) { // Maybe it is an email address
-            // No no in strict mode
-            if ($this->_strict_mode) {
-                $this->_error('Only simple domain name parts can be handled in strict mode');
-                return false;
-            }
-            list ($email_pref, $input) = explode('@', $input, 2);
-            $arr = explode('.', $input);
-            foreach ($arr as $k => $v) {
-                if (preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $v)) {
-                    $conv = $this->_decode($v);
-                    if ($conv) $arr[$k] = $conv;
-                }
-            }
-            $input = join('.', $arr);
-            $arr = explode('.', $email_pref);
-            foreach ($arr as $k => $v) {
-                if (preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $v)) {
-                    $conv = $this->_decode($v);
-                    if ($conv) $arr[$k] = $conv;
-                }
-            }
-            $email_pref = join('.', $arr);
-            $return = $email_pref . '@' . $input;
-        } elseif (preg_match('![:\./]!', $input)) { // Or a complete domain name (with or without paths / parameters)
-            // No no in strict mode
-            if ($this->_strict_mode) {
-                $this->_error('Only simple domain name parts can be handled in strict mode');
-                return false;
-            }
-            $parsed = parse_url($input);
-            if (isset($parsed['host'])) {
-                $arr = explode('.', $parsed['host']);
-                foreach ($arr as $k => $v) {
-                    $conv = $this->_decode($v);
-                    if ($conv) $arr[$k] = $conv;
-                }
-                $parsed['host'] = join('.', $arr);
-                $return =
-                        (empty($parsed['scheme']) ? '' : $parsed['scheme'].(strtolower($parsed['scheme']) == 'mailto' ? ':' : '://'))
-                        .(empty($parsed['user']) ? '' : $parsed['user'].(empty($parsed['pass']) ? '' : ':'.$parsed['pass']).'@')
-                        .$parsed['host']
-                        .(empty($parsed['port']) ? '' : ':'.$parsed['port'])
-                        .(empty($parsed['path']) ? '' : $parsed['path'])
-                        .(empty($parsed['query']) ? '' : '?'.$parsed['query'])
-                        .(empty($parsed['fragment']) ? '' : '#'.$parsed['fragment']);
-            } else { // parse_url seems to have failed, try without it
-                $arr = explode('.', $input);
-                foreach ($arr as $k => $v) {
-                    $conv = $this->_decode($v);
-                    $arr[$k] = ($conv) ? $conv : $v;
-                }
-                $return = join('.', $arr);
-            }
-        } else { // Otherwise we consider it being a pure domain name string
-            $return = $this->_decode($input);
-            if (!$return) $return = $input;
-        }
-        // The output is UTF-8 by default, other output formats need conversion here
-        // If one time encoding is given, use this, else the objects property
-        switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) {
-        case 'utf8':
-            return $return;
-            break;
-        case 'ucs4_string':
-           return $this->_ucs4_to_ucs4_string($this->_utf8_to_ucs4($return));
-           break;
-        case 'ucs4_array':
-            return $this->_utf8_to_ucs4($return);
-            break;
-        default:
-            $this->_error('Unsupported output format');
-            return false;
-        }
-    }
-
-    /**
-     * Encode a given UTF-8 domain name
-     * @param    string   Domain name (UTF-8 or UCS-4)
-     * [@param    string   Desired input encoding, see {@link set_parameter}]
-     * @return   string   Encoded Domain name (ACE string)
-     * @access   public
-     */
-    function encode($decoded, $one_time_encoding = false)
-    {
-        // Forcing conversion of input to UCS4 array
-        // If one time encoding is given, use this, else the objects property
-        switch ($one_time_encoding ? $one_time_encoding : $this->_api_encoding) {
-        case 'utf8':
-            $decoded = $this->_utf8_to_ucs4($decoded);
-            break;
-        case 'ucs4_string':
-           $decoded = $this->_ucs4_string_to_ucs4($decoded);
-        case 'ucs4_array':
-           break;
-        default:
-            $this->_error('Unsupported input format: '.($one_time_encoding ? $one_time_encoding : $this->_api_encoding));
-            return false;
-        }
-
-        // No input, no output, what else did you expect?
-        if (empty($decoded)) return '';
-
-        // Anchors for iteration
-        $last_begin = 0;
-        // Output string
-        $output = '';
-        foreach ($decoded as $k => $v) {
-            // Make sure to use just the plain dot
-            switch($v) {
-            case 0x3002:
-            case 0xFF0E:
-            case 0xFF61:
-                $decoded[$k] = 0x2E;
-                // Right, no break here, the above are converted to dots anyway
-            // Stumbling across an anchoring character
-            case 0x2E:
-            case 0x2F:
-            case 0x3A:
-            case 0x3F:
-            case 0x40:
-                // Neither email addresses nor URLs allowed in strict mode
-                if ($this->_strict_mode) {
-                   $this->_error('Neither email addresses nor URLs are allowed in strict mode.');
-                   return false;
-                } else {
-                    // Skip first char
-                    if ($k) {
-                        $encoded = '';
-                        $encoded = $this->_encode(array_slice($decoded, $last_begin, (($k)-$last_begin)));
-                        if ($encoded) {
-                            $output .= $encoded;
-                        } else {
-                            $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($k)-$last_begin)));
-                        }
-                        $output .= chr($decoded[$k]);
-                    }
-                    $last_begin = $k + 1;
-                }
-            }
-        }
-        // Catch the rest of the string
-        if ($last_begin) {
-            $inp_len = sizeof($decoded);
-            $encoded = '';
-            $encoded = $this->_encode(array_slice($decoded, $last_begin, (($inp_len)-$last_begin)));
-            if ($encoded) {
-                $output .= $encoded;
-            } else {
-                $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($inp_len)-$last_begin)));
-            }
-            return $output;
-        } else {
-            if ($output = $this->_encode($decoded)) {
-                return $output;
-            } else {
-                return $this->_ucs4_to_utf8($decoded);
-            }
-        }
-    }
-
-    /**
-     * Use this method to get the last error ocurred
-     * @param    void
-     * @return   string   The last error, that occured
-     * @access   public
-     */
-    function get_last_error()
-    {
-        return $this->_error;
-    }
-
-    /**
-     * The actual decoding algorithm
-     * @access   private
-     */
-    function _decode($encoded)
-    {
-        // We do need to find the Punycode prefix
-        if (!preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $encoded)) {
-            $this->_error('This is not a punycode string');
-            return false;
-        }
-        $encode_test = preg_replace('!^'.preg_quote($this->_punycode_prefix, '!').'!', '', $encoded);
-        // If nothing left after removing the prefix, it is hopeless
-        if (!$encode_test) {
-            $this->_error('The given encoded string was empty');
-            return false;
-        }
-        // Find last occurence of the delimiter
-        $delim_pos = strrpos($encoded, '-');
-        if ($delim_pos > strlen($this->_punycode_prefix)) {
-            for ($k = strlen($this->_punycode_prefix); $k < $delim_pos; ++$k) {
-                $decoded[] = ord($encoded{$k});
-            }
-        } else {
-            $decoded = array();
-        }
-        $deco_len = count($decoded);
-        $enco_len = strlen($encoded);
-
-        // Wandering through the strings; init
-        $is_first = true;
-        $bias     = $this->_initial_bias;
-        $idx      = 0;
-        $char     = $this->_initial_n;
-
-        for ($enco_idx = ($delim_pos) ? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) {
-            for ($old_idx = $idx, $w = 1, $k = $this->_base; 1 ; $k += $this->_base) {
-                $digit = $this->_decode_digit($encoded{$enco_idx++});
-                $idx += $digit * $w;
-                $t = ($k <= $bias) ? $this->_tmin :
-                        (($k >= $bias + $this->_tmax) ? $this->_tmax : ($k - $bias));
-                if ($digit < $t) break;
-                $w = (int) ($w * ($this->_base - $t));
-            }
-            $bias = $this->_adapt($idx - $old_idx, $deco_len + 1, $is_first);
-            $is_first = false;
-            $char += (int) ($idx / ($deco_len + 1));
-            $idx %= ($deco_len + 1);
-            if ($deco_len > 0) {
-                // Make room for the decoded char
-                for ($i = $deco_len; $i > $idx; $i--) {
-                    $decoded[$i] = $decoded[($i - 1)];
-                }
-            }
-            $decoded[$idx++] = $char;
-        }
-        return $this->_ucs4_to_utf8($decoded);
-    }
-
-    /**
-     * The actual encoding algorithm
-     * @access   private
-     */
-    function _encode($decoded)
-    {
-        // We cannot encode a domain name containing the Punycode prefix
-        $extract = strlen($this->_punycode_prefix);
-        $check_pref = $this->_utf8_to_ucs4($this->_punycode_prefix);
-        $check_deco = array_slice($decoded, 0, $extract);
-
-        if ($check_pref == $check_deco) {
-            $this->_error('This is already a punycode string');
-            return false;
-        }
-        // We will not try to encode strings consisting of basic code points only
-        $encodable = false;
-        foreach ($decoded as $k => $v) {
-            if ($v > 0x7a) {
-                $encodable = true;
-                break;
-            }
-        }
-        if (!$encodable) {
-            $this->_error('The given string does not contain encodable chars');
-            return false;
-        }
-
-        // Do NAMEPREP
-        $decoded = $this->_nameprep($decoded);
-        if (!$decoded || !is_array($decoded)) return false; // NAMEPREP failed
-
-        $deco_len  = count($decoded);
-        if (!$deco_len) return false; // Empty array
-
-        $codecount = 0; // How many chars have been consumed
-
-        $encoded = '';
-        // Copy all basic code points to output
-        for ($i = 0; $i < $deco_len; ++$i) {
-            $test = $decoded[$i];
-            // Will match [-0-9a-zA-Z]
-            if ((0x2F < $test && $test < 0x40) || (0x40 < $test && $test < 0x5B)
-                    || (0x60 < $test && $test <= 0x7B) || (0x2D == $test)) {
-                $encoded .= chr($decoded[$i]);
-                $codecount++;
-            }
-        }
-        if ($codecount == $deco_len) return $encoded; // All codepoints were basic ones
-
-        // Start with the prefix; copy it to output
-        $encoded = $this->_punycode_prefix.$encoded;
-
-        // If we have basic code points in output, add an hyphen to the end
-        if ($codecount) $encoded .= '-';
-
-        // Now find and encode all non-basic code points
-        $is_first  = true;
-        $cur_code  = $this->_initial_n;
-        $bias      = $this->_initial_bias;
-        $delta     = 0;
-        while ($codecount < $deco_len) {
-            // Find the smallest code point >= the current code point and
-            // remember the last ouccrence of it in the input
-            for ($i = 0, $next_code = $this->_max_ucs; $i < $deco_len; $i++) {
-                if ($decoded[$i] >= $cur_code && $decoded[$i] <= $next_code) {
-                    $next_code = $decoded[$i];
-                }
-            }
-
-            $delta += ($next_code - $cur_code) * ($codecount + 1);
-            $cur_code = $next_code;
-
-            // Scan input again and encode all characters whose code point is $cur_code
-            for ($i = 0; $i < $deco_len; $i++) {
-                if ($decoded[$i] < $cur_code) {
-                    $delta++;
-                } elseif ($decoded[$i] == $cur_code) {
-                    for ($q = $delta, $k = $this->_base; 1; $k += $this->_base) {
-                        $t = ($k <= $bias) ? $this->_tmin :
-                                (($k >= $bias + $this->_tmax) ? $this->_tmax : $k - $bias);
-                        if ($q < $t) break;
-                        $encoded .= $this->_encode_digit(intval($t + (($q - $t) % ($this->_base - $t)))); //v0.4.5 Changed from ceil() to intval()
-                        $q = (int) (($q - $t) / ($this->_base - $t));
-                    }
-                    $encoded .= $this->_encode_digit($q);
-                    $bias = $this->_adapt($delta, $codecount+1, $is_first);
-                    $codecount++;
-                    $delta = 0;
-                    $is_first = false;
-                }
-            }
-            $delta++;
-            $cur_code++;
-        }
-        return $encoded;
-    }
-
-    /**
-     * Adapt the bias according to the current code point and position
-     * @access   private
-     */
-    function _adapt($delta, $npoints, $is_first)
-    {
-        $delta = intval($is_first ? ($delta / $this->_damp) : ($delta / 2));
-        $delta += intval($delta / $npoints);
-        for ($k = 0; $delta > (($this->_base - $this->_tmin) * $this->_tmax) / 2; $k += $this->_base) {
-            $delta = intval($delta / ($this->_base - $this->_tmin));
-        }
-        return intval($k + ($this->_base - $this->_tmin + 1) * $delta / ($delta + $this->_skew));
-    }
-
-    /**
-     * Encoding a certain digit
-     * @access   private
-     */
-    function _encode_digit($d)
-    {
-        return chr($d + 22 + 75 * ($d < 26));
-    }
-
-    /**
-     * Decode a certain digit
-     * @access   private
-     */
-    function _decode_digit($cp)
-    {
-        $cp = ord($cp);
-        return ($cp - 48 < 10) ? $cp - 22 : (($cp - 65 < 26) ? $cp - 65 : (($cp - 97 < 26) ? $cp - 97 : $this->_base));
-    }
-
-    /**
-     * Internal error handling method
-     * @access   private
-     */
-    function _error($error = '')
-    {
-        $this->_error = $error;
-    }
-
-    /**
-     * Do Nameprep according to RFC3491 and RFC3454
-     * @param    array    Unicode Characters
-     * @return   string   Unicode Characters, Nameprep'd
-     * @access   private
-     */
-    function _nameprep($input)
-    {
-        $output = array();
-        $error = false;
-        //
-        // Mapping
-        // Walking through the input array, performing the required steps on each of
-        // the input chars and putting the result into the output array
-        // While mapping required chars we apply the cannonical ordering
-        foreach ($input as $v) {
-            // Map to nothing == skip that code point
-            if (in_array($v, $this->NP['map_nothing'])) continue;
-
-            // Try to find prohibited input
-            if (in_array($v, $this->NP['prohibit']) || in_array($v, $this->NP['general_prohibited'])) {
-                $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X', $v));
-                return false;
-            }
-            foreach ($this->NP['prohibit_ranges'] as $range) {
-                if ($range[0] <= $v && $v <= $range[1]) {
-                    $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X', $v));
-                    return false;
-                }
-            }
-            //
-            // Hangul syllable decomposition
-            if (0xAC00 <= $v && $v <= 0xD7AF) {
-                foreach ($this->_hangul_decompose($v) as $out) {
-                    $output[] = (int) $out;
-                }
-            // There's a decomposition mapping for that code point
-            } elseif (isset($this->NP['replacemaps'][$v])) {
-                foreach ($this->_apply_cannonical_ordering($this->NP['replacemaps'][$v]) as $out) {
-                    $output[] = (int) $out;
-                }
-            } else {
-                $output[] = (int) $v;
-            }
-        }
-        // Before applying any Combining, try to rearrange any Hangul syllables
-        $output = $this->_hangul_compose($output);
-        //
-        // Combine code points
-        //
-        $last_class   = 0;
-        $last_starter = 0;
-        $out_len      = count($output);
-        for ($i = 0; $i < $out_len; ++$i) {
-            $class = $this->_get_combining_class($output[$i]);
-            if ((!$last_class || $last_class > $class) && $class) {
-                // Try to match
-                $seq_len = $i - $last_starter;
-                $out = $this->_combine(array_slice($output, $last_starter, $seq_len));
-                // On match: Replace the last starter with the composed character and remove
-                // the now redundant non-starter(s)
-                if ($out) {
-                    $output[$last_starter] = $out;
-                    if (count($out) != $seq_len) {
-                        for ($j = $i+1; $j < $out_len; ++$j) {
-                            $output[$j-1] = $output[$j];
-                        }
-                        unset($output[$out_len]);
-                    }
-                    // Rewind the for loop by one, since there can be more possible compositions
-                    $i--;
-                    $out_len--;
-                    $last_class = ($i == $last_starter) ? 0 : $this->_get_combining_class($output[$i-1]);
-                    continue;
-                }
-            }
-            // The current class is 0
-            if (!$class) $last_starter = $i;
-            $last_class = $class;
-        }
-        return $output;
-    }
-
-    /**
-     * Decomposes a Hangul syllable
-     * (see http://www.unicode.org/unicode/reports/tr15/#Hangul
-     * @param    integer  32bit UCS4 code point
-     * @return   array    Either Hangul Syllable decomposed or original 32bit value as one value array
-     * @access   private
-     */
-    function _hangul_decompose($char)
-    {
-        $sindex = (int) $char - $this->_sbase;
-        if ($sindex < 0 || $sindex >= $this->_scount) {
-            return array($char);
-        }
-        $result = array();
-        $result[] = (int) $this->_lbase + $sindex / $this->_ncount;
-        $result[] = (int) $this->_vbase + ($sindex % $this->_ncount) / $this->_tcount;
-        $T = intval($this->_tbase + $sindex % $this->_tcount);
-        if ($T != $this->_tbase) $result[] = $T;
-        return $result;
-    }
-    /**
-     * Ccomposes a Hangul syllable
-     * (see http://www.unicode.org/unicode/reports/tr15/#Hangul
-     * @param    array    Decomposed UCS4 sequence
-     * @return   array    UCS4 sequence with syllables composed
-     * @access   private
-     */
-    function _hangul_compose($input)
-    {
-        $inp_len = count($input);
-        if (!$inp_len) return array();
-        $result = array();
-        $last = (int) $input[0];
-        $result[] = $last; // copy first char from input to output
-
-        for ($i = 1; $i < $inp_len; ++$i) {
-            $char = (int) $input[$i];
-            $sindex = $last - $this->_sbase;
-            $lindex = $last - $this->_lbase;
-            $vindex = $char - $this->_vbase;
-            $tindex = $char - $this->_tbase;
-            // Find out, whether two current characters are LV and T
-            if (0 <= $sindex && $sindex < $this->_scount && ($sindex % $this->_tcount == 0)
-                    && 0 <= $tindex && $tindex <= $this->_tcount) {
-                // create syllable of form LVT
-                $last += $tindex;
-                $result[(count($result) - 1)] = $last; // reset last
-                continue; // discard char
-            }
-            // Find out, whether two current characters form L and V
-            if (0 <= $lindex && $lindex < $this->_lcount && 0 <= $vindex && $vindex < $this->_vcount) {
-                // create syllable of form LV
-                $last = (int) $this->_sbase + ($lindex * $this->_vcount + $vindex) * $this->_tcount;
-                $result[(count($result) - 1)] = $last; // reset last
-                continue; // discard char
-            }
-            // if neither case was true, just add the character
-            $last = $char;
-            $result[] = $char;
-        }
-        return $result;
-    }
-
-    /**
-     * Returns the combining class of a certain wide char
-     * @param    integer    Wide char to check (32bit integer)
-     * @return   integer    Combining class if found, else 0
-     * @access   private
-     */
-    function _get_combining_class($char)
-    {
-        return isset($this->NP['norm_combcls'][$char]) ? $this->NP['norm_combcls'][$char] : 0;
-    }
-
-    /**
-     * Apllies the cannonical ordering of a decomposed UCS4 sequence
-     * @param    array      Decomposed UCS4 sequence
-     * @return   array      Ordered USC4 sequence
-     * @access   private
-     */
-    function _apply_cannonical_ordering($input)
-    {
-        $swap = true;
-        $size = count($input);
-        while ($swap) {
-            $swap = false;
-            $last = $this->_get_combining_class(intval($input[0]));
-            for ($i = 0; $i < $size-1; ++$i) {
-                $next = $this->_get_combining_class(intval($input[$i+1]));
-                if ($next != 0 && $last > $next) {
-                    // Move item leftward until it fits
-                    for ($j = $i + 1; $j > 0; --$j) {
-                        if ($this->_get_combining_class(intval($input[$j-1])) <= $next) break;
-                        $t = intval($input[$j]);
-                        $input[$j] = intval($input[$j-1]);
-                        $input[$j-1] = $t;
-                        $swap = true;
-                    }
-                    // Reentering the loop looking at the old character again
-                    $next = $last;
-                }
-                $last = $next;
-            }
-        }
-        return $input;
-    }
-
-    /**
-     * Do composition of a sequence of starter and non-starter
-     * @param    array      UCS4 Decomposed sequence
-     * @return   array      Ordered USC4 sequence
-     * @access   private
-     */
-    function _combine($input)
-    {
-        $inp_len = count($input);
-        foreach ($this->NP['replacemaps'] as $np_src => $np_target) {
-            if ($np_target[0] != $input[0]) continue;
-            if (count($np_target) != $inp_len) continue;
-            $hit = false;
-            foreach ($input as $k2 => $v2) {
-                if ($v2 == $np_target[$k2]) {
-                    $hit = true;
-                } else {
-                    $hit = false;
-                    break;
-                }
-            }
-            if ($hit) return $np_src;
-        }
-        return false;
-    }
-
-    /**
-     * This converts an UTF-8 encoded string to its UCS-4 representation
-     * By talking about UCS-4 "strings" we mean arrays of 32bit integers representing
-     * each of the "chars". This is due to PHP not being able to handle strings with
-     * bit depth different from 8. This apllies to the reverse method _ucs4_to_utf8(), too.
-     * The following UTF-8 encodings are supported:
-     * bytes bits  representation
-     * 1        7  0xxxxxxx
-     * 2       11  110xxxxx 10xxxxxx
-     * 3       16  1110xxxx 10xxxxxx 10xxxxxx
-     * 4       21  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
-     * 5       26  111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
-     * 6       31  1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
-     * Each x represents a bit that can be used to store character data.
-     * The five and six byte sequences are part of Annex D of ISO/IEC 10646-1:2000
-     * @access   private
-     */
-    function _utf8_to_ucs4($input)
-    {
-        $output = array();
-        $out_len = 0;
-        $inp_len = strlen($input);
-        $mode = 'next';
-        $test = 'none';
-        for ($k = 0; $k < $inp_len; ++$k) {
-            $v = ord($input{$k}); // Extract byte from input string
-
-            if ($v < 128) { // We found an ASCII char - put into stirng as is
-                $output[$out_len] = $v;
-                ++$out_len;
-                if ('add' == $mode) {
-                    $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k);
-                    return false;
-                }
-                continue;
-            }
-            if ('next' == $mode) { // Try to find the next start byte; determine the width of the Unicode char
-                $start_byte = $v;
-                $mode = 'add';
-                $test = 'range';
-                if ($v >> 5 == 6) { // &110xxxxx 10xxxxx
-                    $next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left
-                    $v = ($v - 192) << 6;
-                } elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx
-                    $next_byte = 1;
-                    $v = ($v - 224) << 12;
-                } elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
-                    $next_byte = 2;
-                    $v = ($v - 240) << 18;
-                } elseif ($v >> 2 == 62) { // &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
-                    $next_byte = 3;
-                    $v = ($v - 248) << 24;
-                } elseif ($v >> 1 == 126) { // &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
-                    $next_byte = 4;
-                    $v = ($v - 252) << 30;
-                } else {
-                    $this->_error('This might be UTF-8, but I don\'t understand it at byte '.$k);
-                    return false;
-                }
-                if ('add' == $mode) {
-                    $output[$out_len] = (int) $v;
-                    ++$out_len;
-                    continue;
-                }
-            }
-            if ('add' == $mode) {
-                if (!$this->_allow_overlong && $test == 'range') {
-                    $test = 'none';
-                    if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) {
-                        $this->_error('Bogus UTF-8 character detected (out of legal range) at byte '.$k);
-                        return false;
-                    }
-                }
-                if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx
-                    $v = ($v - 128) << ($next_byte * 6);
-                    $output[($out_len - 1)] += $v;
-                    --$next_byte;
-                } else {
-                    $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k);
-                    return false;
-                }
-                if ($next_byte < 0) {
-                    $mode = 'next';
-                }
-            }
-        } // for
-        return $output;
-    }
-
-    /**
-     * Convert UCS-4 string into UTF-8 string
-     * See _utf8_to_ucs4() for details
-     * @access   private
-     */
-    function _ucs4_to_utf8($input)
-    {
-        $output = '';
-        $k = 0;
-        foreach ($input as $v) {
-            ++$k;
-            // $v = ord($v);
-            if ($v < 128) { // 7bit are transferred literally
-                $output .= chr($v);
-            } elseif ($v < (1 << 11)) { // 2 bytes
-                $output .= chr(192 + ($v >> 6)) . chr(128 + ($v & 63));
-            } elseif ($v < (1 << 16)) { // 3 bytes
-                $output .= chr(224 + ($v >> 12)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63));
-            } elseif ($v < (1 << 21)) { // 4 bytes
-                $output .= chr(240 + ($v >> 18)) . chr(128 + (($v >> 12) & 63))
-                         . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63));
-            } elseif ($v < (1 << 26)) { // 5 bytes
-                $output .= chr(248 + ($v >> 24)) . chr(128 + (($v >> 18) & 63))
-                         . chr(128 + (($v >> 12) & 63)) . chr(128 + (($v >> 6) & 63))
-                         . chr(128 + ($v & 63));
-            } elseif ($v < (1 << 31)) { // 6 bytes
-                $output .= chr(252 + ($v >> 30)) . chr(128 + (($v >> 24) & 63))
-                         . chr(128 + (($v >> 18) & 63)) . chr(128 + (($v >> 12) & 63))
-                         . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63));
-            } else {
-                $this->_error('Conversion from UCS-4 to UTF-8 failed: malformed input at byte '.$k);
-                return false;
-            }
-        }
-        return $output;
-    }
-
-    /**
-      * Convert UCS-4 array into UCS-4 string
-      *
-      * @access   private
-      */
-    function _ucs4_to_ucs4_string($input)
-    {
-        $output = '';
-        // Take array values and split output to 4 bytes per value
-        // The bit mask is 255, which reads &11111111
-        foreach ($input as $v) {
-            $output .= chr(($v >> 24) & 255).chr(($v >> 16) & 255).chr(($v >> 8) & 255).chr($v & 255);
-        }
-        return $output;
-    }
-
-    /**
-      * Convert UCS-4 strin into UCS-4 garray
-      *
-      * @access   private
-      */
-    function _ucs4_string_to_ucs4($input)
-    {
-        $output = array();
-        $inp_len = strlen($input);
-        // Input length must be dividable by 4
-        if ($inp_len % 4) {
-            $this->_error('Input UCS4 string is broken');
-            return false;
-        }
-        // Empty input - return empty output
-        if (!$inp_len) return $output;
-        for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) {
-            // Increment output position every 4 input bytes
-            if (!($i % 4)) {
-                $out_len++;
-                $output[$out_len] = 0;
-            }
-            $output[$out_len] += ord($input{$i}) << (8 * (3 - ($i % 4) ) );
-        }
-        return $output;
-    }
-}
-
-/**
-* Adapter class for aligning the API of idna_convert with that of Net_IDNA
-* @author  Matthias Sommerfeld <mso@phlylabs.de>
-*/
-class Net_IDNA_php4 extends idna_convert
-{
-    /**
-     * Sets a new option value. Available options and values:
-     * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8,
-     *         'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8]
-     * [overlong - Unicode does not allow unnecessarily long encodings of chars,
-     *             to allow this, set this parameter to true, else to false;
-     *             default is false.]
-     * [strict - true: strict mode, good for registration purposes - Causes errors
-     *           on failures; false: loose mode, ideal for "wildlife" applications
-     *           by silently ignoring errors and returning the original input instead
-     *
-     * @param    mixed     Parameter to set (string: single parameter; array of Parameter => Value pairs)
-     * @param    string    Value to use (if parameter 1 is a string)
-     * @return   boolean   true on success, false otherwise
-     * @access   public
-     */
-    function setParams($option, $param = false)
-    {
-        return $this->IC->set_parameters($option, $param);
-    }
-}
-
-?>
\ No newline at end of file
diff --git a/library/simplepie/idn/npdata.ser b/library/simplepie/idn/npdata.ser
deleted file mode 100644 (file)
index d7ce6d0..0000000
+++ /dev/null
@@ -1 +0,0 @@
-a:6:{s:11:"map_nothing";a:27:{i:0;i:173;i:1;i:847;i:2;i:6150;i:3;i:6155;i:4;i:6156;i:5;i:6157;i:6;i:8203;i:7;i:8204;i:8;i:8205;i:9;i:8288;i:10;i:65024;i:11;i:65025;i:12;i:65026;i:13;i:65027;i:14;i:65028;i:15;i:65029;i:16;i:65030;i:17;i:65031;i:18;i:65032;i:19;i:65033;i:20;i:65034;i:21;i:65035;i:22;i:65036;i:23;i:65037;i:24;i:65038;i:25;i:65039;i:26;i:65279;}s:18:"general_prohibited";a:64:{i:0;i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:7;i:7;i:8;i:8;i:9;i:9;i:10;i:10;i:11;i:11;i:12;i:12;i:13;i:13;i:14;i:14;i:15;i:15;i:16;i:16;i:17;i:17;i:18;i:18;i:19;i:19;i:20;i:20;i:21;i:21;i:22;i:22;i:23;i:23;i:24;i:24;i:25;i:25;i:26;i:26;i:27;i:27;i:28;i:28;i:29;i:29;i:30;i:30;i:31;i:31;i:32;i:32;i:33;i:33;i:34;i:34;i:35;i:35;i:36;i:36;i:37;i:37;i:38;i:38;i:39;i:39;i:40;i:40;i:41;i:41;i:42;i:42;i:43;i:43;i:44;i:44;i:45;i:47;i:46;i:59;i:47;i:60;i:48;i:61;i:49;i:62;i:50;i:63;i:51;i:64;i:52;i:91;i:53;i:92;i:54;i:93;i:55;i:94;i:56;i:95;i:57;i:96;i:58;i:123;i:59;i:124;i:60;i:125;i:61;i:126;i:62;i:127;i:63;i:12290;}s:8:"prohibit";a:84:{i:0;i:160;i:1;i:5760;i:2;i:8192;i:3;i:8193;i:4;i:8194;i:5;i:8195;i:6;i:8196;i:7;i:8197;i:8;i:8198;i:9;i:8199;i:10;i:8200;i:11;i:8201;i:12;i:8202;i:13;i:8203;i:14;i:8239;i:15;i:8287;i:16;i:12288;i:17;i:1757;i:18;i:1807;i:19;i:6158;i:20;i:8204;i:21;i:8205;i:22;i:8232;i:23;i:8233;i:24;i:65279;i:25;i:65529;i:26;i:65530;i:27;i:65531;i:28;i:65532;i:29;i:65534;i:30;i:65535;i:31;i:131070;i:32;i:131071;i:33;i:196606;i:34;i:196607;i:35;i:262142;i:36;i:262143;i:37;i:327678;i:38;i:327679;i:39;i:393214;i:40;i:393215;i:41;i:458750;i:42;i:458751;i:43;i:524286;i:44;i:524287;i:45;i:589822;i:46;i:589823;i:47;i:655358;i:48;i:655359;i:49;i:720894;i:50;i:720895;i:51;i:786430;i:52;i:786431;i:53;i:851966;i:54;i:851967;i:55;i:917502;i:56;i:917503;i:57;i:983038;i:58;i:983039;i:59;i:1048574;i:60;i:1048575;i:61;i:1114110;i:62;i:1114111;i:63;i:65529;i:64;i:65530;i:65;i:65531;i:66;i:65532;i:67;i:65533;i:68;i:832;i:69;i:833;i:70;i:8206;i:71;i:8207;i:72;i:8234;i:73;i:8235;i:74;i:8236;i:75;i:8237;i:76;i:8238;i:77;i:8298;i:78;i:8299;i:79;i:8300;i:80;i:8301;i:81;i:8302;i:82;i:8303;i:83;i:917505;}s:15:"prohibit_ranges";a:10:{i:0;a:2:{i:0;i:128;i:1;i:159;}i:1;a:2:{i:0;i:8288;i:1;i:8303;}i:2;a:2:{i:0;i:119155;i:1;i:119162;}i:3;a:2:{i:0;i:57344;i:1;i:63743;}i:4;a:2:{i:0;i:983040;i:1;i:1048573;}i:5;a:2:{i:0;i:1048576;i:1;i:1114109;}i:6;a:2:{i:0;i:64976;i:1;i:65007;}i:7;a:2:{i:0;i:55296;i:1;i:57343;}i:8;a:2:{i:0;i:12272;i:1;i:12283;}i:9;a:2:{i:0;i:917536;i:1;i:917631;}}s:11:"replacemaps";a:1401:{i:65;a:1:{i:0;i:97;}i:66;a:1:{i:0;i:98;}i:67;a:1:{i:0;i:99;}i:68;a:1:{i:0;i:100;}i:69;a:1:{i:0;i:101;}i:70;a:1:{i:0;i:102;}i:71;a:1:{i:0;i:103;}i:72;a:1:{i:0;i:104;}i:73;a:1:{i:0;i:105;}i:74;a:1:{i:0;i:106;}i:75;a:1:{i:0;i:107;}i:76;a:1:{i:0;i:108;}i:77;a:1:{i:0;i:109;}i:78;a:1:{i:0;i:110;}i:79;a:1:{i:0;i:111;}i:80;a:1:{i:0;i:112;}i:81;a:1:{i:0;i:113;}i:82;a:1:{i:0;i:114;}i:83;a:1:{i:0;i:115;}i:84;a:1:{i:0;i:116;}i:85;a:1:{i:0;i:117;}i:86;a:1:{i:0;i:118;}i:87;a:1:{i:0;i:119;}i:88;a:1:{i:0;i:120;}i:89;a:1:{i:0;i:121;}i:90;a:1:{i:0;i:122;}i:181;a:1:{i:0;i:956;}i:192;a:1:{i:0;i:224;}i:193;a:1:{i:0;i:225;}i:194;a:1:{i:0;i:226;}i:195;a:1:{i:0;i:227;}i:196;a:1:{i:0;i:228;}i:197;a:1:{i:0;i:229;}i:198;a:1:{i:0;i:230;}i:199;a:1:{i:0;i:231;}i:200;a:1:{i:0;i:232;}i:201;a:1:{i:0;i:233;}i:202;a:1:{i:0;i:234;}i:203;a:1:{i:0;i:235;}i:204;a:1:{i:0;i:236;}i:205;a:1:{i:0;i:237;}i:206;a:1:{i:0;i:238;}i:207;a:1:{i:0;i:239;}i:208;a:1:{i:0;i:240;}i:209;a:1:{i:0;i:241;}i:210;a:1:{i:0;i:242;}i:211;a:1:{i:0;i:243;}i:212;a:1:{i:0;i:244;}i:213;a:1:{i:0;i:245;}i:214;a:1:{i:0;i:246;}i:216;a:1:{i:0;i:248;}i:217;a:1:{i:0;i:249;}i:218;a:1:{i:0;i:250;}i:219;a:1:{i:0;i:251;}i:220;a:1:{i:0;i:252;}i:221;a:1:{i:0;i:253;}i:222;a:1:{i:0;i:254;}i:223;a:2:{i:0;i:115;i:1;i:115;}i:256;a:1:{i:0;i:257;}i:258;a:1:{i:0;i:259;}i:260;a:1:{i:0;i:261;}i:262;a:1:{i:0;i:263;}i:264;a:1:{i:0;i:265;}i:266;a:1:{i:0;i:267;}i:268;a:1:{i:0;i:269;}i:270;a:1:{i:0;i:271;}i:272;a:1:{i:0;i:273;}i:274;a:1:{i:0;i:275;}i:276;a:1:{i:0;i:277;}i:278;a:1:{i:0;i:279;}i:280;a:1:{i:0;i:281;}i:282;a:1:{i:0;i:283;}i:284;a:1:{i:0;i:285;}i:286;a:1:{i:0;i:287;}i:288;a:1:{i:0;i:289;}i:290;a:1:{i:0;i:291;}i:292;a:1:{i:0;i:293;}i:294;a:1:{i:0;i:295;}i:296;a:1:{i:0;i:297;}i:298;a:1:{i:0;i:299;}i:300;a:1:{i:0;i:301;}i:302;a:1:{i:0;i:303;}i:304;a:2:{i:0;i:105;i:1;i:775;}i:306;a:1:{i:0;i:307;}i:308;a:1:{i:0;i:309;}i:310;a:1:{i:0;i:311;}i:313;a:1:{i:0;i:314;}i:315;a:1:{i:0;i:316;}i:317;a:1:{i:0;i:318;}i:319;a:1:{i:0;i:320;}i:321;a:1:{i:0;i:322;}i:323;a:1:{i:0;i:324;}i:325;a:1:{i:0;i:326;}i:327;a:1:{i:0;i:328;}i:329;a:2:{i:0;i:700;i:1;i:110;}i:330;a:1:{i:0;i:331;}i:332;a:1:{i:0;i:333;}i:334;a:1:{i:0;i:335;}i:336;a:1:{i:0;i:337;}i:338;a:1:{i:0;i:339;}i:340;a:1:{i:0;i:341;}i:342;a:1:{i:0;i:343;}i:344;a:1:{i:0;i:345;}i:346;a:1:{i:0;i:347;}i:348;a:1:{i:0;i:349;}i:350;a:1:{i:0;i:351;}i:352;a:1:{i:0;i:353;}i:354;a:1:{i:0;i:355;}i:356;a:1:{i:0;i:357;}i:358;a:1:{i:0;i:359;}i:360;a:1:{i:0;i:361;}i:362;a:1:{i:0;i:363;}i:364;a:1:{i:0;i:365;}i:366;a:1:{i:0;i:367;}i:368;a:1:{i:0;i:369;}i:370;a:1:{i:0;i:371;}i:372;a:1:{i:0;i:373;}i:374;a:1:{i:0;i:375;}i:376;a:1:{i:0;i:255;}i:377;a:1:{i:0;i:378;}i:379;a:1:{i:0;i:380;}i:381;a:1:{i:0;i:382;}i:383;a:1:{i:0;i:115;}i:385;a:1:{i:0;i:595;}i:386;a:1:{i:0;i:387;}i:388;a:1:{i:0;i:389;}i:390;a:1:{i:0;i:596;}i:391;a:1:{i:0;i:392;}i:393;a:1:{i:0;i:598;}i:394;a:1:{i:0;i:599;}i:395;a:1:{i:0;i:396;}i:398;a:1:{i:0;i:477;}i:399;a:1:{i:0;i:601;}i:400;a:1:{i:0;i:603;}i:401;a:1:{i:0;i:402;}i:403;a:1:{i:0;i:608;}i:404;a:1:{i:0;i:611;}i:406;a:1:{i:0;i:617;}i:407;a:1:{i:0;i:616;}i:408;a:1:{i:0;i:409;}i:412;a:1:{i:0;i:623;}i:413;a:1:{i:0;i:626;}i:415;a:1:{i:0;i:629;}i:416;a:1:{i:0;i:417;}i:418;a:1:{i:0;i:419;}i:420;a:1:{i:0;i:421;}i:422;a:1:{i:0;i:640;}i:423;a:1:{i:0;i:424;}i:425;a:1:{i:0;i:643;}i:428;a:1:{i:0;i:429;}i:430;a:1:{i:0;i:648;}i:431;a:1:{i:0;i:432;}i:433;a:1:{i:0;i:650;}i:434;a:1:{i:0;i:651;}i:435;a:1:{i:0;i:436;}i:437;a:1:{i:0;i:438;}i:439;a:1:{i:0;i:658;}i:440;a:1:{i:0;i:441;}i:444;a:1:{i:0;i:445;}i:452;a:1:{i:0;i:454;}i:453;a:1:{i:0;i:454;}i:455;a:1:{i:0;i:457;}i:456;a:1:{i:0;i:457;}i:458;a:1:{i:0;i:460;}i:459;a:1:{i:0;i:460;}i:461;a:1:{i:0;i:462;}i:463;a:1:{i:0;i:464;}i:465;a:1:{i:0;i:466;}i:467;a:1:{i:0;i:468;}i:469;a:1:{i:0;i:470;}i:471;a:1:{i:0;i:472;}i:473;a:1:{i:0;i:474;}i:475;a:1:{i:0;i:476;}i:478;a:1:{i:0;i:479;}i:480;a:1:{i:0;i:481;}i:482;a:1:{i:0;i:483;}i:484;a:1:{i:0;i:485;}i:486;a:1:{i:0;i:487;}i:488;a:1:{i:0;i:489;}i:490;a:1:{i:0;i:491;}i:492;a:1:{i:0;i:493;}i:494;a:1:{i:0;i:495;}i:496;a:2:{i:0;i:106;i:1;i:780;}i:497;a:1:{i:0;i:499;}i:498;a:1:{i:0;i:499;}i:500;a:1:{i:0;i:501;}i:502;a:1:{i:0;i:405;}i:503;a:1:{i:0;i:447;}i:504;a:1:{i:0;i:505;}i:506;a:1:{i:0;i:507;}i:508;a:1:{i:0;i:509;}i:510;a:1:{i:0;i:511;}i:512;a:1:{i:0;i:513;}i:514;a:1:{i:0;i:515;}i:516;a:1:{i:0;i:517;}i:518;a:1:{i:0;i:519;}i:520;a:1:{i:0;i:521;}i:522;a:1:{i:0;i:523;}i:524;a:1:{i:0;i:525;}i:526;a:1:{i:0;i:527;}i:528;a:1:{i:0;i:529;}i:530;a:1:{i:0;i:531;}i:532;a:1:{i:0;i:533;}i:534;a:1:{i:0;i:535;}i:536;a:1:{i:0;i:537;}i:538;a:1:{i:0;i:539;}i:540;a:1:{i:0;i:541;}i:542;a:1:{i:0;i:543;}i:544;a:1:{i:0;i:414;}i:546;a:1:{i:0;i:547;}i:548;a:1:{i:0;i:549;}i:550;a:1:{i:0;i:551;}i:552;a:1:{i:0;i:553;}i:554;a:1:{i:0;i:555;}i:556;a:1:{i:0;i:557;}i:558;a:1:{i:0;i:559;}i:560;a:1:{i:0;i:561;}i:562;a:1:{i:0;i:563;}i:837;a:1:{i:0;i:953;}i:890;a:2:{i:0;i:32;i:1;i:953;}i:902;a:1:{i:0;i:940;}i:904;a:1:{i:0;i:941;}i:905;a:1:{i:0;i:942;}i:906;a:1:{i:0;i:943;}i:908;a:1:{i:0;i:972;}i:910;a:1:{i:0;i:973;}i:911;a:1:{i:0;i:974;}i:912;a:3:{i:0;i:953;i:1;i:776;i:2;i:769;}i:913;a:1:{i:0;i:945;}i:914;a:1:{i:0;i:946;}i:915;a:1:{i:0;i:947;}i:916;a:1:{i:0;i:948;}i:917;a:1:{i:0;i:949;}i:918;a:1:{i:0;i:950;}i:919;a:1:{i:0;i:951;}i:920;a:1:{i:0;i:952;}i:921;a:1:{i:0;i:953;}i:922;a:1:{i:0;i:954;}i:923;a:1:{i:0;i:955;}i:924;a:1:{i:0;i:956;}i:925;a:1:{i:0;i:957;}i:926;a:1:{i:0;i:958;}i:927;a:1:{i:0;i:959;}i:928;a:1:{i:0;i:960;}i:929;a:1:{i:0;i:961;}i:931;a:1:{i:0;i:963;}i:932;a:1:{i:0;i:964;}i:933;a:1:{i:0;i:965;}i:934;a:1:{i:0;i:966;}i:935;a:1:{i:0;i:967;}i:936;a:1:{i:0;i:968;}i:937;a:1:{i:0;i:969;}i:938;a:1:{i:0;i:970;}i:939;a:1:{i:0;i:971;}i:944;a:3:{i:0;i:965;i:1;i:776;i:2;i:769;}i:962;a:1:{i:0;i:963;}i:976;a:1:{i:0;i:946;}i:977;a:1:{i:0;i:952;}i:978;a:1:{i:0;i:965;}i:979;a:1:{i:0;i:973;}i:980;a:1:{i:0;i:971;}i:981;a:1:{i:0;i:966;}i:982;a:1:{i:0;i:960;}i:984;a:1:{i:0;i:985;}i:986;a:1:{i:0;i:987;}i:988;a:1:{i:0;i:989;}i:990;a:1:{i:0;i:991;}i:992;a:1:{i:0;i:993;}i:994;a:1:{i:0;i:995;}i:996;a:1:{i:0;i:997;}i:998;a:1:{i:0;i:999;}i:1000;a:1:{i:0;i:1001;}i:1002;a:1:{i:0;i:1003;}i:1004;a:1:{i:0;i:1005;}i:1006;a:1:{i:0;i:1007;}i:1008;a:1:{i:0;i:954;}i:1009;a:1:{i:0;i:961;}i:1010;a:1:{i:0;i:963;}i:1012;a:1:{i:0;i:952;}i:1013;a:1:{i:0;i:949;}i:1024;a:1:{i:0;i:1104;}i:1025;a:1:{i:0;i:1105;}i:1026;a:1:{i:0;i:1106;}i:1027;a:1:{i:0;i:1107;}i:1028;a:1:{i:0;i:1108;}i:1029;a:1:{i:0;i:1109;}i:1030;a:1:{i:0;i:1110;}i:1031;a:1:{i:0;i:1111;}i:1032;a:1:{i:0;i:1112;}i:1033;a:1:{i:0;i:1113;}i:1034;a:1:{i:0;i:1114;}i:1035;a:1:{i:0;i:1115;}i:1036;a:1:{i:0;i:1116;}i:1037;a:1:{i:0;i:1117;}i:1038;a:1:{i:0;i:1118;}i:1039;a:1:{i:0;i:1119;}i:1040;a:1:{i:0;i:1072;}i:1041;a:1:{i:0;i:1073;}i:1042;a:1:{i:0;i:1074;}i:1043;a:1:{i:0;i:1075;}i:1044;a:1:{i:0;i:1076;}i:1045;a:1:{i:0;i:1077;}i:1046;a:1:{i:0;i:1078;}i:1047;a:1:{i:0;i:1079;}i:1048;a:1:{i:0;i:1080;}i:1049;a:1:{i:0;i:1081;}i:1050;a:1:{i:0;i:1082;}i:1051;a:1:{i:0;i:1083;}i:1052;a:1:{i:0;i:1084;}i:1053;a:1:{i:0;i:1085;}i:1054;a:1:{i:0;i:1086;}i:1055;a:1:{i:0;i:1087;}i:1056;a:1:{i:0;i:1088;}i:1057;a:1:{i:0;i:1089;}i:1058;a:1:{i:0;i:1090;}i:1059;a:1:{i:0;i:1091;}i:1060;a:1:{i:0;i:1092;}i:1061;a:1:{i:0;i:1093;}i:1062;a:1:{i:0;i:1094;}i:1063;a:1:{i:0;i:1095;}i:1064;a:1:{i:0;i:1096;}i:1065;a:1:{i:0;i:1097;}i:1066;a:1:{i:0;i:1098;}i:1067;a:1:{i:0;i:1099;}i:1068;a:1:{i:0;i:1100;}i:1069;a:1:{i:0;i:1101;}i:1070;a:1:{i:0;i:1102;}i:1071;a:1:{i:0;i:1103;}i:1120;a:1:{i:0;i:1121;}i:1122;a:1:{i:0;i:1123;}i:1124;a:1:{i:0;i:1125;}i:1126;a:1:{i:0;i:1127;}i:1128;a:1:{i:0;i:1129;}i:1130;a:1:{i:0;i:1131;}i:1132;a:1:{i:0;i:1133;}i:1134;a:1:{i:0;i:1135;}i:1136;a:1:{i:0;i:1137;}i:1138;a:1:{i:0;i:1139;}i:1140;a:1:{i:0;i:1141;}i:1142;a:1:{i:0;i:1143;}i:1144;a:1:{i:0;i:1145;}i:1146;a:1:{i:0;i:1147;}i:1148;a:1:{i:0;i:1149;}i:1150;a:1:{i:0;i:1151;}i:1152;a:1:{i:0;i:1153;}i:1162;a:1:{i:0;i:1163;}i:1164;a:1:{i:0;i:1165;}i:1166;a:1:{i:0;i:1167;}i:1168;a:1:{i:0;i:1169;}i:1170;a:1:{i:0;i:1171;}i:1172;a:1:{i:0;i:1173;}i:1174;a:1:{i:0;i:1175;}i:1176;a:1:{i:0;i:1177;}i:1178;a:1:{i:0;i:1179;}i:1180;a:1:{i:0;i:1181;}i:1182;a:1:{i:0;i:1183;}i:1184;a:1:{i:0;i:1185;}i:1186;a:1:{i:0;i:1187;}i:1188;a:1:{i:0;i:1189;}i:1190;a:1:{i:0;i:1191;}i:1192;a:1:{i:0;i:1193;}i:1194;a:1:{i:0;i:1195;}i:1196;a:1:{i:0;i:1197;}i:1198;a:1:{i:0;i:1199;}i:1200;a:1:{i:0;i:1201;}i:1202;a:1:{i:0;i:1203;}i:1204;a:1:{i:0;i:1205;}i:1206;a:1:{i:0;i:1207;}i:1208;a:1:{i:0;i:1209;}i:1210;a:1:{i:0;i:1211;}i:1212;a:1:{i:0;i:1213;}i:1214;a:1:{i:0;i:1215;}i:1217;a:1:{i:0;i:1218;}i:1219;a:1:{i:0;i:1220;}i:1221;a:1:{i:0;i:1222;}i:1223;a:1:{i:0;i:1224;}i:1225;a:1:{i:0;i:1226;}i:1227;a:1:{i:0;i:1228;}i:1229;a:1:{i:0;i:1230;}i:1232;a:1:{i:0;i:1233;}i:1234;a:1:{i:0;i:1235;}i:1236;a:1:{i:0;i:1237;}i:1238;a:1:{i:0;i:1239;}i:1240;a:1:{i:0;i:1241;}i:1242;a:1:{i:0;i:1243;}i:1244;a:1:{i:0;i:1245;}i:1246;a:1:{i:0;i:1247;}i:1248;a:1:{i:0;i:1249;}i:1250;a:1:{i:0;i:1251;}i:1252;a:1:{i:0;i:1253;}i:1254;a:1:{i:0;i:1255;}i:1256;a:1:{i:0;i:1257;}i:1258;a:1:{i:0;i:1259;}i:1260;a:1:{i:0;i:1261;}i:1262;a:1:{i:0;i:1263;}i:1264;a:1:{i:0;i:1265;}i:1266;a:1:{i:0;i:1267;}i:1268;a:1:{i:0;i:1269;}i:1272;a:1:{i:0;i:1273;}i:1280;a:1:{i:0;i:1281;}i:1282;a:1:{i:0;i:1283;}i:1284;a:1:{i:0;i:1285;}i:1286;a:1:{i:0;i:1287;}i:1288;a:1:{i:0;i:1289;}i:1290;a:1:{i:0;i:1291;}i:1292;a:1:{i:0;i:1293;}i:1294;a:1:{i:0;i:1295;}i:1329;a:1:{i:0;i:1377;}i:1330;a:1:{i:0;i:1378;}i:1331;a:1:{i:0;i:1379;}i:1332;a:1:{i:0;i:1380;}i:1333;a:1:{i:0;i:1381;}i:1334;a:1:{i:0;i:1382;}i:1335;a:1:{i:0;i:1383;}i:1336;a:1:{i:0;i:1384;}i:1337;a:1:{i:0;i:1385;}i:1338;a:1:{i:0;i:1386;}i:1339;a:1:{i:0;i:1387;}i:1340;a:1:{i:0;i:1388;}i:1341;a:1:{i:0;i:1389;}i:1342;a:1:{i:0;i:1390;}i:1343;a:1:{i:0;i:1391;}i:1344;a:1:{i:0;i:1392;}i:1345;a:1:{i:0;i:1393;}i:1346;a:1:{i:0;i:1394;}i:1347;a:1:{i:0;i:1395;}i:1348;a:1:{i:0;i:1396;}i:1349;a:1:{i:0;i:1397;}i:1350;a:1:{i:0;i:1398;}i:1351;a:1:{i:0;i:1399;}i:1352;a:1:{i:0;i:1400;}i:1353;a:1:{i:0;i:1401;}i:1354;a:1:{i:0;i:1402;}i:1355;a:1:{i:0;i:1403;}i:1356;a:1:{i:0;i:1404;}i:1357;a:1:{i:0;i:1405;}i:1358;a:1:{i:0;i:1406;}i:1359;a:1:{i:0;i:1407;}i:1360;a:1:{i:0;i:1408;}i:1361;a:1:{i:0;i:1409;}i:1362;a:1:{i:0;i:1410;}i:1363;a:1:{i:0;i:1411;}i:1364;a:1:{i:0;i:1412;}i:1365;a:1:{i:0;i:1413;}i:1366;a:1:{i:0;i:1414;}i:1415;a:2:{i:0;i:1381;i:1;i:1410;}i:7680;a:1:{i:0;i:7681;}i:7682;a:1:{i:0;i:7683;}i:7684;a:1:{i:0;i:7685;}i:7686;a:1:{i:0;i:7687;}i:7688;a:1:{i:0;i:7689;}i:7690;a:1:{i:0;i:7691;}i:7692;a:1:{i:0;i:7693;}i:7694;a:1:{i:0;i:7695;}i:7696;a:1:{i:0;i:7697;}i:7698;a:1:{i:0;i:7699;}i:7700;a:1:{i:0;i:7701;}i:7702;a:1:{i:0;i:7703;}i:7704;a:1:{i:0;i:7705;}i:7706;a:1:{i:0;i:7707;}i:7708;a:1:{i:0;i:7709;}i:7710;a:1:{i:0;i:7711;}i:7712;a:1:{i:0;i:7713;}i:7714;a:1:{i:0;i:7715;}i:7716;a:1:{i:0;i:7717;}i:7718;a:1:{i:0;i:7719;}i:7720;a:1:{i:0;i:7721;}i:7722;a:1:{i:0;i:7723;}i:7724;a:1:{i:0;i:7725;}i:7726;a:1:{i:0;i:7727;}i:7728;a:1:{i:0;i:7729;}i:7730;a:1:{i:0;i:7731;}i:7732;a:1:{i:0;i:7733;}i:7734;a:1:{i:0;i:7735;}i:7736;a:1:{i:0;i:7737;}i:7738;a:1:{i:0;i:7739;}i:7740;a:1:{i:0;i:7741;}i:7742;a:1:{i:0;i:7743;}i:7744;a:1:{i:0;i:7745;}i:7746;a:1:{i:0;i:7747;}i:7748;a:1:{i:0;i:7749;}i:7750;a:1:{i:0;i:7751;}i:7752;a:1:{i:0;i:7753;}i:7754;a:1:{i:0;i:7755;}i:7756;a:1:{i:0;i:7757;}i:7758;a:1:{i:0;i:7759;}i:7760;a:1:{i:0;i:7761;}i:7762;a:1:{i:0;i:7763;}i:7764;a:1:{i:0;i:7765;}i:7766;a:1:{i:0;i:7767;}i:7768;a:1:{i:0;i:7769;}i:7770;a:1:{i:0;i:7771;}i:7772;a:1:{i:0;i:7773;}i:7774;a:1:{i:0;i:7775;}i:7776;a:1:{i:0;i:7777;}i:7778;a:1:{i:0;i:7779;}i:7780;a:1:{i:0;i:7781;}i:7782;a:1:{i:0;i:7783;}i:7784;a:1:{i:0;i:7785;}i:7786;a:1:{i:0;i:7787;}i:7788;a:1:{i:0;i:7789;}i:7790;a:1:{i:0;i:7791;}i:7792;a:1:{i:0;i:7793;}i:7794;a:1:{i:0;i:7795;}i:7796;a:1:{i:0;i:7797;}i:7798;a:1:{i:0;i:7799;}i:7800;a:1:{i:0;i:7801;}i:7802;a:1:{i:0;i:7803;}i:7804;a:1:{i:0;i:7805;}i:7806;a:1:{i:0;i:7807;}i:7808;a:1:{i:0;i:7809;}i:7810;a:1:{i:0;i:7811;}i:7812;a:1:{i:0;i:7813;}i:7814;a:1:{i:0;i:7815;}i:7816;a:1:{i:0;i:7817;}i:7818;a:1:{i:0;i:7819;}i:7820;a:1:{i:0;i:7821;}i:7822;a:1:{i:0;i:7823;}i:7824;a:1:{i:0;i:7825;}i:7826;a:1:{i:0;i:7827;}i:7828;a:1:{i:0;i:7829;}i:7830;a:2:{i:0;i:104;i:1;i:817;}i:7831;a:2:{i:0;i:116;i:1;i:776;}i:7832;a:2:{i:0;i:119;i:1;i:778;}i:7833;a:2:{i:0;i:121;i:1;i:778;}i:7834;a:2:{i:0;i:97;i:1;i:702;}i:7835;a:1:{i:0;i:7777;}i:7840;a:1:{i:0;i:7841;}i:7842;a:1:{i:0;i:7843;}i:7844;a:1:{i:0;i:7845;}i:7846;a:1:{i:0;i:7847;}i:7848;a:1:{i:0;i:7849;}i:7850;a:1:{i:0;i:7851;}i:7852;a:1:{i:0;i:7853;}i:7854;a:1:{i:0;i:7855;}i:7856;a:1:{i:0;i:7857;}i:7858;a:1:{i:0;i:7859;}i:7860;a:1:{i:0;i:7861;}i:7862;a:1:{i:0;i:7863;}i:7864;a:1:{i:0;i:7865;}i:7866;a:1:{i:0;i:7867;}i:7868;a:1:{i:0;i:7869;}i:7870;a:1:{i:0;i:7871;}i:7872;a:1:{i:0;i:7873;}i:7874;a:1:{i:0;i:7875;}i:7876;a:1:{i:0;i:7877;}i:7878;a:1:{i:0;i:7879;}i:7880;a:1:{i:0;i:7881;}i:7882;a:1:{i:0;i:7883;}i:7884;a:1:{i:0;i:7885;}i:7886;a:1:{i:0;i:7887;}i:7888;a:1:{i:0;i:7889;}i:7890;a:1:{i:0;i:7891;}i:7892;a:1:{i:0;i:7893;}i:7894;a:1:{i:0;i:7895;}i:7896;a:1:{i:0;i:7897;}i:7898;a:1:{i:0;i:7899;}i:7900;a:1:{i:0;i:7901;}i:7902;a:1:{i:0;i:7903;}i:7904;a:1:{i:0;i:7905;}i:7906;a:1:{i:0;i:7907;}i:7908;a:1:{i:0;i:7909;}i:7910;a:1:{i:0;i:7911;}i:7912;a:1:{i:0;i:7913;}i:7914;a:1:{i:0;i:7915;}i:7916;a:1:{i:0;i:7917;}i:7918;a:1:{i:0;i:7919;}i:7920;a:1:{i:0;i:7921;}i:7922;a:1:{i:0;i:7923;}i:7924;a:1:{i:0;i:7925;}i:7926;a:1:{i:0;i:7927;}i:7928;a:1:{i:0;i:7929;}i:7944;a:1:{i:0;i:7936;}i:7945;a:1:{i:0;i:7937;}i:7946;a:1:{i:0;i:7938;}i:7947;a:1:{i:0;i:7939;}i:7948;a:1:{i:0;i:7940;}i:7949;a:1:{i:0;i:7941;}i:7950;a:1:{i:0;i:7942;}i:7951;a:1:{i:0;i:7943;}i:7960;a:1:{i:0;i:7952;}i:7961;a:1:{i:0;i:7953;}i:7962;a:1:{i:0;i:7954;}i:7963;a:1:{i:0;i:7955;}i:7964;a:1:{i:0;i:7956;}i:7965;a:1:{i:0;i:7957;}i:7976;a:1:{i:0;i:7968;}i:7977;a:1:{i:0;i:7969;}i:7978;a:1:{i:0;i:7970;}i:7979;a:1:{i:0;i:7971;}i:7980;a:1:{i:0;i:7972;}i:7981;a:1:{i:0;i:7973;}i:7982;a:1:{i:0;i:7974;}i:7983;a:1:{i:0;i:7975;}i:7992;a:1:{i:0;i:7984;}i:7993;a:1:{i:0;i:7985;}i:7994;a:1:{i:0;i:7986;}i:7995;a:1:{i:0;i:7987;}i:7996;a:1:{i:0;i:7988;}i:7997;a:1:{i:0;i:7989;}i:7998;a:1:{i:0;i:7990;}i:7999;a:1:{i:0;i:7991;}i:8008;a:1:{i:0;i:8000;}i:8009;a:1:{i:0;i:8001;}i:8010;a:1:{i:0;i:8002;}i:8011;a:1:{i:0;i:8003;}i:8012;a:1:{i:0;i:8004;}i:8013;a:1:{i:0;i:8005;}i:8016;a:2:{i:0;i:965;i:1;i:787;}i:8018;a:3:{i:0;i:965;i:1;i:787;i:2;i:768;}i:8020;a:3:{i:0;i:965;i:1;i:787;i:2;i:769;}i:8022;a:3:{i:0;i:965;i:1;i:787;i:2;i:834;}i:8025;a:1:{i:0;i:8017;}i:8027;a:1:{i:0;i:8019;}i:8029;a:1:{i:0;i:8021;}i:8031;a:1:{i:0;i:8023;}i:8040;a:1:{i:0;i:8032;}i:8041;a:1:{i:0;i:8033;}i:8042;a:1:{i:0;i:8034;}i:8043;a:1:{i:0;i:8035;}i:8044;a:1:{i:0;i:8036;}i:8045;a:1:{i:0;i:8037;}i:8046;a:1:{i:0;i:8038;}i:8047;a:1:{i:0;i:8039;}i:8064;a:2:{i:0;i:7936;i:1;i:953;}i:8065;a:2:{i:0;i:7937;i:1;i:953;}i:8066;a:2:{i:0;i:7938;i:1;i:953;}i:8067;a:2:{i:0;i:7939;i:1;i:953;}i:8068;a:2:{i:0;i:7940;i:1;i:953;}i:8069;a:2:{i:0;i:7941;i:1;i:953;}i:8070;a:2:{i:0;i:7942;i:1;i:953;}i:8071;a:2:{i:0;i:7943;i:1;i:953;}i:8072;a:2:{i:0;i:7936;i:1;i:953;}i:8073;a:2:{i:0;i:7937;i:1;i:953;}i:8074;a:2:{i:0;i:7938;i:1;i:953;}i:8075;a:2:{i:0;i:7939;i:1;i:953;}i:8076;a:2:{i:0;i:7940;i:1;i:953;}i:8077;a:2:{i:0;i:7941;i:1;i:953;}i:8078;a:2:{i:0;i:7942;i:1;i:953;}i:8079;a:2:{i:0;i:7943;i:1;i:953;}i:8080;a:2:{i:0;i:7968;i:1;i:953;}i:8081;a:2:{i:0;i:7969;i:1;i:953;}i:8082;a:2:{i:0;i:7970;i:1;i:953;}i:8083;a:2:{i:0;i:7971;i:1;i:953;}i:8084;a:2:{i:0;i:7972;i:1;i:953;}i:8085;a:2:{i:0;i:7973;i:1;i:953;}i:8086;a:2:{i:0;i:7974;i:1;i:953;}i:8087;a:2:{i:0;i:7975;i:1;i:953;}i:8088;a:2:{i:0;i:7968;i:1;i:953;}i:8089;a:2:{i:0;i:7969;i:1;i:953;}i:8090;a:2:{i:0;i:7970;i:1;i:953;}i:8091;a:2:{i:0;i:7971;i:1;i:953;}i:8092;a:2:{i:0;i:7972;i:1;i:953;}i:8093;a:2:{i:0;i:7973;i:1;i:953;}i:8094;a:2:{i:0;i:7974;i:1;i:953;}i:8095;a:2:{i:0;i:7975;i:1;i:953;}i:8096;a:2:{i:0;i:8032;i:1;i:953;}i:8097;a:2:{i:0;i:8033;i:1;i:953;}i:8098;a:2:{i:0;i:8034;i:1;i:953;}i:8099;a:2:{i:0;i:8035;i:1;i:953;}i:8100;a:2:{i:0;i:8036;i:1;i:953;}i:8101;a:2:{i:0;i:8037;i:1;i:953;}i:8102;a:2:{i:0;i:8038;i:1;i:953;}i:8103;a:2:{i:0;i:8039;i:1;i:953;}i:8104;a:2:{i:0;i:8032;i:1;i:953;}i:8105;a:2:{i:0;i:8033;i:1;i:953;}i:8106;a:2:{i:0;i:8034;i:1;i:953;}i:8107;a:2:{i:0;i:8035;i:1;i:953;}i:8108;a:2:{i:0;i:8036;i:1;i:953;}i:8109;a:2:{i:0;i:8037;i:1;i:953;}i:8110;a:2:{i:0;i:8038;i:1;i:953;}i:8111;a:2:{i:0;i:8039;i:1;i:953;}i:8114;a:2:{i:0;i:8048;i:1;i:953;}i:8115;a:2:{i:0;i:945;i:1;i:953;}i:8116;a:2:{i:0;i:940;i:1;i:953;}i:8118;a:2:{i:0;i:945;i:1;i:834;}i:8119;a:3:{i:0;i:945;i:1;i:834;i:2;i:953;}i:8120;a:1:{i:0;i:8112;}i:8121;a:1:{i:0;i:8113;}i:8122;a:1:{i:0;i:8048;}i:8123;a:1:{i:0;i:8049;}i:8124;a:2:{i:0;i:945;i:1;i:953;}i:8126;a:1:{i:0;i:953;}i:8130;a:2:{i:0;i:8052;i:1;i:953;}i:8131;a:2:{i:0;i:951;i:1;i:953;}i:8132;a:2:{i:0;i:942;i:1;i:953;}i:8134;a:2:{i:0;i:951;i:1;i:834;}i:8135;a:3:{i:0;i:951;i:1;i:834;i:2;i:953;}i:8136;a:1:{i:0;i:8050;}i:8137;a:1:{i:0;i:8051;}i:8138;a:1:{i:0;i:8052;}i:8139;a:1:{i:0;i:8053;}i:8140;a:2:{i:0;i:951;i:1;i:953;}i:8146;a:3:{i:0;i:953;i:1;i:776;i:2;i:768;}i:8147;a:3:{i:0;i:953;i:1;i:776;i:2;i:769;}i:8150;a:2:{i:0;i:953;i:1;i:834;}i:8151;a:3:{i:0;i:953;i:1;i:776;i:2;i:834;}i:8152;a:1:{i:0;i:8144;}i:8153;a:1:{i:0;i:8145;}i:8154;a:1:{i:0;i:8054;}i:8155;a:1:{i:0;i:8055;}i:8162;a:3:{i:0;i:965;i:1;i:776;i:2;i:768;}i:8163;a:3:{i:0;i:965;i:1;i:776;i:2;i:769;}i:8164;a:2:{i:0;i:961;i:1;i:787;}i:8166;a:2:{i:0;i:965;i:1;i:834;}i:8167;a:3:{i:0;i:965;i:1;i:776;i:2;i:834;}i:8168;a:1:{i:0;i:8160;}i:8169;a:1:{i:0;i:8161;}i:8170;a:1:{i:0;i:8058;}i:8171;a:1:{i:0;i:8059;}i:8172;a:1:{i:0;i:8165;}i:8178;a:2:{i:0;i:8060;i:1;i:953;}i:8179;a:2:{i:0;i:969;i:1;i:953;}i:8180;a:2:{i:0;i:974;i:1;i:953;}i:8182;a:2:{i:0;i:969;i:1;i:834;}i:8183;a:3:{i:0;i:969;i:1;i:834;i:2;i:953;}i:8184;a:1:{i:0;i:8056;}i:8185;a:1:{i:0;i:8057;}i:8186;a:1:{i:0;i:8060;}i:8187;a:1:{i:0;i:8061;}i:8188;a:2:{i:0;i:969;i:1;i:953;}i:8360;a:2:{i:0;i:114;i:1;i:115;}i:8450;a:1:{i:0;i:99;}i:8451;a:2:{i:0;i:176;i:1;i:99;}i:8455;a:1:{i:0;i:603;}i:8457;a:2:{i:0;i:176;i:1;i:102;}i:8459;a:1:{i:0;i:104;}i:8460;a:1:{i:0;i:104;}i:8461;a:1:{i:0;i:104;}i:8464;a:1:{i:0;i:105;}i:8465;a:1:{i:0;i:105;}i:8466;a:1:{i:0;i:108;}i:8469;a:1:{i:0;i:110;}i:8470;a:2:{i:0;i:110;i:1;i:111;}i:8473;a:1:{i:0;i:112;}i:8474;a:1:{i:0;i:113;}i:8475;a:1:{i:0;i:114;}i:8476;a:1:{i:0;i:114;}i:8477;a:1:{i:0;i:114;}i:8480;a:2:{i:0;i:115;i:1;i:109;}i:8481;a:3:{i:0;i:116;i:1;i:101;i:2;i:108;}i:8482;a:2:{i:0;i:116;i:1;i:109;}i:8484;a:1:{i:0;i:122;}i:8486;a:1:{i:0;i:969;}i:8488;a:1:{i:0;i:122;}i:8490;a:1:{i:0;i:107;}i:8491;a:1:{i:0;i:229;}i:8492;a:1:{i:0;i:98;}i:8493;a:1:{i:0;i:99;}i:8496;a:1:{i:0;i:101;}i:8497;a:1:{i:0;i:102;}i:8499;a:1:{i:0;i:109;}i:8510;a:1:{i:0;i:947;}i:8511;a:1:{i:0;i:960;}i:8517;a:1:{i:0;i:100;}i:8544;a:1:{i:0;i:8560;}i:8545;a:1:{i:0;i:8561;}i:8546;a:1:{i:0;i:8562;}i:8547;a:1:{i:0;i:8563;}i:8548;a:1:{i:0;i:8564;}i:8549;a:1:{i:0;i:8565;}i:8550;a:1:{i:0;i:8566;}i:8551;a:1:{i:0;i:8567;}i:8552;a:1:{i:0;i:8568;}i:8553;a:1:{i:0;i:8569;}i:8554;a:1:{i:0;i:8570;}i:8555;a:1:{i:0;i:8571;}i:8556;a:1:{i:0;i:8572;}i:8557;a:1:{i:0;i:8573;}i:8558;a:1:{i:0;i:8574;}i:8559;a:1:{i:0;i:8575;}i:9398;a:1:{i:0;i:9424;}i:9399;a:1:{i:0;i:9425;}i:9400;a:1:{i:0;i:9426;}i:9401;a:1:{i:0;i:9427;}i:9402;a:1:{i:0;i:9428;}i:9403;a:1:{i:0;i:9429;}i:9404;a:1:{i:0;i:9430;}i:9405;a:1:{i:0;i:9431;}i:9406;a:1:{i:0;i:9432;}i:9407;a:1:{i:0;i:9433;}i:9408;a:1:{i:0;i:9434;}i:9409;a:1:{i:0;i:9435;}i:9410;a:1:{i:0;i:9436;}i:9411;a:1:{i:0;i:9437;}i:9412;a:1:{i:0;i:9438;}i:9413;a:1:{i:0;i:9439;}i:9414;a:1:{i:0;i:9440;}i:9415;a:1:{i:0;i:9441;}i:9416;a:1:{i:0;i:9442;}i:9417;a:1:{i:0;i:9443;}i:9418;a:1:{i:0;i:9444;}i:9419;a:1:{i:0;i:9445;}i:9420;a:1:{i:0;i:9446;}i:9421;a:1:{i:0;i:9447;}i:9422;a:1:{i:0;i:9448;}i:9423;a:1:{i:0;i:9449;}i:13169;a:3:{i:0;i:104;i:1;i:112;i:2;i:97;}i:13171;a:2:{i:0;i:97;i:1;i:117;}i:13173;a:2:{i:0;i:111;i:1;i:118;}i:13184;a:2:{i:0;i:112;i:1;i:97;}i:13185;a:2:{i:0;i:110;i:1;i:97;}i:13186;a:2:{i:0;i:956;i:1;i:97;}i:13187;a:2:{i:0;i:109;i:1;i:97;}i:13188;a:2:{i:0;i:107;i:1;i:97;}i:13189;a:2:{i:0;i:107;i:1;i:98;}i:13190;a:2:{i:0;i:109;i:1;i:98;}i:13191;a:2:{i:0;i:103;i:1;i:98;}i:13194;a:2:{i:0;i:112;i:1;i:102;}i:13195;a:2:{i:0;i:110;i:1;i:102;}i:13196;a:2:{i:0;i:956;i:1;i:102;}i:13200;a:2:{i:0;i:104;i:1;i:122;}i:13201;a:3:{i:0;i:107;i:1;i:104;i:2;i:122;}i:13202;a:3:{i:0;i:109;i:1;i:104;i:2;i:122;}i:13203;a:3:{i:0;i:103;i:1;i:104;i:2;i:122;}i:13204;a:3:{i:0;i:116;i:1;i:104;i:2;i:122;}i:13225;a:2:{i:0;i:112;i:1;i:97;}i:13226;a:3:{i:0;i:107;i:1;i:112;i:2;i:97;}i:13227;a:3:{i:0;i:109;i:1;i:112;i:2;i:97;}i:13228;a:3:{i:0;i:103;i:1;i:112;i:2;i:97;}i:13236;a:2:{i:0;i:112;i:1;i:118;}i:13237;a:2:{i:0;i:110;i:1;i:118;}i:13238;a:2:{i:0;i:956;i:1;i:118;}i:13239;a:2:{i:0;i:109;i:1;i:118;}i:13240;a:2:{i:0;i:107;i:1;i:118;}i:13241;a:2:{i:0;i:109;i:1;i:118;}i:13242;a:2:{i:0;i:112;i:1;i:119;}i:13243;a:2:{i:0;i:110;i:1;i:119;}i:13244;a:2:{i:0;i:956;i:1;i:119;}i:13245;a:2:{i:0;i:109;i:1;i:119;}i:13246;a:2:{i:0;i:107;i:1;i:119;}i:13247;a:2:{i:0;i:109;i:1;i:119;}i:13248;a:2:{i:0;i:107;i:1;i:969;}i:13249;a:2:{i:0;i:109;i:1;i:969;}i:13251;a:2:{i:0;i:98;i:1;i:113;}i:13254;a:4:{i:0;i:99;i:1;i:8725;i:2;i:107;i:3;i:103;}i:13255;a:3:{i:0;i:99;i:1;i:111;i:2;i:46;}i:13256;a:2:{i:0;i:100;i:1;i:98;}i:13257;a:2:{i:0;i:103;i:1;i:121;}i:13259;a:2:{i:0;i:104;i:1;i:112;}i:13261;a:2:{i:0;i:107;i:1;i:107;}i:13262;a:2:{i:0;i:107;i:1;i:109;}i:13271;a:2:{i:0;i:112;i:1;i:104;}i:13273;a:3:{i:0;i:112;i:1;i:112;i:2;i:109;}i:13274;a:2:{i:0;i:112;i:1;i:114;}i:13276;a:2:{i:0;i:115;i:1;i:118;}i:13277;a:2:{i:0;i:119;i:1;i:98;}i:64256;a:2:{i:0;i:102;i:1;i:102;}i:64257;a:2:{i:0;i:102;i:1;i:105;}i:64258;a:2:{i:0;i:102;i:1;i:108;}i:64259;a:3:{i:0;i:102;i:1;i:102;i:2;i:105;}i:64260;a:3:{i:0;i:102;i:1;i:102;i:2;i:108;}i:64261;a:2:{i:0;i:115;i:1;i:116;}i:64262;a:2:{i:0;i:115;i:1;i:116;}i:64275;a:2:{i:0;i:1396;i:1;i:1398;}i:64276;a:2:{i:0;i:1396;i:1;i:1381;}i:64277;a:2:{i:0;i:1396;i:1;i:1387;}i:64278;a:2:{i:0;i:1406;i:1;i:1398;}i:64279;a:2:{i:0;i:1396;i:1;i:1389;}i:65313;a:1:{i:0;i:65345;}i:65314;a:1:{i:0;i:65346;}i:65315;a:1:{i:0;i:65347;}i:65316;a:1:{i:0;i:65348;}i:65317;a:1:{i:0;i:65349;}i:65318;a:1:{i:0;i:65350;}i:65319;a:1:{i:0;i:65351;}i:65320;a:1:{i:0;i:65352;}i:65321;a:1:{i:0;i:65353;}i:65322;a:1:{i:0;i:65354;}i:65323;a:1:{i:0;i:65355;}i:65324;a:1:{i:0;i:65356;}i:65325;a:1:{i:0;i:65357;}i:65326;a:1:{i:0;i:65358;}i:65327;a:1:{i:0;i:65359;}i:65328;a:1:{i:0;i:65360;}i:65329;a:1:{i:0;i:65361;}i:65330;a:1:{i:0;i:65362;}i:65331;a:1:{i:0;i:65363;}i:65332;a:1:{i:0;i:65364;}i:65333;a:1:{i:0;i:65365;}i:65334;a:1:{i:0;i:65366;}i:65335;a:1:{i:0;i:65367;}i:65336;a:1:{i:0;i:65368;}i:65337;a:1:{i:0;i:65369;}i:65338;a:1:{i:0;i:65370;}i:66560;a:1:{i:0;i:66600;}i:66561;a:1:{i:0;i:66601;}i:66562;a:1:{i:0;i:66602;}i:66563;a:1:{i:0;i:66603;}i:66564;a:1:{i:0;i:66604;}i:66565;a:1:{i:0;i:66605;}i:66566;a:1:{i:0;i:66606;}i:66567;a:1:{i:0;i:66607;}i:66568;a:1:{i:0;i:66608;}i:66569;a:1:{i:0;i:66609;}i:66570;a:1:{i:0;i:66610;}i:66571;a:1:{i:0;i:66611;}i:66572;a:1:{i:0;i:66612;}i:66573;a:1:{i:0;i:66613;}i:66574;a:1:{i:0;i:66614;}i:66575;a:1:{i:0;i:66615;}i:66576;a:1:{i:0;i:66616;}i:66577;a:1:{i:0;i:66617;}i:66578;a:1:{i:0;i:66618;}i:66579;a:1:{i:0;i:66619;}i:66580;a:1:{i:0;i:66620;}i:66581;a:1:{i:0;i:66621;}i:66582;a:1:{i:0;i:66622;}i:66583;a:1:{i:0;i:66623;}i:66584;a:1:{i:0;i:66624;}i:66585;a:1:{i:0;i:66625;}i:66586;a:1:{i:0;i:66626;}i:66587;a:1:{i:0;i:66627;}i:66588;a:1:{i:0;i:66628;}i:66589;a:1:{i:0;i:66629;}i:66590;a:1:{i:0;i:66630;}i:66591;a:1:{i:0;i:66631;}i:66592;a:1:{i:0;i:66632;}i:66593;a:1:{i:0;i:66633;}i:66594;a:1:{i:0;i:66634;}i:66595;a:1:{i:0;i:66635;}i:66596;a:1:{i:0;i:66636;}i:66597;a:1:{i:0;i:66637;}i:119808;a:1:{i:0;i:97;}i:119809;a:1:{i:0;i:98;}i:119810;a:1:{i:0;i:99;}i:119811;a:1:{i:0;i:100;}i:119812;a:1:{i:0;i:101;}i:119813;a:1:{i:0;i:102;}i:119814;a:1:{i:0;i:103;}i:119815;a:1:{i:0;i:104;}i:119816;a:1:{i:0;i:105;}i:119817;a:1:{i:0;i:106;}i:119818;a:1:{i:0;i:107;}i:119819;a:1:{i:0;i:108;}i:119820;a:1:{i:0;i:109;}i:119821;a:1:{i:0;i:110;}i:119822;a:1:{i:0;i:111;}i:119823;a:1:{i:0;i:112;}i:119824;a:1:{i:0;i:113;}i:119825;a:1:{i:0;i:114;}i:119826;a:1:{i:0;i:115;}i:119827;a:1:{i:0;i:116;}i:119828;a:1:{i:0;i:117;}i:119829;a:1:{i:0;i:118;}i:119830;a:1:{i:0;i:119;}i:119831;a:1:{i:0;i:120;}i:119832;a:1:{i:0;i:121;}i:119833;a:1:{i:0;i:122;}i:119860;a:1:{i:0;i:97;}i:119861;a:1:{i:0;i:98;}i:119862;a:1:{i:0;i:99;}i:119863;a:1:{i:0;i:100;}i:119864;a:1:{i:0;i:101;}i:119865;a:1:{i:0;i:102;}i:119866;a:1:{i:0;i:103;}i:119867;a:1:{i:0;i:104;}i:119868;a:1:{i:0;i:105;}i:119869;a:1:{i:0;i:106;}i:119870;a:1:{i:0;i:107;}i:119871;a:1:{i:0;i:108;}i:119872;a:1:{i:0;i:109;}i:119873;a:1:{i:0;i:110;}i:119874;a:1:{i:0;i:111;}i:119875;a:1:{i:0;i:112;}i:119876;a:1:{i:0;i:113;}i:119877;a:1:{i:0;i:114;}i:119878;a:1:{i:0;i:115;}i:119879;a:1:{i:0;i:116;}i:119880;a:1:{i:0;i:117;}i:119881;a:1:{i:0;i:118;}i:119882;a:1:{i:0;i:119;}i:119883;a:1:{i:0;i:120;}i:119884;a:1:{i:0;i:121;}i:119885;a:1:{i:0;i:122;}i:119912;a:1:{i:0;i:97;}i:119913;a:1:{i:0;i:98;}i:119914;a:1:{i:0;i:99;}i:119915;a:1:{i:0;i:100;}i:119916;a:1:{i:0;i:101;}i:119917;a:1:{i:0;i:102;}i:119918;a:1:{i:0;i:103;}i:119919;a:1:{i:0;i:104;}i:119920;a:1:{i:0;i:105;}i:119921;a:1:{i:0;i:106;}i:119922;a:1:{i:0;i:107;}i:119923;a:1:{i:0;i:108;}i:119924;a:1:{i:0;i:109;}i:119925;a:1:{i:0;i:110;}i:119926;a:1:{i:0;i:111;}i:119927;a:1:{i:0;i:112;}i:119928;a:1:{i:0;i:113;}i:119929;a:1:{i:0;i:114;}i:119930;a:1:{i:0;i:115;}i:119931;a:1:{i:0;i:116;}i:119932;a:1:{i:0;i:117;}i:119933;a:1:{i:0;i:118;}i:119934;a:1:{i:0;i:119;}i:119935;a:1:{i:0;i:120;}i:119936;a:1:{i:0;i:121;}i:119937;a:1:{i:0;i:122;}i:119964;a:1:{i:0;i:97;}i:119966;a:1:{i:0;i:99;}i:119967;a:1:{i:0;i:100;}i:119970;a:1:{i:0;i:103;}i:119973;a:1:{i:0;i:106;}i:119974;a:1:{i:0;i:107;}i:119977;a:1:{i:0;i:110;}i:119978;a:1:{i:0;i:111;}i:119979;a:1:{i:0;i:112;}i:119980;a:1:{i:0;i:113;}i:119982;a:1:{i:0;i:115;}i:119983;a:1:{i:0;i:116;}i:119984;a:1:{i:0;i:117;}i:119985;a:1:{i:0;i:118;}i:119986;a:1:{i:0;i:119;}i:119987;a:1:{i:0;i:120;}i:119988;a:1:{i:0;i:121;}i:119989;a:1:{i:0;i:122;}i:120016;a:1:{i:0;i:97;}i:120017;a:1:{i:0;i:98;}i:120018;a:1:{i:0;i:99;}i:120019;a:1:{i:0;i:100;}i:120020;a:1:{i:0;i:101;}i:120021;a:1:{i:0;i:102;}i:120022;a:1:{i:0;i:103;}i:120023;a:1:{i:0;i:104;}i:120024;a:1:{i:0;i:105;}i:120025;a:1:{i:0;i:106;}i:120026;a:1:{i:0;i:107;}i:120027;a:1:{i:0;i:108;}i:120028;a:1:{i:0;i:109;}i:120029;a:1:{i:0;i:110;}i:120030;a:1:{i:0;i:111;}i:120031;a:1:{i:0;i:112;}i:120032;a:1:{i:0;i:113;}i:120033;a:1:{i:0;i:114;}i:120034;a:1:{i:0;i:115;}i:120035;a:1:{i:0;i:116;}i:120036;a:1:{i:0;i:117;}i:120037;a:1:{i:0;i:118;}i:120038;a:1:{i:0;i:119;}i:120039;a:1:{i:0;i:120;}i:120040;a:1:{i:0;i:121;}i:120041;a:1:{i:0;i:122;}i:120068;a:1:{i:0;i:97;}i:120069;a:1:{i:0;i:98;}i:120071;a:1:{i:0;i:100;}i:120072;a:1:{i:0;i:101;}i:120073;a:1:{i:0;i:102;}i:120074;a:1:{i:0;i:103;}i:120077;a:1:{i:0;i:106;}i:120078;a:1:{i:0;i:107;}i:120079;a:1:{i:0;i:108;}i:120080;a:1:{i:0;i:109;}i:120081;a:1:{i:0;i:110;}i:120082;a:1:{i:0;i:111;}i:120083;a:1:{i:0;i:112;}i:120084;a:1:{i:0;i:113;}i:120086;a:1:{i:0;i:115;}i:120087;a:1:{i:0;i:116;}i:120088;a:1:{i:0;i:117;}i:120089;a:1:{i:0;i:118;}i:120090;a:1:{i:0;i:119;}i:120091;a:1:{i:0;i:120;}i:120092;a:1:{i:0;i:121;}i:120120;a:1:{i:0;i:97;}i:120121;a:1:{i:0;i:98;}i:120123;a:1:{i:0;i:100;}i:120124;a:1:{i:0;i:101;}i:120125;a:1:{i:0;i:102;}i:120126;a:1:{i:0;i:103;}i:120128;a:1:{i:0;i:105;}i:120129;a:1:{i:0;i:106;}i:120130;a:1:{i:0;i:107;}i:120131;a:1:{i:0;i:108;}i:120132;a:1:{i:0;i:109;}i:120134;a:1:{i:0;i:111;}i:120138;a:1:{i:0;i:115;}i:120139;a:1:{i:0;i:116;}i:120140;a:1:{i:0;i:117;}i:120141;a:1:{i:0;i:118;}i:120142;a:1:{i:0;i:119;}i:120143;a:1:{i:0;i:120;}i:120144;a:1:{i:0;i:121;}i:120172;a:1:{i:0;i:97;}i:120173;a:1:{i:0;i:98;}i:120174;a:1:{i:0;i:99;}i:120175;a:1:{i:0;i:100;}i:120176;a:1:{i:0;i:101;}i:120177;a:1:{i:0;i:102;}i:120178;a:1:{i:0;i:103;}i:120179;a:1:{i:0;i:104;}i:120180;a:1:{i:0;i:105;}i:120181;a:1:{i:0;i:106;}i:120182;a:1:{i:0;i:107;}i:120183;a:1:{i:0;i:108;}i:120184;a:1:{i:0;i:109;}i:120185;a:1:{i:0;i:110;}i:120186;a:1:{i:0;i:111;}i:120187;a:1:{i:0;i:112;}i:120188;a:1:{i:0;i:113;}i:120189;a:1:{i:0;i:114;}i:120190;a:1:{i:0;i:115;}i:120191;a:1:{i:0;i:116;}i:120192;a:1:{i:0;i:117;}i:120193;a:1:{i:0;i:118;}i:120194;a:1:{i:0;i:119;}i:120195;a:1:{i:0;i:120;}i:120196;a:1:{i:0;i:121;}i:120197;a:1:{i:0;i:122;}i:120224;a:1:{i:0;i:97;}i:120225;a:1:{i:0;i:98;}i:120226;a:1:{i:0;i:99;}i:120227;a:1:{i:0;i:100;}i:120228;a:1:{i:0;i:101;}i:120229;a:1:{i:0;i:102;}i:120230;a:1:{i:0;i:103;}i:120231;a:1:{i:0;i:104;}i:120232;a:1:{i:0;i:105;}i:120233;a:1:{i:0;i:106;}i:120234;a:1:{i:0;i:107;}i:120235;a:1:{i:0;i:108;}i:120236;a:1:{i:0;i:109;}i:120237;a:1:{i:0;i:110;}i:120238;a:1:{i:0;i:111;}i:120239;a:1:{i:0;i:112;}i:120240;a:1:{i:0;i:113;}i:120241;a:1:{i:0;i:114;}i:120242;a:1:{i:0;i:115;}i:120243;a:1:{i:0;i:116;}i:120244;a:1:{i:0;i:117;}i:120245;a:1:{i:0;i:118;}i:120246;a:1:{i:0;i:119;}i:120247;a:1:{i:0;i:120;}i:120248;a:1:{i:0;i:121;}i:120249;a:1:{i:0;i:122;}i:120276;a:1:{i:0;i:97;}i:120277;a:1:{i:0;i:98;}i:120278;a:1:{i:0;i:99;}i:120279;a:1:{i:0;i:100;}i:120280;a:1:{i:0;i:101;}i:120281;a:1:{i:0;i:102;}i:120282;a:1:{i:0;i:103;}i:120283;a:1:{i:0;i:104;}i:120284;a:1:{i:0;i:105;}i:120285;a:1:{i:0;i:106;}i:120286;a:1:{i:0;i:107;}i:120287;a:1:{i:0;i:108;}i:120288;a:1:{i:0;i:109;}i:120289;a:1:{i:0;i:110;}i:120290;a:1:{i:0;i:111;}i:120291;a:1:{i:0;i:112;}i:120292;a:1:{i:0;i:113;}i:120293;a:1:{i:0;i:114;}i:120294;a:1:{i:0;i:115;}i:120295;a:1:{i:0;i:116;}i:120296;a:1:{i:0;i:117;}i:120297;a:1:{i:0;i:118;}i:120298;a:1:{i:0;i:119;}i:120299;a:1:{i:0;i:120;}i:120300;a:1:{i:0;i:121;}i:120301;a:1:{i:0;i:122;}i:120328;a:1:{i:0;i:97;}i:120329;a:1:{i:0;i:98;}i:120330;a:1:{i:0;i:99;}i:120331;a:1:{i:0;i:100;}i:120332;a:1:{i:0;i:101;}i:120333;a:1:{i:0;i:102;}i:120334;a:1:{i:0;i:103;}i:120335;a:1:{i:0;i:104;}i:120336;a:1:{i:0;i:105;}i:120337;a:1:{i:0;i:106;}i:120338;a:1:{i:0;i:107;}i:120339;a:1:{i:0;i:108;}i:120340;a:1:{i:0;i:109;}i:120341;a:1:{i:0;i:110;}i:120342;a:1:{i:0;i:111;}i:120343;a:1:{i:0;i:112;}i:120344;a:1:{i:0;i:113;}i:120345;a:1:{i:0;i:114;}i:120346;a:1:{i:0;i:115;}i:120347;a:1:{i:0;i:116;}i:120348;a:1:{i:0;i:117;}i:120349;a:1:{i:0;i:118;}i:120350;a:1:{i:0;i:119;}i:120351;a:1:{i:0;i:120;}i:120352;a:1:{i:0;i:121;}i:120353;a:1:{i:0;i:122;}i:120380;a:1:{i:0;i:97;}i:120381;a:1:{i:0;i:98;}i:120382;a:1:{i:0;i:99;}i:120383;a:1:{i:0;i:100;}i:120384;a:1:{i:0;i:101;}i:120385;a:1:{i:0;i:102;}i:120386;a:1:{i:0;i:103;}i:120387;a:1:{i:0;i:104;}i:120388;a:1:{i:0;i:105;}i:120389;a:1:{i:0;i:106;}i:120390;a:1:{i:0;i:107;}i:120391;a:1:{i:0;i:108;}i:120392;a:1:{i:0;i:109;}i:120393;a:1:{i:0;i:110;}i:120394;a:1:{i:0;i:111;}i:120395;a:1:{i:0;i:112;}i:120396;a:1:{i:0;i:113;}i:120397;a:1:{i:0;i:114;}i:120398;a:1:{i:0;i:115;}i:120399;a:1:{i:0;i:116;}i:120400;a:1:{i:0;i:117;}i:120401;a:1:{i:0;i:118;}i:120402;a:1:{i:0;i:119;}i:120403;a:1:{i:0;i:120;}i:120404;a:1:{i:0;i:121;}i:120405;a:1:{i:0;i:122;}i:120432;a:1:{i:0;i:97;}i:120433;a:1:{i:0;i:98;}i:120434;a:1:{i:0;i:99;}i:120435;a:1:{i:0;i:100;}i:120436;a:1:{i:0;i:101;}i:120437;a:1:{i:0;i:102;}i:120438;a:1:{i:0;i:103;}i:120439;a:1:{i:0;i:104;}i:120440;a:1:{i:0;i:105;}i:120441;a:1:{i:0;i:106;}i:120442;a:1:{i:0;i:107;}i:120443;a:1:{i:0;i:108;}i:120444;a:1:{i:0;i:109;}i:120445;a:1:{i:0;i:110;}i:120446;a:1:{i:0;i:111;}i:120447;a:1:{i:0;i:112;}i:120448;a:1:{i:0;i:113;}i:120449;a:1:{i:0;i:114;}i:120450;a:1:{i:0;i:115;}i:120451;a:1:{i:0;i:116;}i:120452;a:1:{i:0;i:117;}i:120453;a:1:{i:0;i:118;}i:120454;a:1:{i:0;i:119;}i:120455;a:1:{i:0;i:120;}i:120456;a:1:{i:0;i:121;}i:120457;a:1:{i:0;i:122;}i:120488;a:1:{i:0;i:945;}i:120489;a:1:{i:0;i:946;}i:120490;a:1:{i:0;i:947;}i:120491;a:1:{i:0;i:948;}i:120492;a:1:{i:0;i:949;}i:120493;a:1:{i:0;i:950;}i:120494;a:1:{i:0;i:951;}i:120495;a:1:{i:0;i:952;}i:120496;a:1:{i:0;i:953;}i:120497;a:1:{i:0;i:954;}i:120498;a:1:{i:0;i:955;}i:120499;a:1:{i:0;i:956;}i:120500;a:1:{i:0;i:957;}i:120501;a:1:{i:0;i:958;}i:120502;a:1:{i:0;i:959;}i:120503;a:1:{i:0;i:960;}i:120504;a:1:{i:0;i:961;}i:120505;a:1:{i:0;i:952;}i:120506;a:1:{i:0;i:963;}i:120507;a:1:{i:0;i:964;}i:120508;a:1:{i:0;i:965;}i:120509;a:1:{i:0;i:966;}i:120510;a:1:{i:0;i:967;}i:120511;a:1:{i:0;i:968;}i:120512;a:1:{i:0;i:969;}i:120531;a:1:{i:0;i:963;}i:120546;a:1:{i:0;i:945;}i:120547;a:1:{i:0;i:946;}i:120548;a:1:{i:0;i:947;}i:120549;a:1:{i:0;i:948;}i:120550;a:1:{i:0;i:949;}i:120551;a:1:{i:0;i:950;}i:120552;a:1:{i:0;i:951;}i:120553;a:1:{i:0;i:952;}i:120554;a:1:{i:0;i:953;}i:120555;a:1:{i:0;i:954;}i:120556;a:1:{i:0;i:955;}i:120557;a:1:{i:0;i:956;}i:120558;a:1:{i:0;i:957;}i:120559;a:1:{i:0;i:958;}i:120560;a:1:{i:0;i:959;}i:120561;a:1:{i:0;i:960;}i:120562;a:1:{i:0;i:961;}i:120563;a:1:{i:0;i:952;}i:120564;a:1:{i:0;i:963;}i:120565;a:1:{i:0;i:964;}i:120566;a:1:{i:0;i:965;}i:120567;a:1:{i:0;i:966;}i:120568;a:1:{i:0;i:967;}i:120569;a:1:{i:0;i:968;}i:120570;a:1:{i:0;i:969;}i:120589;a:1:{i:0;i:963;}i:120604;a:1:{i:0;i:945;}i:120605;a:1:{i:0;i:946;}i:120606;a:1:{i:0;i:947;}i:120607;a:1:{i:0;i:948;}i:120608;a:1:{i:0;i:949;}i:120609;a:1:{i:0;i:950;}i:120610;a:1:{i:0;i:951;}i:120611;a:1:{i:0;i:952;}i:120612;a:1:{i:0;i:953;}i:120613;a:1:{i:0;i:954;}i:120614;a:1:{i:0;i:955;}i:120615;a:1:{i:0;i:956;}i:120616;a:1:{i:0;i:957;}i:120617;a:1:{i:0;i:958;}i:120618;a:1:{i:0;i:959;}i:120619;a:1:{i:0;i:960;}i:120620;a:1:{i:0;i:961;}i:120621;a:1:{i:0;i:952;}i:120622;a:1:{i:0;i:963;}i:120623;a:1:{i:0;i:964;}i:120624;a:1:{i:0;i:965;}i:120625;a:1:{i:0;i:966;}i:120626;a:1:{i:0;i:967;}i:120627;a:1:{i:0;i:968;}i:120628;a:1:{i:0;i:969;}i:120647;a:1:{i:0;i:963;}i:120662;a:1:{i:0;i:945;}i:120663;a:1:{i:0;i:946;}i:120664;a:1:{i:0;i:947;}i:120665;a:1:{i:0;i:948;}i:120666;a:1:{i:0;i:949;}i:120667;a:1:{i:0;i:950;}i:120668;a:1:{i:0;i:951;}i:120669;a:1:{i:0;i:952;}i:120670;a:1:{i:0;i:953;}i:120671;a:1:{i:0;i:954;}i:120672;a:1:{i:0;i:955;}i:120673;a:1:{i:0;i:956;}i:120674;a:1:{i:0;i:957;}i:120675;a:1:{i:0;i:958;}i:120676;a:1:{i:0;i:959;}i:120677;a:1:{i:0;i:960;}i:120678;a:1:{i:0;i:961;}i:120679;a:1:{i:0;i:952;}i:120680;a:1:{i:0;i:963;}i:120681;a:1:{i:0;i:964;}i:120682;a:1:{i:0;i:965;}i:120683;a:1:{i:0;i:966;}i:120684;a:1:{i:0;i:967;}i:120685;a:1:{i:0;i:968;}i:120686;a:1:{i:0;i:969;}i:120705;a:1:{i:0;i:963;}i:120720;a:1:{i:0;i:945;}i:120721;a:1:{i:0;i:946;}i:120722;a:1:{i:0;i:947;}i:120723;a:1:{i:0;i:948;}i:120724;a:1:{i:0;i:949;}i:120725;a:1:{i:0;i:950;}i:120726;a:1:{i:0;i:951;}i:120727;a:1:{i:0;i:952;}i:120728;a:1:{i:0;i:953;}i:120729;a:1:{i:0;i:954;}i:120730;a:1:{i:0;i:955;}i:120731;a:1:{i:0;i:956;}i:120732;a:1:{i:0;i:957;}i:120733;a:1:{i:0;i:958;}i:120734;a:1:{i:0;i:959;}i:120735;a:1:{i:0;i:960;}i:120736;a:1:{i:0;i:961;}i:120737;a:1:{i:0;i:952;}i:120738;a:1:{i:0;i:963;}i:120739;a:1:{i:0;i:964;}i:120740;a:1:{i:0;i:965;}i:120741;a:1:{i:0;i:966;}i:120742;a:1:{i:0;i:967;}i:120743;a:1:{i:0;i:968;}i:120744;a:1:{i:0;i:969;}i:120763;a:1:{i:0;i:963;}i:1017;a:1:{i:0;i:963;}i:7468;a:1:{i:0;i:97;}i:7469;a:1:{i:0;i:230;}i:7470;a:1:{i:0;i:98;}i:7472;a:1:{i:0;i:100;}i:7473;a:1:{i:0;i:101;}i:7474;a:1:{i:0;i:477;}i:7475;a:1:{i:0;i:103;}i:7476;a:1:{i:0;i:104;}i:7477;a:1:{i:0;i:105;}i:7478;a:1:{i:0;i:106;}i:7479;a:1:{i:0;i:107;}i:7480;a:1:{i:0;i:108;}i:7481;a:1:{i:0;i:109;}i:7482;a:1:{i:0;i:110;}i:7484;a:1:{i:0;i:111;}i:7485;a:1:{i:0;i:547;}i:7486;a:1:{i:0;i:112;}i:7487;a:1:{i:0;i:114;}i:7488;a:1:{i:0;i:116;}i:7489;a:1:{i:0;i:117;}i:7490;a:1:{i:0;i:119;}i:8507;a:3:{i:0;i:102;i:1;i:97;i:2;i:120;}i:12880;a:3:{i:0;i:112;i:1;i:116;i:2;i:101;}i:13004;a:2:{i:0;i:104;i:1;i:103;}i:13006;a:2:{i:0;i:101;i:1;i:118;}i:13007;a:3:{i:0;i:108;i:1;i:116;i:2;i:100;}i:13178;a:2:{i:0;i:105;i:1;i:117;}i:13278;a:3:{i:0;i:118;i:1;i:8725;i:2;i:109;}i:13279;a:3:{i:0;i:97;i:1;i:8725;i:2;i:109;}}s:12:"norm_combcls";a:341:{i:820;i:1;i:821;i:1;i:822;i:1;i:823;i:1;i:824;i:1;i:2364;i:7;i:2492;i:7;i:2620;i:7;i:2748;i:7;i:2876;i:7;i:3260;i:7;i:4151;i:7;i:12441;i:8;i:12442;i:8;i:2381;i:9;i:2509;i:9;i:2637;i:9;i:2765;i:9;i:2893;i:9;i:3021;i:9;i:3149;i:9;i:3277;i:9;i:3405;i:9;i:3530;i:9;i:3642;i:9;i:3972;i:9;i:4153;i:9;i:5908;i:9;i:5940;i:9;i:6098;i:9;i:1456;i:10;i:1457;i:11;i:1458;i:12;i:1459;i:13;i:1460;i:14;i:1461;i:15;i:1462;i:16;i:1463;i:17;i:1464;i:18;i:1465;i:19;i:1467;i:20;i:1468;i:21;i:1469;i:22;i:1471;i:23;i:1473;i:24;i:1474;i:25;i:64286;i:26;i:1611;i:27;i:1612;i:28;i:1613;i:29;i:1614;i:30;i:1615;i:31;i:1616;i:32;i:1617;i:33;i:1618;i:34;i:1648;i:35;i:1809;i:36;i:3157;i:84;i:3158;i:91;i:3640;i:103;i:3641;i:103;i:3656;i:107;i:3657;i:107;i:3658;i:107;i:3659;i:107;i:3768;i:118;i:3769;i:118;i:3784;i:122;i:3785;i:122;i:3786;i:122;i:3787;i:122;i:3953;i:129;i:3954;i:130;i:3962;i:130;i:3963;i:130;i:3964;i:130;i:3965;i:130;i:3968;i:130;i:3956;i:132;i:801;i:202;i:802;i:202;i:807;i:202;i:808;i:202;i:795;i:216;i:3897;i:216;i:119141;i:216;i:119142;i:216;i:119150;i:216;i:119151;i:216;i:119152;i:216;i:119153;i:216;i:119154;i:216;i:12330;i:218;i:790;i:220;i:791;i:220;i:792;i:220;i:793;i:220;i:796;i:220;i:797;i:220;i:798;i:220;i:799;i:220;i:800;i:220;i:803;i:220;i:804;i:220;i:805;i:220;i:806;i:220;i:809;i:220;i:810;i:220;i:811;i:220;i:812;i:220;i:813;i:220;i:814;i:220;i:815;i:220;i:816;i:220;i:817;i:220;i:818;i:220;i:819;i:220;i:825;i:220;i:826;i:220;i:827;i:220;i:828;i:220;i:839;i:220;i:840;i:220;i:841;i:220;i:845;i:220;i:846;i:220;i:851;i:220;i:852;i:220;i:853;i:220;i:854;i:220;i:1425;i:220;i:1430;i:220;i:1435;i:220;i:1443;i:220;i:1444;i:220;i:1445;i:220;i:1446;i:220;i:1447;i:220;i:1450;i:220;i:1621;i:220;i:1622;i:220;i:1763;i:220;i:1770;i:220;i:1773;i:220;i:1841;i:220;i:1844;i:220;i:1847;i:220;i:1848;i:220;i:1849;i:220;i:1851;i:220;i:1852;i:220;i:1854;i:220;i:1858;i:220;i:1860;i:220;i:1862;i:220;i:1864;i:220;i:2386;i:220;i:3864;i:220;i:3865;i:220;i:3893;i:220;i:3895;i:220;i:4038;i:220;i:6459;i:220;i:8424;i:220;i:119163;i:220;i:119164;i:220;i:119165;i:220;i:119166;i:220;i:119167;i:220;i:119168;i:220;i:119169;i:220;i:119170;i:220;i:119178;i:220;i:119179;i:220;i:1434;i:222;i:1453;i:222;i:6441;i:222;i:12333;i:222;i:12334;i:224;i:12335;i:224;i:119149;i:226;i:1454;i:228;i:6313;i:228;i:12331;i:228;i:768;i:230;i:769;i:230;i:770;i:230;i:771;i:230;i:772;i:230;i:773;i:230;i:774;i:230;i:775;i:230;i:776;i:230;i:777;i:230;i:778;i:230;i:779;i:230;i:780;i:230;i:781;i:230;i:782;i:230;i:783;i:230;i:784;i:230;i:785;i:230;i:786;i:230;i:787;i:230;i:788;i:230;i:829;i:230;i:830;i:230;i:831;i:230;i:832;i:230;i:833;i:230;i:834;i:230;i:835;i:230;i:836;i:230;i:838;i:230;i:842;i:230;i:843;i:230;i:844;i:230;i:848;i:230;i:849;i:230;i:850;i:230;i:855;i:230;i:867;i:230;i:868;i:230;i:869;i:230;i:870;i:230;i:871;i:230;i:872;i:230;i:873;i:230;i:874;i:230;i:875;i:230;i:876;i:230;i:877;i:230;i:878;i:230;i:879;i:230;i:1155;i:230;i:1156;i:230;i:1157;i:230;i:1158;i:230;i:1426;i:230;i:1427;i:230;i:1428;i:230;i:1429;i:230;i:1431;i:230;i:1432;i:230;i:1433;i:230;i:1436;i:230;i:1437;i:230;i:1438;i:230;i:1439;i:230;i:1440;i:230;i:1441;i:230;i:1448;i:230;i:1449;i:230;i:1451;i:230;i:1452;i:230;i:1455;i:230;i:1476;i:230;i:1552;i:230;i:1553;i:230;i:1554;i:230;i:1555;i:230;i:1556;i:230;i:1557;i:230;i:1619;i:230;i:1620;i:230;i:1623;i:230;i:1624;i:230;i:1750;i:230;i:1751;i:230;i:1752;i:230;i:1753;i:230;i:1754;i:230;i:1755;i:230;i:1756;i:230;i:1759;i:230;i:1760;i:230;i:1761;i:230;i:1762;i:230;i:1764;i:230;i:1767;i:230;i:1768;i:230;i:1771;i:230;i:1772;i:230;i:1840;i:230;i:1842;i:230;i:1843;i:230;i:1845;i:230;i:1846;i:230;i:1850;i:230;i:1853;i:230;i:1855;i:230;i:1856;i:230;i:1857;i:230;i:1859;i:230;i:1861;i:230;i:1863;i:230;i:1865;i:230;i:1866;i:230;i:2385;i:230;i:2387;i:230;i:2388;i:230;i:3970;i:230;i:3971;i:230;i:3974;i:230;i:3975;i:230;i:5901;i:230;i:6458;i:230;i:8400;i:230;i:8401;i:230;i:8404;i:230;i:8405;i:230;i:8406;i:230;i:8407;i:230;i:8411;i:230;i:8412;i:230;i:8417;i:230;i:8423;i:230;i:8425;i:230;i:65056;i:230;i:65057;i:230;i:65058;i:230;i:65059;i:230;i:119173;i:230;i:119174;i:230;i:119175;i:230;i:119177;i:230;i:119176;i:230;i:119210;i:230;i:119211;i:230;i:119212;i:230;i:119213;i:230;i:789;i:232;i:794;i:232;i:12332;i:232;i:863;i:233;i:866;i:233;i:861;i:234;i:862;i:234;i:864;i:234;i:865;i:234;i:837;i:240;}}
\ No newline at end of file
diff --git a/library/simplepie/simplepie.inc b/library/simplepie/simplepie.inc
deleted file mode 100644 (file)
index 96ad066..0000000
+++ /dev/null
@@ -1,15150 +0,0 @@
-<?php
-/**
- * SimplePie
- *
- * A PHP-Based RSS and Atom Feed Framework.
- * Takes the hard work out of managing a complete RSS/Atom solution.
- *
- * Copyright (c) 2004-2009, Ryan Parman and Geoffrey Sneddon
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are
- * permitted provided that the following conditions are met:
- *
- *     * Redistributions of source code must retain the above copyright notice, this list of
- *       conditions and the following disclaimer.
- *
- *     * Redistributions in binary form must reproduce the above copyright notice, this list
- *       of conditions and the following disclaimer in the documentation and/or other materials
- *       provided with the distribution.
- *
- *     * Neither the name of the SimplePie Team nor the names of its contributors may be used
- *       to endorse or promote products derived from this software without specific prior
- *       written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
- * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * @package SimplePie
- * @version 1.2.1-dev
- * @copyright 2004-2009 Ryan Parman, Geoffrey Sneddon
- * @author Ryan Parman
- * @author Geoffrey Sneddon
- * @link http://simplepie.org/ SimplePie
- * @link http://simplepie.org/support/ Please submit all bug reports and feature requests to the SimplePie forums
- * @license http://www.opensource.org/licenses/bsd-license.php BSD License
- * @todo phpDoc comments
- */
-
-/**
- * SimplePie Name
- */
-define('SIMPLEPIE_NAME', 'SimplePie');
-
-/**
- * SimplePie Version
- */
-define('SIMPLEPIE_VERSION', '1.2.1-dev');
-
-/**
- * SimplePie Build
- * @todo Hardcode for release (there's no need to have to call SimplePie_Misc::parse_date() only every load of simplepie.inc)
- */
-define('SIMPLEPIE_BUILD', gmdate('YmdHis', SimplePie_Misc::parse_date(substr('$Date$', 7, 25)) ? SimplePie_Misc::parse_date(substr('$Date$', 7, 25)) : filemtime(__FILE__)));
-
-/**
- * SimplePie Website URL
- */
-define('SIMPLEPIE_URL', 'http://simplepie.org');
-
-/**
- * SimplePie Useragent
- * @see SimplePie::set_useragent()
- */
-define('SIMPLEPIE_USERAGENT', SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION . ' (Feed Parser; ' . SIMPLEPIE_URL . '; Allow like Gecko) Build/' . SIMPLEPIE_BUILD);
-
-/**
- * SimplePie Linkback
- */
-define('SIMPLEPIE_LINKBACK', '<a href="' . SIMPLEPIE_URL . '" title="' . SIMPLEPIE_NAME . ' ' . SIMPLEPIE_VERSION . '">' . SIMPLEPIE_NAME . '</a>');
-
-/**
- * No Autodiscovery
- * @see SimplePie::set_autodiscovery_level()
- */
-define('SIMPLEPIE_LOCATOR_NONE', 0);
-
-/**
- * Feed Link Element Autodiscovery
- * @see SimplePie::set_autodiscovery_level()
- */
-define('SIMPLEPIE_LOCATOR_AUTODISCOVERY', 1);
-
-/**
- * Local Feed Extension Autodiscovery
- * @see SimplePie::set_autodiscovery_level()
- */
-define('SIMPLEPIE_LOCATOR_LOCAL_EXTENSION', 2);
-
-/**
- * Local Feed Body Autodiscovery
- * @see SimplePie::set_autodiscovery_level()
- */
-define('SIMPLEPIE_LOCATOR_LOCAL_BODY', 4);
-
-/**
- * Remote Feed Extension Autodiscovery
- * @see SimplePie::set_autodiscovery_level()
- */
-define('SIMPLEPIE_LOCATOR_REMOTE_EXTENSION', 8);
-
-/**
- * Remote Feed Body Autodiscovery
- * @see SimplePie::set_autodiscovery_level()
- */
-define('SIMPLEPIE_LOCATOR_REMOTE_BODY', 16);
-
-/**
- * All Feed Autodiscovery
- * @see SimplePie::set_autodiscovery_level()
- */
-define('SIMPLEPIE_LOCATOR_ALL', 31);
-
-/**
- * No known feed type
- */
-define('SIMPLEPIE_TYPE_NONE', 0);
-
-/**
- * RSS 0.90
- */
-define('SIMPLEPIE_TYPE_RSS_090', 1);
-
-/**
- * RSS 0.91 (Netscape)
- */
-define('SIMPLEPIE_TYPE_RSS_091_NETSCAPE', 2);
-
-/**
- * RSS 0.91 (Userland)
- */
-define('SIMPLEPIE_TYPE_RSS_091_USERLAND', 4);
-
-/**
- * RSS 0.91 (both Netscape and Userland)
- */
-define('SIMPLEPIE_TYPE_RSS_091', 6);
-
-/**
- * RSS 0.92
- */
-define('SIMPLEPIE_TYPE_RSS_092', 8);
-
-/**
- * RSS 0.93
- */
-define('SIMPLEPIE_TYPE_RSS_093', 16);
-
-/**
- * RSS 0.94
- */
-define('SIMPLEPIE_TYPE_RSS_094', 32);
-
-/**
- * RSS 1.0
- */
-define('SIMPLEPIE_TYPE_RSS_10', 64);
-
-/**
- * RSS 2.0
- */
-define('SIMPLEPIE_TYPE_RSS_20', 128);
-
-/**
- * RDF-based RSS
- */
-define('SIMPLEPIE_TYPE_RSS_RDF', 65);
-
-/**
- * Non-RDF-based RSS (truly intended as syndication format)
- */
-define('SIMPLEPIE_TYPE_RSS_SYNDICATION', 190);
-
-/**
- * All RSS
- */
-define('SIMPLEPIE_TYPE_RSS_ALL', 255);
-
-/**
- * Atom 0.3
- */
-define('SIMPLEPIE_TYPE_ATOM_03', 256);
-
-/**
- * Atom 1.0
- */
-define('SIMPLEPIE_TYPE_ATOM_10', 512);
-
-/**
- * All Atom
- */
-define('SIMPLEPIE_TYPE_ATOM_ALL', 768);
-
-/**
- * All feed types
- */
-define('SIMPLEPIE_TYPE_ALL', 1023);
-
-/**
- * No construct
- */
-define('SIMPLEPIE_CONSTRUCT_NONE', 0);
-
-/**
- * Text construct
- */
-define('SIMPLEPIE_CONSTRUCT_TEXT', 1);
-
-/**
- * HTML construct
- */
-define('SIMPLEPIE_CONSTRUCT_HTML', 2);
-
-/**
- * XHTML construct
- */
-define('SIMPLEPIE_CONSTRUCT_XHTML', 4);
-
-/**
- * base64-encoded construct
- */
-define('SIMPLEPIE_CONSTRUCT_BASE64', 8);
-
-/**
- * IRI construct
- */
-define('SIMPLEPIE_CONSTRUCT_IRI', 16);
-
-/**
- * A construct that might be HTML
- */
-define('SIMPLEPIE_CONSTRUCT_MAYBE_HTML', 32);
-
-/**
- * All constructs
- */
-define('SIMPLEPIE_CONSTRUCT_ALL', 63);
-
-/**
- * Don't change case
- */
-define('SIMPLEPIE_SAME_CASE', 1);
-
-/**
- * Change to lowercase
- */
-define('SIMPLEPIE_LOWERCASE', 2);
-
-/**
- * Change to uppercase
- */
-define('SIMPLEPIE_UPPERCASE', 4);
-
-/**
- * PCRE for HTML attributes
- */
-define('SIMPLEPIE_PCRE_HTML_ATTRIBUTE', '((?:[\x09\x0A\x0B\x0C\x0D\x20]+[^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?)*)[\x09\x0A\x0B\x0C\x0D\x20]*');
-
-/**
- * PCRE for XML attributes
- */
-define('SIMPLEPIE_PCRE_XML_ATTRIBUTE', '((?:\s+(?:(?:[^\s:]+:)?[^\s:]+)\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'))*)\s*');
-
-/**
- * XML Namespace
- */
-define('SIMPLEPIE_NAMESPACE_XML', 'http://www.w3.org/XML/1998/namespace');
-
-/**
- * Atom 1.0 Namespace
- */
-define('SIMPLEPIE_NAMESPACE_ATOM_10', 'http://www.w3.org/2005/Atom');
-
-/**
- * Atom 0.3 Namespace
- */
-define('SIMPLEPIE_NAMESPACE_ATOM_03', 'http://purl.org/atom/ns#');
-
-/**
- * RDF Namespace
- */
-define('SIMPLEPIE_NAMESPACE_RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
-
-/**
- * RSS 0.90 Namespace
- */
-define('SIMPLEPIE_NAMESPACE_RSS_090', 'http://my.netscape.com/rdf/simple/0.9/');
-
-/**
- * RSS 1.0 Namespace
- */
-define('SIMPLEPIE_NAMESPACE_RSS_10', 'http://purl.org/rss/1.0/');
-
-/**
- * RSS 1.0 Content Module Namespace
- */
-define('SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT', 'http://purl.org/rss/1.0/modules/content/');
-
-/**
- * RSS 2.0 Namespace
- * (Stupid, I know, but I'm certain it will confuse people less with support.)
- */
-define('SIMPLEPIE_NAMESPACE_RSS_20', '');
-
-/**
- * DC 1.0 Namespace
- */
-define('SIMPLEPIE_NAMESPACE_DC_10', 'http://purl.org/dc/elements/1.0/');
-
-/**
- * DC 1.1 Namespace
- */
-define('SIMPLEPIE_NAMESPACE_DC_11', 'http://purl.org/dc/elements/1.1/');
-
-/**
- * W3C Basic Geo (WGS84 lat/long) Vocabulary Namespace
- */
-define('SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO', 'http://www.w3.org/2003/01/geo/wgs84_pos#');
-
-/**
- * GeoRSS Namespace
- */
-define('SIMPLEPIE_NAMESPACE_GEORSS', 'http://www.georss.org/georss');
-
-/**
- * Media RSS Namespace
- */
-define('SIMPLEPIE_NAMESPACE_MEDIARSS', 'http://search.yahoo.com/mrss/');
-
-/**
- * Wrong Media RSS Namespace
- */
-define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG', 'http://search.yahoo.com/mrss');
-
-/**
- * iTunes RSS Namespace
- */
-define('SIMPLEPIE_NAMESPACE_ITUNES', 'http://www.itunes.com/dtds/podcast-1.0.dtd');
-
-/**
- * XHTML Namespace
- */
-define('SIMPLEPIE_NAMESPACE_XHTML', 'http://www.w3.org/1999/xhtml');
-
-/**
- * IANA Link Relations Registry
- */
-define('SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY', 'http://www.iana.org/assignments/relation/');
-
-/**
- * Whether we're running on PHP5
- */
-define('SIMPLEPIE_PHP5', version_compare(PHP_VERSION, '5.0.0', '>='));
-
-/**
- * No file source
- */
-define('SIMPLEPIE_FILE_SOURCE_NONE', 0);
-
-/**
- * Remote file source
- */
-define('SIMPLEPIE_FILE_SOURCE_REMOTE', 1);
-
-/**
- * Local file source
- */
-define('SIMPLEPIE_FILE_SOURCE_LOCAL', 2);
-
-/**
- * fsockopen() file source
- */
-define('SIMPLEPIE_FILE_SOURCE_FSOCKOPEN', 4);
-
-/**
- * cURL file source
- */
-define('SIMPLEPIE_FILE_SOURCE_CURL', 8);
-
-/**
- * file_get_contents() file source
- */
-define('SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS', 16);
-
-/**
- * SimplePie
- *
- * @package SimplePie
- */
-class SimplePie
-{
-       /**
-        * @var array Raw data
-        * @access private
-        */
-       var $data = array();
-
-       /**
-        * @var mixed Error string
-        * @access private
-        */
-       var $error;
-
-       /**
-        * @var object Instance of SimplePie_Sanitize (or other class)
-        * @see SimplePie::set_sanitize_class()
-        * @access private
-        */
-       var $sanitize;
-
-       /**
-        * @var string SimplePie Useragent
-        * @see SimplePie::set_useragent()
-        * @access private
-        */
-       var $useragent = SIMPLEPIE_USERAGENT;
-
-       /**
-        * @var string Feed URL
-        * @see SimplePie::set_feed_url()
-        * @access private
-        */
-       var $feed_url;
-
-       /**
-        * @var object Instance of SimplePie_File to use as a feed
-        * @see SimplePie::set_file()
-        * @access private
-        */
-       var $file;
-
-       /**
-        * @var string Raw feed data
-        * @see SimplePie::set_raw_data()
-        * @access private
-        */
-       var $raw_data;
-
-       /**
-        * @var int Timeout for fetching remote files
-        * @see SimplePie::set_timeout()
-        * @access private
-        */
-       var $timeout = 10;
-
-       /**
-        * @var bool Forces fsockopen() to be used for remote files instead
-        * of cURL, even if a new enough version is installed
-        * @see SimplePie::force_fsockopen()
-        * @access private
-        */
-       var $force_fsockopen = false;
-
-       /**
-        * @var bool Force the given data/URL to be treated as a feed no matter what
-        * it appears like
-        * @see SimplePie::force_feed()
-        * @access private
-        */
-       var $force_feed = false;
-
-       /**
-        * @var bool Enable/Disable XML dump
-        * @see SimplePie::enable_xml_dump()
-        * @access private
-        */
-       var $xml_dump = false;
-
-       /**
-        * @var bool Enable/Disable Caching
-        * @see SimplePie::enable_cache()
-        * @access private
-        */
-       var $cache = true;
-
-       /**
-        * @var int Cache duration (in seconds)
-        * @see SimplePie::set_cache_duration()
-        * @access private
-        */
-       var $cache_duration = 3600;
-
-       /**
-        * @var int Auto-discovery cache duration (in seconds)
-        * @see SimplePie::set_autodiscovery_cache_duration()
-        * @access private
-        */
-       var $autodiscovery_cache_duration = 604800; // 7 Days.
-
-       /**
-        * @var string Cache location (relative to executing script)
-        * @see SimplePie::set_cache_location()
-        * @access private
-        */
-       var $cache_location = './cache';
-
-       /**
-        * @var string Function that creates the cache filename
-        * @see SimplePie::set_cache_name_function()
-        * @access private
-        */
-       var $cache_name_function = 'md5';
-
-       /**
-        * @var bool Reorder feed by date descending
-        * @see SimplePie::enable_order_by_date()
-        * @access private
-        */
-       var $order_by_date = true;
-
-       /**
-        * @var mixed Force input encoding to be set to the follow value
-        * (false, or anything type-cast to false, disables this feature)
-        * @see SimplePie::set_input_encoding()
-        * @access private
-        */
-       var $input_encoding = false;
-
-       /**
-        * @var int Feed Autodiscovery Level
-        * @see SimplePie::set_autodiscovery_level()
-        * @access private
-        */
-       var $autodiscovery = SIMPLEPIE_LOCATOR_ALL;
-
-       /**
-        * @var string Class used for caching feeds
-        * @see SimplePie::set_cache_class()
-        * @access private
-        */
-       var $cache_class = 'SimplePie_Cache';
-
-       /**
-        * @var string Class used for locating feeds
-        * @see SimplePie::set_locator_class()
-        * @access private
-        */
-       var $locator_class = 'SimplePie_Locator';
-
-       /**
-        * @var string Class used for parsing feeds
-        * @see SimplePie::set_parser_class()
-        * @access private
-        */
-       var $parser_class = 'SimplePie_Parser';
-
-       /**
-        * @var string Class used for fetching feeds
-        * @see SimplePie::set_file_class()
-        * @access private
-        */
-       var $file_class = 'SimplePie_File';
-
-       /**
-        * @var string Class used for items
-        * @see SimplePie::set_item_class()
-        * @access private
-        */
-       var $item_class = 'SimplePie_Item';
-
-       /**
-        * @var string Class used for authors
-        * @see SimplePie::set_author_class()
-        * @access private
-        */
-       var $author_class = 'SimplePie_Author';
-
-       /**
-        * @var string Class used for categories
-        * @see SimplePie::set_category_class()
-        * @access private
-        */
-       var $category_class = 'SimplePie_Category';
-
-       /**
-        * @var string Class used for enclosures
-        * @see SimplePie::set_enclosures_class()
-        * @access private
-        */
-       var $enclosure_class = 'SimplePie_Enclosure';
-
-       /**
-        * @var string Class used for Media RSS <media:text> captions
-        * @see SimplePie::set_caption_class()
-        * @access private
-        */
-       var $caption_class = 'SimplePie_Caption';
-
-       /**
-        * @var string Class used for Media RSS <media:copyright>
-        * @see SimplePie::set_copyright_class()
-        * @access private
-        */
-       var $copyright_class = 'SimplePie_Copyright';
-
-       /**
-        * @var string Class used for Media RSS <media:credit>
-        * @see SimplePie::set_credit_class()
-        * @access private
-        */
-       var $credit_class = 'SimplePie_Credit';
-
-       /**
-        * @var string Class used for Media RSS <media:rating>
-        * @see SimplePie::set_rating_class()
-        * @access private
-        */
-       var $rating_class = 'SimplePie_Rating';
-
-       /**
-        * @var string Class used for Media RSS <media:restriction>
-        * @see SimplePie::set_restriction_class()
-        * @access private
-        */
-       var $restriction_class = 'SimplePie_Restriction';
-
-       /**
-        * @var string Class used for content-type sniffing
-        * @see SimplePie::set_content_type_sniffer_class()
-        * @access private
-        */
-       var $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer';
-
-       /**
-        * @var string Class used for item sources.
-        * @see SimplePie::set_source_class()
-        * @access private
-        */
-       var $source_class = 'SimplePie_Source';
-
-       /**
-        * @var mixed Set javascript query string parameter (false, or
-        * anything type-cast to false, disables this feature)
-        * @see SimplePie::set_javascript()
-        * @access private
-        */
-       var $javascript = 'js';
-
-       /**
-        * @var int Maximum number of feeds to check with autodiscovery
-        * @see SimplePie::set_max_checked_feeds()
-        * @access private
-        */
-       var $max_checked_feeds = 10;
-
-       /**
-        * @var array All the feeds found during the autodiscovery process
-        * @see SimplePie::get_all_discovered_feeds()
-        * @access private
-        */
-       var $all_discovered_feeds = array();
-
-       /**
-        * @var string Web-accessible path to the handler_favicon.php file.
-        * @see SimplePie::set_favicon_handler()
-        * @access private
-        */
-       var $favicon_handler = '';
-
-       /**
-        * @var string Web-accessible path to the handler_image.php file.
-        * @see SimplePie::set_image_handler()
-        * @access private
-        */
-       var $image_handler = '';
-
-       /**
-        * @var array Stores the URLs when multiple feeds are being initialized.
-        * @see SimplePie::set_feed_url()
-        * @access private
-        */
-       var $multifeed_url = array();
-
-       /**
-        * @var array Stores SimplePie objects when multiple feeds initialized.
-        * @access private
-        */
-       var $multifeed_objects = array();
-
-       /**
-        * @var array Stores the get_object_vars() array for use with multifeeds.
-        * @see SimplePie::set_feed_url()
-        * @access private
-        */
-       var $config_settings = null;
-
-       /**
-        * @var integer Stores the number of items to return per-feed with multifeeds.
-        * @see SimplePie::set_item_limit()
-        * @access private
-        */
-       var $item_limit = 0;
-
-       /**
-        * @var array Stores the default attributes to be stripped by strip_attributes().
-        * @see SimplePie::strip_attributes()
-        * @access private
-        */
-       var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc');
-
-       /**
-        * @var array Stores the default tags to be stripped by strip_htmltags().
-        * @see SimplePie::strip_htmltags()
-        * @access private
-        */
-       var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style');
-
-       /**
-        * The SimplePie class contains feed level data and options
-        *
-        * There are two ways that you can create a new SimplePie object. The first
-        * is by passing a feed URL as a parameter to the SimplePie constructor
-        * (as well as optionally setting the cache location and cache expiry). This
-        * will initialise the whole feed with all of the default settings, and you
-        * can begin accessing methods and properties immediately.
-        *
-        * The second way is to create the SimplePie object with no parameters
-        * at all. This will enable you to set configuration options. After setting
-        * them, you must initialise the feed using $feed->init(). At that point the
-        * object's methods and properties will be available to you. This format is
-        * what is used throughout this documentation.
-        *
-        * @access public
-        * @since 1.0 Preview Release
-        * @param string $feed_url This is the URL you want to parse.
-        * @param string $cache_location This is where you want the cache to be stored.
-        * @param int $cache_duration This is the number of seconds that you want to store the cache file for.
-        */
-       function SimplePie($feed_url = null, $cache_location = null, $cache_duration = null)
-       {
-               // Other objects, instances created here so we can set options on them
-               $this->sanitize = new SimplePie_Sanitize;
-
-               // Set options if they're passed to the constructor
-               if ($cache_location !== null)
-               {
-                       $this->set_cache_location($cache_location);
-               }
-
-               if ($cache_duration !== null)
-               {
-                       $this->set_cache_duration($cache_duration);
-               }
-
-               // Only init the script if we're passed a feed URL
-               if ($feed_url !== null)
-               {
-                       $this->set_feed_url($feed_url);
-                       $this->init();
-               }
-       }
-
-       /**
-        * Used for converting object to a string
-        */
-       function __toString()
-       {
-               return md5(serialize($this->data));
-       }
-
-       /**
-        * Remove items that link back to this before destroying this object
-        */
-       function __destruct()
-       {
-               if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode'))
-               {
-                       if (!empty($this->data['items']))
-                       {
-                               foreach ($this->data['items'] as $item)
-                               {
-                                       $item->__destruct();
-                               }
-                               unset($item, $this->data['items']);
-                       }
-                       if (!empty($this->data['ordered_items']))
-                       {
-                               foreach ($this->data['ordered_items'] as $item)
-                               {
-                                       $item->__destruct();
-                               }
-                               unset($item, $this->data['ordered_items']);
-                       }
-               }
-       }
-
-       /**
-        * Force the given data/URL to be treated as a feed no matter what it
-        * appears like
-        *
-        * @access public
-        * @since 1.1
-        * @param bool $enable Force the given data/URL to be treated as a feed
-        */
-       function force_feed($enable = false)
-       {
-               $this->force_feed = (bool) $enable;
-       }
-
-       /**
-        * This is the URL of the feed you want to parse.
-        *
-        * This allows you to enter the URL of the feed you want to parse, or the
-        * website you want to try to use auto-discovery on. This takes priority
-        * over any set raw data.
-        *
-        * You can set multiple feeds to mash together by passing an array instead
-        * of a string for the $url. Remember that with each additional feed comes
-        * additional processing and resources.
-        *
-        * @access public
-        * @since 1.0 Preview Release
-        * @param mixed $url This is the URL (or array of URLs) that you want to parse.
-        * @see SimplePie::set_raw_data()
-        */
-       function set_feed_url($url)
-       {
-               if (is_array($url))
-               {
-                       $this->multifeed_url = array();
-                       foreach ($url as $value)
-                       {
-                               $this->multifeed_url[] = SimplePie_Misc::fix_protocol($value, 1);
-                       }
-               }
-               else
-               {
-                       $this->feed_url = SimplePie_Misc::fix_protocol($url, 1);
-               }
-       }
-
-       /**
-        * Provides an instance of SimplePie_File to use as a feed
-        *
-        * @access public
-        * @param object &$file Instance of SimplePie_File (or subclass)
-        * @return bool True on success, false on failure
-        */
-       function set_file(&$file)
-       {
-               if (is_a($file, 'SimplePie_File'))
-               {
-                       $this->feed_url = $file->url;
-                       $this->file =& $file;
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Allows you to use a string of RSS/Atom data instead of a remote feed.
-        *
-        * If you have a feed available as a string in PHP, you can tell SimplePie
-        * to parse that data string instead of a remote feed. Any set feed URL
-        * takes precedence.
-        *
-        * @access public
-        * @since 1.0 Beta 3
-        * @param string $data RSS or Atom data as a string.
-        * @see SimplePie::set_feed_url()
-        */
-       function set_raw_data($data)
-       {
-               $this->raw_data = $data;
-       }
-
-       /**
-        * Allows you to override the default timeout for fetching remote feeds.
-        *
-        * This allows you to change the maximum time the feed's server to respond
-        * and send the feed back.
-        *
-        * @access public
-        * @since 1.0 Beta 3
-        * @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed.
-        */
-       function set_timeout($timeout = 10)
-       {
-               $this->timeout = (int) $timeout;
-       }
-
-       /**
-        * Forces SimplePie to use fsockopen() instead of the preferred cURL
-        * functions.
-        *
-        * @access public
-        * @since 1.0 Beta 3
-        * @param bool $enable Force fsockopen() to be used
-        */
-       function force_fsockopen($enable = false)
-       {
-               $this->force_fsockopen = (bool) $enable;
-       }
-
-       /**
-        * Outputs the raw XML content of the feed, after it has gone through
-        * SimplePie's filters.
-        *
-        * Used only for debugging, this function will output the XML content as
-        * text/xml. When SimplePie reads in a feed, it does a bit of cleaning up
-        * before trying to parse it. Many parts of the feed are re-written in
-        * memory, and in the end, you have a parsable feed. XML dump shows you the
-        * actual XML that SimplePie tries to parse, which may or may not be very
-        * different from the original feed.
-        *
-        * @access public
-        * @since 1.0 Preview Release
-        * @param bool $enable Enable XML dump
-        */
-       function enable_xml_dump($enable = false)
-       {
-               $this->xml_dump = (bool) $enable;
-       }
-
-       /**
-        * Enables/disables caching in SimplePie.
-        *
-        * This option allows you to disable caching all-together in SimplePie.
-        * However, disabling the cache can lead to longer load times.
-        *
-        * @access public
-        * @since 1.0 Preview Release
-        * @param bool $enable Enable caching
-        */
-       function enable_cache($enable = true)
-       {
-               $this->cache = (bool) $enable;
-       }
-
-       /**
-        * Set the length of time (in seconds) that the contents of a feed
-        * will be cached.
-        *
-        * @access public
-        * @param int $seconds The feed content cache duration.
-        */
-       function set_cache_duration($seconds = 3600)
-       {
-               $this->cache_duration = (int) $seconds;
-       }
-
-       /**
-        * Set the length of time (in seconds) that the autodiscovered feed
-        * URL will be cached.
-        *
-        * @access public
-        * @param int $seconds The autodiscovered feed URL cache duration.
-        */
-       function set_autodiscovery_cache_duration($seconds = 604800)
-       {
-               $this->autodiscovery_cache_duration = (int) $seconds;
-       }
-
-       /**
-        * Set the file system location where the cached files should be stored.
-        *
-        * @access public
-        * @param string $location The file system location.
-        */
-       function set_cache_location($location = './cache')
-       {
-               $this->cache_location = (string) $location;
-       }
-
-       /**
-        * Determines whether feed items should be sorted into reverse chronological order.
-        *
-        * @access public
-        * @param bool $enable Sort as reverse chronological order.
-        */
-       function enable_order_by_date($enable = true)
-       {
-               $this->order_by_date = (bool) $enable;
-       }
-
-       /**
-        * Allows you to override the character encoding reported by the feed.
-        *
-        * @access public
-        * @param string $encoding Character encoding.
-        */
-       function set_input_encoding($encoding = false)
-       {
-               if ($encoding)
-               {
-                       $this->input_encoding = (string) $encoding;
-               }
-               else
-               {
-                       $this->input_encoding = false;
-               }
-       }
-
-       /**
-        * Set how much feed autodiscovery to do
-        *
-        * @access public
-        * @see SIMPLEPIE_LOCATOR_NONE
-        * @see SIMPLEPIE_LOCATOR_AUTODISCOVERY
-        * @see SIMPLEPIE_LOCATOR_LOCAL_EXTENSION
-        * @see SIMPLEPIE_LOCATOR_LOCAL_BODY
-        * @see SIMPLEPIE_LOCATOR_REMOTE_EXTENSION
-        * @see SIMPLEPIE_LOCATOR_REMOTE_BODY
-        * @see SIMPLEPIE_LOCATOR_ALL
-        * @param int $level Feed Autodiscovery Level (level can be a
-        * combination of the above constants, see bitwise OR operator)
-        */
-       function set_autodiscovery_level($level = SIMPLEPIE_LOCATOR_ALL)
-       {
-               $this->autodiscovery = (int) $level;
-       }
-
-       /**
-        * Allows you to change which class SimplePie uses for caching.
-        * Useful when you are overloading or extending SimplePie's default classes.
-        *
-        * @access public
-        * @param string $class Name of custom class.
-        * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation
-        * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
-        */
-       function set_cache_class($class = 'SimplePie_Cache')
-       {
-               if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Cache'))
-               {
-                       $this->cache_class = $class;
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Allows you to change which class SimplePie uses for auto-discovery.
-        * Useful when you are overloading or extending SimplePie's default classes.
-        *
-        * @access public
-        * @param string $class Name of custom class.
-        * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation
-        * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
-        */
-       function set_locator_class($class = 'SimplePie_Locator')
-       {
-               if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Locator'))
-               {
-                       $this->locator_class = $class;
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Allows you to change which class SimplePie uses for XML parsing.
-        * Useful when you are overloading or extending SimplePie's default classes.
-        *
-        * @access public
-        * @param string $class Name of custom class.
-        * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation
-        * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
-        */
-       function set_parser_class($class = 'SimplePie_Parser')
-       {
-               if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Parser'))
-               {
-                       $this->parser_class = $class;
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Allows you to change which class SimplePie uses for remote file fetching.
-        * Useful when you are overloading or extending SimplePie's default classes.
-        *
-        * @access public
-        * @param string $class Name of custom class.
-        * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation
-        * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
-        */
-       function set_file_class($class = 'SimplePie_File')
-       {
-               if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_File'))
-               {
-                       $this->file_class = $class;
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Allows you to change which class SimplePie uses for data sanitization.
-        * Useful when you are overloading or extending SimplePie's default classes.
-        *
-        * @access public
-        * @param string $class Name of custom class.
-        * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation
-        * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
-        */
-       function set_sanitize_class($class = 'SimplePie_Sanitize')
-       {
-               if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Sanitize'))
-               {
-                       $this->sanitize = new $class;
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Allows you to change which class SimplePie uses for handling feed items.
-        * Useful when you are overloading or extending SimplePie's default classes.
-        *
-        * @access public
-        * @param string $class Name of custom class.
-        * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation
-        * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
-        */
-       function set_item_class($class = 'SimplePie_Item')
-       {
-               if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Item'))
-               {
-                       $this->item_class = $class;
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Allows you to change which class SimplePie uses for handling author data.
-        * Useful when you are overloading or extending SimplePie's default classes.
-        *
-        * @access public
-        * @param string $class Name of custom class.
-        * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation
-        * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
-        */
-       function set_author_class($class = 'SimplePie_Author')
-       {
-               if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Author'))
-               {
-                       $this->author_class = $class;
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Allows you to change which class SimplePie uses for handling category data.
-        * Useful when you are overloading or extending SimplePie's default classes.
-        *
-        * @access public
-        * @param string $class Name of custom class.
-        * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation
-        * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
-        */
-       function set_category_class($class = 'SimplePie_Category')
-       {
-               if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Category'))
-               {
-                       $this->category_class = $class;
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Allows you to change which class SimplePie uses for feed enclosures.
-        * Useful when you are overloading or extending SimplePie's default classes.
-        *
-        * @access public
-        * @param string $class Name of custom class.
-        * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation
-        * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
-        */
-       function set_enclosure_class($class = 'SimplePie_Enclosure')
-       {
-               if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Enclosure'))
-               {
-                       $this->enclosure_class = $class;
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Allows you to change which class SimplePie uses for <media:text> captions
-        * Useful when you are overloading or extending SimplePie's default classes.
-        *
-        * @access public
-        * @param string $class Name of custom class.
-        * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation
-        * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
-        */
-       function set_caption_class($class = 'SimplePie_Caption')
-       {
-               if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Caption'))
-               {
-                       $this->caption_class = $class;
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Allows you to change which class SimplePie uses for <media:copyright>
-        * Useful when you are overloading or extending SimplePie's default classes.
-        *
-        * @access public
-        * @param string $class Name of custom class.
-        * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation
-        * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
-        */
-       function set_copyright_class($class = 'SimplePie_Copyright')
-       {
-               if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Copyright'))
-               {
-                       $this->copyright_class = $class;
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Allows you to change which class SimplePie uses for <media:credit>
-        * Useful when you are overloading or extending SimplePie's default classes.
-        *
-        * @access public
-        * @param string $class Name of custom class.
-        * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation
-        * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
-        */
-       function set_credit_class($class = 'SimplePie_Credit')
-       {
-               if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Credit'))
-               {
-                       $this->credit_class = $class;
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Allows you to change which class SimplePie uses for <media:rating>
-        * Useful when you are overloading or extending SimplePie's default classes.
-        *
-        * @access public
-        * @param string $class Name of custom class.
-        * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation
-        * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
-        */
-       function set_rating_class($class = 'SimplePie_Rating')
-       {
-               if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Rating'))
-               {
-                       $this->rating_class = $class;
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Allows you to change which class SimplePie uses for <media:restriction>
-        * Useful when you are overloading or extending SimplePie's default classes.
-        *
-        * @access public
-        * @param string $class Name of custom class.
-        * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation
-        * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
-        */
-       function set_restriction_class($class = 'SimplePie_Restriction')
-       {
-               if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Restriction'))
-               {
-                       $this->restriction_class = $class;
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Allows you to change which class SimplePie uses for content-type sniffing.
-        * Useful when you are overloading or extending SimplePie's default classes.
-        *
-        * @access public
-        * @param string $class Name of custom class.
-        * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation
-        * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
-        */
-       function set_content_type_sniffer_class($class = 'SimplePie_Content_Type_Sniffer')
-       {
-               if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Content_Type_Sniffer'))
-               {
-                       $this->content_type_sniffer_class = $class;
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Allows you to change which class SimplePie uses item sources.
-        * Useful when you are overloading or extending SimplePie's default classes.
-        *
-        * @access public
-        * @param string $class Name of custom class.
-        * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation
-        * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
-        */
-       function set_source_class($class = 'SimplePie_Source')
-       {
-               if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Source'))
-               {
-                       $this->source_class = $class;
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Allows you to override the default user agent string.
-        *
-        * @access public
-        * @param string $ua New user agent string.
-        */
-       function set_useragent($ua = SIMPLEPIE_USERAGENT)
-       {
-               $this->useragent = (string) $ua;
-       }
-
-       /**
-        * Set callback function to create cache filename with
-        *
-        * @access public
-        * @param mixed $function Callback function
-        */
-       function set_cache_name_function($function = 'md5')
-       {
-               if (is_callable($function))
-               {
-                       $this->cache_name_function = $function;
-               }
-       }
-
-       /**
-        * Set javascript query string parameter
-        *
-        * @access public
-        * @param mixed $get Javascript query string parameter
-        */
-       function set_javascript($get = 'js')
-       {
-               if ($get)
-               {
-                       $this->javascript = (string) $get;
-               }
-               else
-               {
-                       $this->javascript = false;
-               }
-       }
-
-       /**
-        * Set options to make SP as fast as possible.  Forgoes a
-        * substantial amount of data sanitization in favor of speed.
-        *
-        * @access public
-        * @param bool $set Whether to set them or not
-        */
-       function set_stupidly_fast($set = false)
-       {
-               if ($set)
-               {
-                       $this->enable_order_by_date(false);
-                       $this->remove_div(false);
-                       $this->strip_comments(false);
-                       $this->strip_htmltags(false);
-                       $this->strip_attributes(false);
-                       $this->set_image_handler(false);
-               }
-       }
-
-       /**
-        * Set maximum number of feeds to check with autodiscovery
-        *
-        * @access public
-        * @param int $max Maximum number of feeds to check
-        */
-       function set_max_checked_feeds($max = 10)
-       {
-               $this->max_checked_feeds = (int) $max;
-       }
-
-       function remove_div($enable = true)
-       {
-               $this->sanitize->remove_div($enable);
-       }
-
-       function strip_htmltags($tags = '', $encode = null)
-       {
-               if ($tags === '')
-               {
-                       $tags = $this->strip_htmltags;
-               }
-               $this->sanitize->strip_htmltags($tags);
-               if ($encode !== null)
-               {
-                       $this->sanitize->encode_instead_of_strip($tags);
-               }
-       }
-
-       function encode_instead_of_strip($enable = true)
-       {
-               $this->sanitize->encode_instead_of_strip($enable);
-       }
-
-       function strip_attributes($attribs = '')
-       {
-               if ($attribs === '')
-               {
-                       $attribs = $this->strip_attributes;
-               }
-               $this->sanitize->strip_attributes($attribs);
-       }
-
-       function set_output_encoding($encoding = 'UTF-8')
-       {
-               $this->sanitize->set_output_encoding($encoding);
-       }
-
-       function strip_comments($strip = false)
-       {
-               $this->sanitize->strip_comments($strip);
-       }
-
-       /**
-        * Set element/attribute key/value pairs of HTML attributes
-        * containing URLs that need to be resolved relative to the feed
-        *
-        * @access public
-        * @since 1.0
-        * @param array $element_attribute Element/attribute key/value pairs
-        */
-       function set_url_replacements($element_attribute = array('a' => 'href', 'area' => 'href', 'blockquote' => 'cite', 'del' => 'cite', 'form' => 'action', 'img' => array('longdesc', 'src'), 'input' => 'src', 'ins' => 'cite', 'q' => 'cite'))
-       {
-               $this->sanitize->set_url_replacements($element_attribute);
-       }
-
-       /**
-        * Set the handler to enable the display of cached favicons.
-        *
-        * @access public
-        * @param str $page Web-accessible path to the handler_favicon.php file.
-        * @param str $qs The query string that the value should be passed to.
-        */
-       function set_favicon_handler($page = false, $qs = 'i')
-       {
-               if ($page !== false)
-               {
-                       $this->favicon_handler = $page . '?' . $qs . '=';
-               }
-               else
-               {
-                       $this->favicon_handler = '';
-               }
-       }
-
-       /**
-        * Set the handler to enable the display of cached images.
-        *
-        * @access public
-        * @param str $page Web-accessible path to the handler_image.php file.
-        * @param str $qs The query string that the value should be passed to.
-        */
-       function set_image_handler($page = false, $qs = 'i')
-       {
-               if ($page !== false)
-               {
-                       $this->sanitize->set_image_handler($page . '?' . $qs . '=');
-               }
-               else
-               {
-                       $this->image_handler = '';
-               }
-       }
-
-       /**
-        * Set the limit for items returned per-feed with multifeeds.
-        *
-        * @access public
-        * @param integer $limit The maximum number of items to return.
-        */
-       function set_item_limit($limit = 0)
-       {
-               $this->item_limit = (int) $limit;
-       }
-
-       function init()
-       {
-               // Check absolute bare minimum requirements.
-               if ((function_exists('version_compare') && version_compare(PHP_VERSION, '4.3.0', '<')) || !extension_loaded('xml') || !extension_loaded('pcre'))
-               {
-                       return false;
-               }
-               // Then check the xml extension is sane (i.e., libxml 2.7.x issue on PHP < 5.2.9 and libxml 2.7.0 to 2.7.2 on any version) if we don't have xmlreader.
-               elseif (!extension_loaded('xmlreader'))
-               {
-                       static $xml_is_sane = null;
-                       if ($xml_is_sane === null)
-                       {
-                               $parser_check = xml_parser_create();
-                               xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
-                               xml_parser_free($parser_check);
-                               $xml_is_sane = isset($values[0]['value']);
-                       }
-                       if (!$xml_is_sane)
-                       {
-                               return false;
-                       }
-               }
-
-               if (isset($_GET[$this->javascript]))
-               {
-                       SimplePie_Misc::output_javascript();
-                       exit;
-               }
-
-               // Pass whatever was set with config options over to the sanitizer.
-               $this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->cache_class);
-               $this->sanitize->pass_file_data($this->file_class, $this->timeout, $this->useragent, $this->force_fsockopen);
-
-               if ($this->feed_url !== null || $this->raw_data !== null)
-               {
-                       $this->data = array();
-                       $this->multifeed_objects = array();
-                       $cache = false;
-
-                       if ($this->feed_url !== null)
-                       {
-                               $parsed_feed_url = SimplePie_Misc::parse_url($this->feed_url);
-                               // Decide whether to enable caching
-                               if ($this->cache && $parsed_feed_url['scheme'] !== '')
-                               {
-                                       $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc');
-                               }
-                               // If it's enabled and we don't want an XML dump, use the cache
-                               if ($cache && !$this->xml_dump)
-                               {
-                                       // Load the Cache
-                                       $this->data = $cache->load();
-                                       if (!empty($this->data))
-                                       {
-                                               // If the cache is for an outdated build of SimplePie
-                                               if (!isset($this->data['build']) || $this->data['build'] !== SIMPLEPIE_BUILD)
-                                               {
-                                                       $cache->unlink();
-                                                       $this->data = array();
-                                               }
-                                               // If we've hit a collision just rerun it with caching disabled
-                                               elseif (isset($this->data['url']) && $this->data['url'] !== $this->feed_url)
-                                               {
-                                                       $cache = false;
-                                                       $this->data = array();
-                                               }
-                                               // If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL.
-                                               elseif (isset($this->data['feed_url']))
-                                               {
-                                                       // If the autodiscovery cache is still valid use it.
-                                                       if ($cache->mtime() + $this->autodiscovery_cache_duration > time())
-                                                       {
-                                                               // Do not need to do feed autodiscovery yet.
-                                                               if ($this->data['feed_url'] === $this->data['url'])
-                                                               {
-                                                                       $cache->unlink();
-                                                                       $this->data = array();
-                                                               }
-                                                               else
-                                                               {
-                                                                       $this->set_feed_url($this->data['feed_url']);
-                                                                       return $this->init();
-                                                               }
-                                                       }
-                                               }
-                                               // Check if the cache has been updated
-                                               elseif ($cache->mtime() + $this->cache_duration < time())
-                                               {
-                                                       // If we have last-modified and/or etag set
-                                                       if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag']))
-                                                       {
-                                                               $headers = array();
-                                                               if (isset($this->data['headers']['last-modified']))
-                                                               {
-                                                                       $headers['if-modified-since'] = $this->data['headers']['last-modified'];
-                                                               }
-                                                               if (isset($this->data['headers']['etag']))
-                                                               {
-                                                                       $headers['if-none-match'] = '"' . $this->data['headers']['etag'] . '"';
-                                                               }
-                                                               $file = new $this->file_class($this->feed_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen);
-                                                               if ($file->success)
-                                                               {
-                                                                       if ($file->status_code === 304)
-                                                                       {
-                                                                               $cache->touch();
-                                                                               return true;
-                                                                       }
-                                                                       else
-                                                                       {
-                                                                               $headers = $file->headers;
-                                                                       }
-                                                               }
-                                                               else
-                                                               {
-                                                                       unset($file);
-                                                               }
-                                                       }
-                                               }
-                                               // If the cache is still valid, just return true
-                                               else
-                                               {
-                                                       return true;
-                                               }
-                                       }
-                                       // If the cache is empty, delete it
-                                       else
-                                       {
-                                               $cache->unlink();
-                                               $this->data = array();
-                                       }
-                               }
-                               // If we don't already have the file (it'll only exist if we've opened it to check if the cache has been modified), open it.
-                               if (!isset($file))
-                               {
-                                       if (is_a($this->file, 'SimplePie_File') && $this->file->url === $this->feed_url)
-                                       {
-                                               $file =& $this->file;
-                                       }
-                                       else
-                                       {
-                                               $file = new $this->file_class($this->feed_url, $this->timeout, 5, null, $this->useragent, $this->force_fsockopen);
-                                       }
-                               }
-                               // If the file connection has an error, set SimplePie::error to that and quit
-                               if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
-                               {
-                                       $this->error = $file->error;
-                                       if (!empty($this->data))
-                                       {
-                                               return true;
-                                       }
-                                       else
-                                       {
-                                               return false;
-                                       }
-                               }
-
-                               if (!$this->force_feed)
-                               {
-                                       // Check if the supplied URL is a feed, if it isn't, look for it.
-                                       $locate = new $this->locator_class($file, $this->timeout, $this->useragent, $this->file_class, $this->max_checked_feeds, $this->content_type_sniffer_class);
-                                       if (!$locate->is_feed($file))
-                                       {
-                                               // We need to unset this so that if SimplePie::set_file() has been called that object is untouched
-                                               unset($file);
-                                               if ($file = $locate->find($this->autodiscovery, $this->all_discovered_feeds))
-                                               {
-                                                       if ($cache)
-                                                       {
-                                                               $this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD);
-                                                               if (!$cache->save($this))
-                                                               {
-                                                                       trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
-                                                               }
-                                                               $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc');
-                                                       }
-                                                       $this->feed_url = $file->url;
-                                               }
-                                               else
-                                               {
-                                                       $this->error = "A feed could not be found at $this->feed_url. A feed with an invalid mime type may fall victim to this error, or " . SIMPLEPIE_NAME . " was unable to auto-discover it.. Use force_feed() if you are certain this URL is a real feed.";
-                                                       SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__);
-                                                       return false;
-                                               }
-                                       }
-                                       $locate = null;
-                               }
-
-                               $headers = $file->headers;
-                               $data = $file->body;
-                               $sniffer = new $this->content_type_sniffer_class($file);
-                               $sniffed = $sniffer->get_type();
-                       }
-                       else
-                       {
-                               $data = $this->raw_data;
-                       }
-
-                       // Set up array of possible encodings
-                       $encodings = array();
-
-                       // First check to see if input has been overridden.
-                       if ($this->input_encoding !== false)
-                       {
-                               $encodings[] = $this->input_encoding;
-                       }
-
-                       $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity');
-                       $text_types = array('text/xml', 'text/xml-external-parsed-entity');
-
-                       // RFC 3023 (only applies to sniffed content)
-                       if (isset($sniffed))
-                       {
-                               if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml')
-                               {
-                                       if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset))
-                                       {
-                                               $encodings[] = strtoupper($charset[1]);
-                                       }
-                                       $encodings = array_merge($encodings, SimplePie_Misc::xml_encoding($data));
-                                       $encodings[] = 'UTF-8';
-                               }
-                               elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml')
-                               {
-                                       if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset))
-                                       {
-                                               $encodings[] = $charset[1];
-                                       }
-                                       $encodings[] = 'US-ASCII';
-                               }
-                               // Text MIME-type default
-                               elseif (substr($sniffed, 0, 5) === 'text/')
-                               {
-                                       $encodings[] = 'US-ASCII';
-                               }
-                       }
-
-                       // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1
-                       $encodings = array_merge($encodings, SimplePie_Misc::xml_encoding($data));
-                       $encodings[] = 'UTF-8';
-                       $encodings[] = 'ISO-8859-1';
-
-                       // There's no point in trying an encoding twice
-                       $encodings = array_unique($encodings);
-
-                       // If we want the XML, just output that with the most likely encoding and quit
-                       if ($this->xml_dump)
-                       {
-                               header('Content-type: text/xml; charset=' . $encodings[0]);
-                               echo $data;
-                               exit;
-                       }
-
-                       // Loop through each possible encoding, till we return something, or run out of possibilities
-                       foreach ($encodings as $encoding)
-                       {
-                               // Change the encoding to UTF-8 (as we always use UTF-8 internally)
-                               if ($utf8_data = SimplePie_Misc::change_encoding($data, $encoding, 'UTF-8'))
-                               {
-                                       // Create new parser
-                                       $parser = new $this->parser_class();
-
-                                       // If it's parsed fine
-                                       if ($parser->parse($utf8_data, 'UTF-8'))
-                                       {
-                                               $this->data = $parser->get_data();
-                                               if ($this->get_type() & ~SIMPLEPIE_TYPE_NONE)
-                                               {
-                                                       if (isset($headers))
-                                                       {
-                                                               $this->data['headers'] = $headers;
-                                                       }
-                                                       $this->data['build'] = SIMPLEPIE_BUILD;
-
-                                                       // Cache the file if caching is enabled
-                                                       if ($cache && !$cache->save($this))
-                                                       {
-                                                               trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
-                                                       }
-                                                       return true;
-                                               }
-                                               else
-                                               {
-                                                       $this->error = "A feed could not be found at $this->feed_url. This does not appear to be a valid RSS or Atom feed.";
-                                                       SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__);
-                                                       return false;
-                                               }
-                                       }
-                               }
-                       }
-                       if (isset($parser))
-                       {
-                               // We have an error, just set SimplePie_Misc::error to it and quit
-                               $this->error = sprintf('This XML document is invalid, likely due to invalid characters. XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column());
-                       }
-                       else
-                       {
-                               $this->error = 'The data could not be converted to UTF-8. You MUST have either the iconv or mbstring extension installed. Upgrading to PHP 5.x (which includes iconv) is highly recommended.';
-                       }
-                       SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__);
-                       return false;
-               }
-               elseif (!empty($this->multifeed_url))
-               {
-                       $i = 0;
-                       $success = 0;
-                       $this->multifeed_objects = array();
-                       foreach ($this->multifeed_url as $url)
-                       {
-                               if (SIMPLEPIE_PHP5)
-                               {
-                                       // This keyword needs to defy coding standards for PHP4 compatibility
-                                       $this->multifeed_objects[$i] = clone($this);
-                               }
-                               else
-                               {
-                                       $this->multifeed_objects[$i] = $this;
-                               }
-                               $this->multifeed_objects[$i]->set_feed_url($url);
-                               $success |= $this->multifeed_objects[$i]->init();
-                               $i++;
-                       }
-                       return (bool) $success;
-               }
-               else
-               {
-                       return false;
-               }
-       }
-
-       /**
-        * Return the error message for the occured error
-        *
-        * @access public
-        * @return string Error message
-        */
-       function error()
-       {
-               return $this->error;
-       }
-
-       function get_encoding()
-       {
-               return $this->sanitize->output_encoding;
-       }
-
-       function handle_content_type($mime = 'text/html')
-       {
-               if (!headers_sent())
-               {
-                       $header = "Content-type: $mime;";
-                       if ($this->get_encoding())
-                       {
-                               $header .= ' charset=' . $this->get_encoding();
-                       }
-                       else
-                       {
-                               $header .= ' charset=UTF-8';
-                       }
-                       header($header);
-               }
-       }
-
-       function get_type()
-       {
-               if (!isset($this->data['type']))
-               {
-                       $this->data['type'] = SIMPLEPIE_TYPE_ALL;
-                       if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed']))
-                       {
-                               $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_10;
-                       }
-                       elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed']))
-                       {
-                               $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_03;
-                       }
-                       elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF']))
-                       {
-                               if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['channel'])
-                               || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['image'])
-                               || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item'])
-                               || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['textinput']))
-                               {
-                                       $this->data['type'] &= SIMPLEPIE_TYPE_RSS_10;
-                               }
-                               if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['channel'])
-                               || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['image'])
-                               || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item'])
-                               || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['textinput']))
-                               {
-                                       $this->data['type'] &= SIMPLEPIE_TYPE_RSS_090;
-                               }
-                       }
-                       elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss']))
-                       {
-                               $this->data['type'] &= SIMPLEPIE_TYPE_RSS_ALL;
-                               if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version']))
-                               {
-                                       switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version']))
-                                       {
-                                               case '0.91':
-                                                       $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091;
-                                                       if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data']))
-                                                       {
-                                                               switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data']))
-                                                               {
-                                                                       case '0':
-                                                                               $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_NETSCAPE;
-                                                                               break;
-
-                                                                       case '24':
-                                                                               $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_USERLAND;
-                                                                               break;
-                                                               }
-                                                       }
-                                                       break;
-
-                                               case '0.92':
-                                                       $this->data['type'] &= SIMPLEPIE_TYPE_RSS_092;
-                                                       break;
-
-                                               case '0.93':
-                                                       $this->data['type'] &= SIMPLEPIE_TYPE_RSS_093;
-                                                       break;
-
-                                               case '0.94':
-                                                       $this->data['type'] &= SIMPLEPIE_TYPE_RSS_094;
-                                                       break;
-
-                                               case '2.0':
-                                                       $this->data['type'] &= SIMPLEPIE_TYPE_RSS_20;
-                                                       break;
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               $this->data['type'] = SIMPLEPIE_TYPE_NONE;
-                       }
-               }
-               return $this->data['type'];
-       }
-
-       /**
-        * Returns the URL for the favicon of the feed's website.
-        *
-        * @todo Cache atom:icon
-        * @access public
-        * @since 1.0
-        */
-       function get_favicon()
-       {
-               if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
-               }
-               elseif (($url = $this->get_link()) !== null && preg_match('/^http(s)?:\/\//i', $url))
-               {
-                       $favicon = SimplePie_Misc::absolutize_url('/favicon.ico', $url);
-
-                       if ($this->cache && $this->favicon_handler)
-                       {
-                               $favicon_filename = call_user_func($this->cache_name_function, $favicon);
-                               $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, $favicon_filename, 'spi');
-
-                               if ($cache->load())
-                               {
-                                       return $this->sanitize($this->favicon_handler . $favicon_filename, SIMPLEPIE_CONSTRUCT_IRI);
-                               }
-                               else
-                               {
-                                       $file = new $this->file_class($favicon, $this->timeout / 10, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen);
-
-                                       if ($file->success && ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)) && strlen($file->body) > 0)
-                                       {
-                                               $sniffer = new $this->content_type_sniffer_class($file);
-                                               if (substr($sniffer->get_type(), 0, 6) === 'image/')
-                                               {
-                                                       if ($cache->save(array('headers' => $file->headers, 'body' => $file->body)))
-                                                       {
-                                                               return $this->sanitize($this->favicon_handler . $favicon_filename, SIMPLEPIE_CONSTRUCT_IRI);
-                                                       }
-                                                       else
-                                                       {
-                                                               trigger_error("$cache->name is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
-                                                               return $this->sanitize($favicon, SIMPLEPIE_CONSTRUCT_IRI);
-                                                       }
-                                               }
-                                               // not an image
-                                               else
-                                               {
-                                                       return false;
-                                               }
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               return $this->sanitize($favicon, SIMPLEPIE_CONSTRUCT_IRI);
-                       }
-               }
-               return false;
-       }
-
-       /**
-        * @todo If we have a perm redirect we should return the new URL
-        * @todo When we make the above change, let's support <itunes:new-feed-url> as well
-        * @todo Also, |atom:link|@rel=self
-        */
-       function subscribe_url()
-       {
-               if ($this->feed_url !== null)
-               {
-                       return $this->sanitize($this->feed_url, SIMPLEPIE_CONSTRUCT_IRI);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function subscribe_feed()
-       {
-               if ($this->feed_url !== null)
-               {
-                       return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 2), SIMPLEPIE_CONSTRUCT_IRI);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function subscribe_outlook()
-       {
-               if ($this->feed_url !== null)
-               {
-                       return $this->sanitize('outlook' . SimplePie_Misc::fix_protocol($this->feed_url, 2), SIMPLEPIE_CONSTRUCT_IRI);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function subscribe_podcast()
-       {
-               if ($this->feed_url !== null)
-               {
-                       return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 3), SIMPLEPIE_CONSTRUCT_IRI);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function subscribe_itunes()
-       {
-               if ($this->feed_url !== null)
-               {
-                       return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 4), SIMPLEPIE_CONSTRUCT_IRI);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       /**
-        * Creates the subscribe_* methods' return data
-        *
-        * @access private
-        * @param string $feed_url String to prefix to the feed URL
-        * @param string $site_url String to prefix to the site URL (and
-        * suffix to the feed URL)
-        * @return mixed URL if feed exists, false otherwise
-        */
-       function subscribe_service($feed_url, $site_url = null)
-       {
-               if ($this->subscribe_url())
-               {
-                       $return = $feed_url . rawurlencode($this->feed_url);
-                       if ($site_url !== null && $this->get_link() !== null)
-                       {
-                               $return .= $site_url . rawurlencode($this->get_link());
-                       }
-                       return $this->sanitize($return, SIMPLEPIE_CONSTRUCT_IRI);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function subscribe_aol()
-       {
-               return $this->subscribe_service('http://feeds.my.aol.com/add.jsp?url=');
-       }
-
-       function subscribe_bloglines()
-       {
-               return $this->subscribe_service('http://www.bloglines.com/sub/');
-       }
-
-       function subscribe_eskobo()
-       {
-               return $this->subscribe_service('http://www.eskobo.com/?AddToMyPage=');
-       }
-
-       function subscribe_feedfeeds()
-       {
-               return $this->subscribe_service('http://www.feedfeeds.com/add?feed=');
-       }
-
-       function subscribe_feedster()
-       {
-               return $this->subscribe_service('http://www.feedster.com/myfeedster.php?action=addrss&confirm=no&rssurl=');
-       }
-
-       function subscribe_google()
-       {
-               return $this->subscribe_service('http://fusion.google.com/add?feedurl=');
-       }
-
-       function subscribe_gritwire()
-       {
-               return $this->subscribe_service('http://my.gritwire.com/feeds/addExternalFeed.aspx?FeedUrl=');
-       }
-
-       function subscribe_msn()
-       {
-               return $this->subscribe_service('http://my.msn.com/addtomymsn.armx?id=rss&ut=', '&ru=');
-       }
-
-       function subscribe_netvibes()
-       {
-               return $this->subscribe_service('http://www.netvibes.com/subscribe.php?url=');
-       }
-
-       function subscribe_newsburst()
-       {
-               return $this->subscribe_service('http://www.newsburst.com/Source/?add=');
-       }
-
-       function subscribe_newsgator()
-       {
-               return $this->subscribe_service('http://www.newsgator.com/ngs/subscriber/subext.aspx?url=');
-       }
-
-       function subscribe_odeo()
-       {
-               return $this->subscribe_service('http://www.odeo.com/listen/subscribe?feed=');
-       }
-
-       function subscribe_podnova()
-       {
-               return $this->subscribe_service('http://www.podnova.com/index_your_podcasts.srf?action=add&url=');
-       }
-
-       function subscribe_rojo()
-       {
-               return $this->subscribe_service('http://www.rojo.com/add-subscription?resource=');
-       }
-
-       function subscribe_yahoo()
-       {
-               return $this->subscribe_service('http://add.my.yahoo.com/rss?url=');
-       }
-
-       function get_feed_tags($namespace, $tag)
-       {
-               $type = $this->get_type();
-               if ($type & SIMPLEPIE_TYPE_ATOM_10)
-               {
-                       if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag]))
-                       {
-                               return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag];
-                       }
-               }
-               if ($type & SIMPLEPIE_TYPE_ATOM_03)
-               {
-                       if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag]))
-                       {
-                               return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag];
-                       }
-               }
-               if ($type & SIMPLEPIE_TYPE_RSS_RDF)
-               {
-                       if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag]))
-                       {
-                               return $this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag];
-                       }
-               }
-               if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
-               {
-                       if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag]))
-                       {
-                               return $this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag];
-                       }
-               }
-               return null;
-       }
-
-       function get_channel_tags($namespace, $tag)
-       {
-               $type = $this->get_type();
-               if ($type & SIMPLEPIE_TYPE_ATOM_ALL)
-               {
-                       if ($return = $this->get_feed_tags($namespace, $tag))
-                       {
-                               return $return;
-                       }
-               }
-               if ($type & SIMPLEPIE_TYPE_RSS_10)
-               {
-                       if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'channel'))
-                       {
-                               if (isset($channel[0]['child'][$namespace][$tag]))
-                               {
-                                       return $channel[0]['child'][$namespace][$tag];
-                               }
-                       }
-               }
-               if ($type & SIMPLEPIE_TYPE_RSS_090)
-               {
-                       if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'channel'))
-                       {
-                               if (isset($channel[0]['child'][$namespace][$tag]))
-                               {
-                                       return $channel[0]['child'][$namespace][$tag];
-                               }
-                       }
-               }
-               if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
-               {
-                       if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'channel'))
-                       {
-                               if (isset($channel[0]['child'][$namespace][$tag]))
-                               {
-                                       return $channel[0]['child'][$namespace][$tag];
-                               }
-                       }
-               }
-               return null;
-       }
-
-       function get_image_tags($namespace, $tag)
-       {
-               $type = $this->get_type();
-               if ($type & SIMPLEPIE_TYPE_RSS_10)
-               {
-                       if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'image'))
-                       {
-                               if (isset($image[0]['child'][$namespace][$tag]))
-                               {
-                                       return $image[0]['child'][$namespace][$tag];
-                               }
-                       }
-               }
-               if ($type & SIMPLEPIE_TYPE_RSS_090)
-               {
-                       if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'image'))
-                       {
-                               if (isset($image[0]['child'][$namespace][$tag]))
-                               {
-                                       return $image[0]['child'][$namespace][$tag];
-                               }
-                       }
-               }
-               if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
-               {
-                       if ($image = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'image'))
-                       {
-                               if (isset($image[0]['child'][$namespace][$tag]))
-                               {
-                                       return $image[0]['child'][$namespace][$tag];
-                               }
-                       }
-               }
-               return null;
-       }
-
-       function get_base($element = array())
-       {
-               if (!($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION) && !empty($element['xml_base_explicit']) && isset($element['xml_base']))
-               {
-                       return $element['xml_base'];
-               }
-               elseif ($this->get_link() !== null)
-               {
-                       return $this->get_link();
-               }
-               else
-               {
-                       return $this->subscribe_url();
-               }
-       }
-
-       function sanitize($data, $type, $base = '')
-       {
-               return $this->sanitize->sanitize($data, $type, $base);
-       }
-
-       function get_title()
-       {
-               if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_category($key = 0)
-       {
-               $categories = $this->get_categories();
-               if (isset($categories[$key]))
-               {
-                       return $categories[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_categories()
-       {
-               $categories = array();
-
-               foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
-               {
-                       $term = null;
-                       $scheme = null;
-                       $label = null;
-                       if (isset($category['attribs']['']['term']))
-                       {
-                               $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($category['attribs']['']['scheme']))
-                       {
-                               $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($category['attribs']['']['label']))
-                       {
-                               $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       $categories[] = new $this->category_class($term, $scheme, $label);
-               }
-               foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
-               {
-                       // This is really the label, but keep this as the term also for BC.
-                       // Label will also work on retrieving because that falls back to term.
-                       $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       if (isset($category['attribs']['']['domain']))
-                       {
-                               $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       else
-                       {
-                               $scheme = null;
-                       }
-                       $categories[] = new $this->category_class($term, $scheme, null);
-               }
-               foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
-               {
-                       $categories[] = new $this->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
-               }
-               foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
-               {
-                       $categories[] = new $this->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
-               }
-
-               if (!empty($categories))
-               {
-                       return SimplePie_Misc::array_unique($categories);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_author($key = 0)
-       {
-               $authors = $this->get_authors();
-               if (isset($authors[$key]))
-               {
-                       return $authors[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_authors()
-       {
-               $authors = array();
-               foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
-               {
-                       $name = null;
-                       $uri = null;
-                       $email = null;
-                       $avatar = null;
-                       $name_date = null;
-                       $uri_date = null;
-                       $avatar_date = null;
-
-                       if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
-                       {
-                               $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
-                       {
-                               $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
-                       }
-                       if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
-                       {
-                               $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0]['data']))
-                       {
-                               $avatar = $this->sanitize($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0]));
-                       }
-                       if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['name-updated'][0]['data']))
-                       {
-                               $name_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['name-updated'][0]['data'];
-                       }
-                       if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['uri-updated'][0]['data']))
-                       {
-                               $uri_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['uri-updated'][0]['data'];
-                       }
-                       if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar-updated'][0]['data']))
-                       {
-                               $avatar_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar-updated'][0]['data'];
-                       }
-
-                       if ($name !== null || $email !== null || $uri !== null || $avatar !== null || $name_date !== null || $uri_date !== null || $avatar_date !== null )
-                       {
-                               $authors[] = new $this->author_class($name, $uri, $email, $avatar, $name_date, $uri_date, $avatar_date);
-                       }
-               }
-               if ($author = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
-               {
-                       $name = null;
-                       $url = null;
-                       $email = null;
-                       if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
-                       {
-                               $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
-                       {
-                               $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
-                       }
-                       if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
-                       {
-                               $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if ($name !== null || $email !== null || $url !== null)
-                       {
-                               $authors[] = new $this->author_class($name, $url, $email);
-                       }
-               }
-               foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
-               {
-                       $authors[] = new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
-               }
-               foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
-               {
-                       $authors[] = new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
-               }
-               foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
-               {
-                       $authors[] = new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
-               }
-
-               if (!empty($authors))
-               {
-                       return SimplePie_Misc::array_unique($authors);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_contributor($key = 0)
-       {
-               $contributors = $this->get_contributors();
-               if (isset($contributors[$key]))
-               {
-                       return $contributors[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_contributors()
-       {
-               $contributors = array();
-               foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
-               {
-                       $name = null;
-                       $uri = null;
-                       $email = null;
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
-                       {
-                               $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
-                       {
-                               $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
-                       }
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
-                       {
-                               $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if ($name !== null || $email !== null || $uri !== null)
-                       {
-                               $contributors[] = new $this->author_class($name, $uri, $email);
-                       }
-               }
-               foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
-               {
-                       $name = null;
-                       $url = null;
-                       $email = null;
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
-                       {
-                               $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
-                       {
-                               $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
-                       }
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
-                       {
-                               $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if ($name !== null || $email !== null || $url !== null)
-                       {
-                               $contributors[] = new $this->author_class($name, $url, $email);
-                       }
-               }
-
-               if (!empty($contributors))
-               {
-                       return SimplePie_Misc::array_unique($contributors);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_link($key = 0, $rel = 'alternate')
-       {
-               $links = $this->get_links($rel);
-               if (isset($links[$key]))
-               {
-                       return $links[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       /**
-        * Added for parity between the parent-level and the item/entry-level.
-        */
-       function get_permalink()
-       {
-               return $this->get_link(0);
-       }
-
-       function get_links($rel = 'alternate')
-       {
-               if (!isset($this->data['links']))
-               {
-                       $this->data['links'] = array();
-                       if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link'))
-                       {
-                               foreach ($links as $link)
-                               {
-                                       if (isset($link['attribs']['']['href']))
-                                       {
-                                               $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
-                                               $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
-                                       }
-                               }
-                       }
-                       if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link'))
-                       {
-                               foreach ($links as $link)
-                               {
-                                       if (isset($link['attribs']['']['href']))
-                                       {
-                                               $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
-                                               $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
-
-                                       }
-                               }
-                       }
-                       if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
-                       {
-                               $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
-                       }
-                       if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
-                       {
-                               $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
-                       }
-                       if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
-                       {
-                               $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
-                       }
-
-                       $keys = array_keys($this->data['links']);
-                       foreach ($keys as $key)
-                       {
-                               if (SimplePie_Misc::is_isegment_nz_nc($key))
-                               {
-                                       if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
-                                       {
-                                               $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
-                                               $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
-                                       }
-                                       else
-                                       {
-                                               $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
-                                       }
-                               }
-                               elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
-                               {
-                                       $this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
-                               }
-                               $this->data['links'][$key] = array_unique($this->data['links'][$key]);
-                       }
-               }
-
-               if (isset($this->data['links'][$rel]))
-               {
-                       return $this->data['links'][$rel];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_all_discovered_feeds()
-       {
-               return $this->all_discovered_feeds;
-       }
-
-       function get_description()
-       {
-               if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle'))
-               {
-                       return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline'))
-               {
-                       return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_copyright()
-       {
-               if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
-               {
-                       return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright'))
-               {
-                       return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_language()
-       {
-               if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang']))
-               {
-                       return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang']))
-               {
-                       return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang']))
-               {
-                       return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif (isset($this->data['headers']['content-language']))
-               {
-                       return $this->sanitize($this->data['headers']['content-language'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_latitude()
-       {
-               
-               if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
-               {
-                       return (float) $return[0]['data'];
-               }
-               elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
-               {
-                       return (float) $match[1];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_longitude()
-       {
-               if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
-               {
-                       return (float) $return[0]['data'];
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
-               {
-                       return (float) $return[0]['data'];
-               }
-               elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
-               {
-                       return (float) $match[2];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_image_title()
-       {
-               if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_image_url()
-       {
-               if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image'))
-               {
-                       return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI);
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'url'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'url'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_image_link()
-       {
-               if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_image_width()
-       {
-               if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'width'))
-               {
-                       return round($return[0]['data']);
-               }
-               elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
-               {
-                       return 88.0;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_image_height()
-       {
-               if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'height'))
-               {
-                       return round($return[0]['data']);
-               }
-               elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
-               {
-                       return 31.0;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_item_quantity($max = 0)
-       {
-               $max = (int) $max;
-               $qty = count($this->get_items());
-               if ($max === 0)
-               {
-                       return $qty;
-               }
-               else
-               {
-                       return ($qty > $max) ? $max : $qty;
-               }
-       }
-
-       function get_item($key = 0)
-       {
-               $items = $this->get_items();
-               if (isset($items[$key]))
-               {
-                       return $items[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_items($start = 0, $end = 0)
-       {
-               if (!isset($this->data['items']))
-               {
-                       if (!empty($this->multifeed_objects))
-                       {
-                               $this->data['items'] = SimplePie::merge_items($this->multifeed_objects, $start, $end, $this->item_limit);
-                       }
-                       else
-                       {
-                               $this->data['items'] = array();
-                               if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'entry'))
-                               {
-                                       $keys = array_keys($items);
-                                       foreach ($keys as $key)
-                                       {
-                                               $this->data['items'][] = new $this->item_class($this, $items[$key]);
-                                       }
-                               }
-                               if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'entry'))
-                               {
-                                       $keys = array_keys($items);
-                                       foreach ($keys as $key)
-                                       {
-                                               $this->data['items'][] = new $this->item_class($this, $items[$key]);
-                                       }
-                               }
-                               if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'item'))
-                               {
-                                       $keys = array_keys($items);
-                                       foreach ($keys as $key)
-                                       {
-                                               $this->data['items'][] = new $this->item_class($this, $items[$key]);
-                                       }
-                               }
-                               if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'item'))
-                               {
-                                       $keys = array_keys($items);
-                                       foreach ($keys as $key)
-                                       {
-                                               $this->data['items'][] = new $this->item_class($this, $items[$key]);
-                                       }
-                               }
-                               if ($items = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'item'))
-                               {
-                                       $keys = array_keys($items);
-                                       foreach ($keys as $key)
-                                       {
-                                               $this->data['items'][] = new $this->item_class($this, $items[$key]);
-                                       }
-                               }
-                       }
-               }
-
-               if (!empty($this->data['items']))
-               {
-                       // If we want to order it by date, check if all items have a date, and then sort it
-                       if ($this->order_by_date && empty($this->multifeed_objects))
-                       {
-                               if (!isset($this->data['ordered_items']))
-                               {
-                                       $do_sort = true;
-                                       foreach ($this->data['items'] as $item)
-                                       {
-                                               if (!$item->get_date('U'))
-                                               {
-                                                       $do_sort = false;
-                                                       break;
-                                               }
-                                       }
-                                       $item = null;
-                                       $this->data['ordered_items'] = $this->data['items'];
-                                       if ($do_sort)
-                                       {
-                                               usort($this->data['ordered_items'], array(&$this, 'sort_items'));
-                                       }
-                               }
-                               $items = $this->data['ordered_items'];
-                       }
-                       else
-                       {
-                               $items = $this->data['items'];
-                       }
-
-                       // Slice the data as desired
-                       if ($end === 0)
-                       {
-                               return array_slice($items, $start);
-                       }
-                       else
-                       {
-                               return array_slice($items, $start, $end);
-                       }
-               }
-               else
-               {
-                       return array();
-               }
-       }
-
-       /**
-        * @static
-        */
-       function sort_items($a, $b)
-       {
-               return $a->get_date('U') <= $b->get_date('U');
-       }
-
-       /**
-        * @static
-        */
-       function merge_items($urls, $start = 0, $end = 0, $limit = 0)
-       {
-               if (is_array($urls) && sizeof($urls) > 0)
-               {
-                       $items = array();
-                       foreach ($urls as $arg)
-                       {
-                               if (is_a($arg, 'SimplePie'))
-                               {
-                                       $items = array_merge($items, $arg->get_items(0, $limit));
-                               }
-                               else
-                               {
-                                       trigger_error('Arguments must be SimplePie objects', E_USER_WARNING);
-                               }
-                       }
-
-                       $do_sort = true;
-                       foreach ($items as $item)
-                       {
-                               if (!$item->get_date('U'))
-                               {
-                                       $do_sort = false;
-                                       break;
-                               }
-                       }
-                       $item = null;
-                       if ($do_sort)
-                       {
-                               usort($items, array('SimplePie', 'sort_items'));
-                       }
-
-                       if ($end === 0)
-                       {
-                               return array_slice($items, $start);
-                       }
-                       else
-                       {
-                               return array_slice($items, $start, $end);
-                       }
-               }
-               else
-               {
-                       trigger_error('Cannot merge zero SimplePie objects', E_USER_WARNING);
-                       return array();
-               }
-       }
-}
-
-class SimplePie_Item
-{
-       var $feed;
-       var $data = array();
-
-       function SimplePie_Item($feed, $data)
-       {
-               $this->feed = $feed;
-               $this->data = $data;
-       }
-
-       function __toString()
-       {
-               return md5(serialize($this->data));
-       }
-
-       /**
-        * Remove items that link back to this before destroying this object
-        */
-       function __destruct()
-       {
-               if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode'))
-               {
-                       unset($this->feed);
-               }
-       }
-
-       function get_item_tags($namespace, $tag)
-       {
-               if (isset($this->data['child'][$namespace][$tag]))
-               {
-                       return $this->data['child'][$namespace][$tag];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_base($element = array())
-       {
-               return $this->feed->get_base($element);
-       }
-
-       function sanitize($data, $type, $base = '')
-       {
-               return $this->feed->sanitize($data, $type, $base);
-       }
-
-       function get_feed()
-       {
-               return $this->feed;
-       }
-
-       function get_id($hash = false)
-       {
-               if (!$hash)
-               {
-                       if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id'))
-                       {
-                               return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'id'))
-                       {
-                               return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid'))
-                       {
-                               return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'identifier'))
-                       {
-                               return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'identifier'))
-                       {
-                               return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       elseif (($return = $this->get_permalink()) !== null)
-                       {
-                               return $return;
-                       }
-                       elseif (($return = $this->get_title()) !== null)
-                       {
-                               return $return;
-                       }
-               }
-               if ($this->get_permalink() !== null || $this->get_title() !== null)
-               {
-                       return md5($this->get_permalink() . $this->get_title());
-               }
-               else
-               {
-                       return md5(serialize($this->data));
-               }
-       }
-
-       function get_title()
-       {
-               if (!isset($this->data['title']))
-               {
-                       if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
-                       {
-                               $this->data['title'] = $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-                       }
-                       elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
-                       {
-                               $this->data['title'] = $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-                       }
-                       elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
-                       {
-                               $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
-                       }
-                       elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
-                       {
-                               $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
-                       }
-                       elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
-                       {
-                               $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
-                       }
-                       elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
-                       {
-                               $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
-                       {
-                               $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       else
-                       {
-                               $this->data['title'] = null;
-                       }
-               }
-               return $this->data['title'];
-       }
-
-       function get_description($description_only = false)
-       {
-               if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'summary'))
-               {
-                       return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'summary'))
-               {
-                       return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif (!$description_only)
-               {
-                       return $this->get_content(true);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_content($content_only = false)
-       {
-               if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'content'))
-               {
-                       return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_content_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'content'))
-               {
-                       return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
-               }
-               elseif (!$content_only)
-               {
-                       return $this->get_description(true);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_category($key = 0)
-       {
-               $categories = $this->get_categories();
-               if (isset($categories[$key]))
-               {
-                       return $categories[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_categories()
-       {
-               $categories = array();
-
-               foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
-               {
-                       $term = null;
-                       $scheme = null;
-                       $label = null;
-                       if (isset($category['attribs']['']['term']))
-                       {
-                               $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($category['attribs']['']['scheme']))
-                       {
-                               $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($category['attribs']['']['label']))
-                       {
-                               $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       $categories[] = new $this->feed->category_class($term, $scheme, $label);
-               }
-               foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
-               {
-                       // This is really the label, but keep this as the term also for BC.
-                       // Label will also work on retrieving because that falls back to term.
-                       $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       if (isset($category['attribs']['']['domain']))
-                       {
-                               $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       else
-                       {
-                               $scheme = null;
-                       }
-                       $categories[] = new $this->feed->category_class($term, $scheme, null);
-               }
-               foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
-               {
-                       $categories[] = new $this->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
-               }
-               foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
-               {
-                       $categories[] = new $this->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
-               }
-
-               if (!empty($categories))
-               {
-                       return SimplePie_Misc::array_unique($categories);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_author($key = 0)
-       {
-               $authors = $this->get_authors();
-               if (isset($authors[$key]))
-               {
-                       return $authors[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_contributor($key = 0)
-       {
-               $contributors = $this->get_contributors();
-               if (isset($contributors[$key]))
-               {
-                       return $contributors[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_contributors()
-       {
-               $contributors = array();
-               foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
-               {
-                       $name = null;
-                       $uri = null;
-                       $email = null;
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
-                       {
-                               $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
-                       {
-                               $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
-                       }
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
-                       {
-                               $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if ($name !== null || $email !== null || $uri !== null)
-                       {
-                               $contributors[] = new $this->feed->author_class($name, $uri, $email);
-                       }
-               }
-               foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
-               {
-                       $name = null;
-                       $url = null;
-                       $email = null;
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
-                       {
-                               $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
-                       {
-                               $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
-                       }
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
-                       {
-                               $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if ($name !== null || $email !== null || $url !== null)
-                       {
-                               $contributors[] = new $this->feed->author_class($name, $url, $email);
-                       }
-               }
-
-               if (!empty($contributors))
-               {
-                       return SimplePie_Misc::array_unique($contributors);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_authors()
-       {
-               $authors = array();
-               foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
-               {
-                       $name = null;
-                       $uri = null;
-                       $email = null;
-                       $avatar = null;
-                       $name_date = null;
-                       $uri_date = null;
-                       $avatar_date = null;
-                       if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
-                       {
-                               $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
-                       {
-                               $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
-                       }
-                       if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
-                       {
-                               $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0]['data']))
-                       {
-                               $avatar = $this->sanitize($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0]));
-                       }
-                       if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['name-updated'][0]['data']))
-                       {
-                               $name_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['name-updated'][0]['data'];
-                       }
-                       if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['uri-updated'][0]['data']))
-                       {
-                               $uri_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['uri-updated'][0]['data'];
-                       }
-                       if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar-updated'][0]['data']))
-                       {
-                               $avatar_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar-updated'][0]['data'];
-                       }
-
-                       if ($name !== null || $email !== null || $uri !== null || $avatar !== null || $name_date !== null || $uri_date !== null || $avatar_date !== null )
-                       {
-                               $authors[] = new $this->feed->author_class($name, $uri, $email, $avatar, $name_date, $uri_date, $avatar_date);
-                       }
-               }
-               if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
-               {
-                       $name = null;
-                       $url = null;
-                       $email = null;
-                       if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
-                       {
-                               $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
-                       {
-                               $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
-                       }
-                       if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
-                       {
-                               $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if ($name !== null || $email !== null || $url !== null)
-                       {
-                               $authors[] = new $this->feed->author_class($name, $url, $email);
-                       }
-               }
-               if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'author'))
-               {
-                       $authors[] = new $this->feed->author_class(null, null, $this->sanitize($author[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
-               }
-               foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
-               {
-                       $authors[] = new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
-               }
-               foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
-               {
-                       $authors[] = new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
-               }
-               foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
-               {
-                       $authors[] = new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
-               }
-
-               if (!empty($authors))
-               {
-                       return SimplePie_Misc::array_unique($authors);
-               }
-               elseif (($source = $this->get_source()) && ($authors = $source->get_authors()))
-               {
-                       return $authors;
-               }
-               elseif ($authors = $this->feed->get_authors())
-               {
-                       return $authors;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_copyright()
-       {
-               if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
-               {
-                       return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_date($date_format = 'j F Y, g:i a')
-       {
-               if (!isset($this->data['date']))
-               {
-                       if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published'))
-                       {
-                               $this->data['date']['raw'] = $return[0]['data'];
-                       }
-                       elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated'))
-                       {
-                               $this->data['date']['raw'] = $return[0]['data'];
-                       }
-                       elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'issued'))
-                       {
-                               $this->data['date']['raw'] = $return[0]['data'];
-                       }
-                       elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'created'))
-                       {
-                               $this->data['date']['raw'] = $return[0]['data'];
-                       }
-                       elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'modified'))
-                       {
-                               $this->data['date']['raw'] = $return[0]['data'];
-                       }
-                       elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'pubDate'))
-                       {
-                               $this->data['date']['raw'] = $return[0]['data'];
-                       }
-                       elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date'))
-                       {
-                               $this->data['date']['raw'] = $return[0]['data'];
-                       }
-                       elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date'))
-                       {
-                               $this->data['date']['raw'] = $return[0]['data'];
-                       }
-
-                       if (!empty($this->data['date']['raw']))
-                       {
-                               $parser = SimplePie_Parse_Date::get();
-                               $this->data['date']['parsed'] = $parser->parse($this->data['date']['raw']);
-                       }
-                       else
-                       {
-                               $this->data['date'] = null;
-                       }
-               }
-               if ($this->data['date'])
-               {
-                       $date_format = (string) $date_format;
-                       switch ($date_format)
-                       {
-                               case '':
-                                       return $this->sanitize($this->data['date']['raw'], SIMPLEPIE_CONSTRUCT_TEXT);
-
-                               case 'U':
-                                       return $this->data['date']['parsed'];
-
-                               default:
-                                       return date($date_format, $this->data['date']['parsed']);
-                       }
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_local_date($date_format = '%c')
-       {
-               if (!$date_format)
-               {
-                       return $this->sanitize($this->get_date(''), SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif (($date = $this->get_date('U')) !== null)
-               {
-                       return strftime($date_format, $date);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_permalink()
-       {
-               $link = $this->get_link();
-               $enclosure = $this->get_enclosure(0);
-               if ($link !== null)
-               {
-                       return $link;
-               }
-               elseif ($enclosure !== null)
-               {
-                       return $enclosure->get_link();
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_link($key = 0, $rel = 'alternate')
-       {
-               $links = $this->get_links($rel);
-               if ($links[$key] !== null)
-               {
-                       return $links[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_links($rel = 'alternate')
-       {
-               if (!isset($this->data['links']))
-               {
-                       $this->data['links'] = array();
-                       foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link)
-                       {
-                               if (isset($link['attribs']['']['href']))
-                               {
-                                       $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
-                                       $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
-
-                               }
-                       }
-                       foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link)
-                       {
-                               if (isset($link['attribs']['']['href']))
-                               {
-                                       $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
-                                       $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
-                               }
-                       }
-                       if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
-                       {
-                               $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
-                       }
-                       if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
-                       {
-                               $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
-                       }
-                       if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
-                       {
-                               $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
-                       }
-                       if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid'))
-                       {
-                               if (!isset($links[0]['attribs']['']['isPermaLink']) || strtolower(trim($links[0]['attribs']['']['isPermaLink'])) === 'true')
-                               {
-                                       $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
-                               }
-                       }
-
-                       $keys = array_keys($this->data['links']);
-                       foreach ($keys as $key)
-                       {
-                               if (SimplePie_Misc::is_isegment_nz_nc($key))
-                               {
-                                       if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
-                                       {
-                                               $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
-                                               $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
-                                       }
-                                       else
-                                       {
-                                               $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
-                                       }
-                               }
-                               elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
-                               {
-                                       $this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
-                               }
-                               $this->data['links'][$key] = array_unique($this->data['links'][$key]);
-                       }
-               }
-               if (isset($this->data['links'][$rel]))
-               {
-                       return $this->data['links'][$rel];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       /**
-        * @todo Add ability to prefer one type of content over another (in a media group).
-        */
-       function get_enclosure($key = 0, $prefer = null)
-       {
-               $enclosures = $this->get_enclosures();
-               if (isset($enclosures[$key]))
-               {
-                       return $enclosures[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       /**
-        * Grabs all available enclosures (podcasts, etc.)
-        *
-        * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS.
-        *
-        * At this point, we're pretty much assuming that all enclosures for an item are the same content.  Anything else is too complicated to properly support.
-        *
-        * @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4).
-        * @todo If an element exists at a level, but it's value is empty, we should fall back to the value from the parent (if it exists).
-        */
-       function get_enclosures()
-       {
-               if (!isset($this->data['enclosures']))
-               {
-                       $this->data['enclosures'] = array();
-
-                       // Elements
-                       $captions_parent = null;
-                       $categories_parent = null;
-                       $copyrights_parent = null;
-                       $credits_parent = null;
-                       $description_parent = null;
-                       $duration_parent = null;
-                       $hashes_parent = null;
-                       $keywords_parent = null;
-                       $player_parent = null;
-                       $ratings_parent = null;
-                       $restrictions_parent = null;
-                       $thumbnails_parent = null;
-                       $title_parent = null;
-
-                       // Let's do the channel and item-level ones first, and just re-use them if we need to.
-                       $parent = $this->get_feed();
-
-                       // CAPTIONS
-                       if ($captions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text'))
-                       {
-                               foreach ($captions as $caption)
-                               {
-                                       $caption_type = null;
-                                       $caption_lang = null;
-                                       $caption_startTime = null;
-                                       $caption_endTime = null;
-                                       $caption_text = null;
-                                       if (isset($caption['attribs']['']['type']))
-                                       {
-                                               $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($caption['attribs']['']['lang']))
-                                       {
-                                               $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($caption['attribs']['']['start']))
-                                       {
-                                               $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($caption['attribs']['']['end']))
-                                       {
-                                               $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($caption['data']))
-                                       {
-                                               $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       $captions_parent[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text);
-                               }
-                       }
-                       elseif ($captions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text'))
-                       {
-                               foreach ($captions as $caption)
-                               {
-                                       $caption_type = null;
-                                       $caption_lang = null;
-                                       $caption_startTime = null;
-                                       $caption_endTime = null;
-                                       $caption_text = null;
-                                       if (isset($caption['attribs']['']['type']))
-                                       {
-                                               $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($caption['attribs']['']['lang']))
-                                       {
-                                               $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($caption['attribs']['']['start']))
-                                       {
-                                               $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($caption['attribs']['']['end']))
-                                       {
-                                               $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($caption['data']))
-                                       {
-                                               $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       $captions_parent[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text);
-                               }
-                       }
-                       if (is_array($captions_parent))
-                       {
-                               $captions_parent = array_values(SimplePie_Misc::array_unique($captions_parent));
-                       }
-
-                       // CATEGORIES
-                       foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category)
-                       {
-                               $term = null;
-                               $scheme = null;
-                               $label = null;
-                               if (isset($category['data']))
-                               {
-                                       $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                               }
-                               if (isset($category['attribs']['']['scheme']))
-                               {
-                                       $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                               }
-                               else
-                               {
-                                       $scheme = 'http://search.yahoo.com/mrss/category_schema';
-                               }
-                               if (isset($category['attribs']['']['label']))
-                               {
-                                       $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
-                               }
-                               $categories_parent[] = new $this->feed->category_class($term, $scheme, $label);
-                       }
-                       foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category)
-                       {
-                               $term = null;
-                               $scheme = null;
-                               $label = null;
-                               if (isset($category['data']))
-                               {
-                                       $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                               }
-                               if (isset($category['attribs']['']['scheme']))
-                               {
-                                       $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                               }
-                               else
-                               {
-                                       $scheme = 'http://search.yahoo.com/mrss/category_schema';
-                               }
-                               if (isset($category['attribs']['']['label']))
-                               {
-                                       $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
-                               }
-                               $categories_parent[] = new $this->feed->category_class($term, $scheme, $label);
-                       }
-                       foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'category') as $category)
-                       {
-                               $term = null;
-                               $scheme = 'http://www.itunes.com/dtds/podcast-1.0.dtd';
-                               $label = null;
-                               if (isset($category['attribs']['']['text']))
-                               {
-                                       $label = $this->sanitize($category['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT);
-                               }
-                               $categories_parent[] = new $this->feed->category_class($term, $scheme, $label);
-
-                               if (isset($category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category']))
-                               {
-                                       foreach ((array) $category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'] as $subcategory)
-                                       {
-                                               if (isset($subcategory['attribs']['']['text']))
-                                               {
-                                                       $label = $this->sanitize($subcategory['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                               }
-                                               $categories_parent[] = new $this->feed->category_class($term, $scheme, $label);
-                                       }
-                               }
-                       }
-                       if (is_array($categories_parent))
-                       {
-                               $categories_parent = array_values(SimplePie_Misc::array_unique($categories_parent));
-                       }
-
-                       // COPYRIGHT
-                       if ($copyright = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright'))
-                       {
-                               $copyright_url = null;
-                               $copyright_label = null;
-                               if (isset($copyright[0]['attribs']['']['url']))
-                               {
-                                       $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
-                               }
-                               if (isset($copyright[0]['data']))
-                               {
-                                       $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                               }
-                               $copyrights_parent = new $this->feed->copyright_class($copyright_url, $copyright_label);
-                       }
-                       elseif ($copyright = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright'))
-                       {
-                               $copyright_url = null;
-                               $copyright_label = null;
-                               if (isset($copyright[0]['attribs']['']['url']))
-                               {
-                                       $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
-                               }
-                               if (isset($copyright[0]['data']))
-                               {
-                                       $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                               }
-                               $copyrights_parent = new $this->feed->copyright_class($copyright_url, $copyright_label);
-                       }
-
-                       // CREDITS
-                       if ($credits = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit'))
-                       {
-                               foreach ($credits as $credit)
-                               {
-                                       $credit_role = null;
-                                       $credit_scheme = null;
-                                       $credit_name = null;
-                                       if (isset($credit['attribs']['']['role']))
-                                       {
-                                               $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($credit['attribs']['']['scheme']))
-                                       {
-                                               $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       else
-                                       {
-                                               $credit_scheme = 'urn:ebu';
-                                       }
-                                       if (isset($credit['data']))
-                                       {
-                                               $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       $credits_parent[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name);
-                               }
-                       }
-                       elseif ($credits = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit'))
-                       {
-                               foreach ($credits as $credit)
-                               {
-                                       $credit_role = null;
-                                       $credit_scheme = null;
-                                       $credit_name = null;
-                                       if (isset($credit['attribs']['']['role']))
-                                       {
-                                               $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($credit['attribs']['']['scheme']))
-                                       {
-                                               $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       else
-                                       {
-                                               $credit_scheme = 'urn:ebu';
-                                       }
-                                       if (isset($credit['data']))
-                                       {
-                                               $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       $credits_parent[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name);
-                               }
-                       }
-                       if (is_array($credits_parent))
-                       {
-                               $credits_parent = array_values(SimplePie_Misc::array_unique($credits_parent));
-                       }
-
-                       // DESCRIPTION
-                       if ($description_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description'))
-                       {
-                               if (isset($description_parent[0]['data']))
-                               {
-                                       $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                               }
-                       }
-                       elseif ($description_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description'))
-                       {
-                               if (isset($description_parent[0]['data']))
-                               {
-                                       $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                               }
-                       }
-
-                       // DURATION
-                       if ($duration_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'duration'))
-                       {
-                               $seconds = null;
-                               $minutes = null;
-                               $hours = null;
-                               if (isset($duration_parent[0]['data']))
-                               {
-                                       $temp = explode(':', $this->sanitize($duration_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
-                                       if (sizeof($temp) > 0)
-                                       {
-                                               (int) $seconds = array_pop($temp);
-                                       }
-                                       if (sizeof($temp) > 0)
-                                       {
-                                               (int) $minutes = array_pop($temp);
-                                               $seconds += $minutes * 60;
-                                       }
-                                       if (sizeof($temp) > 0)
-                                       {
-                                               (int) $hours = array_pop($temp);
-                                               $seconds += $hours * 3600;
-                                       }
-                                       unset($temp);
-                                       $duration_parent = $seconds;
-                               }
-                       }
-
-                       // HASHES
-                       if ($hashes_iterator = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash'))
-                       {
-                               foreach ($hashes_iterator as $hash)
-                               {
-                                       $value = null;
-                                       $algo = null;
-                                       if (isset($hash['data']))
-                                       {
-                                               $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($hash['attribs']['']['algo']))
-                                       {
-                                               $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       else
-                                       {
-                                               $algo = 'md5';
-                                       }
-                                       $hashes_parent[] = $algo.':'.$value;
-                               }
-                       }
-                       elseif ($hashes_iterator = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash'))
-                       {
-                               foreach ($hashes_iterator as $hash)
-                               {
-                                       $value = null;
-                                       $algo = null;
-                                       if (isset($hash['data']))
-                                       {
-                                               $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($hash['attribs']['']['algo']))
-                                       {
-                                               $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       else
-                                       {
-                                               $algo = 'md5';
-                                       }
-                                       $hashes_parent[] = $algo.':'.$value;
-                               }
-                       }
-                       if (is_array($hashes_parent))
-                       {
-                               $hashes_parent = array_values(SimplePie_Misc::array_unique($hashes_parent));
-                       }
-
-                       // KEYWORDS
-                       if ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords'))
-                       {
-                               if (isset($keywords[0]['data']))
-                               {
-                                       $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
-                                       foreach ($temp as $word)
-                                       {
-                                               $keywords_parent[] = trim($word);
-                                       }
-                               }
-                               unset($temp);
-                       }
-                       elseif ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords'))
-                       {
-                               if (isset($keywords[0]['data']))
-                               {
-                                       $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
-                                       foreach ($temp as $word)
-                                       {
-                                               $keywords_parent[] = trim($word);
-                                       }
-                               }
-                               unset($temp);
-                       }
-                       elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords'))
-                       {
-                               if (isset($keywords[0]['data']))
-                               {
-                                       $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
-                                       foreach ($temp as $word)
-                                       {
-                                               $keywords_parent[] = trim($word);
-                                       }
-                               }
-                               unset($temp);
-                       }
-                       elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords'))
-                       {
-                               if (isset($keywords[0]['data']))
-                               {
-                                       $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
-                                       foreach ($temp as $word)
-                                       {
-                                               $keywords_parent[] = trim($word);
-                                       }
-                               }
-                               unset($temp);
-                       }
-                       if (is_array($keywords_parent))
-                       {
-                               $keywords_parent = array_values(SimplePie_Misc::array_unique($keywords_parent));
-                       }
-
-                       // PLAYER
-                       if ($player_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player'))
-                       {
-                               if (isset($player_parent[0]['attribs']['']['url']))
-                               {
-                                       $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
-                               }
-                       }
-                       elseif ($player_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player'))
-                       {
-                               if (isset($player_parent[0]['attribs']['']['url']))
-                               {
-                                       $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
-                               }
-                       }
-
-                       // RATINGS
-                       if ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating'))
-                       {
-                               foreach ($ratings as $rating)
-                               {
-                                       $rating_scheme = null;
-                                       $rating_value = null;
-                                       if (isset($rating['attribs']['']['scheme']))
-                                       {
-                                               $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       else
-                                       {
-                                               $rating_scheme = 'urn:simple';
-                                       }
-                                       if (isset($rating['data']))
-                                       {
-                                               $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       $ratings_parent[] = new $this->feed->rating_class($rating_scheme, $rating_value);
-                               }
-                       }
-                       elseif ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit'))
-                       {
-                               foreach ($ratings as $rating)
-                               {
-                                       $rating_scheme = 'urn:itunes';
-                                       $rating_value = null;
-                                       if (isset($rating['data']))
-                                       {
-                                               $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       $ratings_parent[] = new $this->feed->rating_class($rating_scheme, $rating_value);
-                               }
-                       }
-                       elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating'))
-                       {
-                               foreach ($ratings as $rating)
-                               {
-                                       $rating_scheme = null;
-                                       $rating_value = null;
-                                       if (isset($rating['attribs']['']['scheme']))
-                                       {
-                                               $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       else
-                                       {
-                                               $rating_scheme = 'urn:simple';
-                                       }
-                                       if (isset($rating['data']))
-                                       {
-                                               $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       $ratings_parent[] = new $this->feed->rating_class($rating_scheme, $rating_value);
-                               }
-                       }
-                       elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit'))
-                       {
-                               foreach ($ratings as $rating)
-                               {
-                                       $rating_scheme = 'urn:itunes';
-                                       $rating_value = null;
-                                       if (isset($rating['data']))
-                                       {
-                                               $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       $ratings_parent[] = new $this->feed->rating_class($rating_scheme, $rating_value);
-                               }
-                       }
-                       if (is_array($ratings_parent))
-                       {
-                               $ratings_parent = array_values(SimplePie_Misc::array_unique($ratings_parent));
-                       }
-
-                       // RESTRICTIONS
-                       if ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction'))
-                       {
-                               foreach ($restrictions as $restriction)
-                               {
-                                       $restriction_relationship = null;
-                                       $restriction_type = null;
-                                       $restriction_value = null;
-                                       if (isset($restriction['attribs']['']['relationship']))
-                                       {
-                                               $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($restriction['attribs']['']['type']))
-                                       {
-                                               $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($restriction['data']))
-                                       {
-                                               $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       $restrictions_parent[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value);
-                               }
-                       }
-                       elseif ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block'))
-                       {
-                               foreach ($restrictions as $restriction)
-                               {
-                                       $restriction_relationship = 'allow';
-                                       $restriction_type = null;
-                                       $restriction_value = 'itunes';
-                                       if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes')
-                                       {
-                                               $restriction_relationship = 'deny';
-                                       }
-                                       $restrictions_parent[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value);
-                               }
-                       }
-                       elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction'))
-                       {
-                               foreach ($restrictions as $restriction)
-                               {
-                                       $restriction_relationship = null;
-                                       $restriction_type = null;
-                                       $restriction_value = null;
-                                       if (isset($restriction['attribs']['']['relationship']))
-                                       {
-                                               $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($restriction['attribs']['']['type']))
-                                       {
-                                               $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($restriction['data']))
-                                       {
-                                               $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       $restrictions_parent[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value);
-                               }
-                       }
-                       elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block'))
-                       {
-                               foreach ($restrictions as $restriction)
-                               {
-                                       $restriction_relationship = 'allow';
-                                       $restriction_type = null;
-                                       $restriction_value = 'itunes';
-                                       if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes')
-                                       {
-                                               $restriction_relationship = 'deny';
-                                       }
-                                       $restrictions_parent[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value);
-                               }
-                       }
-                       if (is_array($restrictions_parent))
-                       {
-                               $restrictions_parent = array_values(SimplePie_Misc::array_unique($restrictions_parent));
-                       }
-
-                       // THUMBNAILS
-                       if ($thumbnails = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail'))
-                       {
-                               foreach ($thumbnails as $thumbnail)
-                               {
-                                       if (isset($thumbnail['attribs']['']['url']))
-                                       {
-                                               $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
-                                       }
-                               }
-                       }
-                       elseif ($thumbnails = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail'))
-                       {
-                               foreach ($thumbnails as $thumbnail)
-                               {
-                                       if (isset($thumbnail['attribs']['']['url']))
-                                       {
-                                               $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
-                                       }
-                               }
-                       }
-
-                       // TITLES
-                       if ($title_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title'))
-                       {
-                               if (isset($title_parent[0]['data']))
-                               {
-                                       $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                               }
-                       }
-                       elseif ($title_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title'))
-                       {
-                               if (isset($title_parent[0]['data']))
-                               {
-                                       $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                               }
-                       }
-
-                       // Clear the memory
-                       unset($parent);
-
-                       // Attributes
-                       $bitrate = null;
-                       $channels = null;
-                       $duration = null;
-                       $expression = null;
-                       $framerate = null;
-                       $height = null;
-                       $javascript = null;
-                       $lang = null;
-                       $length = null;
-                       $medium = null;
-                       $samplingrate = null;
-                       $type = null;
-                       $url = null;
-                       $width = null;
-
-                       // Elements
-                       $captions = null;
-                       $categories = null;
-                       $copyrights = null;
-                       $credits = null;
-                       $description = null;
-                       $hashes = null;
-                       $keywords = null;
-                       $player = null;
-                       $ratings = null;
-                       $restrictions = null;
-                       $thumbnails = null;
-                       $title = null;
-
-                       // If we have media:group tags, loop through them.
-                       foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group') as $group)
-                       {
-                               if(isset($group['child']) && isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content']))
-                               {
-                                       // If we have media:content tags, loop through them.
-                                       foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content)
-                                       {
-                                               if (isset($content['attribs']['']['url']))
-                                               {
-                                                       // Attributes
-                                                       $bitrate = null;
-                                                       $channels = null;
-                                                       $duration = null;
-                                                       $expression = null;
-                                                       $framerate = null;
-                                                       $height = null;
-                                                       $javascript = null;
-                                                       $lang = null;
-                                                       $length = null;
-                                                       $medium = null;
-                                                       $samplingrate = null;
-                                                       $type = null;
-                                                       $url = null;
-                                                       $width = null;
-
-                                                       // Elements
-                                                       $captions = null;
-                                                       $categories = null;
-                                                       $copyrights = null;
-                                                       $credits = null;
-                                                       $description = null;
-                                                       $hashes = null;
-                                                       $keywords = null;
-                                                       $player = null;
-                                                       $ratings = null;
-                                                       $restrictions = null;
-                                                       $thumbnails = null;
-                                                       $title = null;
-
-                                                       // Start checking the attributes of media:content
-                                                       if (isset($content['attribs']['']['bitrate']))
-                                                       {
-                                                               $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                       }
-                                                       if (isset($content['attribs']['']['channels']))
-                                                       {
-                                                               $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                       }
-                                                       if (isset($content['attribs']['']['duration']))
-                                                       {
-                                                               $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                       }
-                                                       else
-                                                       {
-                                                               $duration = $duration_parent;
-                                                       }
-                                                       if (isset($content['attribs']['']['expression']))
-                                                       {
-                                                               $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                       }
-                                                       if (isset($content['attribs']['']['framerate']))
-                                                       {
-                                                               $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                       }
-                                                       if (isset($content['attribs']['']['height']))
-                                                       {
-                                                               $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                       }
-                                                       if (isset($content['attribs']['']['lang']))
-                                                       {
-                                                               $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                       }
-                                                       if (isset($content['attribs']['']['fileSize']))
-                                                       {
-                                                               $length = ceil($content['attribs']['']['fileSize']);
-                                                       }
-                                                       if (isset($content['attribs']['']['medium']))
-                                                       {
-                                                               $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                       }
-                                                       if (isset($content['attribs']['']['samplingrate']))
-                                                       {
-                                                               $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                       }
-                                                       if (isset($content['attribs']['']['type']))
-                                                       {
-                                                               $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                       }
-                                                       if (isset($content['attribs']['']['width']))
-                                                       {
-                                                               $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                       }
-                                                       $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
-
-                                                       // Checking the other optional media: elements. Priority: media:content, media:group, item, channel
-
-                                                       // CAPTIONS
-                                                       if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
-                                                       {
-                                                               foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
-                                                               {
-                                                                       $caption_type = null;
-                                                                       $caption_lang = null;
-                                                                       $caption_startTime = null;
-                                                                       $caption_endTime = null;
-                                                                       $caption_text = null;
-                                                                       if (isset($caption['attribs']['']['type']))
-                                                                       {
-                                                                               $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($caption['attribs']['']['lang']))
-                                                                       {
-                                                                               $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($caption['attribs']['']['start']))
-                                                                       {
-                                                                               $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($caption['attribs']['']['end']))
-                                                                       {
-                                                                               $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($caption['data']))
-                                                                       {
-                                                                               $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       $captions[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text);
-                                                               }
-                                                               if (is_array($captions))
-                                                               {
-                                                                       $captions = array_values(SimplePie_Misc::array_unique($captions));
-                                                               }
-                                                       }
-                                                       elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
-                                                       {
-                                                               foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
-                                                               {
-                                                                       $caption_type = null;
-                                                                       $caption_lang = null;
-                                                                       $caption_startTime = null;
-                                                                       $caption_endTime = null;
-                                                                       $caption_text = null;
-                                                                       if (isset($caption['attribs']['']['type']))
-                                                                       {
-                                                                               $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($caption['attribs']['']['lang']))
-                                                                       {
-                                                                               $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($caption['attribs']['']['start']))
-                                                                       {
-                                                                               $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($caption['attribs']['']['end']))
-                                                                       {
-                                                                               $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($caption['data']))
-                                                                       {
-                                                                               $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       $captions[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text);
-                                                               }
-                                                               if (is_array($captions))
-                                                               {
-                                                                       $captions = array_values(SimplePie_Misc::array_unique($captions));
-                                                               }
-                                                       }
-                                                       else
-                                                       {
-                                                               $captions = $captions_parent;
-                                                       }
-
-                                                       // CATEGORIES
-                                                       if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
-                                                       {
-                                                               foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
-                                                               {
-                                                                       $term = null;
-                                                                       $scheme = null;
-                                                                       $label = null;
-                                                                       if (isset($category['data']))
-                                                                       {
-                                                                               $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($category['attribs']['']['scheme']))
-                                                                       {
-                                                                               $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       else
-                                                                       {
-                                                                               $scheme = 'http://search.yahoo.com/mrss/category_schema';
-                                                                       }
-                                                                       if (isset($category['attribs']['']['label']))
-                                                                       {
-                                                                               $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       $categories[] = new $this->feed->category_class($term, $scheme, $label);
-                                                               }
-                                                       }
-                                                       if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
-                                                       {
-                                                               foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
-                                                               {
-                                                                       $term = null;
-                                                                       $scheme = null;
-                                                                       $label = null;
-                                                                       if (isset($category['data']))
-                                                                       {
-                                                                               $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($category['attribs']['']['scheme']))
-                                                                       {
-                                                                               $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       else
-                                                                       {
-                                                                               $scheme = 'http://search.yahoo.com/mrss/category_schema';
-                                                                       }
-                                                                       if (isset($category['attribs']['']['label']))
-                                                                       {
-                                                                               $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       $categories[] = new $this->feed->category_class($term, $scheme, $label);
-                                                               }
-                                                       }
-                                                       if (is_array($categories) && is_array($categories_parent))
-                                                       {
-                                                               $categories = array_values(SimplePie_Misc::array_unique(array_merge($categories, $categories_parent)));
-                                                       }
-                                                       elseif (is_array($categories))
-                                                       {
-                                                               $categories = array_values(SimplePie_Misc::array_unique($categories));
-                                                       }
-                                                       elseif (is_array($categories_parent))
-                                                       {
-                                                               $categories = array_values(SimplePie_Misc::array_unique($categories_parent));
-                                                       }
-
-                                                       // COPYRIGHTS
-                                                       if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
-                                                       {
-                                                               $copyright_url = null;
-                                                               $copyright_label = null;
-                                                               if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
-                                                               {
-                                                                       $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
-                                                               {
-                                                                       $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               $copyrights = new $this->feed->copyright_class($copyright_url, $copyright_label);
-                                                       }
-                                                       elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
-                                                       {
-                                                               $copyright_url = null;
-                                                               $copyright_label = null;
-                                                               if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
-                                                               {
-                                                                       $copyright_url = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
-                                                               {
-                                                                       $copyright_label = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               $copyrights = new $this->feed->copyright_class($copyright_url, $copyright_label);
-                                                       }
-                                                       else
-                                                       {
-                                                               $copyrights = $copyrights_parent;
-                                                       }
-
-                                                       // CREDITS
-                                                       if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
-                                                       {
-                                                               foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
-                                                               {
-                                                                       $credit_role = null;
-                                                                       $credit_scheme = null;
-                                                                       $credit_name = null;
-                                                                       if (isset($credit['attribs']['']['role']))
-                                                                       {
-                                                                               $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($credit['attribs']['']['scheme']))
-                                                                       {
-                                                                               $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       else
-                                                                       {
-                                                                               $credit_scheme = 'urn:ebu';
-                                                                       }
-                                                                       if (isset($credit['data']))
-                                                                       {
-                                                                               $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       $credits[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name);
-                                                               }
-                                                               if (is_array($credits))
-                                                               {
-                                                                       $credits = array_values(SimplePie_Misc::array_unique($credits));
-                                                               }
-                                                       }
-                                                       elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
-                                                       {
-                                                               foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
-                                                               {
-                                                                       $credit_role = null;
-                                                                       $credit_scheme = null;
-                                                                       $credit_name = null;
-                                                                       if (isset($credit['attribs']['']['role']))
-                                                                       {
-                                                                               $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($credit['attribs']['']['scheme']))
-                                                                       {
-                                                                               $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       else
-                                                                       {
-                                                                               $credit_scheme = 'urn:ebu';
-                                                                       }
-                                                                       if (isset($credit['data']))
-                                                                       {
-                                                                               $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       $credits[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name);
-                                                               }
-                                                               if (is_array($credits))
-                                                               {
-                                                                       $credits = array_values(SimplePie_Misc::array_unique($credits));
-                                                               }
-                                                       }
-                                                       else
-                                                       {
-                                                               $credits = $credits_parent;
-                                                       }
-
-                                                       // DESCRIPTION
-                                                       if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
-                                                       {
-                                                               $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                       }
-                                                       elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
-                                                       {
-                                                               $description = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                       }
-                                                       else
-                                                       {
-                                                               $description = $description_parent;
-                                                       }
-
-                                                       // HASHES
-                                                       if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
-                                                       {
-                                                               foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
-                                                               {
-                                                                       $value = null;
-                                                                       $algo = null;
-                                                                       if (isset($hash['data']))
-                                                                       {
-                                                                               $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($hash['attribs']['']['algo']))
-                                                                       {
-                                                                               $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       else
-                                                                       {
-                                                                               $algo = 'md5';
-                                                                       }
-                                                                       $hashes[] = $algo.':'.$value;
-                                                               }
-                                                               if (is_array($hashes))
-                                                               {
-                                                                       $hashes = array_values(SimplePie_Misc::array_unique($hashes));
-                                                               }
-                                                       }
-                                                       elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
-                                                       {
-                                                               foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
-                                                               {
-                                                                       $value = null;
-                                                                       $algo = null;
-                                                                       if (isset($hash['data']))
-                                                                       {
-                                                                               $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($hash['attribs']['']['algo']))
-                                                                       {
-                                                                               $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       else
-                                                                       {
-                                                                               $algo = 'md5';
-                                                                       }
-                                                                       $hashes[] = $algo.':'.$value;
-                                                               }
-                                                               if (is_array($hashes))
-                                                               {
-                                                                       $hashes = array_values(SimplePie_Misc::array_unique($hashes));
-                                                               }
-                                                       }
-                                                       else
-                                                       {
-                                                               $hashes = $hashes_parent;
-                                                       }
-
-                                                       // KEYWORDS
-                                                       if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
-                                                       {
-                                                               if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
-                                                               {
-                                                                       $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
-                                                                       foreach ($temp as $word)
-                                                                       {
-                                                                               $keywords[] = trim($word);
-                                                                       }
-                                                                       unset($temp);
-                                                               }
-                                                               if (is_array($keywords))
-                                                               {
-                                                                       $keywords = array_values(SimplePie_Misc::array_unique($keywords));
-                                                               }
-                                                       }
-                                                       elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
-                                                       {
-                                                               if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
-                                                               {
-                                                                       $temp = explode(',', $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
-                                                                       foreach ($temp as $word)
-                                                                       {
-                                                                               $keywords[] = trim($word);
-                                                                       }
-                                                                       unset($temp);
-                                                               }
-                                                               if (is_array($keywords))
-                                                               {
-                                                                       $keywords = array_values(SimplePie_Misc::array_unique($keywords));
-                                                               }
-                                                       }
-                                                       else
-                                                       {
-                                                               $keywords = $keywords_parent;
-                                                       }
-
-                                                       // PLAYER
-                                                       if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
-                                                       {
-                                                               $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
-                                                       }
-                                                       elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
-                                                       {
-                                                               $player = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
-                                                       }
-                                                       else
-                                                       {
-                                                               $player = $player_parent;
-                                                       }
-
-                                                       // RATINGS
-                                                       if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
-                                                       {
-                                                               foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
-                                                               {
-                                                                       $rating_scheme = null;
-                                                                       $rating_value = null;
-                                                                       if (isset($rating['attribs']['']['scheme']))
-                                                                       {
-                                                                               $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       else
-                                                                       {
-                                                                               $rating_scheme = 'urn:simple';
-                                                                       }
-                                                                       if (isset($rating['data']))
-                                                                       {
-                                                                               $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       $ratings[] = new $this->feed->rating_class($rating_scheme, $rating_value);
-                                                               }
-                                                               if (is_array($ratings))
-                                                               {
-                                                                       $ratings = array_values(SimplePie_Misc::array_unique($ratings));
-                                                               }
-                                                       }
-                                                       elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
-                                                       {
-                                                               foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
-                                                               {
-                                                                       $rating_scheme = null;
-                                                                       $rating_value = null;
-                                                                       if (isset($rating['attribs']['']['scheme']))
-                                                                       {
-                                                                               $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       else
-                                                                       {
-                                                                               $rating_scheme = 'urn:simple';
-                                                                       }
-                                                                       if (isset($rating['data']))
-                                                                       {
-                                                                               $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       $ratings[] = new $this->feed->rating_class($rating_scheme, $rating_value);
-                                                               }
-                                                               if (is_array($ratings))
-                                                               {
-                                                                       $ratings = array_values(SimplePie_Misc::array_unique($ratings));
-                                                               }
-                                                       }
-                                                       else
-                                                       {
-                                                               $ratings = $ratings_parent;
-                                                       }
-
-                                                       // RESTRICTIONS
-                                                       if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
-                                                       {
-                                                               foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
-                                                               {
-                                                                       $restriction_relationship = null;
-                                                                       $restriction_type = null;
-                                                                       $restriction_value = null;
-                                                                       if (isset($restriction['attribs']['']['relationship']))
-                                                                       {
-                                                                               $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($restriction['attribs']['']['type']))
-                                                                       {
-                                                                               $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($restriction['data']))
-                                                                       {
-                                                                               $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       $restrictions[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value);
-                                                               }
-                                                               if (is_array($restrictions))
-                                                               {
-                                                                       $restrictions = array_values(SimplePie_Misc::array_unique($restrictions));
-                                                               }
-                                                       }
-                                                       elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
-                                                       {
-                                                               foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
-                                                               {
-                                                                       $restriction_relationship = null;
-                                                                       $restriction_type = null;
-                                                                       $restriction_value = null;
-                                                                       if (isset($restriction['attribs']['']['relationship']))
-                                                                       {
-                                                                               $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($restriction['attribs']['']['type']))
-                                                                       {
-                                                                               $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       if (isset($restriction['data']))
-                                                                       {
-                                                                               $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                                       }
-                                                                       $restrictions[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value);
-                                                               }
-                                                               if (is_array($restrictions))
-                                                               {
-                                                                       $restrictions = array_values(SimplePie_Misc::array_unique($restrictions));
-                                                               }
-                                                       }
-                                                       else
-                                                       {
-                                                               $restrictions = $restrictions_parent;
-                                                       }
-
-                                                       // THUMBNAILS
-                                                       if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
-                                                       {
-                                                               foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
-                                                               {
-                                                                       $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
-                                                               }
-                                                               if (is_array($thumbnails))
-                                                               {
-                                                                       $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails));
-                                                               }
-                                                       }
-                                                       elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
-                                                       {
-                                                               foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
-                                                               {
-                                                                       $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
-                                                               }
-                                                               if (is_array($thumbnails))
-                                                               {
-                                                                       $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails));
-                                                               }
-                                                       }
-                                                       else
-                                                       {
-                                                               $thumbnails = $thumbnails_parent;
-                                                       }
-
-                                                       // TITLES
-                                                       if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
-                                                       {
-                                                               $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                       }
-                                                       elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
-                                                       {
-                                                               $title = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                       }
-                                                       else
-                                                       {
-                                                               $title = $title_parent;
-                                                       }
-
-                                                       $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width);
-                                               }
-                                       }
-                               }
-                       }
-
-                       // If we have standalone media:content tags, loop through them.
-                       if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content']))
-                       {
-                               foreach ((array) $this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content)
-                               {
-                                       if (isset($content['attribs']['']['url']))
-                                       {
-                                               // Attributes
-                                               $bitrate = null;
-                                               $channels = null;
-                                               $duration = null;
-                                               $expression = null;
-                                               $framerate = null;
-                                               $height = null;
-                                               $javascript = null;
-                                               $lang = null;
-                                               $length = null;
-                                               $medium = null;
-                                               $samplingrate = null;
-                                               $type = null;
-                                               $url = null;
-                                               $width = null;
-
-                                               // Elements
-                                               $captions = null;
-                                               $categories = null;
-                                               $copyrights = null;
-                                               $credits = null;
-                                               $description = null;
-                                               $hashes = null;
-                                               $keywords = null;
-                                               $player = null;
-                                               $ratings = null;
-                                               $restrictions = null;
-                                               $thumbnails = null;
-                                               $title = null;
-
-                                               // Start checking the attributes of media:content
-                                               if (isset($content['attribs']['']['bitrate']))
-                                               {
-                                                       $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                               }
-                                               if (isset($content['attribs']['']['channels']))
-                                               {
-                                                       $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                               }
-                                               if (isset($content['attribs']['']['duration']))
-                                               {
-                                                       $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                               }
-                                               else
-                                               {
-                                                       $duration = $duration_parent;
-                                               }
-                                               if (isset($content['attribs']['']['expression']))
-                                               {
-                                                       $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                               }
-                                               if (isset($content['attribs']['']['framerate']))
-                                               {
-                                                       $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                               }
-                                               if (isset($content['attribs']['']['height']))
-                                               {
-                                                       $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                               }
-                                               if (isset($content['attribs']['']['lang']))
-                                               {
-                                                       $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                               }
-                                               if (isset($content['attribs']['']['fileSize']))
-                                               {
-                                                       $length = ceil($content['attribs']['']['fileSize']);
-                                               }
-                                               if (isset($content['attribs']['']['medium']))
-                                               {
-                                                       $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                               }
-                                               if (isset($content['attribs']['']['samplingrate']))
-                                               {
-                                                       $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                               }
-                                               if (isset($content['attribs']['']['type']))
-                                               {
-                                                       $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                               }
-                                               if (isset($content['attribs']['']['width']))
-                                               {
-                                                       $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                               }
-                                               $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
-
-                                               // Checking the other optional media: elements. Priority: media:content, media:group, item, channel
-
-                                               // CAPTIONS
-                                               if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
-                                               {
-                                                       foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
-                                                       {
-                                                               $caption_type = null;
-                                                               $caption_lang = null;
-                                                               $caption_startTime = null;
-                                                               $caption_endTime = null;
-                                                               $caption_text = null;
-                                                               if (isset($caption['attribs']['']['type']))
-                                                               {
-                                                                       $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               if (isset($caption['attribs']['']['lang']))
-                                                               {
-                                                                       $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               if (isset($caption['attribs']['']['start']))
-                                                               {
-                                                                       $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               if (isset($caption['attribs']['']['end']))
-                                                               {
-                                                                       $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               if (isset($caption['data']))
-                                                               {
-                                                                       $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               $captions[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text);
-                                                       }
-                                                       if (is_array($captions))
-                                                       {
-                                                               $captions = array_values(SimplePie_Misc::array_unique($captions));
-                                                       }
-                                               }
-                                               else
-                                               {
-                                                       $captions = $captions_parent;
-                                               }
-
-                                               // CATEGORIES
-                                               if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
-                                               {
-                                                       foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
-                                                       {
-                                                               $term = null;
-                                                               $scheme = null;
-                                                               $label = null;
-                                                               if (isset($category['data']))
-                                                               {
-                                                                       $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               if (isset($category['attribs']['']['scheme']))
-                                                               {
-                                                                       $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               else
-                                                               {
-                                                                       $scheme = 'http://search.yahoo.com/mrss/category_schema';
-                                                               }
-                                                               if (isset($category['attribs']['']['label']))
-                                                               {
-                                                                       $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               $categories[] = new $this->feed->category_class($term, $scheme, $label);
-                                                       }
-                                               }
-                                               if (is_array($categories) && is_array($categories_parent))
-                                               {
-                                                       $categories = array_values(SimplePie_Misc::array_unique(array_merge($categories, $categories_parent)));
-                                               }
-                                               elseif (is_array($categories))
-                                               {
-                                                       $categories = array_values(SimplePie_Misc::array_unique($categories));
-                                               }
-                                               elseif (is_array($categories_parent))
-                                               {
-                                                       $categories = array_values(SimplePie_Misc::array_unique($categories_parent));
-                                               }
-                                               else
-                                               {
-                                                       $categories = null;
-                                               }
-
-                                               // COPYRIGHTS
-                                               if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
-                                               {
-                                                       $copyright_url = null;
-                                                       $copyright_label = null;
-                                                       if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
-                                                       {
-                                                               $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                       }
-                                                       if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
-                                                       {
-                                                               $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                       }
-                                                       $copyrights = new $this->feed->copyright_class($copyright_url, $copyright_label);
-                                               }
-                                               else
-                                               {
-                                                       $copyrights = $copyrights_parent;
-                                               }
-
-                                               // CREDITS
-                                               if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
-                                               {
-                                                       foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
-                                                       {
-                                                               $credit_role = null;
-                                                               $credit_scheme = null;
-                                                               $credit_name = null;
-                                                               if (isset($credit['attribs']['']['role']))
-                                                               {
-                                                                       $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               if (isset($credit['attribs']['']['scheme']))
-                                                               {
-                                                                       $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               else
-                                                               {
-                                                                       $credit_scheme = 'urn:ebu';
-                                                               }
-                                                               if (isset($credit['data']))
-                                                               {
-                                                                       $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               $credits[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name);
-                                                       }
-                                                       if (is_array($credits))
-                                                       {
-                                                               $credits = array_values(SimplePie_Misc::array_unique($credits));
-                                                       }
-                                               }
-                                               else
-                                               {
-                                                       $credits = $credits_parent;
-                                               }
-
-                                               // DESCRIPTION
-                                               if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
-                                               {
-                                                       $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                               }
-                                               else
-                                               {
-                                                       $description = $description_parent;
-                                               }
-
-                                               // HASHES
-                                               if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
-                                               {
-                                                       foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
-                                                       {
-                                                               $value = null;
-                                                               $algo = null;
-                                                               if (isset($hash['data']))
-                                                               {
-                                                                       $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               if (isset($hash['attribs']['']['algo']))
-                                                               {
-                                                                       $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               else
-                                                               {
-                                                                       $algo = 'md5';
-                                                               }
-                                                               $hashes[] = $algo.':'.$value;
-                                                       }
-                                                       if (is_array($hashes))
-                                                       {
-                                                               $hashes = array_values(SimplePie_Misc::array_unique($hashes));
-                                                       }
-                                               }
-                                               else
-                                               {
-                                                       $hashes = $hashes_parent;
-                                               }
-
-                                               // KEYWORDS
-                                               if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
-                                               {
-                                                       if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
-                                                       {
-                                                               $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
-                                                               foreach ($temp as $word)
-                                                               {
-                                                                       $keywords[] = trim($word);
-                                                               }
-                                                               unset($temp);
-                                                       }
-                                                       if (is_array($keywords))
-                                                       {
-                                                               $keywords = array_values(SimplePie_Misc::array_unique($keywords));
-                                                       }
-                                               }
-                                               else
-                                               {
-                                                       $keywords = $keywords_parent;
-                                               }
-
-                                               // PLAYER
-                                               if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
-                                               {
-                                                       $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
-                                               }
-                                               else
-                                               {
-                                                       $player = $player_parent;
-                                               }
-
-                                               // RATINGS
-                                               if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
-                                               {
-                                                       foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
-                                                       {
-                                                               $rating_scheme = null;
-                                                               $rating_value = null;
-                                                               if (isset($rating['attribs']['']['scheme']))
-                                                               {
-                                                                       $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               else
-                                                               {
-                                                                       $rating_scheme = 'urn:simple';
-                                                               }
-                                                               if (isset($rating['data']))
-                                                               {
-                                                                       $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               $ratings[] = new $this->feed->rating_class($rating_scheme, $rating_value);
-                                                       }
-                                                       if (is_array($ratings))
-                                                       {
-                                                               $ratings = array_values(SimplePie_Misc::array_unique($ratings));
-                                                       }
-                                               }
-                                               else
-                                               {
-                                                       $ratings = $ratings_parent;
-                                               }
-
-                                               // RESTRICTIONS
-                                               if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
-                                               {
-                                                       foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
-                                                       {
-                                                               $restriction_relationship = null;
-                                                               $restriction_type = null;
-                                                               $restriction_value = null;
-                                                               if (isset($restriction['attribs']['']['relationship']))
-                                                               {
-                                                                       $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               if (isset($restriction['attribs']['']['type']))
-                                                               {
-                                                                       $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               if (isset($restriction['data']))
-                                                               {
-                                                                       $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                                               }
-                                                               $restrictions[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value);
-                                                       }
-                                                       if (is_array($restrictions))
-                                                       {
-                                                               $restrictions = array_values(SimplePie_Misc::array_unique($restrictions));
-                                                       }
-                                               }
-                                               else
-                                               {
-                                                       $restrictions = $restrictions_parent;
-                                               }
-
-                                               // THUMBNAILS
-                                               if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
-                                               {
-                                                       foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
-                                                       {
-                                                               $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
-                                                       }
-                                                       if (is_array($thumbnails))
-                                                       {
-                                                               $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails));
-                                                       }
-                                               }
-                                               else
-                                               {
-                                                       $thumbnails = $thumbnails_parent;
-                                               }
-
-                                               // TITLES
-                                               if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
-                                               {
-                                                       $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                               }
-                                               else
-                                               {
-                                                       $title = $title_parent;
-                                               }
-
-                                               $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width);
-                                       }
-                               }
-                       }
-
-                       foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link)
-                       {
-                               if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure')
-                               {
-                                       // Attributes
-                                       $bitrate = null;
-                                       $channels = null;
-                                       $duration = null;
-                                       $expression = null;
-                                       $framerate = null;
-                                       $height = null;
-                                       $javascript = null;
-                                       $lang = null;
-                                       $length = null;
-                                       $medium = null;
-                                       $samplingrate = null;
-                                       $type = null;
-                                       $url = null;
-                                       $width = null;
-                                       $title = $title_parent;
-
-                                       $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
-                                       if (isset($link['attribs']['']['type']))
-                                       {
-                                               $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($link['attribs']['']['length']))
-                                       {
-                                               $length = ceil($link['attribs']['']['length']);
-                                       }
-                                       if (isset($link['attribs']['']['title']))
-                                       {
-                                               $title = $this->sanitize($link['attribs']['']['title'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-
-                                       // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
-                                       $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title, $width);
-                               }
-                       }
-
-                       foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link)
-                       {
-                               if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure')
-                               {
-                                       // Attributes
-                                       $bitrate = null;
-                                       $channels = null;
-                                       $duration = null;
-                                       $expression = null;
-                                       $framerate = null;
-                                       $height = null;
-                                       $javascript = null;
-                                       $lang = null;
-                                       $length = null;
-                                       $medium = null;
-                                       $samplingrate = null;
-                                       $type = null;
-                                       $url = null;
-                                       $width = null;
-
-                                       $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
-                                       if (isset($link['attribs']['']['type']))
-                                       {
-                                               $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($link['attribs']['']['length']))
-                                       {
-                                               $length = ceil($link['attribs']['']['length']);
-                                       }
-
-                                       // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
-                                       $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width);
-                               }
-                       }
-
-                       if ($enclosure = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'enclosure'))
-                       {
-                               if (isset($enclosure[0]['attribs']['']['url']))
-                               {
-                                       // Attributes
-                                       $bitrate = null;
-                                       $channels = null;
-                                       $duration = null;
-                                       $expression = null;
-                                       $framerate = null;
-                                       $height = null;
-                                       $javascript = null;
-                                       $lang = null;
-                                       $length = null;
-                                       $medium = null;
-                                       $samplingrate = null;
-                                       $type = null;
-                                       $url = null;
-                                       $width = null;
-
-                                       $url = $this->sanitize($enclosure[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($enclosure[0]));
-                                       if (isset($enclosure[0]['attribs']['']['type']))
-                                       {
-                                               $type = $this->sanitize($enclosure[0]['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
-                                       }
-                                       if (isset($enclosure[0]['attribs']['']['length']))
-                                       {
-                                               $length = ceil($enclosure[0]['attribs']['']['length']);
-                                       }
-
-                                       // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
-                                       $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width);
-                               }
-                       }
-
-                       if (sizeof($this->data['enclosures']) === 0 && ($url || $type || $length || $bitrate || $captions_parent || $categories_parent || $channels || $copyrights_parent || $credits_parent || $description_parent || $duration_parent || $expression || $framerate || $hashes_parent || $height || $keywords_parent || $lang || $medium || $player_parent || $ratings_parent || $restrictions_parent || $samplingrate || $thumbnails_parent || $title_parent || $width))
-                       {
-                               // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
-                               $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width);
-                       }
-
-                       $this->data['enclosures'] = array_values(SimplePie_Misc::array_unique($this->data['enclosures']));
-               }
-               if (!empty($this->data['enclosures']))
-               {
-                       return $this->data['enclosures'];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_latitude()
-       {
-               if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
-               {
-                       return (float) $return[0]['data'];
-               }
-               elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
-               {
-                       return (float) $match[1];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_longitude()
-       {
-               if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
-               {
-                       return (float) $return[0]['data'];
-               }
-               elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
-               {
-                       return (float) $return[0]['data'];
-               }
-               elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
-               {
-                       return (float) $match[2];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_source()
-       {
-               if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'source'))
-               {
-                       return new $this->feed->source_class($this, $return[0]);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       /**
-        * Creates the add_to_* methods' return data
-        *
-        * @access private
-        * @param string $item_url String to prefix to the item permalink
-        * @param string $title_url String to prefix to the item title
-        * (and suffix to the item permalink)
-        * @return mixed URL if feed exists, false otherwise
-        */
-       function add_to_service($item_url, $title_url = null, $summary_url = null)
-       {
-               if ($this->get_permalink() !== null)
-               {
-                       $return = $item_url . rawurlencode($this->get_permalink());
-                       if ($title_url !== null && $this->get_title() !== null)
-                       {
-                               $return .= $title_url . rawurlencode($this->get_title());
-                       }
-                       if ($summary_url !== null && $this->get_description() !== null)
-                       {
-                               $return .= $summary_url . rawurlencode($this->get_description());
-                       }
-                       return $this->sanitize($return, SIMPLEPIE_CONSTRUCT_IRI);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function add_to_blinklist()
-       {
-               return $this->add_to_service('http://www.blinklist.com/index.php?Action=Blink/addblink.php&Description=&Url=', '&Title=');
-       }
-
-       function add_to_blogmarks()
-       {
-               return $this->add_to_service('http://blogmarks.net/my/new.php?mini=1&simple=1&url=', '&title=');
-       }
-
-       function add_to_delicious()
-       {
-               return $this->add_to_service('http://del.icio.us/post/?v=4&url=', '&title=');
-       }
-
-       function add_to_digg()
-       {
-               return $this->add_to_service('http://digg.com/submit?url=', '&title=', '&bodytext=');
-       }
-
-       function add_to_furl()
-       {
-               return $this->add_to_service('http://www.furl.net/storeIt.jsp?u=', '&t=');
-       }
-
-       function add_to_magnolia()
-       {
-               return $this->add_to_service('http://ma.gnolia.com/bookmarklet/add?url=', '&title=');
-       }
-
-       function add_to_myweb20()
-       {
-               return $this->add_to_service('http://myweb2.search.yahoo.com/myresults/bookmarklet?u=', '&t=');
-       }
-
-       function add_to_newsvine()
-       {
-               return $this->add_to_service('http://www.newsvine.com/_wine/save?u=', '&h=');
-       }
-
-       function add_to_reddit()
-       {
-               return $this->add_to_service('http://reddit.com/submit?url=', '&title=');
-       }
-
-       function add_to_segnalo()
-       {
-               return $this->add_to_service('http://segnalo.com/post.html.php?url=', '&title=');
-       }
-
-       function add_to_simpy()
-       {
-               return $this->add_to_service('http://www.simpy.com/simpy/LinkAdd.do?href=', '&title=');
-       }
-
-       function add_to_spurl()
-       {
-               return $this->add_to_service('http://www.spurl.net/spurl.php?v=3&url=', '&title=');
-       }
-
-       function add_to_wists()
-       {
-               return $this->add_to_service('http://wists.com/r.php?c=&r=', '&title=');
-       }
-
-       function search_technorati()
-       {
-               return $this->add_to_service('http://www.technorati.com/search/');
-       }
-}
-
-class SimplePie_Source
-{
-       var $item;
-       var $data = array();
-
-       function SimplePie_Source($item, $data)
-       {
-               $this->item = $item;
-               $this->data = $data;
-       }
-
-       function __toString()
-       {
-               return md5(serialize($this->data));
-       }
-
-       function get_source_tags($namespace, $tag)
-       {
-               if (isset($this->data['child'][$namespace][$tag]))
-               {
-                       return $this->data['child'][$namespace][$tag];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_base($element = array())
-       {
-               return $this->item->get_base($element);
-       }
-
-       function sanitize($data, $type, $base = '')
-       {
-               return $this->item->sanitize($data, $type, $base);
-       }
-
-       function get_item()
-       {
-               return $this->item;
-       }
-
-       function get_title()
-       {
-               if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_category($key = 0)
-       {
-               $categories = $this->get_categories();
-               if (isset($categories[$key]))
-               {
-                       return $categories[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_categories()
-       {
-               $categories = array();
-
-               foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
-               {
-                       $term = null;
-                       $scheme = null;
-                       $label = null;
-                       if (isset($category['attribs']['']['term']))
-                       {
-                               $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($category['attribs']['']['scheme']))
-                       {
-                               $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($category['attribs']['']['label']))
-                       {
-                               $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       $categories[] = new $this->item->feed->category_class($term, $scheme, $label);
-               }
-               foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
-               {
-                       // This is really the label, but keep this as the term also for BC.
-                       // Label will also work on retrieving because that falls back to term.
-                       $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       if (isset($category['attribs']['']['domain']))
-                       {
-                               $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       else
-                       {
-                               $scheme = null;
-                       }
-                       $categories[] = new $this->item->feed->category_class($term, $scheme, null);
-               }
-               foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
-               {
-                       $categories[] = new $this->item->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
-               }
-               foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
-               {
-                       $categories[] = new $this->item->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
-               }
-
-               if (!empty($categories))
-               {
-                       return SimplePie_Misc::array_unique($categories);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_author($key = 0)
-       {
-               $authors = $this->get_authors();
-               if (isset($authors[$key]))
-               {
-                       return $authors[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_authors()
-       {
-               $authors = array();
-               foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
-               {
-                       $name = null;
-                       $uri = null;
-                       $email = null;
-                       $avatar = null;
-                       $name_date = null;
-                       $uri_date = null;
-                       $avatar_date = null;
-
-                       if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
-                       {
-                               $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
-                       {
-                               $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
-                       }
-                       if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
-                       {
-                               $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0]['data']))
-                       {
-                               $avatar = $this->sanitize($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0]));
-                       }
-                       if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['name-updated'][0]['data']))
-                       {
-                               $name_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['name-updated'][0]['data'];
-                       }
-                       if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['uri-updated'][0]['data']))
-                       {
-                               $uri_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['uri-updated'][0]['data'];
-                       }
-                       if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar-updated'][0]['data']))
-                       {
-                               $avatar_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar-updated'][0]['data'];
-                       }
-
-                       if ($name !== null || $email !== null || $uri !== null || $avatar !== null || $name_date !== null || $uri_date !== null || $avatar_date !== null )
-                       {
-                               $authors[] = new $this->item->feed->author_class($name, $uri, $email, $avatar, $name_date, $uri_date, $avatar_date);
-                       }
-               }
-               if ($author = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
-               {
-                       $name = null;
-                       $url = null;
-                       $email = null;
-                       if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
-                       {
-                               $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
-                       {
-                               $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
-                       }
-                       if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
-                       {
-                               $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if ($name !== null || $email !== null || $url !== null)
-                       {
-                               $authors[] = new $this->item->feed->author_class($name, $url, $email);
-                       }
-               }
-               foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
-               {
-                       $authors[] = new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
-               }
-               foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
-               {
-                       $authors[] = new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
-               }
-               foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
-               {
-                       $authors[] = new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
-               }
-
-               if (!empty($authors))
-               {
-                       return SimplePie_Misc::array_unique($authors);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_contributor($key = 0)
-       {
-               $contributors = $this->get_contributors();
-               if (isset($contributors[$key]))
-               {
-                       return $contributors[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_contributors()
-       {
-               $contributors = array();
-               foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
-               {
-                       $name = null;
-                       $uri = null;
-                       $email = null;
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
-                       {
-                               $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
-                       {
-                               $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
-                       }
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
-                       {
-                               $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if ($name !== null || $email !== null || $uri !== null)
-                       {
-                               $contributors[] = new $this->item->feed->author_class($name, $uri, $email);
-                       }
-               }
-               foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
-               {
-                       $name = null;
-                       $url = null;
-                       $email = null;
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
-                       {
-                               $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
-                       {
-                               $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
-                       }
-                       if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
-                       {
-                               $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-                       }
-                       if ($name !== null || $email !== null || $url !== null)
-                       {
-                               $contributors[] = new $this->item->feed->author_class($name, $url, $email);
-                       }
-               }
-
-               if (!empty($contributors))
-               {
-                       return SimplePie_Misc::array_unique($contributors);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_link($key = 0, $rel = 'alternate')
-       {
-               $links = $this->get_links($rel);
-               if (isset($links[$key]))
-               {
-                       return $links[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       /**
-        * Added for parity between the parent-level and the item/entry-level.
-        */
-       function get_permalink()
-       {
-               return $this->get_link(0);
-       }
-
-       function get_links($rel = 'alternate')
-       {
-               if (!isset($this->data['links']))
-               {
-                       $this->data['links'] = array();
-                       if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link'))
-                       {
-                               foreach ($links as $link)
-                               {
-                                       if (isset($link['attribs']['']['href']))
-                                       {
-                                               $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
-                                               $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
-                                       }
-                               }
-                       }
-                       if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link'))
-                       {
-                               foreach ($links as $link)
-                               {
-                                       if (isset($link['attribs']['']['href']))
-                                       {
-                                               $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
-                                               $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
-
-                                       }
-                               }
-                       }
-                       if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
-                       {
-                               $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
-                       }
-                       if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
-                       {
-                               $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
-                       }
-                       if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
-                       {
-                               $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
-                       }
-
-                       $keys = array_keys($this->data['links']);
-                       foreach ($keys as $key)
-                       {
-                               if (SimplePie_Misc::is_isegment_nz_nc($key))
-                               {
-                                       if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
-                                       {
-                                               $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
-                                               $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
-                                       }
-                                       else
-                                       {
-                                               $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
-                                       }
-                               }
-                               elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
-                               {
-                                       $this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
-                               }
-                               $this->data['links'][$key] = array_unique($this->data['links'][$key]);
-                       }
-               }
-
-               if (isset($this->data['links'][$rel]))
-               {
-                       return $this->data['links'][$rel];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_description()
-       {
-               if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle'))
-               {
-                       return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline'))
-               {
-                       return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_copyright()
-       {
-               if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
-               {
-                       return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright'))
-               {
-                       return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_language()
-       {
-               if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               elseif (isset($this->data['xml_lang']))
-               {
-                       return $this->sanitize($this->data['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_latitude()
-       {
-               if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
-               {
-                       return (float) $return[0]['data'];
-               }
-               elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
-               {
-                       return (float) $match[1];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_longitude()
-       {
-               if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
-               {
-                       return (float) $return[0]['data'];
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
-               {
-                       return (float) $return[0]['data'];
-               }
-               elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
-               {
-                       return (float) $match[2];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_image_url()
-       {
-               if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image'))
-               {
-                       return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI);
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
-               }
-               elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon'))
-               {
-                       return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
-               }
-               else
-               {
-                       return null;
-               }
-       }
-}
-
-class SimplePie_Author
-{
-       var $name;
-       var $link;
-       var $email;
-       var $avatar;
-       var $name_date;
-       var $uri_date;
-       var $avatar_date;
-
-       // Constructor, used to input the data
-       function SimplePie_Author($name = null, $link = null, $email = null, $avatar = null, $name_date = null, $uri_date = null, $avatar_date = null)
-       {
-               $this->name = $name;
-               $this->link = $link;
-               $this->email = $email;
-               $this->avatar = $avatar;
-               $this->name_date = $name_date;
-               $this->uri_date = $uri_date;
-               $this->avatar_date = $avatar_date;
-       }
-
-       function __toString()
-       {
-               // There is no $this->data here
-               return md5(serialize($this));
-       }
-
-       function get_name()
-       {
-               if ($this->name !== null)
-               {
-                       return $this->name;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_link()
-       {
-               if ($this->link !== null)
-               {
-                       return $this->link;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_email()
-       {
-               if ($this->email !== null)
-               {
-                       return $this->email;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_avatar()
-       {
-               if ($this->avatar !== null)
-               {
-                       return $this->avatar;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_name_date()
-       {
-               if ($this->name_date !== null)
-               {
-                       return $this->name_date;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-       function get_uri_date()
-       {
-               if ($this->uri_date !== null)
-               {
-                       return $this->uri_date;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-       function get_avatar_date()
-       {
-               if ($this->avatar_date !== null)
-               {
-                       return $this->avatar_date;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-
-}
-
-class SimplePie_Category
-{
-       var $term;
-       var $scheme;
-       var $label;
-
-       // Constructor, used to input the data
-       function SimplePie_Category($term = null, $scheme = null, $label = null)
-       {
-               $this->term = $term;
-               $this->scheme = $scheme;
-               $this->label = $label;
-       }
-
-       function __toString()
-       {
-               // There is no $this->data here
-               return md5(serialize($this));
-       }
-
-       function get_term()
-       {
-               if ($this->term !== null)
-               {
-                       return $this->term;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_scheme()
-       {
-               if ($this->scheme !== null)
-               {
-                       return $this->scheme;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_label()
-       {
-               if ($this->label !== null)
-               {
-                       return $this->label;
-               }
-               else
-               {
-                       return $this->get_term();
-               }
-       }
-}
-
-class SimplePie_Enclosure
-{
-       var $bitrate;
-       var $captions;
-       var $categories;
-       var $channels;
-       var $copyright;
-       var $credits;
-       var $description;
-       var $duration;
-       var $expression;
-       var $framerate;
-       var $handler;
-       var $hashes;
-       var $height;
-       var $javascript;
-       var $keywords;
-       var $lang;
-       var $length;
-       var $link;
-       var $medium;
-       var $player;
-       var $ratings;
-       var $restrictions;
-       var $samplingrate;
-       var $thumbnails;
-       var $title;
-       var $type;
-       var $width;
-
-       // Constructor, used to input the data
-       function SimplePie_Enclosure($link = null, $type = null, $length = null, $javascript = null, $bitrate = null, $captions = null, $categories = null, $channels = null, $copyright = null, $credits = null, $description = null, $duration = null, $expression = null, $framerate = null, $hashes = null, $height = null, $keywords = null, $lang = null, $medium = null, $player = null, $ratings = null, $restrictions = null, $samplingrate = null, $thumbnails = null, $title = null, $width = null)
-       {
-               $this->bitrate = $bitrate;
-               $this->captions = $captions;
-               $this->categories = $categories;
-               $this->channels = $channels;
-               $this->copyright = $copyright;
-               $this->credits = $credits;
-               $this->description = $description;
-               $this->duration = $duration;
-               $this->expression = $expression;
-               $this->framerate = $framerate;
-               $this->hashes = $hashes;
-               $this->height = $height;
-               $this->javascript = $javascript;
-               $this->keywords = $keywords;
-               $this->lang = $lang;
-               $this->length = $length;
-               $this->link = $link;
-               $this->medium = $medium;
-               $this->player = $player;
-               $this->ratings = $ratings;
-               $this->restrictions = $restrictions;
-               $this->samplingrate = $samplingrate;
-               $this->thumbnails = $thumbnails;
-               $this->title = $title;
-               $this->type = $type;
-               $this->width = $width;
-               if (class_exists('idna_convert'))
-               {
-                       $idn = new idna_convert;
-                       $parsed = SimplePie_Misc::parse_url($link);
-                       $this->link = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
-               }
-               $this->handler = $this->get_handler(); // Needs to load last
-       }
-
-       function __toString()
-       {
-               // There is no $this->data here
-               return md5(serialize($this));
-       }
-
-       function get_bitrate()
-       {
-               if ($this->bitrate !== null)
-               {
-                       return $this->bitrate;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_caption($key = 0)
-       {
-               $captions = $this->get_captions();
-               if (isset($captions[$key]))
-               {
-                       return $captions[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_captions()
-       {
-               if ($this->captions !== null)
-               {
-                       return $this->captions;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_category($key = 0)
-       {
-               $categories = $this->get_categories();
-               if (isset($categories[$key]))
-               {
-                       return $categories[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_categories()
-       {
-               if ($this->categories !== null)
-               {
-                       return $this->categories;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_channels()
-       {
-               if ($this->channels !== null)
-               {
-                       return $this->channels;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_copyright()
-       {
-               if ($this->copyright !== null)
-               {
-                       return $this->copyright;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_credit($key = 0)
-       {
-               $credits = $this->get_credits();
-               if (isset($credits[$key]))
-               {
-                       return $credits[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_credits()
-       {
-               if ($this->credits !== null)
-               {
-                       return $this->credits;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_description()
-       {
-               if ($this->description !== null)
-               {
-                       return $this->description;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_duration($convert = false)
-       {
-               if ($this->duration !== null)
-               {
-                       if ($convert)
-                       {
-                               $time = SimplePie_Misc::time_hms($this->duration);
-                               return $time;
-                       }
-                       else
-                       {
-                               return $this->duration;
-                       }
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_expression()
-       {
-               if ($this->expression !== null)
-               {
-                       return $this->expression;
-               }
-               else
-               {
-                       return 'full';
-               }
-       }
-
-       function get_extension()
-       {
-               if ($this->link !== null)
-               {
-                       $url = SimplePie_Misc::parse_url($this->link);
-                       if ($url['path'] !== '')
-                       {
-                               return pathinfo($url['path'], PATHINFO_EXTENSION);
-                       }
-               }
-               return null;
-       }
-
-       function get_framerate()
-       {
-               if ($this->framerate !== null)
-               {
-                       return $this->framerate;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_handler()
-       {
-               return $this->get_real_type(true);
-       }
-
-       function get_hash($key = 0)
-       {
-               $hashes = $this->get_hashes();
-               if (isset($hashes[$key]))
-               {
-                       return $hashes[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_hashes()
-       {
-               if ($this->hashes !== null)
-               {
-                       return $this->hashes;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_height()
-       {
-               if ($this->height !== null)
-               {
-                       return $this->height;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_language()
-       {
-               if ($this->lang !== null)
-               {
-                       return $this->lang;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_keyword($key = 0)
-       {
-               $keywords = $this->get_keywords();
-               if (isset($keywords[$key]))
-               {
-                       return $keywords[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_keywords()
-       {
-               if ($this->keywords !== null)
-               {
-                       return $this->keywords;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_length()
-       {
-               if ($this->length !== null)
-               {
-                       return $this->length;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_link()
-       {
-               if ($this->link !== null)
-               {
-                       return urldecode($this->link);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_medium()
-       {
-               if ($this->medium !== null)
-               {
-                       return $this->medium;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_player()
-       {
-               if ($this->player !== null)
-               {
-                       return $this->player;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_rating($key = 0)
-       {
-               $ratings = $this->get_ratings();
-               if (isset($ratings[$key]))
-               {
-                       return $ratings[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_ratings()
-       {
-               if ($this->ratings !== null)
-               {
-                       return $this->ratings;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_restriction($key = 0)
-       {
-               $restrictions = $this->get_restrictions();
-               if (isset($restrictions[$key]))
-               {
-                       return $restrictions[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_restrictions()
-       {
-               if ($this->restrictions !== null)
-               {
-                       return $this->restrictions;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_sampling_rate()
-       {
-               if ($this->samplingrate !== null)
-               {
-                       return $this->samplingrate;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_size()
-       {
-               $length = $this->get_length();
-               if ($length !== null)
-               {
-                       return round($length/1048576, 2);
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_thumbnail($key = 0)
-       {
-               $thumbnails = $this->get_thumbnails();
-               if (isset($thumbnails[$key]))
-               {
-                       return $thumbnails[$key];
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_thumbnails()
-       {
-               if ($this->thumbnails !== null)
-               {
-                       return $this->thumbnails;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_title()
-       {
-               if ($this->title !== null)
-               {
-                       return $this->title;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_type()
-       {
-               if ($this->type !== null)
-               {
-                       return $this->type;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_width()
-       {
-               if ($this->width !== null)
-               {
-                       return $this->width;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function native_embed($options='')
-       {
-               return $this->embed($options, true);
-       }
-
-       /**
-        * @todo If the dimensions for media:content are defined, use them when width/height are set to 'auto'.
-        */
-       function embed($options = '', $native = false)
-       {
-               // Set up defaults
-               $audio = '';
-               $video = '';
-               $alt = '';
-               $altclass = '';
-               $loop = 'false';
-               $width = 'auto';
-               $height = 'auto';
-               $bgcolor = '#ffffff';
-               $mediaplayer = '';
-               $widescreen = false;
-               $handler = $this->get_handler();
-               $type = $this->get_real_type();
-
-               // Process options and reassign values as necessary
-               if (is_array($options))
-               {
-                       extract($options);
-               }
-               else
-               {
-                       $options = explode(',', $options);
-                       foreach($options as $option)
-                       {
-                               $opt = explode(':', $option, 2);
-                               if (isset($opt[0], $opt[1]))
-                               {
-                                       $opt[0] = trim($opt[0]);
-                                       $opt[1] = trim($opt[1]);
-                                       switch ($opt[0])
-                                       {
-                                               case 'audio':
-                                                       $audio = $opt[1];
-                                                       break;
-
-                                               case 'video':
-                                                       $video = $opt[1];
-                                                       break;
-
-                                               case 'alt':
-                                                       $alt = $opt[1];
-                                                       break;
-
-                                               case 'altclass':
-                                                       $altclass = $opt[1];
-                                                       break;
-
-                                               case 'loop':
-                                                       $loop = $opt[1];
-                                                       break;
-
-                                               case 'width':
-                                                       $width = $opt[1];
-                                                       break;
-
-                                               case 'height':
-                                                       $height = $opt[1];
-                                                       break;
-
-                                               case 'bgcolor':
-                                                       $bgcolor = $opt[1];
-                                                       break;
-
-                                               case 'mediaplayer':
-                                                       $mediaplayer = $opt[1];
-                                                       break;
-
-                                               case 'widescreen':
-                                                       $widescreen = $opt[1];
-                                                       break;
-                                       }
-                               }
-                       }
-               }
-
-               $mime = explode('/', $type, 2);
-               $mime = $mime[0];
-
-               // Process values for 'auto'
-               if ($width === 'auto')
-               {
-                       if ($mime === 'video')
-                       {
-                               if ($height === 'auto')
-                               {
-                                       $width = 480;
-                               }
-                               elseif ($widescreen)
-                               {
-                                       $width = round((intval($height)/9)*16);
-                               }
-                               else
-                               {
-                                       $width = round((intval($height)/3)*4);
-                               }
-                       }
-                       else
-                       {
-                               $width = '100%';
-                       }
-               }
-
-               if ($height === 'auto')
-               {
-                       if ($mime === 'audio')
-                       {
-                               $height = 0;
-                       }
-                       elseif ($mime === 'video')
-                       {
-                               if ($width === 'auto')
-                               {
-                                       if ($widescreen)
-                                       {
-                                               $height = 270;
-                                       }
-                                       else
-                                       {
-                                               $height = 360;
-                                       }
-                               }
-                               elseif ($widescreen)
-                               {
-                                       $height = round((intval($width)/16)*9);
-                               }
-                               else
-                               {
-                                       $height = round((intval($width)/4)*3);
-                               }
-                       }
-                       else
-                       {
-                               $height = 376;
-                       }
-               }
-               elseif ($mime === 'audio')
-               {
-                       $height = 0;
-               }
-
-               // Set proper placeholder value
-               if ($mime === 'audio')
-               {
-                       $placeholder = $audio;
-               }
-               elseif ($mime === 'video')
-               {
-                       $placeholder = $video;
-               }
-
-               $embed = '';
-
-               // Make sure the JS library is included
-               if (!$native)
-               {
-                       static $javascript_outputted = null;
-                       if (!$javascript_outputted && $this->javascript)
-                       {
-                               $embed .= '<script type="text/javascript" src="?' . htmlspecialchars($this->javascript) . '"></script>';
-                               $javascript_outputted = true;
-                       }
-               }
-
-               // Odeo Feed MP3's
-               if ($handler === 'odeo')
-               {
-                       if ($native)
-                       {
-                               $embed .= '<embed src="http://odeo.com/flash/audio_player_fullsize.swf" pluginspage="http://adobe.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="440" height="80" wmode="transparent" allowScriptAccess="any" flashvars="valid_sample_rate=true&external_url=' . $this->get_link() . '"></embed>';
-                       }
-                       else
-                       {
-                               $embed .= '<script type="text/javascript">embed_odeo("' . $this->get_link() . '");</script>';
-                       }
-               }
-
-               // Flash
-               elseif ($handler === 'flash')
-               {
-                       if ($native)
-                       {
-                               $embed .= "<embed src=\"" . $this->get_link() . "\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"$type\" quality=\"high\" width=\"$width\" height=\"$height\" bgcolor=\"$bgcolor\" loop=\"$loop\"></embed>";
-                       }
-                       else
-                       {
-                               $embed .= "<script type='text/javascript'>embed_flash('$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$loop', '$type');</script>";
-                       }
-               }
-
-               // Flash Media Player file types.
-               // Preferred handler for MP3 file types.
-               elseif ($handler === 'fmedia' || ($handler === 'mp3' && $mediaplayer !== ''))
-               {
-                       $height += 20;
-                       if ($native)
-                       {
-                               $embed .= "<embed src=\"$mediaplayer\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"application/x-shockwave-flash\" quality=\"high\" width=\"$width\" height=\"$height\" wmode=\"transparent\" flashvars=\"file=" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "&autostart=false&repeat=$loop&showdigits=true&showfsbutton=false\"></embed>";
-                       }
-                       else
-                       {
-                               $embed .= "<script type='text/javascript'>embed_flv('$width', '$height', '" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "', '$placeholder', '$loop', '$mediaplayer');</script>";
-                       }
-               }
-
-               // QuickTime 7 file types.  Need to test with QuickTime 6.
-               // Only handle MP3's if the Flash Media Player is not present.
-               elseif ($handler === 'quicktime' || ($handler === 'mp3' && $mediaplayer === ''))
-               {
-                       $height += 16;
-                       if ($native)
-                       {
-                               if ($placeholder !== '')
-                               {
-                                       $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" href=\"" . $this->get_link() . "\" src=\"$placeholder\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"false\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>";
-                               }
-                               else
-                               {
-                                       $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" src=\"" . $this->get_link() . "\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"true\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>";
-                               }
-                       }
-                       else
-                       {
-                               $embed .= "<script type='text/javascript'>embed_quicktime('$type', '$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$placeholder', '$loop');</script>";
-                       }
-               }
-
-               // Windows Media
-               elseif ($handler === 'wmedia')
-               {
-                       $height += 45;
-                       if ($native)
-                       {
-                               $embed .= "<embed type=\"application/x-mplayer2\" src=\"" . $this->get_link() . "\" autosize=\"1\" width=\"$width\" height=\"$height\" showcontrols=\"1\" showstatusbar=\"0\" showdisplay=\"0\" autostart=\"0\"></embed>";
-                       }
-                       else
-                       {
-                               $embed .= "<script type='text/javascript'>embed_wmedia('$width', '$height', '" . $this->get_link() . "');</script>";
-                       }
-               }
-
-               // Everything else
-               else $embed .= '<a href="' . $this->get_link() . '" class="' . $altclass . '">' . $alt . '</a>';
-
-               return $embed;
-       }
-
-       function get_real_type($find_handler = false)
-       {
-               // If it's Odeo, let's get it out of the way.
-               if (substr(strtolower($this->get_link()), 0, 15) === 'http://odeo.com')
-               {
-                       return 'odeo';
-               }
-
-               // Mime-types by handler.
-               $types_flash = array('application/x-shockwave-flash', 'application/futuresplash'); // Flash
-               $types_fmedia = array('video/flv', 'video/x-flv','flv-application/octet-stream'); // Flash Media Player
-               $types_quicktime = array('audio/3gpp', 'audio/3gpp2', 'audio/aac', 'audio/x-aac', 'audio/aiff', 'audio/x-aiff', 'audio/mid', 'audio/midi', 'audio/x-midi', 'audio/mp4', 'audio/m4a', 'audio/x-m4a', 'audio/wav', 'audio/x-wav', 'video/3gpp', 'video/3gpp2', 'video/m4v', 'video/x-m4v', 'video/mp4', 'video/mpeg', 'video/x-mpeg', 'video/quicktime', 'video/sd-video'); // QuickTime
-               $types_wmedia = array('application/asx', 'application/x-mplayer2', 'audio/x-ms-wma', 'audio/x-ms-wax', 'video/x-ms-asf-plugin', 'video/x-ms-asf', 'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wvx'); // Windows Media
-               $types_mp3 = array('audio/mp3', 'audio/x-mp3', 'audio/mpeg', 'audio/x-mpeg'); // MP3
-
-               if ($this->get_type() !== null)
-               {
-                       $type = strtolower($this->type);
-               }
-               else
-               {
-                       $type = null;
-               }
-
-               // If we encounter an unsupported mime-type, check the file extension and guess intelligently.
-               if (!in_array($type, array_merge($types_flash, $types_fmedia, $types_quicktime, $types_wmedia, $types_mp3)))
-               {
-                       switch (strtolower($this->get_extension()))
-                       {
-                               // Audio mime-types
-                               case 'aac':
-                               case 'adts':
-                                       $type = 'audio/acc';
-                                       break;
-
-                               case 'aif':
-                               case 'aifc':
-                               case 'aiff':
-                               case 'cdda':
-                                       $type = 'audio/aiff';
-                                       break;
-
-                               case 'bwf':
-                                       $type = 'audio/wav';
-                                       break;
-
-                               case 'kar':
-                               case 'mid':
-                               case 'midi':
-                               case 'smf':
-                                       $type = 'audio/midi';
-                                       break;
-
-                               case 'm4a':
-                                       $type = 'audio/x-m4a';
-                                       break;
-
-                               case 'mp3':
-                               case 'swa':
-                                       $type = 'audio/mp3';
-                                       break;
-
-                               case 'wav':
-                                       $type = 'audio/wav';
-                                       break;
-
-                               case 'wax':
-                                       $type = 'audio/x-ms-wax';
-                                       break;
-
-                               case 'wma':
-                                       $type = 'audio/x-ms-wma';
-                                       break;
-
-                               // Video mime-types
-                               case '3gp':
-                               case '3gpp':
-                                       $type = 'video/3gpp';
-                                       break;
-
-                               case '3g2':
-                               case '3gp2':
-                                       $type = 'video/3gpp2';
-                                       break;
-
-                               case 'asf':
-                                       $type = 'video/x-ms-asf';
-                                       break;
-
-                               case 'flv':
-                                       $type = 'video/x-flv';
-                                       break;
-
-                               case 'm1a':
-                               case 'm1s':
-                               case 'm1v':
-                               case 'm15':
-                               case 'm75':
-                               case 'mp2':
-                               case 'mpa':
-                               case 'mpeg':
-                               case 'mpg':
-                               case 'mpm':
-                               case 'mpv':
-                                       $type = 'video/mpeg';
-                                       break;
-
-                               case 'm4v':
-                                       $type = 'video/x-m4v';
-                                       break;
-
-                               case 'mov':
-                               case 'qt':
-                                       $type = 'video/quicktime';
-                                       break;
-
-                               case 'mp4':
-                               case 'mpg4':
-                                       $type = 'video/mp4';
-                                       break;
-
-                               case 'sdv':
-                                       $type = 'video/sd-video';
-                                       break;
-
-                               case 'wm':
-                                       $type = 'video/x-ms-wm';
-                                       break;
-
-                               case 'wmv':
-                                       $type = 'video/x-ms-wmv';
-                                       break;
-
-                               case 'wvx':
-                                       $type = 'video/x-ms-wvx';
-                                       break;
-
-                               // Flash mime-types
-                               case 'spl':
-                                       $type = 'application/futuresplash';
-                                       break;
-
-                               case 'swf':
-                                       $type = 'application/x-shockwave-flash';
-                                       break;
-                       }
-               }
-
-               if ($find_handler)
-               {
-                       if (in_array($type, $types_flash))
-                       {
-                               return 'flash';
-                       }
-                       elseif (in_array($type, $types_fmedia))
-                       {
-                               return 'fmedia';
-                       }
-                       elseif (in_array($type, $types_quicktime))
-                       {
-                               return 'quicktime';
-                       }
-                       elseif (in_array($type, $types_wmedia))
-                       {
-                               return 'wmedia';
-                       }
-                       elseif (in_array($type, $types_mp3))
-                       {
-                               return 'mp3';
-                       }
-                       else
-                       {
-                               return null;
-                       }
-               }
-               else
-               {
-                       return $type;
-               }
-       }
-}
-
-class SimplePie_Caption
-{
-       var $type;
-       var $lang;
-       var $startTime;
-       var $endTime;
-       var $text;
-
-       // Constructor, used to input the data
-       function SimplePie_Caption($type = null, $lang = null, $startTime = null, $endTime = null, $text = null)
-       {
-               $this->type = $type;
-               $this->lang = $lang;
-               $this->startTime = $startTime;
-               $this->endTime = $endTime;
-               $this->text = $text;
-       }
-
-       function __toString()
-       {
-               // There is no $this->data here
-               return md5(serialize($this));
-       }
-
-       function get_endtime()
-       {
-               if ($this->endTime !== null)
-               {
-                       return $this->endTime;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_language()
-       {
-               if ($this->lang !== null)
-               {
-                       return $this->lang;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_starttime()
-       {
-               if ($this->startTime !== null)
-               {
-                       return $this->startTime;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_text()
-       {
-               if ($this->text !== null)
-               {
-                       return $this->text;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_type()
-       {
-               if ($this->type !== null)
-               {
-                       return $this->type;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-}
-
-class SimplePie_Credit
-{
-       var $role;
-       var $scheme;
-       var $name;
-
-       // Constructor, used to input the data
-       function SimplePie_Credit($role = null, $scheme = null, $name = null)
-       {
-               $this->role = $role;
-               $this->scheme = $scheme;
-               $this->name = $name;
-       }
-
-       function __toString()
-       {
-               // There is no $this->data here
-               return md5(serialize($this));
-       }
-
-       function get_role()
-       {
-               if ($this->role !== null)
-               {
-                       return $this->role;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_scheme()
-       {
-               if ($this->scheme !== null)
-               {
-                       return $this->scheme;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_name()
-       {
-               if ($this->name !== null)
-               {
-                       return $this->name;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-}
-
-class SimplePie_Copyright
-{
-       var $url;
-       var $label;
-
-       // Constructor, used to input the data
-       function SimplePie_Copyright($url = null, $label = null)
-       {
-               $this->url = $url;
-               $this->label = $label;
-       }
-
-       function __toString()
-       {
-               // There is no $this->data here
-               return md5(serialize($this));
-       }
-
-       function get_url()
-       {
-               if ($this->url !== null)
-               {
-                       return $this->url;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_attribution()
-       {
-               if ($this->label !== null)
-               {
-                       return $this->label;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-}
-
-class SimplePie_Rating
-{
-       var $scheme;
-       var $value;
-
-       // Constructor, used to input the data
-       function SimplePie_Rating($scheme = null, $value = null)
-       {
-               $this->scheme = $scheme;
-               $this->value = $value;
-       }
-
-       function __toString()
-       {
-               // There is no $this->data here
-               return md5(serialize($this));
-       }
-
-       function get_scheme()
-       {
-               if ($this->scheme !== null)
-               {
-                       return $this->scheme;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_value()
-       {
-               if ($this->value !== null)
-               {
-                       return $this->value;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-}
-
-class SimplePie_Restriction
-{
-       var $relationship;
-       var $type;
-       var $value;
-
-       // Constructor, used to input the data
-       function SimplePie_Restriction($relationship = null, $type = null, $value = null)
-       {
-               $this->relationship = $relationship;
-               $this->type = $type;
-               $this->value = $value;
-       }
-
-       function __toString()
-       {
-               // There is no $this->data here
-               return md5(serialize($this));
-       }
-
-       function get_relationship()
-       {
-               if ($this->relationship !== null)
-               {
-                       return $this->relationship;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_type()
-       {
-               if ($this->type !== null)
-               {
-                       return $this->type;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       function get_value()
-       {
-               if ($this->value !== null)
-               {
-                       return $this->value;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-}
-
-/**
- * @todo Move to properly supporting RFC2616 (HTTP/1.1)
- */
-class SimplePie_File
-{
-       var $url;
-       var $useragent;
-       var $success = true;
-       var $headers = array();
-       var $body;
-       var $status_code;
-       var $redirects = 0;
-       var $error;
-       var $method = SIMPLEPIE_FILE_SOURCE_NONE;
-
-       function SimplePie_File($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false)
-       {
-               if (class_exists('idna_convert'))
-               {
-                       $idn = new idna_convert;
-                       $parsed = SimplePie_Misc::parse_url($url);
-                       $url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
-               }
-               $this->url = $url;
-               $this->useragent = $useragent;
-               if (preg_match('/^http(s)?:\/\//i', $url))
-               {
-                       if ($useragent === null)
-                       {
-                               $useragent = ini_get('user_agent');
-                               $this->useragent = $useragent;
-                       }
-                       if (!is_array($headers))
-                       {
-                               $headers = array();
-                       }
-                       if (!$force_fsockopen && function_exists('curl_exec'))
-                       {
-                               $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL;
-                               $fp = curl_init();
-                               $headers2 = array();
-                               foreach ($headers as $key => $value)
-                               {
-                                       $headers2[] = "$key: $value";
-                               }
-                               if (version_compare(SimplePie_Misc::get_curl_version(), '7.10.5', '>='))
-                               {
-                                       curl_setopt($fp, CURLOPT_ENCODING, '');
-                               }
-                               curl_setopt($fp, CURLOPT_URL, $url);
-                               curl_setopt($fp, CURLOPT_HEADER, 1);
-                               curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1);
-                               curl_setopt($fp, CURLOPT_TIMEOUT, $timeout);
-                               curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout);
-                               curl_setopt($fp, CURLOPT_REFERER, $url);
-                               curl_setopt($fp, CURLOPT_USERAGENT, $useragent);
-                               curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2);
-                               if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>='))
-                               {
-                                       curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1);
-                                       curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects);
-                               }
-
-                               $this->headers = curl_exec($fp);
-                               if (curl_errno($fp) === 23 || curl_errno($fp) === 61)
-                               {
-                                       curl_setopt($fp, CURLOPT_ENCODING, 'none');
-                                       $this->headers = curl_exec($fp);
-                               }
-                               if (curl_errno($fp))
-                               {
-                                       $this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp);
-                                       $this->success = false;
-                               }
-                               else
-                               {
-                                       $info = curl_getinfo($fp);
-                                       curl_close($fp);
-                                       $this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 1);
-                                       $this->headers = array_pop($this->headers);
-                                       $parser = new SimplePie_HTTP_Parser($this->headers);
-                                       if ($parser->parse())
-                                       {
-                                               $this->headers = $parser->headers;
-                                               $this->body = $parser->body;
-                                               $this->status_code = $parser->status_code;
-                                               if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects)
-                                               {
-                                                       $this->redirects++;
-                                                       $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
-                                                       return $this->SimplePie_File($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
-                                               }
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN;
-                               $url_parts = parse_url($url);
-                               if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https')
-                               {
-                                       $url_parts['host'] = "ssl://$url_parts[host]";
-                                       $url_parts['port'] = 443;
-                               }
-                               if (!isset($url_parts['port']))
-                               {
-                                       $url_parts['port'] = 80;
-                               }
-                               $fp = @fsockopen($url_parts['host'], $url_parts['port'], $errno, $errstr, $timeout);
-                               if (!$fp)
-                               {
-                                       $this->error = 'fsockopen error: ' . $errstr;
-                                       $this->success = false;
-                               }
-                               else
-                               {
-                                       stream_set_timeout($fp, $timeout);
-                                       if (isset($url_parts['path']))
-                                       {
-                                               if (isset($url_parts['query']))
-                                               {
-                                                       $get = "$url_parts[path]?$url_parts[query]";
-                                               }
-                                               else
-                                               {
-                                                       $get = $url_parts['path'];
-                                               }
-                                       }
-                                       else
-                                       {
-                                               $get = '/';
-                                       }
-                                       $out = "GET $get HTTP/1.0\r\n";
-                                       $out .= "Host: $url_parts[host]\r\n";
-                                       $out .= "User-Agent: $useragent\r\n";
-                                       if (extension_loaded('zlib'))
-                                       {
-                                               $out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n";
-                                       }
-
-                                       if (isset($url_parts['user']) && isset($url_parts['pass']))
-                                       {
-                                               $out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n";
-                                       }
-                                       foreach ($headers as $key => $value)
-                                       {
-                                               $out .= "$key: $value\r\n";
-                                       }
-                                       $out .= "Connection: Close\r\n\r\n";
-                                       fwrite($fp, $out);
-
-                                       $info = stream_get_meta_data($fp);
-
-                                       $this->headers = '';
-                                       while (!$info['eof'] && !$info['timed_out'])
-                                       {
-                                               $this->headers .= fread($fp, 1160);
-                                               $info = stream_get_meta_data($fp);
-                                       }
-                                       if (!$info['timed_out'])
-                                       {
-                                               $parser = new SimplePie_HTTP_Parser($this->headers);
-                                               if ($parser->parse())
-                                               {
-                                                       $this->headers = $parser->headers;
-                                                       $this->body = $parser->body;
-                                                       $this->status_code = $parser->status_code;
-                                                       if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects)
-                                                       {
-                                                               $this->redirects++;
-                                                               $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
-                                                               return $this->SimplePie_File($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
-                                                       }
-                                                       if (isset($this->headers['content-encoding']))
-                                                       {
-                                                               // Hey, we act dumb elsewhere, so let's do that here too
-                                                               switch (strtolower(trim($this->headers['content-encoding'], "\x09\x0A\x0D\x20")))
-                                                               {
-                                                                       case 'gzip':
-                                                                       case 'x-gzip':
-                                                                               $decoder = new SimplePie_gzdecode($this->body);
-                                                                               if (!$decoder->parse())
-                                                                               {
-                                                                                       $this->error = 'Unable to decode HTTP "gzip" stream';
-                                                                                       $this->success = false;
-                                                                               }
-                                                                               else
-                                                                               {
-                                                                                       $this->body = $decoder->data;
-                                                                               }
-                                                                               break;
-
-                                                                       case 'deflate':
-                                                                               if (($body = gzuncompress($this->body)) === false)
-                                                                               {
-                                                                                       if (($body = gzinflate($this->body)) === false)
-                                                                                       {
-                                                                                               $this->error = 'Unable to decode HTTP "deflate" stream';
-                                                                                               $this->success = false;
-                                                                                       }
-                                                                               }
-                                                                               $this->body = $body;
-                                                                               break;
-
-                                                                       default:
-                                                                               $this->error = 'Unknown content coding';
-                                                                               $this->success = false;
-                                                               }
-                                                       }
-                                               }
-                                       }
-                                       else
-                                       {
-                                               $this->error = 'fsocket timed out';
-                                               $this->success = false;
-                                       }
-                                       fclose($fp);
-                               }
-                       }
-               }
-               else
-               {
-                       $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS;
-                       if (!$this->body = file_get_contents($url))
-                       {
-                               $this->error = 'file_get_contents could not read the file';
-                               $this->success = false;
-                       }
-               }
-       }
-}
-
-/**
- * HTTP Response Parser
- *
- * @package SimplePie
- */
-class SimplePie_HTTP_Parser
-{
-       /**
-        * HTTP Version
-        *
-        * @access public
-        * @var float
-        */
-       var $http_version = 0.0;
-
-       /**
-        * Status code
-        *
-        * @access public
-        * @var int
-        */
-       var $status_code = 0;
-
-       /**
-        * Reason phrase
-        *
-        * @access public
-        * @var string
-        */
-       var $reason = '';
-
-       /**
-        * Key/value pairs of the headers
-        *
-        * @access public
-        * @var array
-        */
-       var $headers = array();
-
-       /**
-        * Body of the response
-        *
-        * @access public
-        * @var string
-        */
-       var $body = '';
-
-       /**
-        * Current state of the state machine
-        *
-        * @access private
-        * @var string
-        */
-       var $state = 'http_version';
-
-       /**
-        * Input data
-        *
-        * @access private
-        * @var string
-        */
-       var $data = '';
-
-       /**
-        * Input data length (to avoid calling strlen() everytime this is needed)
-        *
-        * @access private
-        * @var int
-        */
-       var $data_length = 0;
-
-       /**
-        * Current position of the pointer
-        *
-        * @var int
-        * @access private
-        */
-       var $position = 0;
-
-       /**
-        * Name of the hedaer currently being parsed
-        *
-        * @access private
-        * @var string
-        */
-       var $name = '';
-
-       /**
-        * Value of the hedaer currently being parsed
-        *
-        * @access private
-        * @var string
-        */
-       var $value = '';
-
-       /**
-        * Create an instance of the class with the input data
-        *
-        * @access public
-        * @param string $data Input data
-        */
-       function SimplePie_HTTP_Parser($data)
-       {
-               $this->data = $data;
-               $this->data_length = strlen($this->data);
-       }
-
-       /**
-        * Parse the input data
-        *
-        * @access public
-        * @return bool true on success, false on failure
-        */
-       function parse()
-       {
-               while ($this->state && $this->state !== 'emit' && $this->has_data())
-               {
-                       $state = $this->state;
-                       $this->$state();
-               }
-               $this->data = '';
-               if ($this->state === 'emit' || $this->state === 'body')
-               {
-                       return true;
-               }
-               else
-               {
-                       $this->http_version = '';
-                       $this->status_code = '';
-                       $this->reason = '';
-                       $this->headers = array();
-                       $this->body = '';
-                       return false;
-               }
-       }
-
-       /**
-        * Check whether there is data beyond the pointer
-        *
-        * @access private
-        * @return bool true if there is further data, false if not
-        */
-       function has_data()
-       {
-               return (bool) ($this->position < $this->data_length);
-       }
-
-       /**
-        * See if the next character is LWS
-        *
-        * @access private
-        * @return bool true if the next character is LWS, false if not
-        */
-       function is_linear_whitespace()
-       {
-               return (bool) ($this->data[$this->position] === "\x09"
-                       || $this->data[$this->position] === "\x20"
-                       || ($this->data[$this->position] === "\x0A"
-                               && isset($this->data[$this->position + 1])
-                               && ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20")));
-       }
-
-       /**
-        * Parse the HTTP version
-        *
-        * @access private
-        */
-       function http_version()
-       {
-               if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/')
-               {
-                       $len = strspn($this->data, '0123456789.', 5);
-                       $this->http_version = substr($this->data, 5, $len);
-                       $this->position += 5 + $len;
-                       if (substr_count($this->http_version, '.') <= 1)
-                       {
-                               $this->http_version = (float) $this->http_version;
-                               $this->position += strspn($this->data, "\x09\x20", $this->position);
-                               $this->state = 'status';
-                       }
-                       else
-                       {
-                               $this->state = false;
-                       }
-               }
-               else
-               {
-                       $this->state = false;
-               }
-       }
-
-       /**
-        * Parse the status code
-        *
-        * @access private
-        */
-       function status()
-       {
-               if ($len = strspn($this->data, '0123456789', $this->position))
-               {
-                       $this->status_code = (int) substr($this->data, $this->position, $len);
-                       $this->position += $len;
-                       $this->state = 'reason';
-               }
-               else
-               {
-                       $this->state = false;
-               }
-       }
-
-       /**
-        * Parse the reason phrase
-        *
-        * @access private
-        */
-       function reason()
-       {
-               $len = strcspn($this->data, "\x0A", $this->position);
-               $this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20");
-               $this->position += $len + 1;
-               $this->state = 'new_line';
-       }
-
-       /**
-        * Deal with a new line, shifting data around as needed
-        *
-        * @access private
-        */
-       function new_line()
-       {
-               $this->value = trim($this->value, "\x0D\x20");
-               if ($this->name !== '' && $this->value !== '')
-               {
-                       $this->name = strtolower($this->name);
-                       if (isset($this->headers[$this->name]))
-                       {
-                               $this->headers[$this->name] .= ', ' . $this->value;
-                       }
-                       else
-                       {
-                               $this->headers[$this->name] = $this->value;
-                       }
-               }
-               $this->name = '';
-               $this->value = '';
-               if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A")
-               {
-                       $this->position += 2;
-                       $this->state = 'body';
-               }
-               elseif ($this->data[$this->position] === "\x0A")
-               {
-                       $this->position++;
-                       $this->state = 'body';
-               }
-               else
-               {
-                       $this->state = 'name';
-               }
-       }
-
-       /**
-        * Parse a header name
-        *
-        * @access private
-        */
-       function name()
-       {
-               $len = strcspn($this->data, "\x0A:", $this->position);
-               if (isset($this->data[$this->position + $len]))
-               {
-                       if ($this->data[$this->position + $len] === "\x0A")
-                       {
-                               $this->position += $len;
-                               $this->state = 'new_line';
-                       }
-                       else
-                       {
-                               $this->name = substr($this->data, $this->position, $len);
-                               $this->position += $len + 1;
-                               $this->state = 'value';
-                       }
-               }
-               else
-               {
-                       $this->state = false;
-               }
-       }
-
-       /**
-        * Parse LWS, replacing consecutive LWS characters with a single space
-        *
-        * @access private
-        */
-       function linear_whitespace()
-       {
-               do
-               {
-                       if (substr($this->data, $this->position, 2) === "\x0D\x0A")
-                       {
-                               $this->position += 2;
-                       }
-                       elseif ($this->data[$this->position] === "\x0A")
-                       {
-                               $this->position++;
-                       }
-                       $this->position += strspn($this->data, "\x09\x20", $this->position);
-               } while ($this->has_data() && $this->is_linear_whitespace());
-               $this->value .= "\x20";
-       }
-
-       /**
-        * See what state to move to while within non-quoted header values
-        *
-        * @access private
-        */
-       function value()
-       {
-               if ($this->is_linear_whitespace())
-               {
-                       $this->linear_whitespace();
-               }
-               else
-               {
-                       switch ($this->data[$this->position])
-                       {
-                               case '"':
-                                       $this->position++;
-                                       $this->state = 'quote';
-                                       break;
-
-                               case "\x0A":
-                                       $this->position++;
-                                       $this->state = 'new_line';
-                                       break;
-
-                               default:
-                                       $this->state = 'value_char';
-                                       break;
-                       }
-               }
-       }
-
-       /**
-        * Parse a header value while outside quotes
-        *
-        * @access private
-        */
-       function value_char()
-       {
-               $len = strcspn($this->data, "\x09\x20\x0A\"", $this->position);
-               $this->value .= substr($this->data, $this->position, $len);
-               $this->position += $len;
-               $this->state = 'value';
-       }
-
-       /**
-        * See what state to move to while within quoted header values
-        *
-        * @access private
-        */
-       function quote()
-       {
-               if ($this->is_linear_whitespace())
-               {
-                       $this->linear_whitespace();
-               }
-               else
-               {
-                       switch ($this->data[$this->position])
-                       {
-                               case '"':
-                                       $this->position++;
-                                       $this->state = 'value';
-                                       break;
-
-                               case "\x0A":
-                                       $this->position++;
-                                       $this->state = 'new_line';
-                                       break;
-
-                               case '\\':
-                                       $this->position++;
-                                       $this->state = 'quote_escaped';
-                                       break;
-
-                               default:
-                                       $this->state = 'quote_char';
-                                       break;
-                       }
-               }
-       }
-
-       /**
-        * Parse a header value while within quotes
-        *
-        * @access private
-        */
-       function quote_char()
-       {
-               $len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position);
-               $this->value .= substr($this->data, $this->position, $len);
-               $this->position += $len;
-               $this->state = 'value';
-       }
-
-       /**
-        * Parse an escaped character within quotes
-        *
-        * @access private
-        */
-       function quote_escaped()
-       {
-               $this->value .= $this->data[$this->position];
-               $this->position++;
-               $this->state = 'quote';
-       }
-
-       /**
-        * Parse the body
-        *
-        * @access private
-        */
-       function body()
-       {
-               $this->body = substr($this->data, $this->position);
-               $this->state = 'emit';
-       }
-}
-
-/**
- * gzdecode
- *
- * @package SimplePie
- */
-class SimplePie_gzdecode
-{
-       /**
-        * Compressed data
-        *
-        * @access private
-        * @see gzdecode::$data
-        */
-       var $compressed_data;
-
-       /**
-        * Size of compressed data
-        *
-        * @access private
-        */
-       var $compressed_size;
-
-       /**
-        * Minimum size of a valid gzip string
-        *
-        * @access private
-        */
-       var $min_compressed_size = 18;
-
-       /**
-        * Current position of pointer
-        *
-        * @access private
-        */
-       var $position = 0;
-
-       /**
-        * Flags (FLG)
-        *
-        * @access private
-        */
-       var $flags;
-
-       /**
-        * Uncompressed data
-        *
-        * @access public
-        * @see gzdecode::$compressed_data
-        */
-       var $data;
-
-       /**
-        * Modified time
-        *
-        * @access public
-        */
-       var $MTIME;
-
-       /**
-        * Extra Flags
-        *
-        * @access public
-        */
-       var $XFL;
-
-       /**
-        * Operating System
-        *
-        * @access public
-        */
-       var $OS;
-
-       /**
-        * Subfield ID 1
-        *
-        * @access public
-        * @see gzdecode::$extra_field
-        * @see gzdecode::$SI2
-        */
-       var $SI1;
-
-       /**
-        * Subfield ID 2
-        *
-        * @access public
-        * @see gzdecode::$extra_field
-        * @see gzdecode::$SI1
-        */
-       var $SI2;
-
-       /**
-        * Extra field content
-        *
-        * @access public
-        * @see gzdecode::$SI1
-        * @see gzdecode::$SI2
-        */
-       var $extra_field;
-
-       /**
-        * Original filename
-        *
-        * @access public
-        */
-       var $filename;
-
-       /**
-        * Human readable comment
-        *
-        * @access public
-        */
-       var $comment;
-
-       /**
-        * Don't allow anything to be set
-        *
-        * @access public
-        */
-       function __set($name, $value)
-       {
-               trigger_error("Cannot write property $name", E_USER_ERROR);
-       }
-
-       /**
-        * Set the compressed string and related properties
-        *
-        * @access public
-        */
-       function SimplePie_gzdecode($data)
-       {
-               $this->compressed_data = $data;
-               $this->compressed_size = strlen($data);
-       }
-
-       /**
-        * Decode the GZIP stream
-        *
-        * @access public
-        */
-       function parse()
-       {
-               if ($this->compressed_size >= $this->min_compressed_size)
-               {
-                       // Check ID1, ID2, and CM
-                       if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08")
-                       {
-                               return false;
-                       }
-
-                       // Get the FLG (FLaGs)
-                       $this->flags = ord($this->compressed_data[3]);
-
-                       // FLG bits above (1 << 4) are reserved
-                       if ($this->flags > 0x1F)
-                       {
-                               return false;
-                       }
-
-                       // Advance the pointer after the above
-                       $this->position += 4;
-
-                       // MTIME
-                       $mtime = substr($this->compressed_data, $this->position, 4);
-                       // Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness
-                       if (current(unpack('S', "\x00\x01")) === 1)
-                       {
-                               $mtime = strrev($mtime);
-                       }
-                       $this->MTIME = current(unpack('l', $mtime));
-                       $this->position += 4;
-
-                       // Get the XFL (eXtra FLags)
-                       $this->XFL = ord($this->compressed_data[$this->position++]);
-
-                       // Get the OS (Operating System)
-                       $this->OS = ord($this->compressed_data[$this->position++]);
-
-                       // Parse the FEXTRA
-                       if ($this->flags & 4)
-                       {
-                               // Read subfield IDs
-                               $this->SI1 = $this->compressed_data[$this->position++];
-                               $this->SI2 = $this->compressed_data[$this->position++];
-
-                               // SI2 set to zero is reserved for future use
-                               if ($this->SI2 === "\x00")
-                               {
-                                       return false;
-                               }
-
-                               // Get the length of the extra field
-                               $len = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
-                               $position += 2;
-
-                               // Check the length of the string is still valid
-                               $this->min_compressed_size += $len + 4;
-                               if ($this->compressed_size >= $this->min_compressed_size)
-                               {
-                                       // Set the extra field to the given data
-                                       $this->extra_field = substr($this->compressed_data, $this->position, $len);
-                                       $this->position += $len;
-                               }
-                               else
-                               {
-                                       return false;
-                               }
-                       }
-
-                       // Parse the FNAME
-                       if ($this->flags & 8)
-                       {
-                               // Get the length of the filename
-                               $len = strcspn($this->compressed_data, "\x00", $this->position);
-
-                               // Check the length of the string is still valid
-                               $this->min_compressed_size += $len + 1;
-                               if ($this->compressed_size >= $this->min_compressed_size)
-                               {
-                                       // Set the original filename to the given string
-                                       $this->filename = substr($this->compressed_data, $this->position, $len);
-                                       $this->position += $len + 1;
-                               }
-                               else
-                               {
-                                       return false;
-                               }
-                       }
-
-                       // Parse the FCOMMENT
-                       if ($this->flags & 16)
-                       {
-                               // Get the length of the comment
-                               $len = strcspn($this->compressed_data, "\x00", $this->position);
-
-                               // Check the length of the string is still valid
-                               $this->min_compressed_size += $len + 1;
-                               if ($this->compressed_size >= $this->min_compressed_size)
-                               {
-                                       // Set the original comment to the given string
-                                       $this->comment = substr($this->compressed_data, $this->position, $len);
-                                       $this->position += $len + 1;
-                               }
-                               else
-                               {
-                                       return false;
-                               }
-                       }
-
-                       // Parse the FHCRC
-                       if ($this->flags & 2)
-                       {
-                               // Check the length of the string is still valid
-                               $this->min_compressed_size += $len + 2;
-                               if ($this->compressed_size >= $this->min_compressed_size)
-                               {
-                                       // Read the CRC
-                                       $crc = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
-
-                                       // Check the CRC matches
-                                       if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc)
-                                       {
-                                               $this->position += 2;
-                                       }
-                                       else
-                                       {
-                                               return false;
-                                       }
-                               }
-                               else
-                               {
-                                       return false;
-                               }
-                       }
-
-                       // Decompress the actual data
-                       if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false)
-                       {
-                               return false;
-                       }
-                       else
-                       {
-                               $this->position = $this->compressed_size - 8;
-                       }
-
-                       // Check CRC of data
-                       $crc = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
-                       $this->position += 4;
-                       /*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc))
-                       {
-                               return false;
-                       }*/
-
-                       // Check ISIZE of data
-                       $isize = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
-                       $this->position += 4;
-                       if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize))
-                       {
-                               return false;
-                       }
-
-                       // Wow, against all odds, we've actually got a valid gzip string
-                       return true;
-               }
-               else
-               {
-                       return false;
-               }
-       }
-}
-
-class SimplePie_Cache
-{
-       /**
-        * Don't call the constructor. Please.
-        *
-        * @access private
-        */
-       function SimplePie_Cache()
-       {
-               trigger_error('Please call SimplePie_Cache::create() instead of the constructor', E_USER_ERROR);
-       }
-
-       /**
-        * Create a new SimplePie_Cache object
-        *
-        * @static
-        * @access public
-        */
-       function create($location, $filename, $extension)
-       {
-               $location_iri = new SimplePie_IRI($location);
-               switch ($location_iri->get_scheme())
-               {
-                       case 'mysql':
-                               if (extension_loaded('mysql'))
-                               {
-                                       return new SimplePie_Cache_MySQL($location_iri, $filename, $extension);
-                               }
-                               break;
-
-                       default:
-                               return new SimplePie_Cache_File($location, $filename, $extension);
-               }
-       }
-}
-
-class SimplePie_Cache_File
-{
-       var $location;
-       var $filename;
-       var $extension;
-       var $name;
-
-       function SimplePie_Cache_File($location, $filename, $extension)
-       {
-               $this->location = $location;
-               $this->filename = $filename;
-               $this->extension = $extension;
-               $this->name = "$this->location/$this->filename.$this->extension";
-       }
-
-       function save($data)
-       {
-               if (file_exists($this->name) && is_writeable($this->name) || file_exists($this->location) && is_writeable($this->location))
-               {
-                       if (is_a($data, 'SimplePie'))
-                       {
-                               $data = $data->data;
-                       }
-
-                       $data = serialize($data);
-
-                       if (function_exists('file_put_contents'))
-                       {
-                               return (bool) file_put_contents($this->name, $data);
-                       }
-                       else
-                       {
-                               $fp = fopen($this->name, 'wb');
-                               if ($fp)
-                               {
-                                       fwrite($fp, $data);
-                                       fclose($fp);
-                                       return true;
-                               }
-                       }
-               }
-               return false;
-       }
-
-       function load()
-       {
-               if (file_exists($this->name) && is_readable($this->name))
-               {
-                       return unserialize(file_get_contents($this->name));
-               }
-               return false;
-       }
-
-       function mtime()
-       {
-               if (file_exists($this->name))
-               {
-                       return filemtime($this->name);
-               }
-               return false;
-       }
-
-       function touch()
-       {
-               if (file_exists($this->name))
-               {
-                       return touch($this->name);
-               }
-               return false;
-       }
-
-       function unlink()
-       {
-               if (file_exists($this->name))
-               {
-                       return unlink($this->name);
-               }
-               return false;
-       }
-}
-
-class SimplePie_Cache_DB
-{
-       function prepare_simplepie_object_for_cache($data)
-       {
-               $items = $data->get_items();
-               $items_by_id = array();
-
-               if (!empty($items))
-               {
-                       foreach ($items as $item)
-                       {
-                               $items_by_id[$item->get_id()] = $item;
-                       }
-
-                       if (count($items_by_id) !== count($items))
-                       {
-                               $items_by_id = array();
-                               foreach ($items as $item)
-                               {
-                                       $items_by_id[$item->get_id(true)] = $item;
-                               }
-                       }
-
-                       if (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]))
-                       {
-                               $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0];
-                       }
-                       elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]))
-                       {
-                               $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0];
-                       }
-                       elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]))
-                       {
-                               $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0];
-                       }
-                       elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0]))
-                       {
-                               $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0];
-                       }
-                       else
-                       {
-                               $channel = null;
-                       }
-
-                       if ($channel !== null)
-                       {
-                               if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']))
-                               {
-                                       unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']);
-                               }
-                               if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']))
-                               {
-                                       unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']);
-                               }
-                               if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']))
-                               {
-                                       unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']);
-                               }
-                               if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']))
-                               {
-                                       unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']);
-                               }
-                               if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']))
-                               {
-                                       unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']);
-                               }
-                       }
-                       if (isset($data->data['items']))
-                       {
-                               unset($data->data['items']);
-                       }
-                       if (isset($data->data['ordered_items']))
-                       {
-                               unset($data->data['ordered_items']);
-                       }
-               }
-               return array(serialize($data->data), $items_by_id);
-       }
-}
-
-class SimplePie_Cache_MySQL extends SimplePie_Cache_DB
-{
-       var $mysql;
-       var $options;
-       var $id;
-
-       function SimplePie_Cache_MySQL($mysql_location, $name, $extension)
-       {
-               $host = $mysql_location->get_host();
-               if (SimplePie_Misc::stripos($host, 'unix(') === 0 && substr($host, -1) === ')')
-               {
-                       $server = ':' . substr($host, 5, -1);
-               }
-               else
-               {
-                       $server = $host;
-                       if ($mysql_location->get_port() !== null)
-                       {
-                               $server .= ':' . $mysql_location->get_port();
-                       }
-               }
-
-               if (strpos($mysql_location->get_userinfo(), ':') !== false)
-               {
-                       list($username, $password) = explode(':', $mysql_location->get_userinfo(), 2);
-               }
-               else
-               {
-                       $username = $mysql_location->get_userinfo();
-                       $password = null;
-               }
-
-               if ($this->mysql = mysql_connect($server, $username, $password))
-               {
-                       $this->id = $name . $extension;
-                       $this->options = SimplePie_Misc::parse_str($mysql_location->get_query());
-                       if (!isset($this->options['prefix'][0]))
-                       {
-                               $this->options['prefix'][0] = '';
-                       }
-
-                       if (mysql_select_db(ltrim($mysql_location->get_path(), '/'))
-                               && mysql_query('SET NAMES utf8')
-                               && ($query = mysql_unbuffered_query('SHOW TABLES')))
-                       {
-                               $db = array();
-                               while ($row = mysql_fetch_row($query))
-                               {
-                                       $db[] = $row[0];
-                               }
-
-                               if (!in_array($this->options['prefix'][0] . 'cache_data', $db))
-                               {
-                                       if (!mysql_query('CREATE TABLE `' . $this->options['prefix'][0] . 'cache_data` (`id` TEXT CHARACTER SET utf8 NOT NULL, `items` SMALLINT NOT NULL DEFAULT 0, `data` BLOB NOT NULL, `mtime` INT UNSIGNED NOT NULL, UNIQUE (`id`(125)))'))
-                                       {
-                                               $this->mysql = null;
-                                       }
-                               }
-
-                               if (!in_array($this->options['prefix'][0] . 'items', $db))
-                               {
-                                       if (!mysql_query('CREATE TABLE `' . $this->options['prefix'][0] . 'items` (`feed_id` TEXT CHARACTER SET utf8 NOT NULL, `id` TEXT CHARACTER SET utf8 NOT NULL, `data` TEXT CHARACTER SET utf8 NOT NULL, `posted` INT UNSIGNED NOT NULL, INDEX `feed_id` (`feed_id`(125)))'))
-                                       {
-                                               $this->mysql = null;
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               $this->mysql = null;
-                       }
-               }
-       }
-
-       function save($data)
-       {
-               if ($this->mysql)
-               {
-                       $feed_id = "'" . mysql_real_escape_string($this->id) . "'";
-
-                       if (is_a($data, 'SimplePie'))
-                       {
-                               if (SIMPLEPIE_PHP5)
-                               {
-                                       // This keyword needs to defy coding standards for PHP4 compatibility
-                                       $data = clone($data);
-                               }
-
-                               $prepared = $this->prepare_simplepie_object_for_cache($data);
-
-                               if ($query = mysql_query('SELECT `id` FROM `' . $this->options['prefix'][0] . 'cache_data` WHERE `id` = ' . $feed_id, $this->mysql))
-                               {
-                                       if (mysql_num_rows($query))
-                                       {
-                                               $items = count($prepared[1]);
-                                               if ($items)
-                                               {
-                                                       $sql = 'UPDATE `' . $this->options['prefix'][0] . 'cache_data` SET `items` = ' . $items . ', `data` = \'' . mysql_real_escape_string($prepared[0]) . '\', `mtime` = ' . time() . ' WHERE `id` = ' . $feed_id;
-                                               }
-                                               else
-                                               {
-                                                       $sql = 'UPDATE `' . $this->options['prefix'][0] . 'cache_data` SET `data` = \'' . mysql_real_escape_string($prepared[0]) . '\', `mtime` = ' . time() . ' WHERE `id` = ' . $feed_id;
-                                               }
-
-                                               if (!mysql_query($sql, $this->mysql))
-                                               {
-                                                       return false;
-                                               }
-                                       }
-                                       elseif (!mysql_query('INSERT INTO `' . $this->options['prefix'][0] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(' . $feed_id . ', ' . count($prepared[1]) . ', \'' . mysql_real_escape_string($prepared[0]) . '\', ' . time() . ')', $this->mysql))
-                                       {
-                                               return false;
-                                       }
-
-                                       $ids = array_keys($prepared[1]);
-                                       if (!empty($ids))
-                                       {
-                                               foreach ($ids as $id)
-                                               {
-                                                       $database_ids[] = mysql_real_escape_string($id);
-                                               }
-
-                                               if ($query = mysql_unbuffered_query('SELECT `id` FROM `' . $this->options['prefix'][0] . 'items` WHERE `id` = \'' . implode('\' OR `id` = \'', $database_ids) . '\' AND `feed_id` = ' . $feed_id, $this->mysql))
-                                               {
-                                                       $existing_ids = array();
-                                                       while ($row = mysql_fetch_row($query))
-                                                       {
-                                                               $existing_ids[] = $row[0];
-                                                       }
-
-                                                       $new_ids = array_diff($ids, $existing_ids);
-
-                                                       foreach ($new_ids as $new_id)
-                                                       {
-                                                               if (!($date = $prepared[1][$new_id]->get_date('U')))
-                                                               {
-                                                                       $date = time();
-                                                               }
-
-                                                               if (!mysql_query('INSERT INTO `' . $this->options['prefix'][0] . 'items` (`feed_id`, `id`, `data`, `posted`) VALUES(' . $feed_id . ', \'' . mysql_real_escape_string($new_id) . '\', \'' . mysql_real_escape_string(serialize($prepared[1][$new_id]->data)) . '\', ' . $date . ')', $this->mysql))
-                                                               {
-                                                                       return false;
-                                                               }
-                                                       }
-                                                       return true;
-                                               }
-                                       }
-                                       else
-                                       {
-                                               return true;
-                                       }
-                               }
-                       }
-                       elseif ($query = mysql_query('SELECT `id` FROM `' . $this->options['prefix'][0] . 'cache_data` WHERE `id` = ' . $feed_id, $this->mysql))
-                       {
-                               if (mysql_num_rows($query))
-                               {
-                                       if (mysql_query('UPDATE `' . $this->options['prefix'][0] . 'cache_data` SET `items` = 0, `data` = \'' . mysql_real_escape_string(serialize($data)) . '\', `mtime` = ' . time() . ' WHERE `id` = ' . $feed_id, $this->mysql))
-                                       {
-                                               return true;
-                                       }
-                               }
-                               elseif (mysql_query('INSERT INTO `' . $this->options['prefix'][0] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(\'' . mysql_real_escape_string($this->id) . '\', 0, \'' . mysql_real_escape_string(serialize($data)) . '\', ' . time() . ')', $this->mysql))
-                               {
-                                       return true;
-                               }
-                       }
-               }
-               return false;
-       }
-
-       function load()
-       {
-               if ($this->mysql && ($query = mysql_query('SELECT `items`, `data` FROM `' . $this->options['prefix'][0] . 'cache_data` WHERE `id` = \'' . mysql_real_escape_string($this->id) . "'", $this->mysql)) && ($row = mysql_fetch_row($query)))
-               {
-                       $data = unserialize($row[1]);
-
-                       if (isset($this->options['items'][0]))
-                       {
-                               $items = (int) $this->options['items'][0];
-                       }
-                       else
-                       {
-                               $items = (int) $row[0];
-                       }
-
-                       if ($items !== 0)
-                       {
-                               if (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]))
-                               {
-                                       $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0];
-                               }
-                               elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]))
-                               {
-                                       $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0];
-                               }
-                               elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]))
-                               {
-                                       $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0];
-                               }
-                               elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]))
-                               {
-                                       $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0];
-                               }
-                               else
-                               {
-                                       $feed = null;
-                               }
-
-                               if ($feed !== null)
-                               {
-                                       $sql = 'SELECT `data` FROM `' . $this->options['prefix'][0] . 'items` WHERE `feed_id` = \'' . mysql_real_escape_string($this->id) . '\' ORDER BY `posted` DESC';
-                                       if ($items > 0)
-                                       {
-                                               $sql .= ' LIMIT ' . $items;
-                                       }
-
-                                       if ($query = mysql_unbuffered_query($sql, $this->mysql))
-                                       {
-                                               while ($row = mysql_fetch_row($query))
-                                               {
-                                                       $feed['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'][] = unserialize($row[0]);
-                                               }
-                                       }
-                                       else
-                                       {
-                                               return false;
-                                       }
-                               }
-                       }
-                       return $data;
-               }
-               return false;
-       }
-
-       function mtime()
-       {
-               if ($this->mysql && ($query = mysql_query('SELECT `mtime` FROM `' . $this->options['prefix'][0] . 'cache_data` WHERE `id` = \'' . mysql_real_escape_string($this->id) . "'", $this->mysql)) && ($row = mysql_fetch_row($query)))
-               {
-                       return $row[0];
-               }
-               else
-               {
-                       return false;
-               }
-       }
-
-       function touch()
-       {
-               if ($this->mysql && ($query = mysql_query('UPDATE `' . $this->options['prefix'][0] . 'cache_data` SET `mtime` = ' . time() . ' WHERE `id` = \'' . mysql_real_escape_string($this->id) . "'", $this->mysql)) && mysql_affected_rows($this->mysql))
-               {
-                       return true;
-               }
-               else
-               {
-                       return false;
-               }
-       }
-
-       function unlink()
-       {
-               if ($this->mysql && ($query = mysql_query('DELETE FROM `' . $this->options['prefix'][0] . 'cache_data` WHERE `id` = \'' . mysql_real_escape_string($this->id) . "'", $this->mysql)) && ($query2 = mysql_query('DELETE FROM `' . $this->options['prefix'][0] . 'items` WHERE `feed_id` = \'' . mysql_real_escape_string($this->id) . "'", $this->mysql)))
-               {
-                       return true;
-               }
-               else
-               {
-                       return false;
-               }
-       }
-}
-
-class SimplePie_Misc
-{
-       function time_hms($seconds)
-       {
-               $time = '';
-
-               $hours = floor($seconds / 3600);
-               $remainder = $seconds % 3600;
-               if ($hours > 0)
-               {
-                       $time .= $hours.':';
-               }
-
-               $minutes = floor($remainder / 60);
-               $seconds = $remainder % 60;
-               if ($minutes < 10 && $hours > 0)
-               {
-                       $minutes = '0' . $minutes;
-               }
-               if ($seconds < 10)
-               {
-                       $seconds = '0' . $seconds;
-               }
-
-               $time .= $minutes.':';
-               $time .= $seconds;
-
-               return $time;
-       }
-
-       function absolutize_url($relative, $base)
-       {
-return $relative;
-               $iri = SimplePie_IRI::absolutize(new SimplePie_IRI($base), $relative);
-               return $iri->get_iri();
-       }
-
-       function remove_dot_segments($input)
-       {
-               $output = '';
-               while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..')
-               {
-                       // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise,
-                       if (strpos($input, '../') === 0)
-                       {
-                               $input = substr($input, 3);
-                       }
-                       elseif (strpos($input, './') === 0)
-                       {
-                               $input = substr($input, 2);
-                       }
-                       // B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise,
-                       elseif (strpos($input, '/./') === 0)
-                       {
-                               $input = substr_replace($input, '/', 0, 3);
-                       }
-                       elseif ($input === '/.')
-                       {
-                               $input = '/';
-                       }
-                       // C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise,
-                       elseif (strpos($input, '/../') === 0)
-                       {
-                               $input = substr_replace($input, '/', 0, 4);
-                               $output = substr_replace($output, '', strrpos($output, '/'));
-                       }
-                       elseif ($input === '/..')
-                       {
-                               $input = '/';
-                               $output = substr_replace($output, '', strrpos($output, '/'));
-                       }
-                       // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise,
-                       elseif ($input === '.' || $input === '..')
-                       {
-                               $input = '';
-                       }
-                       // E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer
-                       elseif (($pos = strpos($input, '/', 1)) !== false)
-                       {
-                               $output .= substr($input, 0, $pos);
-                               $input = substr_replace($input, '', 0, $pos);
-                       }
-                       else
-                       {
-                               $output .= $input;
-                               $input = '';
-                       }
-               }
-               return $output . $input;
-       }
-
-       function get_element($realname, $string)
-       {
-               $return = array();
-               $name = preg_quote($realname, '/');
-               if (preg_match_all("/<($name)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$name>|(\/)?>)/siU", $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE))
-               {
-                       for ($i = 0, $total_matches = count($matches); $i < $total_matches; $i++)
-                       {
-                               $return[$i]['tag'] = $realname;
-                               $return[$i]['full'] = $matches[$i][0][0];
-                               $return[$i]['offset'] = $matches[$i][0][1];
-                               if (strlen($matches[$i][3][0]) <= 2)
-                               {
-                                       $return[$i]['self_closing'] = true;
-                               }
-                               else
-                               {
-                                       $return[$i]['self_closing'] = false;
-                                       $return[$i]['content'] = $matches[$i][4][0];
-                               }
-                               $return[$i]['attribs'] = array();
-                               if (isset($matches[$i][2][0]) && preg_match_all('/[\x09\x0A\x0B\x0C\x0D\x20]+([^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*)(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"([^"]*)"|\'([^\']*)\'|([^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?/', ' ' . $matches[$i][2][0] . ' ', $attribs, PREG_SET_ORDER))
-                               {
-                                       for ($j = 0, $total_attribs = count($attribs); $j < $total_attribs; $j++)
-                                       {
-                                               if (count($attribs[$j]) === 2)
-                                               {
-                                                       $attribs[$j][2] = $attribs[$j][1];
-                                               }
-                                               $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]), 'UTF-8');
-                                       }
-                               }
-                       }
-               }
-               return $return;
-       }
-
-       function element_implode($element)
-       {
-               $full = "<$element[tag]";
-               foreach ($element['attribs'] as $key => $value)
-               {
-                       $key = strtolower($key);
-                       $full .= " $key=\"" . htmlspecialchars($value['data']) . '"';
-               }
-               if ($element['self_closing'])
-               {
-                       $full .= ' />';
-               }
-               else
-               {
-                       $full .= ">$element[content]</$element[tag]>";
-               }
-               return $full;
-       }
-
-       function error($message, $level, $file, $line)
-       {
-               if ((ini_get('error_reporting') & $level) > 0)
-               {
-                       switch ($level)
-                       {
-                               case E_USER_ERROR:
-                                       $note = 'PHP Error';
-                                       break;
-                               case E_USER_WARNING:
-                                       $note = 'PHP Warning';
-                                       break;
-                               case E_USER_NOTICE:
-                                       $note = 'PHP Notice';
-                                       break;
-                               default:
-                                       $note = 'Unknown Error';
-                                       break;
-                       }
-
-                       $log_error = true;
-                       if (!function_exists('error_log'))
-                       {
-                               $log_error = false;
-                       }
-
-                       $log_file = @ini_get('error_log');
-                       if (!empty($log_file) && ('syslog' != $log_file) && !@is_writable($log_file))
-                       {
-                               $log_error = false;
-                       }
-
-                       if ($log_error)
-                       {
-                               @error_log("$note: $message in $file on line $line", 0);
-                       }
-               }
-
-               return $message;
-       }
-
-       /**
-        * If a file has been cached, retrieve and display it.
-        *
-        * This is most useful for caching images (get_favicon(), etc.),
-        * however it works for all cached files.  This WILL NOT display ANY
-        * file/image/page/whatever, but rather only display what has already
-        * been cached by SimplePie.
-        *
-        * @access public
-        * @see SimplePie::get_favicon()
-        * @param str $identifier_url URL that is used to identify the content.
-        * This may or may not be the actual URL of the live content.
-        * @param str $cache_location Location of SimplePie's cache.  Defaults
-        * to './cache'.
-        * @param str $cache_extension The file extension that the file was
-        * cached with.  Defaults to 'spc'.
-        * @param str $cache_class Name of the cache-handling class being used
-        * in SimplePie.  Defaults to 'SimplePie_Cache', and should be left
-        * as-is unless you've overloaded the class.
-        * @param str $cache_name_function Obsolete. Exists for backwards
-        * compatibility reasons only.
-        */
-       function display_cached_file($identifier_url, $cache_location = './cache', $cache_extension = 'spc', $cache_class = 'SimplePie_Cache', $cache_name_function = 'md5')
-       {
-               $cache = call_user_func(array($cache_class, 'create'), $cache_location, $identifier_url, $cache_extension);
-
-               if ($file = $cache->load())
-               {
-                       if (isset($file['headers']['content-type']))
-                       {
-                               header('Content-type:' . $file['headers']['content-type']);
-                       }
-                       else
-                       {
-                               header('Content-type: application/octet-stream');
-                       }
-                       header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days
-                       echo $file['body'];
-                       exit;
-               }
-
-               die('Cached file for ' . $identifier_url . ' cannot be found.');
-       }
-
-       function fix_protocol($url, $http = 1)
-       {
-               $url = SimplePie_Misc::normalize_url($url);
-               $parsed = SimplePie_Misc::parse_url($url);
-               if ($parsed['scheme'] !== '' && $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https')
-               {
-                       return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['authority'], $parsed['path'], $parsed['query'], $parsed['fragment']), $http);
-               }
-
-               if ($parsed['scheme'] === '' && $parsed['authority'] === '' && !file_exists($url))
-               {
-                       return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['path'], '', $parsed['query'], $parsed['fragment']), $http);
-               }
-
-               if ($http === 2 && $parsed['scheme'] !== '')
-               {
-                       return "feed:$url";
-               }
-               elseif ($http === 3 && strtolower($parsed['scheme']) === 'http')
-               {
-                       return substr_replace($url, 'podcast', 0, 4);
-               }
-               elseif ($http === 4 && strtolower($parsed['scheme']) === 'http')
-               {
-                       return substr_replace($url, 'itpc', 0, 4);
-               }
-               else
-               {
-                       return $url;
-               }
-       }
-
-       function parse_url($url)
-       {
-               $iri = new SimplePie_IRI($url);
-               return array(
-                       'scheme' => (string) $iri->get_scheme(),
-                       'authority' => (string) $iri->get_authority(),
-                       'path' => (string) $iri->get_path(),
-                       'query' => (string) $iri->get_query(),
-                       'fragment' => (string) $iri->get_fragment()
-               );
-       }
-
-       function compress_parse_url($scheme = '', $authority = '', $path = '', $query = '', $fragment = '')
-       {
-               $iri = new SimplePie_IRI('');
-               $iri->set_scheme($scheme);
-               $iri->set_authority($authority);
-               $iri->set_path($path);
-               $iri->set_query($query);
-               $iri->set_fragment($fragment);
-               return $iri->get_iri();
-       }
-
-       function normalize_url($url)
-       {
-               $iri = new SimplePie_IRI($url);
-               return $iri->get_iri();
-       }
-
-       function percent_encoding_normalization($match)
-       {
-               $integer = hexdec($match[1]);
-               if ($integer >= 0x41 && $integer <= 0x5A || $integer >= 0x61 && $integer <= 0x7A || $integer >= 0x30 && $integer <= 0x39 || $integer === 0x2D || $integer === 0x2E || $integer === 0x5F || $integer === 0x7E)
-               {
-                       return chr($integer);
-               }
-               else
-               {
-                       return strtoupper($match[0]);
-               }
-       }
-
-       /**
-        * Remove bad UTF-8 bytes
-        *
-        * PCRE Pattern to locate bad bytes in a UTF-8 string comes from W3C
-        * FAQ: Multilingual Forms (modified to include full ASCII range)
-        *
-        * @author Geoffrey Sneddon
-        * @see http://www.w3.org/International/questions/qa-forms-utf-8
-        * @param string $str String to remove bad UTF-8 bytes from
-        * @return string UTF-8 string
-        */
-       function utf8_bad_replace($str)
-       {
-               if (function_exists('iconv') && ($return = @iconv('UTF-8', 'UTF-8//IGNORE', $str)))
-               {
-                       return $return;
-               }
-               elseif (function_exists('mb_convert_encoding') && ($return = @mb_convert_encoding($str, 'UTF-8', 'UTF-8')))
-               {
-                       return $return;
-               }
-               elseif (preg_match_all('/(?:[\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})+/', $str, $matches))
-               {
-                       return implode("\xEF\xBF\xBD", $matches[0]);
-               }
-               elseif ($str !== '')
-               {
-                       return "\xEF\xBF\xBD";
-               }
-               else
-               {
-                       return '';
-               }
-       }
-
-       /**
-        * Converts a Windows-1252 encoded string to a UTF-8 encoded string
-        *
-        * @static
-        * @access public
-        * @param string $string Windows-1252 encoded string
-        * @return string UTF-8 encoded string
-        */
-       function windows_1252_to_utf8($string)
-       {
-               static $convert_table = array("\x80" => "\xE2\x82\xAC", "\x81" => "\xEF\xBF\xBD", "\x82" => "\xE2\x80\x9A", "\x83" => "\xC6\x92", "\x84" => "\xE2\x80\x9E", "\x85" => "\xE2\x80\xA6", "\x86" => "\xE2\x80\xA0", "\x87" => "\xE2\x80\xA1", "\x88" => "\xCB\x86", "\x89" => "\xE2\x80\xB0", "\x8A" => "\xC5\xA0", "\x8B" => "\xE2\x80\xB9", "\x8C" => "\xC5\x92", "\x8D" => "\xEF\xBF\xBD", "\x8E" => "\xC5\xBD", "\x8F" => "\xEF\xBF\xBD", "\x90" => "\xEF\xBF\xBD", "\x91" => "\xE2\x80\x98", "\x92" => "\xE2\x80\x99", "\x93" => "\xE2\x80\x9C", "\x94" => "\xE2\x80\x9D", "\x95" => "\xE2\x80\xA2", "\x96" => "\xE2\x80\x93", "\x97" => "\xE2\x80\x94", "\x98" => "\xCB\x9C", "\x99" => "\xE2\x84\xA2", "\x9A" => "\xC5\xA1", "\x9B" => "\xE2\x80\xBA", "\x9C" => "\xC5\x93", "\x9D" => "\xEF\xBF\xBD", "\x9E" => "\xC5\xBE", "\x9F" => "\xC5\xB8", "\xA0" => "\xC2\xA0", "\xA1" => "\xC2\xA1", "\xA2" => "\xC2\xA2", "\xA3" => "\xC2\xA3", "\xA4" => "\xC2\xA4", "\xA5" => "\xC2\xA5", "\xA6" => "\xC2\xA6", "\xA7" => "\xC2\xA7", "\xA8" => "\xC2\xA8", "\xA9" => "\xC2\xA9", "\xAA" => "\xC2\xAA", "\xAB" => "\xC2\xAB", "\xAC" => "\xC2\xAC", "\xAD" => "\xC2\xAD", "\xAE" => "\xC2\xAE", "\xAF" => "\xC2\xAF", "\xB0" => "\xC2\xB0", "\xB1" => "\xC2\xB1", "\xB2" => "\xC2\xB2", "\xB3" => "\xC2\xB3", "\xB4" => "\xC2\xB4", "\xB5" => "\xC2\xB5", "\xB6" => "\xC2\xB6", "\xB7" => "\xC2\xB7", "\xB8" => "\xC2\xB8", "\xB9" => "\xC2\xB9", "\xBA" => "\xC2\xBA", "\xBB" => "\xC2\xBB", "\xBC" => "\xC2\xBC", "\xBD" => "\xC2\xBD", "\xBE" => "\xC2\xBE", "\xBF" => "\xC2\xBF", "\xC0" => "\xC3\x80", "\xC1" => "\xC3\x81", "\xC2" => "\xC3\x82", "\xC3" => "\xC3\x83", "\xC4" => "\xC3\x84", "\xC5" => "\xC3\x85", "\xC6" => "\xC3\x86", "\xC7" => "\xC3\x87", "\xC8" => "\xC3\x88", "\xC9" => "\xC3\x89", "\xCA" => "\xC3\x8A", "\xCB" => "\xC3\x8B", "\xCC" => "\xC3\x8C", "\xCD" => "\xC3\x8D", "\xCE" => "\xC3\x8E", "\xCF" => "\xC3\x8F", "\xD0" => "\xC3\x90", "\xD1" => "\xC3\x91", "\xD2" => "\xC3\x92", "\xD3" => "\xC3\x93", "\xD4" => "\xC3\x94", "\xD5" => "\xC3\x95", "\xD6" => "\xC3\x96", "\xD7" => "\xC3\x97", "\xD8" => "\xC3\x98", "\xD9" => "\xC3\x99", "\xDA" => "\xC3\x9A", "\xDB" => "\xC3\x9B", "\xDC" => "\xC3\x9C", "\xDD" => "\xC3\x9D", "\xDE" => "\xC3\x9E", "\xDF" => "\xC3\x9F", "\xE0" => "\xC3\xA0", "\xE1" => "\xC3\xA1", "\xE2" => "\xC3\xA2", "\xE3" => "\xC3\xA3", "\xE4" => "\xC3\xA4", "\xE5" => "\xC3\xA5", "\xE6" => "\xC3\xA6", "\xE7" => "\xC3\xA7", "\xE8" => "\xC3\xA8", "\xE9" => "\xC3\xA9", "\xEA" => "\xC3\xAA", "\xEB" => "\xC3\xAB", "\xEC" => "\xC3\xAC", "\xED" => "\xC3\xAD", "\xEE" => "\xC3\xAE", "\xEF" => "\xC3\xAF", "\xF0" => "\xC3\xB0", "\xF1" => "\xC3\xB1", "\xF2" => "\xC3\xB2", "\xF3" => "\xC3\xB3", "\xF4" => "\xC3\xB4", "\xF5" => "\xC3\xB5", "\xF6" => "\xC3\xB6", "\xF7" => "\xC3\xB7", "\xF8" => "\xC3\xB8", "\xF9" => "\xC3\xB9", "\xFA" => "\xC3\xBA", "\xFB" => "\xC3\xBB", "\xFC" => "\xC3\xBC", "\xFD" => "\xC3\xBD", "\xFE" => "\xC3\xBE", "\xFF" => "\xC3\xBF");
-
-               return strtr($string, $convert_table);
-       }
-
-       function change_encoding($data, $input, $output)
-       {
-               $input = SimplePie_Misc::encoding($input);
-               $output = SimplePie_Misc::encoding($output);
-
-               // We fail to fail on non US-ASCII bytes
-               if ($input === 'US-ASCII')
-               {
-                       static $non_ascii_octects = '';
-                       if (!$non_ascii_octects)
-                       {
-                               for ($i = 0x80; $i <= 0xFF; $i++)
-                               {
-                                       $non_ascii_octects .= chr($i);
-                               }
-                       }
-                       $data = substr($data, 0, strcspn($data, $non_ascii_octects));
-               }
-
-               // This is first, as behaviour of this is completely predictable
-               if ($input === 'Windows-1252' && $output === 'UTF-8')
-               {
-                       return SimplePie_Misc::windows_1252_to_utf8($data);
-               }
-               // This is second, as behaviour of this varies only with PHP version (the middle part of this expression checks the encoding is supported).
-               elseif (function_exists('mb_convert_encoding') && @mb_convert_encoding("\x80", 'UTF-16BE', $input) !== "\x00\x80" && ($return = @mb_convert_encoding($data, $output, $input)))
-               {
-                       return $return;
-               }
-               // This is last, as behaviour of this varies with OS userland and PHP version
-               elseif (function_exists('iconv') && ($return = @iconv($input, $output, $data)))
-               {
-                       return $return;
-               }
-               // If we can't do anything, just fail
-               else
-               {
-                       return false;
-               }
-       }
-
-       function encoding($charset)
-       {
-               // Normalization from UTS #22
-               switch (strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\1', $charset)))
-               {
-                       case 'adobestandardencoding':
-                       case 'csadobestandardencoding':
-                               return 'Adobe-Standard-Encoding';
-
-                       case 'adobesymbolencoding':
-                       case 'cshppsmath':
-                               return 'Adobe-Symbol-Encoding';
-
-                       case 'ami1251':
-                       case 'amiga1251':
-                               return 'Amiga-1251';
-
-                       case 'ansix31101983':
-                       case 'csat5001983':
-                       case 'csiso99naplps':
-                       case 'isoir99':
-                       case 'naplps':
-                               return 'ANSI_X3.110-1983';
-
-                       case 'arabic7':
-                       case 'asmo449':
-                       case 'csiso89asmo449':
-                       case 'iso9036':
-                       case 'isoir89':
-                               return 'ASMO_449';
-
-                       case 'big5':
-                       case 'csbig5':
-                       case 'xxbig5':
-                               return 'Big5';
-
-                       case 'big5hkscs':
-                               return 'Big5-HKSCS';
-
-                       case 'bocu1':
-                       case 'csbocu1':
-                               return 'BOCU-1';
-
-                       case 'brf':
-                       case 'csbrf':
-                               return 'BRF';
-
-                       case 'bs4730':
-                       case 'csiso4unitedkingdom':
-                       case 'gb':
-                       case 'iso646gb':
-                       case 'isoir4':
-                       case 'uk':
-                               return 'BS_4730';
-
-                       case 'bsviewdata':
-                       case 'csiso47bsviewdata':
-                       case 'isoir47':
-                               return 'BS_viewdata';
-
-                       case 'cesu8':
-                       case 'cscesu8':
-                               return 'CESU-8';
-
-                       case 'ca':
-                       case 'csa71':
-                       case 'csaz243419851':
-                       case 'csiso121canadian1':
-                       case 'iso646ca':
-                       case 'isoir121':
-                               return 'CSA_Z243.4-1985-1';
-
-                       case 'csa72':
-                       case 'csaz243419852':
-                       case 'csiso122canadian2':
-                       case 'iso646ca2':
-                       case 'isoir122':
-                               return 'CSA_Z243.4-1985-2';
-
-                       case 'csaz24341985gr':
-                       case 'csiso123csaz24341985gr':
-                       case 'isoir123':
-                               return 'CSA_Z243.4-1985-gr';
-
-                       case 'csiso139csn369103':
-                       case 'csn369103':
-                       case 'isoir139':
-                               return 'CSN_369103';
-
-                       case 'csdecmcs':
-                       case 'dec':
-                       case 'decmcs':
-                               return 'DEC-MCS';
-
-                       case 'csiso21german':
-                       case 'de':
-                       case 'din66003':
-                       case 'iso646de':
-                       case 'isoir21':
-                               return 'DIN_66003';
-
-                       case 'csdkus':
-                       case 'dkus':
-                               return 'dk-us';
-
-                       case 'csiso646danish':
-                       case 'dk':
-                       case 'ds2089':
-                       case 'iso646dk':
-                               return 'DS_2089';
-
-                       case 'csibmebcdicatde':
-                       case 'ebcdicatde':
-                               return 'EBCDIC-AT-DE';
-
-                       case 'csebcdicatdea':
-                       case 'ebcdicatdea':
-                               return 'EBCDIC-AT-DE-A';
-
-                       case 'csebcdiccafr':
-                       case 'ebcdiccafr':
-                               return 'EBCDIC-CA-FR';
-
-                       case 'csebcdicdkno':
-                       case 'ebcdicdkno':
-                               return 'EBCDIC-DK-NO';
-
-                       case 'csebcdicdknoa':
-                       case 'ebcdicdknoa':
-                               return 'EBCDIC-DK-NO-A';
-
-                       case 'csebcdices':
-                       case 'ebcdices':
-                               return 'EBCDIC-ES';
-
-                       case 'csebcdicesa':
-                       case 'ebcdicesa':
-                               return 'EBCDIC-ES-A';
-
-                       case 'csebcdicess':
-                       case 'ebcdicess':
-                               return 'EBCDIC-ES-S';
-
-                       case 'csebcdicfise':
-                       case 'ebcdicfise':
-                               return 'EBCDIC-FI-SE';
-
-                       case 'csebcdicfisea':
-                       case 'ebcdicfisea':
-                               return 'EBCDIC-FI-SE-A';
-
-                       case 'csebcdicfr':
-                       case 'ebcdicfr':
-                               return 'EBCDIC-FR';
-
-                       case 'csebcdicit':
-                       case 'ebcdicit':
-                               return 'EBCDIC-IT';
-
-                       case 'csebcdicpt':
-                       case 'ebcdicpt':
-                               return 'EBCDIC-PT';
-
-                       case 'csebcdicuk':
-                       case 'ebcdicuk':
-                               return 'EBCDIC-UK';
-
-                       case 'csebcdicus':
-                       case 'ebcdicus':
-                               return 'EBCDIC-US';
-
-                       case 'csiso111ecmacyrillic':
-                       case 'ecmacyrillic':
-                       case 'isoir111':
-                       case 'koi8e':
-                               return 'ECMA-cyrillic';
-
-                       case 'csiso17spanish':
-                       case 'es':
-                       case 'iso646es':
-                       case 'isoir17':
-                               return 'ES';
-
-                       case 'csiso85spanish2':
-                       case 'es2':
-                       case 'iso646es2':
-                       case 'isoir85':
-                               return 'ES2';
-
-                       case 'cseucfixwidjapanese':
-                       case 'extendedunixcodefixedwidthforjapanese':
-                               return 'Extended_UNIX_Code_Fixed_Width_for_Japanese';
-
-                       case 'cseucpkdfmtjapanese':
-                       case 'eucjp':
-                       case 'extendedunixcodepackedformatforjapanese':
-                               return 'Extended_UNIX_Code_Packed_Format_for_Japanese';
-
-                       case 'gb18030':
-                               return 'GB18030';
-
-                       case 'chinese':
-                       case 'cp936':
-                       case 'csgb2312':
-                       case 'csiso58gb231280':
-                       case 'gb2312':
-                       case 'gb231280':
-                       case 'gbk':
-                       case 'isoir58':
-                       case 'ms936':
-                       case 'windows936':
-                               return 'GBK';
-
-                       case 'cn':
-                       case 'csiso57gb1988':
-                       case 'gb198880':
-                       case 'iso646cn':
-                       case 'isoir57':
-                               return 'GB_1988-80';
-
-                       case 'csiso153gost1976874':
-                       case 'gost1976874':
-                       case 'isoir153':
-                       case 'stsev35888':
-                               return 'GOST_19768-74';
-
-                       case 'csiso150':
-                       case 'csiso150greekccitt':
-                       case 'greekccitt':
-                       case 'isoir150':
-                               return 'greek-ccitt';
-
-                       case 'csiso88greek7':
-                       case 'greek7':
-                       case 'isoir88':
-                               return 'greek7';
-
-                       case 'csiso18greek7old':
-                       case 'greek7old':
-                       case 'isoir18':
-                               return 'greek7-old';
-
-                       case 'cshpdesktop':
-                       case 'hpdesktop':
-                               return 'HP-DeskTop';
-
-                       case 'cshplegal':
-                       case 'hplegal':
-                               return 'HP-Legal';
-
-                       case 'cshpmath8':
-                       case 'hpmath8':
-                               return 'HP-Math8';
-
-                       case 'cshppifont':
-                       case 'hppifont':
-                               return 'HP-Pi-font';
-
-                       case 'cshproman8':
-                       case 'hproman8':
-                       case 'r8':
-                       case 'roman8':
-                               return 'hp-roman8';
-
-                       case 'hzgb2312':
-                               return 'HZ-GB-2312';
-
-                       case 'csibmsymbols':
-                       case 'ibmsymbols':
-                               return 'IBM-Symbols';
-
-                       case 'csibmthai':
-                       case 'ibmthai':
-                               return 'IBM-Thai';
-
-                       case 'ccsid858':
-                       case 'cp858':
-                       case 'ibm858':
-                       case 'pcmultilingual850euro':
-                               return 'IBM00858';
-
-                       case 'ccsid924':
-                       case 'cp924':
-                       case 'ebcdiclatin9euro':
-                       case 'ibm924':
-                               return 'IBM00924';
-
-                       case 'ccsid1140':
-                       case 'cp1140':
-                       case 'ebcdicus37euro':
-                       case 'ibm1140':
-                               return 'IBM01140';
-
-                       case 'ccsid1141':
-                       case 'cp1141':
-                       case 'ebcdicde273euro':
-                       case 'ibm1141':
-                               return 'IBM01141';
-
-                       case 'ccsid1142':
-                       case 'cp1142':
-                       case 'ebcdicdk277euro':
-                       case 'ebcdicno277euro':
-                       case 'ibm1142':
-                               return 'IBM01142';
-
-                       case 'ccsid1143':
-                       case 'cp1143':
-                       case 'ebcdicfi278euro':
-                       case 'ebcdicse278euro':
-                       case 'ibm1143':
-                               return 'IBM01143';
-
-                       case 'ccsid1144':
-                       case 'cp1144':
-                       case 'ebcdicit280euro':
-                       case 'ibm1144':
-                               return 'IBM01144';
-
-                       case 'ccsid1145':
-                       case 'cp1145':
-                       case 'ebcdices284euro':
-                       case 'ibm1145':
-                               return 'IBM01145';
-
-                       case 'ccsid1146':
-                       case 'cp1146':
-                       case 'ebcdicgb285euro':
-                       case 'ibm1146':
-                               return 'IBM01146';
-
-                       case 'ccsid1147':
-                       case 'cp1147':
-                       case 'ebcdicfr297euro':
-                       case 'ibm1147':
-                               return 'IBM01147';
-
-                       case 'ccsid1148':
-                       case 'cp1148':
-                       case 'ebcdicinternational500euro':
-                       case 'ibm1148':
-                               return 'IBM01148';
-
-                       case 'ccsid1149':
-                       case 'cp1149':
-                       case 'ebcdicis871euro':
-                       case 'ibm1149':
-                               return 'IBM01149';
-
-                       case 'cp37':
-                       case 'csibm37':
-                       case 'ebcdiccpca':
-                       case 'ebcdiccpnl':
-                       case 'ebcdiccpus':
-                       case 'ebcdiccpwt':
-                       case 'ibm37':
-                               return 'IBM037';
-
-                       case 'cp38':
-                       case 'csibm38':
-                       case 'ebcdicint':
-                       case 'ibm38':
-                               return 'IBM038';
-
-                       case 'cp273':
-                       case 'csibm273':
-                       case 'ibm273':
-                               return 'IBM273';
-
-                       case 'cp274':
-                       case 'csibm274':
-                       case 'ebcdicbe':
-                       case 'ibm274':
-                               return 'IBM274';
-
-                       case 'cp275':
-                       case 'csibm275':
-                       case 'ebcdicbr':
-                       case 'ibm275':
-                               return 'IBM275';
-
-                       case 'csibm277':
-                       case 'ebcdiccpdk':
-                       case 'ebcdiccpno':
-                       case 'ibm277':
-                               return 'IBM277';
-
-                       case 'cp278':
-                       case 'csibm278':
-                       case 'ebcdiccpfi':
-                       case 'ebcdiccpse':
-                       case 'ibm278':
-                               return 'IBM278';
-
-                       case 'cp280':
-                       case 'csibm280':
-                       case 'ebcdiccpit':
-                       case 'ibm280':
-                               return 'IBM280';
-
-                       case 'cp281':
-                       case 'csibm281':
-                       case 'ebcdicjpe':
-                       case 'ibm281':
-                               return 'IBM281';
-
-                       case 'cp284':
-                       case 'csibm284':
-                       case 'ebcdiccpes':
-                       case 'ibm284':
-                               return 'IBM284';
-
-                       case 'cp285':
-                       case 'csibm285':
-                       case 'ebcdiccpgb':
-                       case 'ibm285':
-                               return 'IBM285';
-
-                       case 'cp290':
-                       case 'csibm290':
-                       case 'ebcdicjpkana':
-                       case 'ibm290':
-                               return 'IBM290';
-
-                       case 'cp297':
-                       case 'csibm297':
-                       case 'ebcdiccpfr':
-                       case 'ibm297':
-                               return 'IBM297';
-
-                       case 'cp420':
-                       case 'csibm420':
-                       case 'ebcdiccpar1':
-                       case 'ibm420':
-                               return 'IBM420';
-
-                       case 'cp423':
-                       case 'csibm423':
-                       case 'ebcdiccpgr':
-                       case 'ibm423':
-                               return 'IBM423';
-
-                       case 'cp424':
-                       case 'csibm424':
-                       case 'ebcdiccphe':
-                       case 'ibm424':
-                               return 'IBM424';
-
-                       case '437':
-                       case 'cp437':
-                       case 'cspc8codepage437':
-                       case 'ibm437':
-                               return 'IBM437';
-
-                       case 'cp500':
-                       case 'csibm500':
-                       case 'ebcdiccpbe':
-                       case 'ebcdiccpch':
-                       case 'ibm500':
-                               return 'IBM500';
-
-                       case 'cp775':
-                       case 'cspc775baltic':
-                       case 'ibm775':
-                               return 'IBM775';
-
-                       case '850':
-                       case 'cp850':
-                       case 'cspc850multilingual':
-                       case 'ibm850':
-                               return 'IBM850';
-
-                       case '851':
-                       case 'cp851':
-                       case 'csibm851':
-                       case 'ibm851':
-                               return 'IBM851';
-
-                       case '852':
-                       case 'cp852':
-                       case 'cspcp852':
-                       case 'ibm852':
-                               return 'IBM852';
-
-                       case '855':
-                       case 'cp855':
-                       case 'csibm855':
-                       case 'ibm855':
-                               return 'IBM855';
-
-                       case '857':
-                       case 'cp857':
-                       case 'csibm857':
-                       case 'ibm857':
-                               return 'IBM857';
-
-                       case '860':
-                       case 'cp860':
-                       case 'csibm860':
-                       case 'ibm860':
-                               return 'IBM860';
-
-                       case '861':
-                       case 'cp861':
-                       case 'cpis':
-                       case 'csibm861':
-                       case 'ibm861':
-                               return 'IBM861';
-
-                       case '862':
-                       case 'cp862':
-                       case 'cspc862latinhebrew':
-                       case 'ibm862':
-                               return 'IBM862';
-
-                       case '863':
-                       case 'cp863':
-                       case 'csibm863':
-                       case 'ibm863':
-                               return 'IBM863';
-
-                       case 'cp864':
-                       case 'csibm864':
-                       case 'ibm864':
-                               return 'IBM864';
-
-                       case '865':
-                       case 'cp865':
-                       case 'csibm865':
-                       case 'ibm865':
-                               return 'IBM865';
-
-                       case '866':
-                       case 'cp866':
-                       case 'csibm866':
-                       case 'ibm866':
-                               return 'IBM866';
-
-                       case 'cp868':
-                       case 'cpar':
-                       case 'csibm868':
-                       case 'ibm868':
-                               return 'IBM868';
-
-                       case '869':
-                       case 'cp869':
-                       case 'cpgr':
-                       case 'csibm869':
-                       case 'ibm869':
-                               return 'IBM869';
-
-                       case 'cp870':
-                       case 'csibm870':
-                       case 'ebcdiccproece':
-                       case 'ebcdiccpyu':
-                       case 'ibm870':
-                               return 'IBM870';
-
-                       case 'cp871':
-                       case 'csibm871':
-                       case 'ebcdiccpis':
-                       case 'ibm871':
-                               return 'IBM871';
-
-                       case 'cp880':
-                       case 'csibm880':
-                       case 'ebcdiccyrillic':
-                       case 'ibm880':
-                               return 'IBM880';
-
-                       case 'cp891':
-                       case 'csibm891':
-                       case 'ibm891':
-                               return 'IBM891';
-
-                       case 'cp903':
-                       case 'csibm903':
-                       case 'ibm903':
-                               return 'IBM903';
-
-                       case '904':
-                       case 'cp904':
-                       case 'csibbm904':
-                       case 'ibm904':
-                               return 'IBM904';
-
-                       case 'cp905':
-                       case 'csibm905':
-                       case 'ebcdiccptr':
-                       case 'ibm905':
-                               return 'IBM905';
-
-                       case 'cp918':
-                       case 'csibm918':
-                       case 'ebcdiccpar2':
-                       case 'ibm918':
-                               return 'IBM918';
-
-                       case 'cp1026':
-                       case 'csibm1026':
-                       case 'ibm1026':
-                               return 'IBM1026';
-
-                       case 'ibm1047':
-                               return 'IBM1047';
-
-                       case 'csiso143iecp271':
-                       case 'iecp271':
-                       case 'isoir143':
-                               return 'IEC_P27-1';
-
-                       case 'csiso49inis':
-                       case 'inis':
-                       case 'isoir49':
-                               return 'INIS';
-
-                       case 'csiso50inis8':
-                       case 'inis8':
-                       case 'isoir50':
-                               return 'INIS-8';
-
-                       case 'csiso51iniscyrillic':
-                       case 'iniscyrillic':
-                       case 'isoir51':
-                               return 'INIS-cyrillic';
-
-                       case 'csinvariant':
-                       case 'invariant':
-                               return 'INVARIANT';
-
-                       case 'iso2022cn':
-                               return 'ISO-2022-CN';
-
-                       case 'iso2022cnext':
-                               return 'ISO-2022-CN-EXT';
-
-                       case 'csiso2022jp':
-                       case 'iso2022jp':
-                               return 'ISO-2022-JP';
-
-                       case 'csiso2022jp2':
-                       case 'iso2022jp2':
-                               return 'ISO-2022-JP-2';
-
-                       case 'csiso2022kr':
-                       case 'iso2022kr':
-                               return 'ISO-2022-KR';
-
-                       case 'cswindows30latin1':
-                       case 'iso88591windows30latin1':
-                               return 'ISO-8859-1-Windows-3.0-Latin-1';
-
-                       case 'cswindows31latin1':
-                       case 'iso88591windows31latin1':
-                               return 'ISO-8859-1-Windows-3.1-Latin-1';
-
-                       case 'csisolatin2':
-                       case 'iso88592':
-                       case 'iso885921987':
-                       case 'isoir101':
-                       case 'l2':
-                       case 'latin2':
-                               return 'ISO-8859-2';
-
-                       case 'cswindows31latin2':
-                       case 'iso88592windowslatin2':
-                               return 'ISO-8859-2-Windows-Latin-2';
-
-                       case 'csisolatin3':
-                       case 'iso88593':
-                       case 'iso885931988':
-                       case 'isoir109':
-                       case 'l3':
-                       case 'latin3':
-                               return 'ISO-8859-3';
-
-                       case 'csisolatin4':
-                       case 'iso88594':
-                       case 'iso885941988':
-                       case 'isoir110':
-                       case 'l4':
-                       case 'latin4':
-                               return 'ISO-8859-4';
-
-                       case 'csisolatincyrillic':
-                       case 'cyrillic':
-                       case 'iso88595':
-                       case 'iso885951988':
-                       case 'isoir144':
-                               return 'ISO-8859-5';
-
-                       case 'arabic':
-                       case 'asmo708':
-                       case 'csisolatinarabic':
-                       case 'ecma114':
-                       case 'iso88596':
-                       case 'iso885961987':
-                       case 'isoir127':
-                               return 'ISO-8859-6';
-
-                       case 'csiso88596e':
-                       case 'iso88596e':
-                               return 'ISO-8859-6-E';
-
-                       case 'csiso88596i':
-                       case 'iso88596i':
-                               return 'ISO-8859-6-I';
-
-                       case 'csisolatingreek':
-                       case 'ecma118':
-                       case 'elot928':
-                       case 'greek':
-                       case 'greek8':
-                       case 'iso88597':
-                       case 'iso885971987':
-                       case 'isoir126':
-                               return 'ISO-8859-7';
-
-                       case 'csisolatinhebrew':
-                       case 'hebrew':
-                       case 'iso88598':
-                       case 'iso885981988':
-                       case 'isoir138':
-                               return 'ISO-8859-8';
-
-                       case 'csiso88598e':
-                       case 'iso88598e':
-                               return 'ISO-8859-8-E';
-
-                       case 'csiso88598i':
-                       case 'iso88598i':
-                               return 'ISO-8859-8-I';
-
-                       case 'cswindows31latin5':
-                       case 'iso88599windowslatin5':
-                               return 'ISO-8859-9-Windows-Latin-5';
-
-                       case 'csisolatin6':
-                       case 'iso885910':
-                       case 'iso8859101992':
-                       case 'isoir157':
-                       case 'l6':
-                       case 'latin6':
-                               return 'ISO-8859-10';
-
-                       case 'iso885913':
-                               return 'ISO-8859-13';
-
-                       case 'iso885914':
-                       case 'iso8859141998':
-                       case 'isoceltic':
-                       case 'isoir199':
-                       case 'l8':
-                       case 'latin8':
-                               return 'ISO-8859-14';
-
-                       case 'iso885915':
-                       case 'latin9':
-                               return 'ISO-8859-15';
-
-                       case 'iso885916':
-                       case 'iso8859162001':
-                       case 'isoir226':
-                       case 'l10':
-                       case 'latin10':
-                               return 'ISO-8859-16';
-
-                       case 'iso10646j1':
-                               return 'ISO-10646-J-1';
-
-                       case 'csunicode':
-                       case 'iso10646ucs2':
-                               return 'ISO-10646-UCS-2';
-
-                       case 'csucs4':
-                       case 'iso10646ucs4':
-                               return 'ISO-10646-UCS-4';
-
-                       case 'csunicodeascii':
-                       case 'iso10646ucsbasic':
-                               return 'ISO-10646-UCS-Basic';
-
-                       case 'csunicodelatin1':
-                       case 'iso10646':
-                       case 'iso10646unicodelatin1':
-                               return 'ISO-10646-Unicode-Latin1';
-
-                       case 'csiso10646utf1':
-                       case 'iso10646utf1':
-                               return 'ISO-10646-UTF-1';
-
-                       case 'csiso115481':
-                       case 'iso115481':
-                       case 'isotr115481':
-                               return 'ISO-11548-1';
-
-                       case 'csiso90':
-                       case 'isoir90':
-                               return 'iso-ir-90';
-
-                       case 'csunicodeibm1261':
-                       case 'isounicodeibm1261':
-                               return 'ISO-Unicode-IBM-1261';
-
-                       case 'csunicodeibm1264':
-                       case 'isounicodeibm1264':
-                               return 'ISO-Unicode-IBM-1264';
-
-                       case 'csunicodeibm1265':
-                       case 'isounicodeibm1265':
-                               return 'ISO-Unicode-IBM-1265';
-
-                       case 'csunicodeibm1268':
-                       case 'isounicodeibm1268':
-                               return 'ISO-Unicode-IBM-1268';
-
-                       case 'csunicodeibm1276':
-                       case 'isounicodeibm1276':
-                               return 'ISO-Unicode-IBM-1276';
-
-                       case 'csiso646basic1983':
-                       case 'iso646basic1983':
-                       case 'ref':
-                               return 'ISO_646.basic:1983';
-
-                       case 'csiso2intlrefversion':
-                       case 'irv':
-                       case 'iso646irv1983':
-                       case 'isoir2':
-                               return 'ISO_646.irv:1983';
-
-                       case 'csiso2033':
-                       case 'e13b':
-                       case 'iso20331983':
-                       case 'isoir98':
-                               return 'ISO_2033-1983';
-
-                       case 'csiso5427cyrillic':
-                       case 'iso5427':
-                       case 'isoir37':
-                               return 'ISO_5427';
-
-                       case 'iso5427cyrillic1981':
-                       case 'iso54271981':
-                       case 'isoir54':
-                               return 'ISO_5427:1981';
-
-                       case 'csiso5428greek':
-                       case 'iso54281980':
-                       case 'isoir55':
-                               return 'ISO_5428:1980';
-
-                       case 'csiso6937add':
-                       case 'iso6937225':
-                       case 'isoir152':
-                               return 'ISO_6937-2-25';
-
-                       case 'csisotextcomm':
-                       case 'iso69372add':
-                       case 'isoir142':
-                               return 'ISO_6937-2-add';
-
-                       case 'csiso8859supp':
-                       case 'iso8859supp':
-                       case 'isoir154':
-                       case 'latin125':
-                               return 'ISO_8859-supp';
-
-                       case 'csiso10367box':
-                       case 'iso10367box':
-                       case 'isoir155':
-                               return 'ISO_10367-box';
-
-                       case 'csiso15italian':
-                       case 'iso646it':
-                       case 'isoir15':
-                       case 'it':
-                               return 'IT';
-
-                       case 'csiso13jisc6220jp':
-                       case 'isoir13':
-                       case 'jisc62201969':
-                       case 'jisc62201969jp':
-                       case 'katakana':
-                       case 'x2017':
-                               return 'JIS_C6220-1969-jp';
-
-                       case 'csiso14jisc6220ro':
-                       case 'iso646jp':
-                       case 'isoir14':
-                       case 'jisc62201969ro':
-                       case 'jp':
-                               return 'JIS_C6220-1969-ro';
-
-                       case 'csiso42jisc62261978':
-                       case 'isoir42':
-                       case 'jisc62261978':
-                               return 'JIS_C6226-1978';
-
-                       case 'csiso87jisx208':
-                       case 'isoir87':
-                       case 'jisc62261983':
-                       case 'jisx2081983':
-                       case 'x208':
-                               return 'JIS_C6226-1983';
-
-                       case 'csiso91jisc62291984a':
-                       case 'isoir91':
-                       case 'jisc62291984a':
-                       case 'jpocra':
-                               return 'JIS_C6229-1984-a';
-
-                       case 'csiso92jisc62991984b':
-                       case 'iso646jpocrb':
-                       case 'isoir92':
-                       case 'jisc62291984b':
-                       case 'jpocrb':
-                               return 'JIS_C6229-1984-b';
-
-                       case 'csiso93jis62291984badd':
-                       case 'isoir93':
-                       case 'jisc62291984badd':
-                       case 'jpocrbadd':
-                               return 'JIS_C6229-1984-b-add';
-
-                       case 'csiso94jis62291984hand':
-                       case 'isoir94':
-                       case 'jisc62291984hand':
-                       case 'jpocrhand':
-                               return 'JIS_C6229-1984-hand';
-
-                       case 'csiso95jis62291984handadd':
-                       case 'isoir95':
-                       case 'jisc62291984handadd':
-                       case 'jpocrhandadd':
-                               return 'JIS_C6229-1984-hand-add';
-
-                       case 'csiso96jisc62291984kana':
-                       case 'isoir96':
-                       case 'jisc62291984kana':
-                               return 'JIS_C6229-1984-kana';
-
-                       case 'csjisencoding':
-                       case 'jisencoding':
-                               return 'JIS_Encoding';
-
-                       case 'cshalfwidthkatakana':
-                       case 'jisx201':
-                       case 'x201':
-                               return 'JIS_X0201';
-
-                       case 'csiso159jisx2121990':
-                       case 'isoir159':
-                       case 'jisx2121990':
-                       case 'x212':
-                               return 'JIS_X0212-1990';
-
-                       case 'csiso141jusib1002':
-                       case 'iso646yu':
-                       case 'isoir141':
-                       case 'js':
-                       case 'jusib1002':
-                       case 'yu':
-                               return 'JUS_I.B1.002';
-
-                       case 'csiso147macedonian':
-                       case 'isoir147':
-                       case 'jusib1003mac':
-                       case 'macedonian':
-                               return 'JUS_I.B1.003-mac';
-
-                       case 'csiso146serbian':
-                       case 'isoir146':
-                       case 'jusib1003serb':
-                       case 'serbian':
-                               return 'JUS_I.B1.003-serb';
-
-                       case 'koi7switched':
-                               return 'KOI7-switched';
-
-                       case 'cskoi8r':
-                       case 'koi8r':
-                               return 'KOI8-R';
-
-                       case 'koi8u':
-                               return 'KOI8-U';
-
-                       case 'csksc5636':
-                       case 'iso646kr':
-                       case 'ksc5636':
-                               return 'KSC5636';
-
-                       case 'cskz1048':
-                       case 'kz1048':
-                       case 'rk1048':
-                       case 'strk10482002':
-                               return 'KZ-1048';
-
-                       case 'csiso19latingreek':
-                       case 'isoir19':
-                       case 'latingreek':
-                               return 'latin-greek';
-
-                       case 'csiso27latingreek1':
-                       case 'isoir27':
-                       case 'latingreek1':
-                               return 'Latin-greek-1';
-
-                       case 'csiso158lap':
-                       case 'isoir158':
-                       case 'lap':
-                       case 'latinlap':
-                               return 'latin-lap';
-
-                       case 'csmacintosh':
-                       case 'mac':
-                       case 'macintosh':
-                               return 'macintosh';
-
-                       case 'csmicrosoftpublishing':
-                       case 'microsoftpublishing':
-                               return 'Microsoft-Publishing';
-
-                       case 'csmnem':
-                       case 'mnem':
-                               return 'MNEM';
-
-                       case 'csmnemonic':
-                       case 'mnemonic':
-                               return 'MNEMONIC';
-
-                       case 'csiso86hungarian':
-                       case 'hu':
-                       case 'iso646hu':
-                       case 'isoir86':
-                       case 'msz77953':
-                               return 'MSZ_7795.3';
-
-                       case 'csnatsdano':
-                       case 'isoir91':
-                       case 'natsdano':
-                               return 'NATS-DANO';
-
-                       case 'csnatsdanoadd':
-                       case 'isoir92':
-                       case 'natsdanoadd':
-                               return 'NATS-DANO-ADD';
-
-                       case 'csnatssefi':
-                       case 'isoir81':
-                       case 'natssefi':
-                               return 'NATS-SEFI';
-
-                       case 'csnatssefiadd':
-                       case 'isoir82':
-                       case 'natssefiadd':
-                               return 'NATS-SEFI-ADD';
-
-                       case 'csiso151cuba':
-                       case 'cuba':
-                       case 'iso646cu':
-                       case 'isoir151':
-                       case 'ncnc1081':
-                               return 'NC_NC00-10:81';
-
-                       case 'csiso69french':
-                       case 'fr':
-                       case 'iso646fr':
-                       case 'isoir69':
-                       case 'nfz62010':
-                               return 'NF_Z_62-010';
-
-                       case 'csiso25french':
-                       case 'iso646fr1':
-                       case 'isoir25':
-                       case 'nfz620101973':
-                               return 'NF_Z_62-010_(1973)';
-
-                       case 'csiso60danishnorwegian':
-                       case 'csiso60norwegian1':
-                       case 'iso646no':
-                       case 'isoir60':
-                       case 'no':
-                       case 'ns45511':
-                               return 'NS_4551-1';
-
-                       case 'csiso61norwegian2':
-                       case 'iso646no2':
-                       case 'isoir61':
-                       case 'no2':
-                       case 'ns45512':
-                               return 'NS_4551-2';
-
-                       case 'osdebcdicdf3irv':
-                               return 'OSD_EBCDIC_DF03_IRV';
-
-                       case 'osdebcdicdf41':
-                               return 'OSD_EBCDIC_DF04_1';
-
-                       case 'osdebcdicdf415':
-                               return 'OSD_EBCDIC_DF04_15';
-
-                       case 'cspc8danishnorwegian':
-                       case 'pc8danishnorwegian':
-                               return 'PC8-Danish-Norwegian';
-
-                       case 'cspc8turkish':
-                       case 'pc8turkish':
-                               return 'PC8-Turkish';
-
-                       case 'csiso16portuguese':
-                       case 'iso646pt':
-                       case 'isoir16':
-                       case 'pt':
-                               return 'PT';
-
-                       case 'csiso84portuguese2':
-                       case 'iso646pt2':
-                       case 'isoir84':
-                       case 'pt2':
-                               return 'PT2';
-
-                       case 'cp154':
-                       case 'csptcp154':
-                       case 'cyrillicasian':
-                       case 'pt154':
-                       case 'ptcp154':
-                               return 'PTCP154';
-
-                       case 'scsu':
-                               return 'SCSU';
-
-                       case 'csiso10swedish':
-                       case 'fi':
-                       case 'iso646fi':
-                       case 'iso646se':
-                       case 'isoir10':
-                       case 'se':
-                       case 'sen850200b':
-                               return 'SEN_850200_B';
-
-                       case 'csiso11swedishfornames':
-                       case 'iso646se2':
-                       case 'isoir11':
-                       case 'se2':
-                       case 'sen850200c':
-                               return 'SEN_850200_C';
-
-                       case 'csshiftjis':
-                       case 'mskanji':
-                       case 'shiftjis':
-                               return 'Shift_JIS';
-
-                       case 'csiso102t617bit':
-                       case 'isoir102':
-                       case 't617bit':
-                               return 'T.61-7bit';
-
-                       case 'csiso103t618bit':
-                       case 'isoir103':
-                       case 't61':
-                       case 't618bit':
-                               return 'T.61-8bit';
-
-                       case 'csiso128t101g2':
-                       case 'isoir128':
-                       case 't101g2':
-                               return 'T.101-G2';
-
-                       case 'cstscii':
-                       case 'tscii':
-                               return 'TSCII';
-
-                       case 'csunicode11':
-                       case 'unicode11':
-                               return 'UNICODE-1-1';
-
-                       case 'csunicode11utf7':
-                       case 'unicode11utf7':
-                               return 'UNICODE-1-1-UTF-7';
-
-                       case 'csunknown8bit':
-                       case 'unknown8bit':
-                               return 'UNKNOWN-8BIT';
-
-                       case 'ansix341968':
-                       case 'ansix341986':
-                       case 'ascii':
-                       case 'cp367':
-                       case 'csascii':
-                       case 'ibm367':
-                       case 'iso646irv1991':
-                       case 'iso646us':
-                       case 'isoir6':
-                       case 'us':
-                       case 'usascii':
-                               return 'US-ASCII';
-
-                       case 'csusdk':
-                       case 'usdk':
-                               return 'us-dk';
-
-                       case 'utf7':
-                               return 'UTF-7';
-
-                       case 'utf8':
-                               return 'UTF-8';
-
-                       case 'utf16':
-                               return 'UTF-16';
-
-                       case 'utf16be':
-                               return 'UTF-16BE';
-
-                       case 'utf16le':
-                               return 'UTF-16LE';
-
-                       case 'utf32':
-                               return 'UTF-32';
-
-                       case 'utf32be':
-                               return 'UTF-32BE';
-
-                       case 'utf32le':
-                               return 'UTF-32LE';
-
-                       case 'csventurainternational':
-                       case 'venturainternational':
-                               return 'Ventura-International';
-
-                       case 'csventuramath':
-                       case 'venturamath':
-                               return 'Ventura-Math';
-
-                       case 'csventuraus':
-                       case 'venturaus':
-                               return 'Ventura-US';
-
-                       case 'csiso70videotexsupp1':
-                       case 'isoir70':
-                       case 'videotexsuppl':
-                               return 'videotex-suppl';
-
-                       case 'csviqr':
-                       case 'viqr':
-                               return 'VIQR';
-
-                       case 'csviscii':
-                       case 'viscii':
-                               return 'VISCII';
-
-                       case 'cswindows31j':
-                       case 'windows31j':
-                               return 'Windows-31J';
-
-                       case 'iso885911':
-                       case 'tis620':
-                               return 'windows-874';
-
-                       case 'cseuckr':
-                       case 'csksc56011987':
-                       case 'euckr':
-                       case 'isoir149':
-                       case 'korean':
-                       case 'ksc5601':
-                       case 'ksc56011987':
-                       case 'ksc56011989':
-                       case 'windows949':
-                               return 'windows-949';
-
-                       case 'windows1250':
-                               return 'windows-1250';
-
-                       case 'windows1251':
-                               return 'windows-1251';
-
-                       case 'cp819':
-                       case 'csisolatin1':
-                       case 'ibm819':
-                       case 'iso88591':
-                       case 'iso885911987':
-                       case 'isoir100':
-                       case 'l1':
-                       case 'latin1':
-                       case 'windows1252':
-                               return 'windows-1252';
-
-                       case 'windows1253':
-                               return 'windows-1253';
-
-                       case 'csisolatin5':
-                       case 'iso88599':
-                       case 'iso885991989':
-                       case 'isoir148':
-                       case 'l5':
-                       case 'latin5':
-                       case 'windows1254':
-                               return 'windows-1254';
-
-                       case 'windows1255':
-                               return 'windows-1255';
-
-                       case 'windows1256':
-                               return 'windows-1256';
-
-                       case 'windows1257':
-                               return 'windows-1257';
-
-                       case 'windows1258':
-                               return 'windows-1258';
-
-                       default:
-                               return $charset;
-               }
-       }
-
-       function get_curl_version()
-       {
-               if (is_array($curl = curl_version()))
-               {
-                       $curl = $curl['version'];
-               }
-               elseif (substr($curl, 0, 5) === 'curl/')
-               {
-                       $curl = substr($curl, 5, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 5));
-               }
-               elseif (substr($curl, 0, 8) === 'libcurl/')
-               {
-                       $curl = substr($curl, 8, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 8));
-               }
-               else
-               {
-                       $curl = 0;
-               }
-               return $curl;
-       }
-
-       function is_subclass_of($class1, $class2)
-       {
-               if (func_num_args() !== 2)
-               {
-                       trigger_error('Wrong parameter count for SimplePie_Misc::is_subclass_of()', E_USER_WARNING);
-               }
-               elseif (version_compare(PHP_VERSION, '5.0.3', '>=') || is_object($class1))
-               {
-                       return is_subclass_of($class1, $class2);
-               }
-               elseif (is_string($class1) && is_string($class2))
-               {
-                       if (class_exists($class1))
-                       {
-                               if (class_exists($class2))
-                               {
-                                       $class2 = strtolower($class2);
-                                       while ($class1 = strtolower(get_parent_class($class1)))
-                                       {
-                                               if ($class1 === $class2)
-                                               {
-                                                       return true;
-                                               }
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               trigger_error('Unknown class passed as parameter', E_USER_WARNNG);
-                       }
-               }
-               return false;
-       }
-
-       /**
-        * Strip HTML comments
-        *
-        * @access public
-        * @param string $data Data to strip comments from
-        * @return string Comment stripped string
-        */
-       function strip_comments($data)
-       {
-               $output = '';
-               while (($start = strpos($data, '<!--')) !== false)
-               {
-                       $output .= substr($data, 0, $start);
-                       if (($end = strpos($data, '-->', $start)) !== false)
-                       {
-                               $data = substr_replace($data, '', 0, $end + 3);
-                       }
-                       else
-                       {
-                               $data = '';
-                       }
-               }
-               return $output . $data;
-       }
-
-       function parse_date($dt)
-       {
-               $parser = SimplePie_Parse_Date::get();
-               return $parser->parse($dt);
-       }
-
-       /**
-        * Decode HTML entities
-        *
-        * @static
-        * @access public
-        * @param string $data Input data
-        * @return string Output data
-        */
-       function entities_decode($data)
-       {
-               $decoder = new SimplePie_Decode_HTML_Entities($data);
-               return $decoder->parse();
-       }
-
-       /**
-        * Remove RFC822 comments
-        *
-        * @access public
-        * @param string $data Data to strip comments from
-        * @return string Comment stripped string
-        */
-       function uncomment_rfc822($string)
-       {
-               $string = (string) $string;
-               $position = 0;
-               $length = strlen($string);
-               $depth = 0;
-
-               $output = '';
-
-               while ($position < $length && ($pos = strpos($string, '(', $position)) !== false)
-               {
-                       $output .= substr($string, $position, $pos - $position);
-                       $position = $pos + 1;
-                       if ($string[$pos - 1] !== '\\')
-                       {
-                               $depth++;
-                               while ($depth && $position < $length)
-                               {
-                                       $position += strcspn($string, '()', $position);
-                                       if ($string[$position - 1] === '\\')
-                                       {
-                                               $position++;
-                                               continue;
-                                       }
-                                       elseif (isset($string[$position]))
-                                       {
-                                               switch ($string[$position])
-                                               {
-                                                       case '(':
-                                                               $depth++;
-                                                               break;
-
-                                                       case ')':
-                                                               $depth--;
-                                                               break;
-                                               }
-                                               $position++;
-                                       }
-                                       else
-                                       {
-                                               break;
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               $output .= '(';
-                       }
-               }
-               $output .= substr($string, $position);
-
-               return $output;
-       }
-
-       function parse_mime($mime)
-       {
-               if (($pos = strpos($mime, ';')) === false)
-               {
-                       return trim($mime);
-               }
-               else
-               {
-                       return trim(substr($mime, 0, $pos));
-               }
-       }
-
-       function htmlspecialchars_decode($string, $quote_style)
-       {
-               if (function_exists('htmlspecialchars_decode'))
-               {
-                       return htmlspecialchars_decode($string, $quote_style);
-               }
-               else
-               {
-                       return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style)));
-               }
-       }
-
-       function atom_03_construct_type($attribs)
-       {
-               if (isset($attribs['']['mode']) && strtolower(trim($attribs['']['mode']) === 'base64'))
-               {
-                       $mode = SIMPLEPIE_CONSTRUCT_BASE64;
-               }
-               else
-               {
-                       $mode = SIMPLEPIE_CONSTRUCT_NONE;
-               }
-               if (isset($attribs['']['type']))
-               {
-                       switch (strtolower(trim($attribs['']['type'])))
-                       {
-                               case 'text':
-                               case 'text/plain':
-                                       return SIMPLEPIE_CONSTRUCT_TEXT | $mode;
-
-                               case 'html':
-                               case 'text/html':
-                                       return SIMPLEPIE_CONSTRUCT_HTML | $mode;
-
-                               case 'xhtml':
-                               case 'application/xhtml+xml':
-                                       return SIMPLEPIE_CONSTRUCT_XHTML | $mode;
-
-                               default:
-                                       return SIMPLEPIE_CONSTRUCT_NONE | $mode;
-                       }
-               }
-               else
-               {
-                       return SIMPLEPIE_CONSTRUCT_TEXT | $mode;
-               }
-       }
-
-       function atom_10_construct_type($attribs)
-       {
-               if (isset($attribs['']['type']))
-               {
-                       switch (strtolower(trim($attribs['']['type'])))
-                       {
-                               case 'text':
-                                       return SIMPLEPIE_CONSTRUCT_TEXT;
-
-                               case 'html':
-                                       return SIMPLEPIE_CONSTRUCT_HTML;
-
-                               case 'xhtml':
-                                       return SIMPLEPIE_CONSTRUCT_XHTML;
-
-                               default:
-                                       return SIMPLEPIE_CONSTRUCT_NONE;
-                       }
-               }
-               return SIMPLEPIE_CONSTRUCT_TEXT;
-       }
-
-       function atom_10_content_construct_type($attribs)
-       {
-               if (isset($attribs['']['type']))
-               {
-                       $type = strtolower(trim($attribs['']['type']));
-                       switch ($type)
-                       {
-                               case 'text':
-                                       return SIMPLEPIE_CONSTRUCT_TEXT;
-
-                               case 'html':
-                                       return SIMPLEPIE_CONSTRUCT_HTML;
-
-                               case 'xhtml':
-                                       return SIMPLEPIE_CONSTRUCT_XHTML;
-                       }
-                       if (in_array(substr($type, -4), array('+xml', '/xml')) || substr($type, 0, 5) === 'text/')
-                       {
-                               return SIMPLEPIE_CONSTRUCT_NONE;
-                       }
-                       else
-                       {
-                               return SIMPLEPIE_CONSTRUCT_BASE64;
-                       }
-               }
-               else
-               {
-                       return SIMPLEPIE_CONSTRUCT_TEXT;
-               }
-       }
-
-       function is_isegment_nz_nc($string)
-       {
-               return (bool) preg_match('/^([A-Za-z0-9\-._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!$&\'()*+,;=@]|(%[0-9ABCDEF]{2}))+$/u', $string);
-       }
-
-       function space_seperated_tokens($string)
-       {
-               $space_characters = "\x20\x09\x0A\x0B\x0C\x0D";
-               $string_length = strlen($string);
-
-               $position = strspn($string, $space_characters);
-               $tokens = array();
-
-               while ($position < $string_length)
-               {
-                       $len = strcspn($string, $space_characters, $position);
-                       $tokens[] = substr($string, $position, $len);
-                       $position += $len;
-                       $position += strspn($string, $space_characters, $position);
-               }
-
-               return $tokens;
-       }
-
-       function array_unique($array)
-       {
-               if (version_compare(PHP_VERSION, '5.2', '>='))
-               {
-                       return array_unique($array);
-               }
-               else
-               {
-                       $array = (array) $array;
-                       $new_array = array();
-                       $new_array_strings = array();
-                       foreach ($array as $key => $value)
-                       {
-                               if (is_object($value))
-                               {
-                                       if (method_exists($value, '__toString'))
-                                       {
-                                               $cmp = $value->__toString();
-                                       }
-                                       else
-                                       {
-                                               trigger_error('Object of class ' . get_class($value) . ' could not be converted to string', E_USER_ERROR);
-                                       }
-                               }
-                               elseif (is_array($value))
-                               {
-                                       $cmp = (string) reset($value);
-                               }
-                               else
-                               {
-                                       $cmp = (string) $value;
-                               }
-                               if (!in_array($cmp, $new_array_strings))
-                               {
-                                       $new_array[$key] = $value;
-                                       $new_array_strings[] = $cmp;
-                               }
-                       }
-                       return $new_array;
-               }
-       }
-
-       /**
-        * Converts a unicode codepoint to a UTF-8 character
-        *
-        * @static
-        * @access public
-        * @param int $codepoint Unicode codepoint
-        * @return string UTF-8 character
-        */
-       function codepoint_to_utf8($codepoint)
-       {
-               $codepoint = (int) $codepoint;
-               if ($codepoint < 0)
-               {
-                       return false;
-               }
-               else if ($codepoint <= 0x7f)
-               {
-                       return chr($codepoint);
-               }
-               else if ($codepoint <= 0x7ff)
-               {
-                       return chr(0xc0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3f));
-               }
-               else if ($codepoint <= 0xffff)
-               {
-                       return chr(0xe0 | ($codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
-               }
-               else if ($codepoint <= 0x10ffff)
-               {
-                       return chr(0xf0 | ($codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3f)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
-               }
-               else
-               {
-                       // U+FFFD REPLACEMENT CHARACTER
-                       return "\xEF\xBF\xBD";
-               }
-       }
-
-       /**
-        * Re-implementation of PHP 5's stripos()
-        *
-        * Returns the numeric position of the first occurrence of needle in the
-        * haystack string.
-        *
-        * @static
-        * @access string
-        * @param object $haystack
-        * @param string $needle Note that the needle may be a string of one or more
-        *     characters. If needle is not a string, it is converted to an integer
-        *     and applied as the ordinal value of a character.
-        * @param int $offset The optional offset parameter allows you to specify which
-        *     character in haystack to start searching. The position returned is still
-        *     relative to the beginning of haystack.
-        * @return bool If needle is not found, stripos() will return boolean false.
-        */
-       function stripos($haystack, $needle, $offset = 0)
-       {
-               if (function_exists('stripos'))
-               {
-                       return stripos($haystack, $needle, $offset);
-               }
-               else
-               {
-                       if (is_string($needle))
-                       {
-                               $needle = strtolower($needle);
-                       }
-                       elseif (is_int($needle) || is_bool($needle) || is_double($needle))
-                       {
-                               $needle = strtolower(chr($needle));
-                       }
-                       else
-                       {
-                               trigger_error('needle is not a string or an integer', E_USER_WARNING);
-                               return false;
-                       }
-
-                       return strpos(strtolower($haystack), $needle, $offset);
-               }
-       }
-
-       /**
-        * Similar to parse_str()
-        *
-        * Returns an associative array of name/value pairs, where the value is an
-        * array of values that have used the same name
-        *
-        * @static
-        * @access string
-        * @param string $str The input string.
-        * @return array
-        */
-       function parse_str($str)
-       {
-               $return = array();
-               $str = explode('&', $str);
-
-               foreach ($str as $section)
-               {
-                       if (strpos($section, '=') !== false)
-                       {
-                               list($name, $value) = explode('=', $section, 2);
-                               $return[urldecode($name)][] = urldecode($value);
-                       }
-                       else
-                       {
-                               $return[urldecode($section)][] = null;
-                       }
-               }
-
-               return $return;
-       }
-
-       /**
-        * Detect XML encoding, as per XML 1.0 Appendix F.1
-        *
-        * @todo Add support for EBCDIC
-        * @param string $data XML data
-        * @return array Possible encodings
-        */
-       function xml_encoding($data)
-       {
-               // UTF-32 Big Endian BOM
-               if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
-               {
-                       $encoding[] = 'UTF-32BE';
-               }
-               // UTF-32 Little Endian BOM
-               elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
-               {
-                       $encoding[] = 'UTF-32LE';
-               }
-               // UTF-16 Big Endian BOM
-               elseif (substr($data, 0, 2) === "\xFE\xFF")
-               {
-                       $encoding[] = 'UTF-16BE';
-               }
-               // UTF-16 Little Endian BOM
-               elseif (substr($data, 0, 2) === "\xFF\xFE")
-               {
-                       $encoding[] = 'UTF-16LE';
-               }
-               // UTF-8 BOM
-               elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
-               {
-                       $encoding[] = 'UTF-8';
-               }
-               // UTF-32 Big Endian Without BOM
-               elseif (substr($data, 0, 20) === "\x00\x00\x00\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C")
-               {
-                       if ($pos = strpos($data, "\x00\x00\x00\x3F\x00\x00\x00\x3E"))
-                       {
-                               $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32BE', 'UTF-8'));
-                               if ($parser->parse())
-                               {
-                                       $encoding[] = $parser->encoding;
-                               }
-                       }
-                       $encoding[] = 'UTF-32BE';
-               }
-               // UTF-32 Little Endian Without BOM
-               elseif (substr($data, 0, 20) === "\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C\x00\x00\x00")
-               {
-                       if ($pos = strpos($data, "\x3F\x00\x00\x00\x3E\x00\x00\x00"))
-                       {
-                               $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32LE', 'UTF-8'));
-                               if ($parser->parse())
-                               {
-                                       $encoding[] = $parser->encoding;
-                               }
-                       }
-                       $encoding[] = 'UTF-32LE';
-               }
-               // UTF-16 Big Endian Without BOM
-               elseif (substr($data, 0, 10) === "\x00\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C")
-               {
-                       if ($pos = strpos($data, "\x00\x3F\x00\x3E"))
-                       {
-                               $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16BE', 'UTF-8'));
-                               if ($parser->parse())
-                               {
-                                       $encoding[] = $parser->encoding;
-                               }
-                       }
-                       $encoding[] = 'UTF-16BE';
-               }
-               // UTF-16 Little Endian Without BOM
-               elseif (substr($data, 0, 10) === "\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C\x00")
-               {
-                       if ($pos = strpos($data, "\x3F\x00\x3E\x00"))
-                       {
-                               $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16LE', 'UTF-8'));
-                               if ($parser->parse())
-                               {
-                                       $encoding[] = $parser->encoding;
-                               }
-                       }
-                       $encoding[] = 'UTF-16LE';
-               }
-               // US-ASCII (or superset)
-               elseif (substr($data, 0, 5) === "\x3C\x3F\x78\x6D\x6C")
-               {
-                       if ($pos = strpos($data, "\x3F\x3E"))
-                       {
-                               $parser = new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5));
-                               if ($parser->parse())
-                               {
-                                       $encoding[] = $parser->encoding;
-                               }
-                       }
-                       $encoding[] = 'UTF-8';
-               }
-               // Fallback to UTF-8
-               else
-               {
-                       $encoding[] = 'UTF-8';
-               }
-               return $encoding;
-       }
-
-       function output_javascript()
-       {
-               if (function_exists('ob_gzhandler'))
-               {
-                       ob_start('ob_gzhandler');
-               }
-               header('Content-type: text/javascript; charset: UTF-8');
-               header('Cache-Control: must-revalidate');
-               header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days
-               ?>
-function embed_odeo(link) {
-       document.writeln('<embed src="http://odeo.com/flash/audio_player_fullsize.swf" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="440" height="80" wmode="transparent" allowScriptAccess="any" flashvars="valid_sample_rate=true&external_url='+link+'"></embed>');
-}
-
-function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) {
-       if (placeholder != '') {
-               document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" href="'+link+'" src="'+placeholder+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="false" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>');
-       }
-       else {
-               document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" src="'+link+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="true" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>');
-       }
-}
-
-function embed_flash(bgcolor, width, height, link, loop, type) {
-       document.writeln('<embed src="'+link+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="'+type+'" quality="high" width="'+width+'" height="'+height+'" bgcolor="'+bgcolor+'" loop="'+loop+'"></embed>');
-}
-
-function embed_flv(width, height, link, placeholder, loop, player) {
-       document.writeln('<embed src="'+player+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="'+width+'" height="'+height+'" wmode="transparent" flashvars="file='+link+'&autostart=false&repeat='+loop+'&showdigits=true&showfsbutton=false"></embed>');
-}
-
-function embed_wmedia(width, height, link) {
-       document.writeln('<embed type="application/x-mplayer2" src="'+link+'" autosize="1" width="'+width+'" height="'+height+'" showcontrols="1" showstatusbar="0" showdisplay="0" autostart="0"></embed>');
-}
-               <?php
-       }
-}
-
-/**
- * Decode HTML Entities
- *
- * This implements HTML5 as of revision 967 (2007-06-28)
- *
- * @package SimplePie
- */
-class SimplePie_Decode_HTML_Entities
-{
-       /**
-        * Data to be parsed
-        *
-        * @access private
-        * @var string
-        */
-       var $data = '';
-
-       /**
-        * Currently consumed bytes
-        *
-        * @access private
-        * @var string
-        */
-       var $consumed = '';
-
-       /**
-        * Position of the current byte being parsed
-        *
-        * @access private
-        * @var int
-        */
-       var $position = 0;
-
-       /**
-        * Create an instance of the class with the input data
-        *
-        * @access public
-        * @param string $data Input data
-        */
-       function SimplePie_Decode_HTML_Entities($data)
-       {
-               $this->data = $data;
-       }
-
-       /**
-        * Parse the input data
-        *
-        * @access public
-        * @return string Output data
-        */
-       function parse()
-       {
-               while (($this->position = strpos($this->data, '&', $this->position)) !== false)
-               {
-                       $this->consume();
-                       $this->entity();
-                       $this->consumed = '';
-               }
-               return $this->data;
-       }
-
-       /**
-        * Consume the next byte
-        *
-        * @access private
-        * @return mixed The next byte, or false, if there is no more data
-        */
-       function consume()
-       {
-               if (isset($this->data[$this->position]))
-               {
-                       $this->consumed .= $this->data[$this->position];
-                       return $this->data[$this->position++];
-               }
-               else
-               {
-                       return false;
-               }
-       }
-
-       /**
-        * Consume a range of characters
-        *
-        * @access private
-        * @param string $chars Characters to consume
-        * @return mixed A series of characters that match the range, or false
-        */
-       function consume_range($chars)
-       {
-               if ($len = strspn($this->data, $chars, $this->position))
-               {
-                       $data = substr($this->data, $this->position, $len);
-                       $this->consumed .= $data;
-                       $this->position += $len;
-                       return $data;
-               }
-               else
-               {
-                       return false;
-               }
-       }
-
-       /**
-        * Unconsume one byte
-        *
-        * @access private
-        */
-       function unconsume()
-       {
-               $this->consumed = substr($this->consumed, 0, -1);
-               $this->position--;
-       }
-
-       /**
-        * Decode an entity
-        *
-        * @access private
-        */
-       function entity()
-       {
-               switch ($this->consume())
-               {
-                       case "\x09":
-                       case "\x0A":
-                       case "\x0B":
-                       case "\x0B":
-                       case "\x0C":
-                       case "\x20":
-                       case "\x3C":
-                       case "\x26":
-                       case false:
-                               break;
-
-                       case "\x23":
-                               switch ($this->consume())
-                               {
-                                       case "\x78":
-                                       case "\x58":
-                                               $range = '0123456789ABCDEFabcdef';
-                                               $hex = true;
-                                               break;
-
-                                       default:
-                                               $range = '0123456789';
-                                               $hex = false;
-                                               $this->unconsume();
-                                               break;
-                               }
-
-                               if ($codepoint = $this->consume_range($range))
-                               {
-                                       static $windows_1252_specials = array(0x0D => "\x0A", 0x80 => "\xE2\x82\xAC", 0x81 => "\xEF\xBF\xBD", 0x82 => "\xE2\x80\x9A", 0x83 => "\xC6\x92", 0x84 => "\xE2\x80\x9E", 0x85 => "\xE2\x80\xA6", 0x86 => "\xE2\x80\xA0", 0x87 => "\xE2\x80\xA1", 0x88 => "\xCB\x86", 0x89 => "\xE2\x80\xB0", 0x8A => "\xC5\xA0", 0x8B => "\xE2\x80\xB9", 0x8C => "\xC5\x92", 0x8D => "\xEF\xBF\xBD", 0x8E => "\xC5\xBD", 0x8F => "\xEF\xBF\xBD", 0x90 => "\xEF\xBF\xBD", 0x91 => "\xE2\x80\x98", 0x92 => "\xE2\x80\x99", 0x93 => "\xE2\x80\x9C", 0x94 => "\xE2\x80\x9D", 0x95 => "\xE2\x80\xA2", 0x96 => "\xE2\x80\x93", 0x97 => "\xE2\x80\x94", 0x98 => "\xCB\x9C", 0x99 => "\xE2\x84\xA2", 0x9A => "\xC5\xA1", 0x9B => "\xE2\x80\xBA", 0x9C => "\xC5\x93", 0x9D => "\xEF\xBF\xBD", 0x9E => "\xC5\xBE", 0x9F => "\xC5\xB8");
-
-                                       if ($hex)
-                                       {
-                                               $codepoint = hexdec($codepoint);
-                                       }
-                                       else
-                                       {
-                                               $codepoint = intval($codepoint);
-                                       }
-
-                                       if (isset($windows_1252_specials[$codepoint]))
-                                       {
-                                               $replacement = $windows_1252_specials[$codepoint];
-                                       }
-                                       else
-                                       {
-                                               $replacement = SimplePie_Misc::codepoint_to_utf8($codepoint);
-                                       }
-
-                                       if (!in_array($this->consume(), array(';', false), true))
-                                       {
-                                               $this->unconsume();
-                                       }
-
-                                       $consumed_length = strlen($this->consumed);
-                                       $this->data = substr_replace($this->data, $replacement, $this->position - $consumed_length, $consumed_length);
-                                       $this->position += strlen($replacement) - $consumed_length;
-                               }
-                               break;
-
-                       default:
-                               static $entities = array('Aacute' => "\xC3\x81", 'aacute' => "\xC3\xA1", 'Aacute;' => "\xC3\x81", 'aacute;' => "\xC3\xA1", 'Acirc' => "\xC3\x82", 'acirc' => "\xC3\xA2", 'Acirc;' => "\xC3\x82", 'acirc;' => "\xC3\xA2", 'acute' => "\xC2\xB4", 'acute;' => "\xC2\xB4", 'AElig' => "\xC3\x86", 'aelig' => "\xC3\xA6", 'AElig;' => "\xC3\x86", 'aelig;' => "\xC3\xA6", 'Agrave' => "\xC3\x80", 'agrave' => "\xC3\xA0", 'Agrave;' => "\xC3\x80", 'agrave;' => "\xC3\xA0", 'alefsym;' => "\xE2\x84\xB5", 'Alpha;' => "\xCE\x91", 'alpha;' => "\xCE\xB1", 'AMP' => "\x26", 'amp' => "\x26", 'AMP;' => "\x26", 'amp;' => "\x26", 'and;' => "\xE2\x88\xA7", 'ang;' => "\xE2\x88\xA0", 'apos;' => "\x27", 'Aring' => "\xC3\x85", 'aring' => "\xC3\xA5", 'Aring;' => "\xC3\x85", 'aring;' => "\xC3\xA5", 'asymp;' => "\xE2\x89\x88", 'Atilde' => "\xC3\x83", 'atilde' => "\xC3\xA3", 'Atilde;' => "\xC3\x83", 'atilde;' => "\xC3\xA3", 'Auml' => "\xC3\x84", 'auml' => "\xC3\xA4", 'Auml;' => "\xC3\x84", 'auml;' => "\xC3\xA4", 'bdquo;' => "\xE2\x80\x9E", 'Beta;' => "\xCE\x92", 'beta;' => "\xCE\xB2", 'brvbar' => "\xC2\xA6", 'brvbar;' => "\xC2\xA6", 'bull;' => "\xE2\x80\xA2", 'cap;' => "\xE2\x88\xA9", 'Ccedil' => "\xC3\x87", 'ccedil' => "\xC3\xA7", 'Ccedil;' => "\xC3\x87", 'ccedil;' => "\xC3\xA7", 'cedil' => "\xC2\xB8", 'cedil;' => "\xC2\xB8", 'cent' => "\xC2\xA2", 'cent;' => "\xC2\xA2", 'Chi;' => "\xCE\xA7", 'chi;' => "\xCF\x87", 'circ;' => "\xCB\x86", 'clubs;' => "\xE2\x99\xA3", 'cong;' => "\xE2\x89\x85", 'COPY' => "\xC2\xA9", 'copy' => "\xC2\xA9", 'COPY;' => "\xC2\xA9", 'copy;' => "\xC2\xA9", 'crarr;' => "\xE2\x86\xB5", 'cup;' => "\xE2\x88\xAA", 'curren' => "\xC2\xA4", 'curren;' => "\xC2\xA4", 'Dagger;' => "\xE2\x80\xA1", 'dagger;' => "\xE2\x80\xA0", 'dArr;' => "\xE2\x87\x93", 'darr;' => "\xE2\x86\x93", 'deg' => "\xC2\xB0", 'deg;' => "\xC2\xB0", 'Delta;' => "\xCE\x94", 'delta;' => "\xCE\xB4", 'diams;' => "\xE2\x99\xA6", 'divide' => "\xC3\xB7", 'divide;' => "\xC3\xB7", 'Eacute' => "\xC3\x89", 'eacute' => "\xC3\xA9", 'Eacute;' => "\xC3\x89", 'eacute;' => "\xC3\xA9", 'Ecirc' => "\xC3\x8A", 'ecirc' => "\xC3\xAA", 'Ecirc;' => "\xC3\x8A", 'ecirc;' => "\xC3\xAA", 'Egrave' => "\xC3\x88", 'egrave' => "\xC3\xA8", 'Egrave;' => "\xC3\x88", 'egrave;' => "\xC3\xA8", 'empty;' => "\xE2\x88\x85", 'emsp;' => "\xE2\x80\x83", 'ensp;' => "\xE2\x80\x82", 'Epsilon;' => "\xCE\x95", 'epsilon;' => "\xCE\xB5", 'equiv;' => "\xE2\x89\xA1", 'Eta;' => "\xCE\x97", 'eta;' => "\xCE\xB7", 'ETH' => "\xC3\x90", 'eth' => "\xC3\xB0", 'ETH;' => "\xC3\x90", 'eth;' => "\xC3\xB0", 'Euml' => "\xC3\x8B", 'euml' => "\xC3\xAB", 'Euml;' => "\xC3\x8B", 'euml;' => "\xC3\xAB", 'euro;' => "\xE2\x82\xAC", 'exist;' => "\xE2\x88\x83", 'fnof;' => "\xC6\x92", 'forall;' => "\xE2\x88\x80", 'frac12' => "\xC2\xBD", 'frac12;' => "\xC2\xBD", 'frac14' => "\xC2\xBC", 'frac14;' => "\xC2\xBC", 'frac34' => "\xC2\xBE", 'frac34;' => "\xC2\xBE", 'frasl;' => "\xE2\x81\x84", 'Gamma;' => "\xCE\x93", 'gamma;' => "\xCE\xB3", 'ge;' => "\xE2\x89\xA5", 'GT' => "\x3E", 'gt' => "\x3E", 'GT;' => "\x3E", 'gt;' => "\x3E", 'hArr;' => "\xE2\x87\x94", 'harr;' => "\xE2\x86\x94", 'hearts;' => "\xE2\x99\xA5", 'hellip;' => "\xE2\x80\xA6", 'Iacute' => "\xC3\x8D", 'iacute' => "\xC3\xAD", 'Iacute;' => "\xC3\x8D", 'iacute;' => "\xC3\xAD", 'Icirc' => "\xC3\x8E", 'icirc' => "\xC3\xAE", 'Icirc;' => "\xC3\x8E", 'icirc;' => "\xC3\xAE", 'iexcl' => "\xC2\xA1", 'iexcl;' => "\xC2\xA1", 'Igrave' => "\xC3\x8C", 'igrave' => "\xC3\xAC", 'Igrave;' => "\xC3\x8C", 'igrave;' => "\xC3\xAC", 'image;' => "\xE2\x84\x91", 'infin;' => "\xE2\x88\x9E", 'int;' => "\xE2\x88\xAB", 'Iota;' => "\xCE\x99", 'iota;' => "\xCE\xB9", 'iquest' => "\xC2\xBF", 'iquest;' => "\xC2\xBF", 'isin;' => "\xE2\x88\x88", 'Iuml' => "\xC3\x8F", 'iuml' => "\xC3\xAF", 'Iuml;' => "\xC3\x8F", 'iuml;' => "\xC3\xAF", 'Kappa;' => "\xCE\x9A", 'kappa;' => "\xCE\xBA", 'Lambda;' => "\xCE\x9B", 'lambda;' => "\xCE\xBB", 'lang;' => "\xE3\x80\x88", 'laquo' => "\xC2\xAB", 'laquo;' => "\xC2\xAB", 'lArr;' => "\xE2\x87\x90", 'larr;' => "\xE2\x86\x90", 'lceil;' => "\xE2\x8C\x88", 'ldquo;' => "\xE2\x80\x9C", 'le;' => "\xE2\x89\xA4", 'lfloor;' => "\xE2\x8C\x8A", 'lowast;' => "\xE2\x88\x97", 'loz;' => "\xE2\x97\x8A", 'lrm;' => "\xE2\x80\x8E", 'lsaquo;' => "\xE2\x80\xB9", 'lsquo;' => "\xE2\x80\x98", 'LT' => "\x3C", 'lt' => "\x3C", 'LT;' => "\x3C", 'lt;' => "\x3C", 'macr' => "\xC2\xAF", 'macr;' => "\xC2\xAF", 'mdash;' => "\xE2\x80\x94", 'micro' => "\xC2\xB5", 'micro;' => "\xC2\xB5", 'middot' => "\xC2\xB7", 'middot;' => "\xC2\xB7", 'minus;' => "\xE2\x88\x92", 'Mu;' => "\xCE\x9C", 'mu;' => "\xCE\xBC", 'nabla;' => "\xE2\x88\x87", 'nbsp' => "\xC2\xA0", 'nbsp;' => "\xC2\xA0", 'ndash;' => "\xE2\x80\x93", 'ne;' => "\xE2\x89\xA0", 'ni;' => "\xE2\x88\x8B", 'not' => "\xC2\xAC", 'not;' => "\xC2\xAC", 'notin;' => "\xE2\x88\x89", 'nsub;' => "\xE2\x8A\x84", 'Ntilde' => "\xC3\x91", 'ntilde' => "\xC3\xB1", 'Ntilde;' => "\xC3\x91", 'ntilde;' => "\xC3\xB1", 'Nu;' => "\xCE\x9D", 'nu;' => "\xCE\xBD", 'Oacute' => "\xC3\x93", 'oacute' => "\xC3\xB3", 'Oacute;' => "\xC3\x93", 'oacute;' => "\xC3\xB3", 'Ocirc' => "\xC3\x94", 'ocirc' => "\xC3\xB4", 'Ocirc;' => "\xC3\x94", 'ocirc;' => "\xC3\xB4", 'OElig;' => "\xC5\x92", 'oelig;' => "\xC5\x93", 'Ograve' => "\xC3\x92", 'ograve' => "\xC3\xB2", 'Ograve;' => "\xC3\x92", 'ograve;' => "\xC3\xB2", 'oline;' => "\xE2\x80\xBE", 'Omega;' => "\xCE\xA9", 'omega;' => "\xCF\x89", 'Omicron;' => "\xCE\x9F", 'omicron;' => "\xCE\xBF", 'oplus;' => "\xE2\x8A\x95", 'or;' => "\xE2\x88\xA8", 'ordf' => "\xC2\xAA", 'ordf;' => "\xC2\xAA", 'ordm' => "\xC2\xBA", 'ordm;' => "\xC2\xBA", 'Oslash' => "\xC3\x98", 'oslash' => "\xC3\xB8", 'Oslash;' => "\xC3\x98", 'oslash;' => "\xC3\xB8", 'Otilde' => "\xC3\x95", 'otilde' => "\xC3\xB5", 'Otilde;' => "\xC3\x95", 'otilde;' => "\xC3\xB5", 'otimes;' => "\xE2\x8A\x97", 'Ouml' => "\xC3\x96", 'ouml' => "\xC3\xB6", 'Ouml;' => "\xC3\x96", 'ouml;' => "\xC3\xB6", 'para' => "\xC2\xB6", 'para;' => "\xC2\xB6", 'part;' => "\xE2\x88\x82", 'permil;' => "\xE2\x80\xB0", 'perp;' => "\xE2\x8A\xA5", 'Phi;' => "\xCE\xA6", 'phi;' => "\xCF\x86", 'Pi;' => "\xCE\xA0", 'pi;' => "\xCF\x80", 'piv;' => "\xCF\x96", 'plusmn' => "\xC2\xB1", 'plusmn;' => "\xC2\xB1", 'pound' => "\xC2\xA3", 'pound;' => "\xC2\xA3", 'Prime;' => "\xE2\x80\xB3", 'prime;' => "\xE2\x80\xB2", 'prod;' => "\xE2\x88\x8F", 'prop;' => "\xE2\x88\x9D", 'Psi;' => "\xCE\xA8", 'psi;' => "\xCF\x88", 'QUOT' => "\x22", 'quot' => "\x22", 'QUOT;' => "\x22", 'quot;' => "\x22", 'radic;' => "\xE2\x88\x9A", 'rang;' => "\xE3\x80\x89", 'raquo' => "\xC2\xBB", 'raquo;' => "\xC2\xBB", 'rArr;' => "\xE2\x87\x92", 'rarr;' => "\xE2\x86\x92", 'rceil;' => "\xE2\x8C\x89", 'rdquo;' => "\xE2\x80\x9D", 'real;' => "\xE2\x84\x9C", 'REG' => "\xC2\xAE", 'reg' => "\xC2\xAE", 'REG;' => "\xC2\xAE", 'reg;' => "\xC2\xAE", 'rfloor;' => "\xE2\x8C\x8B", 'Rho;' => "\xCE\xA1", 'rho;' => "\xCF\x81", 'rlm;' => "\xE2\x80\x8F", 'rsaquo;' => "\xE2\x80\xBA", 'rsquo;' => "\xE2\x80\x99", 'sbquo;' => "\xE2\x80\x9A", 'Scaron;' => "\xC5\xA0", 'scaron;' => "\xC5\xA1", 'sdot;' => "\xE2\x8B\x85", 'sect' => "\xC2\xA7", 'sect;' => "\xC2\xA7", 'shy' => "\xC2\xAD", 'shy;' => "\xC2\xAD", 'Sigma;' => "\xCE\xA3", 'sigma;' => "\xCF\x83", 'sigmaf;' => "\xCF\x82", 'sim;' => "\xE2\x88\xBC", 'spades;' => "\xE2\x99\xA0", 'sub;' => "\xE2\x8A\x82", 'sube;' => "\xE2\x8A\x86", 'sum;' => "\xE2\x88\x91", 'sup;' => "\xE2\x8A\x83", 'sup1' => "\xC2\xB9", 'sup1;' => "\xC2\xB9", 'sup2' => "\xC2\xB2", 'sup2;' => "\xC2\xB2", 'sup3' => "\xC2\xB3", 'sup3;' => "\xC2\xB3", 'supe;' => "\xE2\x8A\x87", 'szlig' => "\xC3\x9F", 'szlig;' => "\xC3\x9F", 'Tau;' => "\xCE\xA4", 'tau;' => "\xCF\x84", 'there4;' => "\xE2\x88\xB4", 'Theta;' => "\xCE\x98", 'theta;' => "\xCE\xB8", 'thetasym;' => "\xCF\x91", 'thinsp;' => "\xE2\x80\x89", 'THORN' => "\xC3\x9E", 'thorn' => "\xC3\xBE", 'THORN;' => "\xC3\x9E", 'thorn;' => "\xC3\xBE", 'tilde;' => "\xCB\x9C", 'times' => "\xC3\x97", 'times;' => "\xC3\x97", 'TRADE;' => "\xE2\x84\xA2", 'trade;' => "\xE2\x84\xA2", 'Uacute' => "\xC3\x9A", 'uacute' => "\xC3\xBA", 'Uacute;' => "\xC3\x9A", 'uacute;' => "\xC3\xBA", 'uArr;' => "\xE2\x87\x91", 'uarr;' => "\xE2\x86\x91", 'Ucirc' => "\xC3\x9B", 'ucirc' => "\xC3\xBB", 'Ucirc;' => "\xC3\x9B", 'ucirc;' => "\xC3\xBB", 'Ugrave' => "\xC3\x99", 'ugrave' => "\xC3\xB9", 'Ugrave;' => "\xC3\x99", 'ugrave;' => "\xC3\xB9", 'uml' => "\xC2\xA8", 'uml;' => "\xC2\xA8", 'upsih;' => "\xCF\x92", 'Upsilon;' => "\xCE\xA5", 'upsilon;' => "\xCF\x85", 'Uuml' => "\xC3\x9C", 'uuml' => "\xC3\xBC", 'Uuml;' => "\xC3\x9C", 'uuml;' => "\xC3\xBC", 'weierp;' => "\xE2\x84\x98", 'Xi;' => "\xCE\x9E", 'xi;' => "\xCE\xBE", 'Yacute' => "\xC3\x9D", 'yacute' => "\xC3\xBD", 'Yacute;' => "\xC3\x9D", 'yacute;' => "\xC3\xBD", 'yen' => "\xC2\xA5", 'yen;' => "\xC2\xA5", 'yuml' => "\xC3\xBF", 'Yuml;' => "\xC5\xB8", 'yuml;' => "\xC3\xBF", 'Zeta;' => "\xCE\x96", 'zeta;' => "\xCE\xB6", 'zwj;' => "\xE2\x80\x8D", 'zwnj;' => "\xE2\x80\x8C");
-
-                               for ($i = 0, $match = null; $i < 9 && $this->consume() !== false; $i++)
-                               {
-                                       $consumed = substr($this->consumed, 1);
-                                       if (isset($entities[$consumed]))
-                                       {
-                                               $match = $consumed;
-                                       }
-                               }
-
-                               if ($match !== null)
-                               {
-                                       $this->data = substr_replace($this->data, $entities[$match], $this->position - strlen($consumed) - 1, strlen($match) + 1);
-                                       $this->position += strlen($entities[$match]) - strlen($consumed) - 1;
-                               }
-                               break;
-               }
-       }
-}
-
-/**
- * IRI parser/serialiser
- *
- * @package SimplePie
- */
-class SimplePie_IRI
-{
-       /**
-        * Scheme
-        *
-        * @access private
-        * @var string
-        */
-       var $scheme;
-
-       /**
-        * User Information
-        *
-        * @access private
-        * @var string
-        */
-       var $userinfo;
-
-       /**
-        * Host
-        *
-        * @access private
-        * @var string
-        */
-       var $host;
-
-       /**
-        * Port
-        *
-        * @access private
-        * @var string
-        */
-       var $port;
-
-       /**
-        * Path
-        *
-        * @access private
-        * @var string
-        */
-       var $path;
-
-       /**
-        * Query
-        *
-        * @access private
-        * @var string
-        */
-       var $query;
-
-       /**
-        * Fragment
-        *
-        * @access private
-        * @var string
-        */
-       var $fragment;
-
-       /**
-        * Whether the object represents a valid IRI
-        *
-        * @access private
-        * @var array
-        */
-       var $valid = array();
-
-       /**
-        * Return the entire IRI when you try and read the object as a string
-        *
-        * @access public
-        * @return string
-        */
-       function __toString()
-       {
-               return $this->get_iri();
-       }
-
-       /**
-        * Create a new IRI object, from a specified string
-        *
-        * @access public
-        * @param string $iri
-        * @return SimplePie_IRI
-        */
-       function SimplePie_IRI($iri)
-       {
-               $iri = (string) $iri;
-               if ($iri !== '')
-               {
-                       $parsed = $this->parse_iri($iri);
-                       $this->set_scheme($parsed['scheme']);
-                       $this->set_authority($parsed['authority']);
-                       $this->set_path($parsed['path']);
-                       $this->set_query($parsed['query']);
-                       $this->set_fragment($parsed['fragment']);
-               }
-       }
-
-       /**
-        * Create a new IRI object by resolving a relative IRI
-        *
-        * @static
-        * @access public
-        * @param SimplePie_IRI $base Base IRI
-        * @param string $relative Relative IRI
-        * @return SimplePie_IRI
-        */
-       function absolutize($base, $relative)
-       {
-               $relative = (string) $relative;
-               if ($relative !== '')
-               {
-                       $relative = new SimplePie_IRI($relative);
-                       if ($relative->get_scheme() !== null)
-                       {
-                               $target = $relative;
-                       }
-                       elseif ($base->get_iri() !== null)
-                       {
-                               if ($relative->get_authority() !== null)
-                               {
-                                       $target = $relative;
-                                       $target->set_scheme($base->get_scheme());
-                               }
-                               else
-                               {
-                                       $target = new SimplePie_IRI('');
-                                       $target->set_scheme($base->get_scheme());
-                                       $target->set_userinfo($base->get_userinfo());
-                                       $target->set_host($base->get_host());
-                                       $target->set_port($base->get_port());
-                                       if ($relative->get_path() !== null)
-                                       {
-                                               if (strpos($relative->get_path(), '/') === 0)
-                                               {
-                                                       $target->set_path($relative->get_path());
-                                               }
-                                               elseif (($base->get_userinfo() !== null || $base->get_host() !== null || $base->get_port() !== null) && $base->get_path() === null)
-                                               {
-                                                       $target->set_path('/' . $relative->get_path());
-                                               }
-                                               elseif (($last_segment = strrpos($base->get_path(), '/')) !== false)
-                                               {
-                                                       $target->set_path(substr($base->get_path(), 0, $last_segment + 1) . $relative->get_path());
-                                               }
-                                               else
-                                               {
-                                                       $target->set_path($relative->get_path());
-                                               }
-                                               $target->set_query($relative->get_query());
-                                       }
-                                       else
-                                       {
-                                               $target->set_path($base->get_path());
-                                               if ($relative->get_query() !== null)
-                                               {
-                                                       $target->set_query($relative->get_query());
-                                               }
-                                               elseif ($base->get_query() !== null)
-                                               {
-                                                       $target->set_query($base->get_query());
-                                               }
-                                       }
-                               }
-                               $target->set_fragment($relative->get_fragment());
-                       }
-                       else
-                       {
-                               // No base URL, just return the relative URL
-                               $target = $relative;
-                       }
-               }
-               else
-               {
-                       $target = $base;
-               }
-               return $target;
-       }
-
-       /**
-        * Parse an IRI into scheme/authority/path/query/fragment segments
-        *
-        * @access private
-        * @param string $iri
-        * @return array
-        */
-       function parse_iri($iri)
-       {
-               preg_match('/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/', $iri, $match);
-               for ($i = count($match); $i <= 9; $i++)
-               {
-                       $match[$i] = '';
-               }
-               return array('scheme' => $match[2], 'authority' => $match[4], 'path' => $match[5], 'query' => $match[7], 'fragment' => $match[9]);
-       }
-
-       /**
-        * Remove dot segments from a path
-        *
-        * @access private
-        * @param string $input
-        * @return string
-        */
-       function remove_dot_segments($input)
-       {
-               $output = '';
-               while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..')
-               {
-                       // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise,
-                       if (strpos($input, '../') === 0)
-                       {
-                               $input = substr($input, 3);
-                       }
-                       elseif (strpos($input, './') === 0)
-                       {
-                               $input = substr($input, 2);
-                       }
-                       // B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise,
-                       elseif (strpos($input, '/./') === 0)
-                       {
-                               $input = substr_replace($input, '/', 0, 3);
-                       }
-                       elseif ($input === '/.')
-                       {
-                               $input = '/';
-                       }
-                       // C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise,
-                       elseif (strpos($input, '/../') === 0)
-                       {
-                               $input = substr_replace($input, '/', 0, 4);
-                               $output = substr_replace($output, '', strrpos($output, '/'));
-                       }
-                       elseif ($input === '/..')
-                       {
-                               $input = '/';
-                               $output = substr_replace($output, '', strrpos($output, '/'));
-                       }
-                       // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise,
-                       elseif ($input === '.' || $input === '..')
-                       {
-                               $input = '';
-                       }
-                       // E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer
-                       elseif (($pos = strpos($input, '/', 1)) !== false)
-                       {
-                               $output .= substr($input, 0, $pos);
-                               $input = substr_replace($input, '', 0, $pos);
-                       }
-                       else
-                       {
-                               $output .= $input;
-                               $input = '';
-                       }
-               }
-               return $output . $input;
-       }
-
-       /**
-        * Replace invalid character with percent encoding
-        *
-        * @access private
-        * @param string $string Input string
-        * @param string $valid_chars Valid characters
-        * @param int $case Normalise case
-        * @return string
-        */
-       function replace_invalid_with_pct_encoding($string, $valid_chars, $case = SIMPLEPIE_SAME_CASE)
-       {
-               // Normalise case
-               if ($case & SIMPLEPIE_LOWERCASE)
-               {
-                       $string = strtolower($string);
-               }
-               elseif ($case & SIMPLEPIE_UPPERCASE)
-               {
-                       $string = strtoupper($string);
-               }
-
-               // Store position and string length (to avoid constantly recalculating this)
-               $position = 0;
-               $strlen = strlen($string);
-
-               // Loop as long as we have invalid characters, advancing the position to the next invalid character
-               while (($position += strspn($string, $valid_chars, $position)) < $strlen)
-               {
-                       // If we have a % character
-                       if ($string[$position] === '%')
-                       {
-                               // If we have a pct-encoded section
-                               if ($position + 2 < $strlen && strspn($string, '0123456789ABCDEFabcdef', $position + 1, 2) === 2)
-                               {
-                                       // Get the the represented character
-                                       $chr = chr(hexdec(substr($string, $position + 1, 2)));
-
-                                       // If the character is valid, replace the pct-encoded with the actual character while normalising case
-                                       if (strpos($valid_chars, $chr) !== false)
-                                       {
-                                               if ($case & SIMPLEPIE_LOWERCASE)
-                                               {
-                                                       $chr = strtolower($chr);
-                                               }
-                                               elseif ($case & SIMPLEPIE_UPPERCASE)
-                                               {
-                                                       $chr = strtoupper($chr);
-                                               }
-                                               $string = substr_replace($string, $chr, $position, 3);
-                                               $strlen -= 2;
-                                               $position++;
-                                       }
-
-                                       // Otherwise just normalise the pct-encoded to uppercase
-                                       else
-                                       {
-                                               $string = substr_replace($string, strtoupper(substr($string, $position + 1, 2)), $position + 1, 2);
-                                               $position += 3;
-                                       }
-                               }
-                               // If we don't have a pct-encoded section, just replace the % with its own esccaped form
-                               else
-                               {
-                                       $string = substr_replace($string, '%25', $position, 1);
-                                       $strlen += 2;
-                                       $position += 3;
-                               }
-                       }
-                       // If we have an invalid character, change into its pct-encoded form
-                       else
-                       {
-                               $replacement = sprintf("%%%02X", ord($string[$position]));
-                               $string = str_replace($string[$position], $replacement, $string);
-                               $strlen = strlen($string);
-                       }
-               }
-               return $string;
-       }
-
-       /**
-        * Check if the object represents a valid IRI
-        *
-        * @access public
-        * @return bool
-        */
-       function is_valid()
-       {
-               return array_sum($this->valid) === count($this->valid);
-       }
-
-       /**
-        * Set the scheme. Returns true on success, false on failure (if there are
-        * any invalid characters).
-        *
-        * @access public
-        * @param string $scheme
-        * @return bool
-        */
-       function set_scheme($scheme)
-       {
-               if ($scheme === null || $scheme === '')
-               {
-                       $this->scheme = null;
-               }
-               else
-               {
-                       $len = strlen($scheme);
-                       switch (true)
-                       {
-                               case $len > 1:
-                                       if (!strspn($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-.', 1))
-                                       {
-                                               $this->scheme = null;
-                                               $this->valid[__FUNCTION__] = false;
-                                               return false;
-                                       }
-
-                               case $len > 0:
-                                       if (!strspn($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 0, 1))
-                                       {
-                                               $this->scheme = null;
-                                               $this->valid[__FUNCTION__] = false;
-                                               return false;
-                                       }
-                       }
-                       $this->scheme = strtolower($scheme);
-               }
-               $this->valid[__FUNCTION__] = true;
-               return true;
-       }
-
-       /**
-        * Set the authority. Returns true on success, false on failure (if there are
-        * any invalid characters).
-        *
-        * @access public
-        * @param string $authority
-        * @return bool
-        */
-       function set_authority($authority)
-       {
-               if (($userinfo_end = strrpos($authority, '@')) !== false)
-               {
-                       $userinfo = substr($authority, 0, $userinfo_end);
-                       $authority = substr($authority, $userinfo_end + 1);
-               }
-               else
-               {
-                       $userinfo = null;
-               }
-
-               if (($port_start = strpos($authority, ':')) !== false)
-               {
-                       $port = substr($authority, $port_start + 1);
-                       $authority = substr($authority, 0, $port_start);
-               }
-               else
-               {
-                       $port = null;
-               }
-
-               return $this->set_userinfo($userinfo) && $this->set_host($authority) && $this->set_port($port);
-       }
-
-       /**
-        * Set the userinfo.
-        *
-        * @access public
-        * @param string $userinfo
-        * @return bool
-        */
-       function set_userinfo($userinfo)
-       {
-               if ($userinfo === null || $userinfo === '')
-               {
-                       $this->userinfo = null;
-               }
-               else
-               {
-                       $this->userinfo = $this->replace_invalid_with_pct_encoding($userinfo, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&\'()*+,;=:');
-               }
-               $this->valid[__FUNCTION__] = true;
-               return true;
-       }
-
-       /**
-        * Set the host. Returns true on success, false on failure (if there are
-        * any invalid characters).
-        *
-        * @access public
-        * @param string $host
-        * @return bool
-        */
-       function set_host($host)
-       {
-               if ($host === null || $host === '')
-               {
-                       $this->host = null;
-                       $this->valid[__FUNCTION__] = true;
-                       return true;
-               }
-               elseif ($host[0] === '[' && substr($host, -1) === ']')
-               {
-                       if (Net_IPv6::checkIPv6(substr($host, 1, -1)))
-                       {
-                               $this->host = $host;
-                               $this->valid[__FUNCTION__] = true;
-                               return true;
-                       }
-                       else
-                       {
-                               $this->host = null;
-                               $this->valid[__FUNCTION__] = false;
-                               return false;
-                       }
-               }
-               else
-               {
-                       $this->host = $this->replace_invalid_with_pct_encoding($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&\'()*+,;=', SIMPLEPIE_LOWERCASE);
-                       $this->valid[__FUNCTION__] = true;
-                       return true;
-               }
-       }
-
-       /**
-        * Set the port. Returns true on success, false on failure (if there are
-        * any invalid characters).
-        *
-        * @access public
-        * @param string $port
-        * @return bool
-        */
-       function set_port($port)
-       {
-               if ($port === null || $port === '')
-               {
-                       $this->port = null;
-                       $this->valid[__FUNCTION__] = true;
-                       return true;
-               }
-               elseif (strspn($port, '0123456789') === strlen($port))
-               {
-                       $this->port = (int) $port;
-                       $this->valid[__FUNCTION__] = true;
-                       return true;
-               }
-               else
-               {
-                       $this->port = null;
-                       $this->valid[__FUNCTION__] = false;
-                       return false;
-               }
-       }
-
-       /**
-        * Set the path.
-        *
-        * @access public
-        * @param string $path
-        * @return bool
-        */
-       function set_path($path)
-       {
-               if ($path === null || $path === '')
-               {
-                       $this->path = null;
-                       $this->valid[__FUNCTION__] = true;
-                       return true;
-               }
-               elseif (substr($path, 0, 2) === '//' && $this->userinfo === null && $this->host === null && $this->port === null)
-               {
-                       $this->path = null;
-                       $this->valid[__FUNCTION__] = false;
-                       return false;
-               }
-               else
-               {
-                       $this->path = $this->replace_invalid_with_pct_encoding($path, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&\'()*+,;=@/');
-                       if ($this->scheme !== null)
-                       {
-                               $this->path = $this->remove_dot_segments($this->path);
-                       }
-                       $this->valid[__FUNCTION__] = true;
-                       return true;
-               }
-       }
-
-       /**
-        * Set the query.
-        *
-        * @access public
-        * @param string $query
-        * @return bool
-        */
-       function set_query($query)
-       {
-               if ($query === null || $query === '')
-               {
-                       $this->query = null;
-               }
-               else
-               {
-                       $this->query = $this->replace_invalid_with_pct_encoding($query, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$\'()*+,;:@/?');
-               }
-               $this->valid[__FUNCTION__] = true;
-               return true;
-       }
-
-       /**
-        * Set the fragment.
-        *
-        * @access public
-        * @param string $fragment
-        * @return bool
-        */
-       function set_fragment($fragment)
-       {
-               if ($fragment === null || $fragment === '')
-               {
-                       $this->fragment = null;
-               }
-               else
-               {
-                       $this->fragment = $this->replace_invalid_with_pct_encoding($fragment, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&\'()*+,;=:@/?');
-               }
-               $this->valid[__FUNCTION__] = true;
-               return true;
-       }
-
-       /**
-        * Get the complete IRI
-        *
-        * @access public
-        * @return string
-        */
-       function get_iri()
-       {
-               $iri = '';
-               if ($this->scheme !== null)
-               {
-                       $iri .= $this->scheme . ':';
-               }
-               if (($authority = $this->get_authority()) !== null)
-               {
-                       $iri .= '//' . $authority;
-               }
-               if ($this->path !== null)
-               {
-                       $iri .= $this->path;
-               }
-               if ($this->query !== null)
-               {
-                       $iri .= '?' . $this->query;
-               }
-               if ($this->fragment !== null)
-               {
-                       $iri .= '#' . $this->fragment;
-               }
-
-               if ($iri !== '')
-               {
-                       return $iri;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       /**
-        * Get the scheme
-        *
-        * @access public
-        * @return string
-        */
-       function get_scheme()
-       {
-               return $this->scheme;
-       }
-
-       /**
-        * Get the complete authority
-        *
-        * @access public
-        * @return string
-        */
-       function get_authority()
-       {
-               $authority = '';
-               if ($this->userinfo !== null)
-               {
-                       $authority .= $this->userinfo . '@';
-               }
-               if ($this->host !== null)
-               {
-                       $authority .= $this->host;
-               }
-               if ($this->port !== null)
-               {
-                       $authority .= ':' . $this->port;
-               }
-
-               if ($authority !== '')
-               {
-                       return $authority;
-               }
-               else
-               {
-                       return null;
-               }
-       }
-
-       /**
-        * Get the user information
-        *
-        * @access public
-        * @return string
-        */
-       function get_userinfo()
-       {
-               return $this->userinfo;
-       }
-
-       /**
-        * Get the host
-        *
-        * @access public
-        * @return string
-        */
-       function get_host()
-       {
-               return $this->host;
-       }
-
-       /**
-        * Get the port
-        *
-        * @access public
-        * @return string
-        */
-       function get_port()
-       {
-               return $this->port;
-       }
-
-       /**
-        * Get the path
-        *
-        * @access public
-        * @return string
-        */
-       function get_path()
-       {
-               return $this->path;
-       }
-
-       /**
-        * Get the query
-        *
-        * @access public
-        * @return string
-        */
-       function get_query()
-       {
-               return $this->query;
-       }
-
-       /**
-        * Get the fragment
-        *
-        * @access public
-        * @return string
-        */
-       function get_fragment()
-       {
-               return $this->fragment;
-       }
-}
-
-/**
- * Class to validate and to work with IPv6 addresses.
- *
- * @package SimplePie
- * @copyright 2003-2005 The PHP Group
- * @license http://www.opensource.org/licenses/bsd-license.php
- * @link http://pear.php.net/package/Net_IPv6
- * @author Alexander Merz <alexander.merz@web.de>
- * @author elfrink at introweb dot nl
- * @author Josh Peck <jmp at joshpeck dot org>
- * @author Geoffrey Sneddon <geoffers@gmail.com>
- */
-class SimplePie_Net_IPv6
-{
-       /**
-        * Removes a possible existing netmask specification of an IP address.
-        *
-        * @param string $ip the (compressed) IP as Hex representation
-        * @return string the IP the without netmask
-        * @since 1.1.0
-        * @access public
-        * @static
-        */
-       function removeNetmaskSpec($ip)
-       {
-               if (strpos($ip, '/') !== false)
-               {
-                       list($addr, $nm) = explode('/', $ip);
-               }
-               else
-               {
-                       $addr = $ip;
-               }
-               return $addr;
-       }
-
-       /**
-        * Uncompresses an IPv6 address
-        *
-        * RFC 2373 allows you to compress zeros in an address to '::'. This
-        * function expects an valid IPv6 address and expands the '::' to
-        * the required zeros.
-        *
-        * Example:      FF01::101      ->      FF01:0:0:0:0:0:0:101
-        *                       ::1            ->      0:0:0:0:0:0:0:1
-        *
-        * @access public
-        * @static
-        * @param string $ip a valid IPv6-address (hex format)
-        * @return string the uncompressed IPv6-address (hex format)
-        */
-       function Uncompress($ip)
-       {
-               $uip = SimplePie_Net_IPv6::removeNetmaskSpec($ip);
-               $c1 = -1;
-               $c2 = -1;
-               if (strpos($ip, '::') !== false)
-               {
-                       list($ip1, $ip2) = explode('::', $ip);
-                       if ($ip1 === '')
-                       {
-                               $c1 = -1;
-                       }
-                       else
-                       {
-                               $pos = 0;
-                               if (($pos = substr_count($ip1, ':')) > 0)
-                               {
-                                       $c1 = $pos;
-                               }
-                               else
-                               {
-                                       $c1 = 0;
-                               }
-                       }
-                       if ($ip2 === '')
-                       {
-                               $c2 = -1;
-                       }
-                       else
-                       {
-                               $pos = 0;
-                               if (($pos = substr_count($ip2, ':')) > 0)
-                               {
-                                       $c2 = $pos;
-                               }
-                               else
-                               {
-                                       $c2 = 0;
-                               }
-                       }
-                       if (strstr($ip2, '.'))
-                       {
-                               $c2++;
-                       }
-                       // ::
-                       if ($c1 === -1 && $c2 === -1)
-                       {
-                               $uip = '0:0:0:0:0:0:0:0';
-                       }
-                       // ::xxx
-                       else if ($c1 === -1)
-                       {
-                               $fill = str_repeat('0:', 7 - $c2);
-                               $uip =  str_replace('::', $fill, $uip);
-                       }
-                       // xxx::
-                       else if ($c2 === -1)
-                       {
-                               $fill = str_repeat(':0', 7 - $c1);
-                               $uip =  str_replace('::', $fill, $uip);
-                       }
-                       // xxx::xxx
-                       else
-                       {
-                               $fill = str_repeat(':0:', 6 - $c2 - $c1);
-                               $uip =  str_replace('::', $fill, $uip);
-                               $uip =  str_replace('::', ':', $uip);
-                       }
-               }
-               return $uip;
-       }
-
-       /**
-        * Splits an IPv6 address into the IPv6 and a possible IPv4 part
-        *
-        * RFC 2373 allows you to note the last two parts of an IPv6 address as
-        * an IPv4 compatible address
-        *
-        * Example:      0:0:0:0:0:0:13.1.68.3
-        *                       0:0:0:0:0:FFFF:129.144.52.38
-        *
-        * @access public
-        * @static
-        * @param string $ip a valid IPv6-address (hex format)
-        * @return array [0] contains the IPv6 part, [1] the IPv4 part (hex format)
-        */
-       function SplitV64($ip)
-       {
-               $ip = SimplePie_Net_IPv6::Uncompress($ip);
-               if (strstr($ip, '.'))
-               {
-                       $pos = strrpos($ip, ':');
-                       $ip[$pos] = '_';
-                       $ipPart = explode('_', $ip);
-                       return $ipPart;
-               }
-               else
-               {
-                       return array($ip, '');
-               }
-       }
-
-       /**
-        * Checks an IPv6 address
-        *
-        * Checks if the given IP is IPv6-compatible
-        *
-        * @access public
-        * @static
-        * @param string $ip a valid IPv6-address
-        * @return bool true if $ip is an IPv6 address
-        */
-       function checkIPv6($ip)
-       {
-               $ipPart = SimplePie_Net_IPv6::SplitV64($ip);
-               $count = 0;
-               if (!empty($ipPart[0]))
-               {
-                       $ipv6 = explode(':', $ipPart[0]);
-                       for ($i = 0; $i < count($ipv6); $i++)
-                       {
-                               $dec = hexdec($ipv6[$i]);
-                               $hex = strtoupper(preg_replace('/^[0]{1,3}(.*[0-9a-fA-F])$/', '\\1', $ipv6[$i]));
-                               if ($ipv6[$i] >= 0 && $dec <= 65535 && $hex === strtoupper(dechex($dec)))
-                               {
-                                       $count++;
-                               }
-                       }
-                       if ($count === 8)
-                       {
-                               return true;
-                       }
-                       elseif ($count === 6 && !empty($ipPart[1]))
-                       {
-                               $ipv4 = explode('.', $ipPart[1]);
-                               $count = 0;
-                               foreach ($ipv4 as $ipv4_part)
-                               {
-                                       if ($ipv4_part >= 0 && $ipv4_part <= 255 && preg_match('/^\d{1,3}$/', $ipv4_part))
-                                       {
-                                               $count++;
-                                       }
-                               }
-                               if ($count === 4)
-                               {
-                                       return true;
-                               }
-                       }
-                       else
-                       {
-                               return false;
-                       }
-
-               }
-               else
-               {
-                       return false;
-               }
-       }
-}
-
-/**
- * Date Parser
- *
- * @package SimplePie
- */
-class SimplePie_Parse_Date
-{
-       /**
-        * Input data
-        *
-        * @access protected
-        * @var string
-        */
-       var $date;
-
-       /**
-        * List of days, calendar day name => ordinal day number in the week
-        *
-        * @access protected
-        * @var array
-        */
-       var $day = array(
-               // English
-               'mon' => 1,
-               'monday' => 1,
-               'tue' => 2,
-               'tuesday' => 2,
-               'wed' => 3,
-               'wednesday' => 3,
-               'thu' => 4,
-               'thursday' => 4,
-               'fri' => 5,
-               'friday' => 5,
-               'sat' => 6,
-               'saturday' => 6,
-               'sun' => 7,
-               'sunday' => 7,
-               // Dutch
-               'maandag' => 1,
-               'dinsdag' => 2,
-               'woensdag' => 3,
-               'donderdag' => 4,
-               'vrijdag' => 5,
-               'zaterdag' => 6,
-               'zondag' => 7,
-               // French
-               'lundi' => 1,
-               'mardi' => 2,
-               'mercredi' => 3,
-               'jeudi' => 4,
-               'vendredi' => 5,
-               'samedi' => 6,
-               'dimanche' => 7,
-               // German
-               'montag' => 1,
-               'dienstag' => 2,
-               'mittwoch' => 3,
-               'donnerstag' => 4,
-               'freitag' => 5,
-               'samstag' => 6,
-               'sonnabend' => 6,
-               'sonntag' => 7,
-               // Italian
-               'lunedì' => 1,
-               'martedì' => 2,
-               'mercoledì' => 3,
-               'giovedì' => 4,
-               'venerdì' => 5,
-               'sabato' => 6,
-               'domenica' => 7,
-               // Spanish
-               'lunes' => 1,
-               'martes' => 2,
-               'miércoles' => 3,
-               'jueves' => 4,
-               'viernes' => 5,
-               'sábado' => 6,
-               'domingo' => 7,
-               // Finnish
-               'maanantai' => 1,
-               'tiistai' => 2,
-               'keskiviikko' => 3,
-               'torstai' => 4,
-               'perjantai' => 5,
-               'lauantai' => 6,
-               'sunnuntai' => 7,
-               // Hungarian
-               'hétfő' => 1,
-               'kedd' => 2,
-               'szerda' => 3,
-               'csütörtok' => 4,
-               'péntek' => 5,
-               'szombat' => 6,
-               'vasárnap' => 7,
-               // Greek
-               'Δευ' => 1,
-               'Τρι' => 2,
-               'Τετ' => 3,
-               'Πεμ' => 4,
-               'Παρ' => 5,
-               'Σαβ' => 6,
-               'Κυρ' => 7,
-       );
-
-       /**
-        * List of months, calendar month name => calendar month number
-        *
-        * @access protected
-        * @var array
-        */
-       var $month = array(
-               // English
-               'jan' => 1,
-               'january' => 1,
-               'feb' => 2,
-               'february' => 2,
-               'mar' => 3,
-               'march' => 3,
-               'apr' => 4,
-               'april' => 4,
-               'may' => 5,
-               // No long form of May
-               'jun' => 6,
-               'june' => 6,
-               'jul' => 7,
-               'july' => 7,
-               'aug' => 8,
-               'august' => 8,
-               'sep' => 9,
-               'september' => 8,
-               'oct' => 10,
-               'october' => 10,
-               'nov' => 11,
-               'november' => 11,
-               'dec' => 12,
-               'december' => 12,
-               // Dutch
-               'januari' => 1,
-               'februari' => 2,
-               'maart' => 3,
-               'april' => 4,
-               'mei' => 5,
-               'juni' => 6,
-               'juli' => 7,
-               'augustus' => 8,
-               'september' => 9,
-               'oktober' => 10,
-               'november' => 11,
-               'december' => 12,
-               // French
-               'janvier' => 1,
-               'février' => 2,
-               'mars' => 3,
-               'avril' => 4,
-               'mai' => 5,
-               'juin' => 6,
-               'juillet' => 7,
-               'août' => 8,
-               'septembre' => 9,
-               'octobre' => 10,
-               'novembre' => 11,
-               'décembre' => 12,
-               // German
-               'januar' => 1,
-               'februar' => 2,
-               'märz' => 3,
-               'april' => 4,
-               'mai' => 5,
-               'juni' => 6,
-               'juli' => 7,
-               'august' => 8,
-               'september' => 9,
-               'oktober' => 10,
-               'november' => 11,
-               'dezember' => 12,
-               // Italian
-               'gennaio' => 1,
-               'febbraio' => 2,
-               'marzo' => 3,
-               'aprile' => 4,
-               'maggio' => 5,
-               'giugno' => 6,
-               'luglio' => 7,
-               'agosto' => 8,
-               'settembre' => 9,
-               'ottobre' => 10,
-               'novembre' => 11,
-               'dicembre' => 12,
-               // Spanish
-               'enero' => 1,
-               'febrero' => 2,
-               'marzo' => 3,
-               'abril' => 4,
-               'mayo' => 5,
-               'junio' => 6,
-               'julio' => 7,
-               'agosto' => 8,
-               'septiembre' => 9,
-               'setiembre' => 9,
-               'octubre' => 10,
-               'noviembre' => 11,
-               'diciembre' => 12,
-               // Finnish
-               'tammikuu' => 1,
-               'helmikuu' => 2,
-               'maaliskuu' => 3,
-               'huhtikuu' => 4,
-               'toukokuu' => 5,
-               'kesäkuu' => 6,
-               'heinäkuu' => 7,
-               'elokuu' => 8,
-               'suuskuu' => 9,
-               'lokakuu' => 10,
-               'marras' => 11,
-               'joulukuu' => 12,
-               // Hungarian
-               'január' => 1,
-               'február' => 2,
-               'március' => 3,
-               'április' => 4,
-               'május' => 5,
-               'június' => 6,
-               'július' => 7,
-               'augusztus' => 8,
-               'szeptember' => 9,
-               'október' => 10,
-               'november' => 11,
-               'december' => 12,
-               // Greek
-               'Ιαν' => 1,
-               'Φεβ' => 2,
-               'Μάώ' => 3,
-               'Μαώ' => 3,
-               'Απρ' => 4,
-               'Μάι' => 5,
-               'Μαϊ' => 5,
-               'Μαι' => 5,
-               'Ιούν' => 6,
-               'Ιον' => 6,
-               'Ιούλ' => 7,
-               'Ιολ' => 7,
-               'Αύγ' => 8,
-               'Αυγ' => 8,
-               'Σεπ' => 9,
-               'Οκτ' => 10,
-               'Νοέ' => 11,
-               'Δεκ' => 12,
-       );
-
-       /**
-        * List of timezones, abbreviation => offset from UTC
-        *
-        * @access protected
-        * @var array
-        */
-       var $timezone = array(
-               'ACDT' => 37800,
-               'ACIT' => 28800,
-               'ACST' => 34200,
-               'ACT' => -18000,
-               'ACWDT' => 35100,
-               'ACWST' => 31500,
-               'AEDT' => 39600,
-               'AEST' => 36000,
-               'AFT' => 16200,
-               'AKDT' => -28800,
-               'AKST' => -32400,
-               'AMDT' => 18000,
-               'AMT' => -14400,
-               'ANAST' => 46800,
-               'ANAT' => 43200,
-               'ART' => -10800,
-               'AZOST' => -3600,
-               'AZST' => 18000,
-               'AZT' => 14400,
-               'BIOT' => 21600,
-               'BIT' => -43200,
-               'BOT' => -14400,
-               'BRST' => -7200,
-               'BRT' => -10800,
-               'BST' => 3600,
-               'BTT' => 21600,
-               'CAST' => 18000,
-               'CAT' => 7200,
-               'CCT' => 23400,
-               'CDT' => -18000,
-               'CEDT' => 7200,
-               'CET' => 3600,
-               'CGST' => -7200,
-               'CGT' => -10800,
-               'CHADT' => 49500,
-               'CHAST' => 45900,
-               'CIST' => -28800,
-               'CKT' => -36000,
-               'CLDT' => -10800,
-               'CLST' => -14400,
-               'COT' => -18000,
-               'CST' => -21600,
-               'CVT' => -3600,
-               'CXT' => 25200,
-               'DAVT' => 25200,
-               'DTAT' => 36000,
-               'EADT' => -18000,
-               'EAST' => -21600,
-               'EAT' => 10800,
-               'ECT' => -18000,
-               'EDT' => -14400,
-               'EEST' => 10800,
-               'EET' => 7200,
-               'EGT' => -3600,
-               'EKST' => 21600,
-               'EST' => -18000,
-               'FJT' => 43200,
-               'FKDT' => -10800,
-               'FKST' => -14400,
-               'FNT' => -7200,
-               'GALT' => -21600,
-               'GEDT' => 14400,
-               'GEST' => 10800,
-               'GFT' => -10800,
-               'GILT' => 43200,
-               'GIT' => -32400,
-               'GST' => 14400,
-               'GST' => -7200,
-               'GYT' => -14400,
-               'HAA' => -10800,
-               'HAC' => -18000,
-               'HADT' => -32400,
-               'HAE' => -14400,
-               'HAP' => -25200,
-               'HAR' => -21600,
-               'HAST' => -36000,
-               'HAT' => -9000,
-               'HAY' => -28800,
-               'HKST' => 28800,
-               'HMT' => 18000,
-               'HNA' => -14400,
-               'HNC' => -21600,
-               'HNE' => -18000,
-               'HNP' => -28800,
-               'HNR' => -25200,
-               'HNT' => -12600,
-               'HNY' => -32400,
-               'IRDT' => 16200,
-               'IRKST' => 32400,
-               'IRKT' => 28800,
-               'IRST' => 12600,
-               'JFDT' => -10800,
-               'JFST' => -14400,
-               'JST' => 32400,
-               'KGST' => 21600,
-               'KGT' => 18000,
-               'KOST' => 39600,
-               'KOVST' => 28800,
-               'KOVT' => 25200,
-               'KRAST' => 28800,
-               'KRAT' => 25200,
-               'KST' => 32400,
-               'LHDT' => 39600,
-               'LHST' => 37800,
-               'LINT' => 50400,
-               'LKT' => 21600,
-               'MAGST' => 43200,
-               'MAGT' => 39600,
-               'MAWT' => 21600,
-               'MDT' => -21600,
-               'MESZ' => 7200,
-               'MEZ' => 3600,
-               'MHT' => 43200,
-               'MIT' => -34200,
-               'MNST' => 32400,
-               'MSDT' => 14400,
-               'MSST' => 10800,
-               'MST' => -25200,
-               'MUT' => 14400,
-               'MVT' => 18000,
-               'MYT' => 28800,
-               'NCT' => 39600,
-               'NDT' => -9000,
-               'NFT' => 41400,
-               'NMIT' => 36000,
-               'NOVST' => 25200,
-               'NOVT' => 21600,
-               'NPT' => 20700,
-               'NRT' => 43200,
-               'NST' => -12600,
-               'NUT' => -39600,
-               'NZDT' => 46800,
-               'NZST' => 43200,
-               'OMSST' => 25200,
-               'OMST' => 21600,
-               'PDT' => -25200,
-               'PET' => -18000,
-               'PETST' => 46800,
-               'PETT' => 43200,
-               'PGT' => 36000,
-               'PHOT' => 46800,
-               'PHT' => 28800,
-               'PKT' => 18000,
-               'PMDT' => -7200,
-               'PMST' => -10800,
-               'PONT' => 39600,
-               'PST' => -28800,
-               'PWT' => 32400,
-               'PYST' => -10800,
-               'PYT' => -14400,
-               'RET' => 14400,
-               'ROTT' => -10800,
-               'SAMST' => 18000,
-               'SAMT' => 14400,
-               'SAST' => 7200,
-               'SBT' => 39600,
-               'SCDT' => 46800,
-               'SCST' => 43200,
-               'SCT' => 14400,
-               'SEST' => 3600,
-               'SGT' => 28800,
-               'SIT' => 28800,
-               'SRT' => -10800,
-               'SST' => -39600,
-               'SYST' => 10800,
-               'SYT' => 7200,
-               'TFT' => 18000,
-               'THAT' => -36000,
-               'TJT' => 18000,
-               'TKT' => -36000,
-               'TMT' => 18000,
-               'TOT' => 46800,
-               'TPT' => 32400,
-               'TRUT' => 36000,
-               'TVT' => 43200,
-               'TWT' => 28800,
-               'UYST' => -7200,
-               'UYT' => -10800,
-               'UZT' => 18000,
-               'VET' => -14400,
-               'VLAST' => 39600,
-               'VLAT' => 36000,
-               'VOST' => 21600,
-               'VUT' => 39600,
-               'WAST' => 7200,
-               'WAT' => 3600,
-               'WDT' => 32400,
-               'WEST' => 3600,
-               'WFT' => 43200,
-               'WIB' => 25200,
-               'WIT' => 32400,
-               'WITA' => 28800,
-               'WKST' => 18000,
-               'WST' => 28800,
-               'YAKST' => 36000,
-               'YAKT' => 32400,
-               'YAPT' => 36000,
-               'YEKST' => 21600,
-               'YEKT' => 18000,
-       );
-
-       /**
-        * Cached PCRE for SimplePie_Parse_Date::$day
-        *
-        * @access protected
-        * @var string
-        */
-       var $day_pcre;
-
-       /**
-        * Cached PCRE for SimplePie_Parse_Date::$month
-        *
-        * @access protected
-        * @var string
-        */
-       var $month_pcre;
-
-       /**
-        * Array of user-added callback methods
-        *
-        * @access private
-        * @var array
-        */
-       var $built_in = array();
-
-       /**
-        * Array of user-added callback methods
-        *
-        * @access private
-        * @var array
-        */
-       var $user = array();
-
-       /**
-        * Create new SimplePie_Parse_Date object, and set self::day_pcre,
-        * self::month_pcre, and self::built_in
-        *
-        * @access private
-        */
-       function SimplePie_Parse_Date()
-       {
-               $this->day_pcre = '(' . implode(array_keys($this->day), '|') . ')';
-               $this->month_pcre = '(' . implode(array_keys($this->month), '|') . ')';
-
-               static $cache;
-               if (!isset($cache[get_class($this)]))
-               {
-                       $all_methods = get_class_methods($this);
-
-                       foreach ($all_methods as $method)
-                       {
-                               if (strtolower(substr($method, 0, 5)) === 'date_')
-                               {
-                                       $cache[get_class($this)][] = $method;
-                               }
-                       }
-               }
-
-               foreach ($cache[get_class($this)] as $method)
-               {
-                       $this->built_in[] = $method;
-               }
-       }
-
-       /**
-        * Get the object
-        *
-        * @access public
-        */
-       function get()
-       {
-               static $object;
-               if (!$object)
-               {
-                       $object = new SimplePie_Parse_Date;
-               }
-               return $object;
-       }
-
-       /**
-        * Parse a date
-        *
-        * @final
-        * @access public
-        * @param string $date Date to parse
-        * @return int Timestamp corresponding to date string, or false on failure
-        */
-       function parse($date)
-       {
-               foreach ($this->user as $method)
-               {
-                       if (($returned = call_user_func($method, $date)) !== false)
-                       {
-                               return $returned;
-                       }
-               }
-
-               foreach ($this->built_in as $method)
-               {
-                       if (($returned = call_user_func(array(&$this, $method), $date)) !== false)
-                       {
-                               return $returned;
-                       }
-               }
-
-               return false;
-       }
-
-       /**
-        * Add a callback method to parse a date
-        *
-        * @final
-        * @access public
-        * @param callback $callback
-        */
-       function add_callback($callback)
-       {
-               if (is_callable($callback))
-               {
-                       $this->user[] = $callback;
-               }
-               else
-               {
-                       trigger_error('User-supplied function must be a valid callback', E_USER_WARNING);
-               }
-       }
-
-       /**
-        * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as
-        * well as allowing any of upper or lower case "T", horizontal tabs, or
-        * spaces to be used as the time seperator (including more than one))
-        *
-        * @access protected
-        * @return int Timestamp
-        */
-       function date_w3cdtf($date)
-       {
-               static $pcre;
-               if (!$pcre)
-               {
-                       $year = '([0-9]{4})';
-                       $month = $day = $hour = $minute = $second = '([0-9]{2})';
-                       $decimal = '([0-9]*)';
-                       $zone = '(?:(Z)|([+\-])([0-9]{1,2}):?([0-9]{1,2}))';
-                       $pcre = '/^' . $year . '(?:-?' . $month . '(?:-?' . $day . '(?:[Tt\x09\x20]+' . $hour . '(?::?' . $minute . '(?::?' . $second . '(?:.' . $decimal . ')?)?)?' . $zone . ')?)?)?$/';
-               }
-               if (preg_match($pcre, $date, $match))
-               {
-                       /*
-                       Capturing subpatterns:
-                       1: Year
-                       2: Month
-                       3: Day
-                       4: Hour
-                       5: Minute
-                       6: Second
-                       7: Decimal fraction of a second
-                       8: Zulu
-                       9: Timezone ±
-                       10: Timezone hours
-                       11: Timezone minutes
-                       */
-
-                       // Fill in empty matches
-                       for ($i = count($match); $i <= 3; $i++)
-                       {
-                               $match[$i] = '1';
-                       }
-
-                       for ($i = count($match); $i <= 7; $i++)
-                       {
-                               $match[$i] = '0';
-                       }
-
-                       // Numeric timezone
-                       if (isset($match[9]) && $match[9] !== '')
-                       {
-                               $timezone = $match[10] * 3600;
-                               $timezone += $match[11] * 60;
-                               if ($match[9] === '-')
-                               {
-                                       $timezone = 0 - $timezone;
-                               }
-                       }
-                       else
-                       {
-                               $timezone = 0;
-                       }
-
-                       // Convert the number of seconds to an integer, taking decimals into account
-                       $second = round($match[6] + $match[7] / pow(10, strlen($match[7])));
-
-                       return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone;
-               }
-               else
-               {
-                       return false;
-               }
-       }
-
-       /**
-        * Remove RFC822 comments
-        *
-        * @access protected
-        * @param string $data Data to strip comments from
-        * @return string Comment stripped string
-        */
-       function remove_rfc2822_comments($string)
-       {
-               $string = (string) $string;
-               $position = 0;
-               $length = strlen($string);
-               $depth = 0;
-
-               $output = '';
-
-               while ($position < $length && ($pos = strpos($string, '(', $position)) !== false)
-               {
-                       $output .= substr($string, $position, $pos - $position);
-                       $position = $pos + 1;
-                       if ($string[$pos - 1] !== '\\')
-                       {
-                               $depth++;
-                               while ($depth && $position < $length)
-                               {
-                                       $position += strcspn($string, '()', $position);
-                                       if ($string[$position - 1] === '\\')
-                                       {
-                                               $position++;
-                                               continue;
-                                       }
-                                       elseif (isset($string[$position]))
-                                       {
-                                               switch ($string[$position])
-                                               {
-                                                       case '(':
-                                                               $depth++;
-                                                               break;
-
-                                                       case ')':
-                                                               $depth--;
-                                                               break;
-                                               }
-                                               $position++;
-                                       }
-                                       else
-                                       {
-                                               break;
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               $output .= '(';
-                       }
-               }
-               $output .= substr($string, $position);
-
-               return $output;
-       }
-
-       /**
-        * Parse RFC2822's date format
-        *
-        * @access protected
-        * @return int Timestamp
-        */
-       function date_rfc2822($date)
-       {
-               static $pcre;
-               if (!$pcre)
-               {
-                       $wsp = '[\x09\x20]';
-                       $fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)';
-                       $optional_fws = $fws . '?';
-                       $day_name = $this->day_pcre;
-                       $month = $this->month_pcre;
-                       $day = '([0-9]{1,2})';
-                       $hour = $minute = $second = '([0-9]{2})';
-                       $year = '([0-9]{2,4})';
-                       $num_zone = '([+\-])([0-9]{2})([0-9]{2})';
-                       $character_zone = '([A-Z]{1,5})';
-                       $zone = '(?:' . $num_zone . '|' . $character_zone . ')';
-                       $pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i';
-               }
-               if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match))
-               {
-                       /*
-                       Capturing subpatterns:
-                       1: Day name
-                       2: Day
-                       3: Month
-                       4: Year
-                       5: Hour
-                       6: Minute
-                       7: Second
-                       8: Timezone ±
-                       9: Timezone hours
-                       10: Timezone minutes
-                       11: Alphabetic timezone
-                       */
-
-                       // Find the month number
-                       $month = $this->month[strtolower($match[3])];
-
-                       // Numeric timezone
-                       if ($match[8] !== '')
-                       {
-                               $timezone = $match[9] * 3600;
-                               $timezone += $match[10] * 60;
-                               if ($match[8] === '-')
-                               {
-                                       $timezone = 0 - $timezone;
-                               }
-                       }
-                       // Character timezone
-                       elseif (isset($this->timezone[strtoupper($match[11])]))
-                       {
-                               $timezone = $this->timezone[strtoupper($match[11])];
-                       }
-                       // Assume everything else to be -0000
-                       else
-                       {
-                               $timezone = 0;
-                       }
-
-                       // Deal with 2/3 digit years
-                       if ($match[4] < 50)
-                       {
-                               $match[4] += 2000;
-                       }
-                       elseif ($match[4] < 1000)
-                       {
-                               $match[4] += 1900;
-                       }
-
-                       // Second is optional, if it is empty set it to zero
-                       if ($match[7] !== '')
-                       {
-                               $second = $match[7];
-                       }
-                       else
-                       {
-                               $second = 0;
-                       }
-
-                       return gmmktime($match[5], $match[6], $second, $month, $match[2], $match[4]) - $timezone;
-               }
-               else
-               {
-                       return false;
-               }
-       }
-
-       /**
-        * Parse RFC850's date format
-        *
-        * @access protected
-        * @return int Timestamp
-        */
-       function date_rfc850($date)
-       {
-               static $pcre;
-               if (!$pcre)
-               {
-                       $space = '[\x09\x20]+';
-                       $day_name = $this->day_pcre;
-                       $month = $this->month_pcre;
-                       $day = '([0-9]{1,2})';
-                       $year = $hour = $minute = $second = '([0-9]{2})';
-                       $zone = '([A-Z]{1,5})';
-                       $pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i';
-               }
-               if (preg_match($pcre, $date, $match))
-               {
-                       /*
-                       Capturing subpatterns:
-                       1: Day name
-                       2: Day
-                       3: Month
-                       4: Year
-                       5: Hour
-                       6: Minute
-                       7: Second
-                       8: Timezone
-                       */
-
-                       // Month
-                       $month = $this->month[strtolower($match[3])];
-
-                       // Character timezone
-                       if (isset($this->timezone[strtoupper($match[8])]))
-                       {
-                               $timezone = $this->timezone[strtoupper($match[8])];
-                       }
-                       // Assume everything else to be -0000
-                       else
-                       {
-                               $timezone = 0;
-                       }
-
-                       // Deal with 2 digit year
-                       if ($match[4] < 50)
-                       {
-                               $match[4] += 2000;
-                       }
-                       else
-                       {
-                               $match[4] += 1900;
-                       }
-
-                       return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone;
-               }
-               else
-               {
-                       return false;
-               }
-       }
-
-       /**
-        * Parse C99's asctime()'s date format
-        *
-        * @access protected
-        * @return int Timestamp
-        */
-       function date_asctime($date)
-       {
-               static $pcre;
-               if (!$pcre)
-               {
-                       $space = '[\x09\x20]+';
-                       $wday_name = $this->day_pcre;
-                       $mon_name = $this->month_pcre;
-                       $day = '([0-9]{1,2})';
-                       $hour = $sec = $min = '([0-9]{2})';
-                       $year = '([0-9]{4})';
-                       $terminator = '\x0A?\x00?';
-                       $pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i';
-               }
-               if (preg_match($pcre, $date, $match))
-               {
-                       /*
-                       Capturing subpatterns:
-                       1: Day name
-                       2: Month
-                       3: Day
-                       4: Hour
-                       5: Minute
-                       6: Second
-                       7: Year
-                       */
-
-                       $month = $this->month[strtolower($match[2])];
-                       return gmmktime($match[4], $match[5], $match[6], $month, $match[3], $match[7]);
-               }
-               else
-               {
-                       return false;
-               }
-       }
-
-       /**
-        * Parse dates using strtotime()
-        *
-        * @access protected
-        * @return int Timestamp
-        */
-       function date_strtotime($date)
-       {
-               $strtotime = strtotime($date);
-               if ($strtotime === -1 || $strtotime === false)
-               {
-                       return false;
-               }
-               else
-               {
-                       return $strtotime;
-               }
-       }
-}
-
-/**
- * Content-type sniffing
- *
- * @package SimplePie
- */
-class SimplePie_Content_Type_Sniffer
-{
-       /**
-        * File object
-        *
-        * @var SimplePie_File
-        * @access private
-        */
-       var $file;
-
-       /**
-        * Create an instance of the class with the input file
-        *
-        * @access public
-        * @param SimplePie_Content_Type_Sniffer $file Input file
-        */
-       function SimplePie_Content_Type_Sniffer($file)
-       {
-               $this->file = $file;
-       }
-
-       /**
-        * Get the Content-Type of the specified file
-        *
-        * @access public
-        * @return string Actual Content-Type
-        */
-       function get_type()
-       {
-               if (isset($this->file->headers['content-type']))
-               {
-                       if (!isset($this->file->headers['content-encoding'])
-                               && ($this->file->headers['content-type'] === 'text/plain'
-                                       || $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1'
-                                       || $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1'))
-                       {
-                               return $this->text_or_binary();
-                       }
-
-                       if (($pos = strpos($this->file->headers['content-type'], ';')) !== false)
-                       {
-                               $official = substr($this->file->headers['content-type'], 0, $pos);
-                       }
-                       else
-                       {
-                               $official = $this->file->headers['content-type'];
-                       }
-                       $official = strtolower($official);
-
-                       if ($official === 'unknown/unknown'
-                               || $official === 'application/unknown')
-                       {
-                               return $this->unknown();
-                       }
-                       elseif (substr($official, -4) === '+xml'
-                               || $official === 'text/xml'
-                               || $official === 'application/xml')
-                       {
-                               return $official;
-                       }
-                       elseif (substr($official, 0, 6) === 'image/')
-                       {
-                               if ($return = $this->image())
-                               {
-                                       return $return;
-                               }
-                               else
-                               {
-                                       return $official;
-                               }
-                       }
-                       elseif ($official === 'text/html')
-                       {
-                               return $this->feed_or_html();
-                       }
-                       else
-                       {
-                               return $official;
-                       }
-               }
-               else
-               {
-                       return $this->unknown();
-               }
-       }
-
-       /**
-        * Sniff text or binary
-        *
-        * @access private
-        * @return string Actual Content-Type
-        */
-       function text_or_binary()
-       {
-               if (substr($this->file->body, 0, 2) === "\xFE\xFF"
-                       || substr($this->file->body, 0, 2) === "\xFF\xFE"
-                       || substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF"
-                       || substr($this->file->body, 0, 3) === "\xEF\xBB\xBF")
-               {
-                       return 'text/plain';
-               }
-               elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body))
-               {
-                       return 'application/octect-stream';
-               }
-               else
-               {
-                       return 'text/plain';
-               }
-       }
-
-       /**
-        * Sniff unknown
-        *
-        * @access private
-        * @return string Actual Content-Type
-        */
-       function unknown()
-       {
-               $ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20");
-               if (strtolower(substr($this->file->body, $ws, 14)) === '<!doctype html'
-                       || strtolower(substr($this->file->body, $ws, 5)) === '<html'
-                       || strtolower(substr($this->file->body, $ws, 7)) === '<script')
-               {
-                       return 'text/html';
-               }
-               elseif (substr($this->file->body, 0, 5) === '%PDF-')
-               {
-                       return 'application/pdf';
-               }
-               elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-')
-               {
-                       return 'application/postscript';
-               }
-               elseif (substr($this->file->body, 0, 6) === 'GIF87a'
-                       || substr($this->file->body, 0, 6) === 'GIF89a')
-               {
-                       return 'image/gif';
-               }
-               elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A")
-               {
-                       return 'image/png';
-               }
-               elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF")
-               {
-                       return 'image/jpeg';
-               }
-               elseif (substr($this->file->body, 0, 2) === "\x42\x4D")
-               {
-                       return 'image/bmp';
-               }
-               else
-               {
-                       return $this->text_or_binary();
-               }
-       }
-
-       /**
-        * Sniff images
-        *
-        * @access private
-        * @return string Actual Content-Type
-        */
-       function image()
-       {
-               if (substr($this->file->body, 0, 6) === 'GIF87a'
-                       || substr($this->file->body, 0, 6) === 'GIF89a')
-               {
-                       return 'image/gif';
-               }
-               elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A")
-               {
-                       return 'image/png';
-               }
-               elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF")
-               {
-                       return 'image/jpeg';
-               }
-               elseif (substr($this->file->body, 0, 2) === "\x42\x4D")
-               {
-                       return 'image/bmp';
-               }
-               else
-               {
-                       return false;
-               }
-       }
-
-       /**
-        * Sniff HTML
-        *
-        * @access private
-        * @return string Actual Content-Type
-        */
-       function feed_or_html()
-       {
-               $len = strlen($this->file->body);
-               $pos = strspn($this->file->body, "\x09\x0A\x0D\x20");
-
-               while ($pos < $len)
-               {
-                       switch ($this->file->body[$pos])
-                       {
-                               case "\x09":
-                               case "\x0A":
-                               case "\x0D":
-                               case "\x20":
-                                       $pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos);
-                                       continue 2;
-
-                               case '<':
-                                       $pos++;
-                                       break;
-
-                               default:
-                                       return 'text/html';
-                       }
-
-                       if (substr($this->file->body, $pos, 3) === '!--')
-                       {
-                               $pos += 3;
-                               if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false)
-                               {
-                                       $pos += 3;
-                               }
-                               else
-                               {
-                                       return 'text/html';
-                               }
-                       }
-                       elseif (substr($this->file->body, $pos, 1) === '!')
-                       {
-                               if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false)
-                               {
-                                       $pos++;
-                               }
-                               else
-                               {
-                                       return 'text/html';
-                               }
-                       }
-                       elseif (substr($this->file->body, $pos, 1) === '?')
-                       {
-                               if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false)
-                               {
-                                       $pos += 2;
-                               }
-                               else
-                               {
-                                       return 'text/html';
-                               }
-                       }
-                       elseif (substr($this->file->body, $pos, 3) === 'rss'
-                               || substr($this->file->body, $pos, 7) === 'rdf:RDF')
-                       {
-                               return 'application/rss+xml';
-                       }
-                       elseif (substr($this->file->body, $pos, 4) === 'feed')
-                       {
-                               return 'application/atom+xml';
-                       }
-                       else
-                       {
-                               return 'text/html';
-                       }
-               }
-
-               return 'text/html';
-       }
-}
-
-/**
- * Parses the XML Declaration
- *
- * @package SimplePie
- */
-class SimplePie_XML_Declaration_Parser
-{
-       /**
-        * XML Version
-        *
-        * @access public
-        * @var string
-        */
-       var $version = '1.0';
-
-       /**
-        * Encoding
-        *
-        * @access public
-        * @var string
-        */
-       var $encoding = 'UTF-8';
-
-       /**
-        * Standalone
-        *
-        * @access public
-        * @var bool
-        */
-       var $standalone = false;
-
-       /**
-        * Current state of the state machine
-        *
-        * @access private
-        * @var string
-        */
-       var $state = 'before_version_name';
-
-       /**
-        * Input data
-        *
-        * @access private
-        * @var string
-        */
-       var $data = '';
-
-       /**
-        * Input data length (to avoid calling strlen() everytime this is needed)
-        *
-        * @access private
-        * @var int
-        */
-       var $data_length = 0;
-
-       /**
-        * Current position of the pointer
-        *
-        * @var int
-        * @access private
-        */
-       var $position = 0;
-
-       /**
-        * Create an instance of the class with the input data
-        *
-        * @access public
-        * @param string $data Input data
-        */
-       function SimplePie_XML_Declaration_Parser($data)
-       {
-               $this->data = $data;
-               $this->data_length = strlen($this->data);
-       }
-
-       /**
-        * Parse the input data
-        *
-        * @access public
-        * @return bool true on success, false on failure
-        */
-       function parse()
-       {
-               while ($this->state && $this->state !== 'emit' && $this->has_data())
-               {
-                       $state = $this->state;
-                       $this->$state();
-               }
-               $this->data = '';
-               if ($this->state === 'emit')
-               {
-                       return true;
-               }
-               else
-               {
-                       $this->version = '';
-                       $this->encoding = '';
-                       $this->standalone = '';
-                       return false;
-               }
-       }
-
-       /**
-        * Check whether there is data beyond the pointer
-        *
-        * @access private
-        * @return bool true if there is further data, false if not
-        */
-       function has_data()
-       {
-               return (bool) ($this->position < $this->data_length);
-       }
-
-       /**
-        * Advance past any whitespace
-        *
-        * @return int Number of whitespace characters passed
-        */
-       function skip_whitespace()
-       {
-               $whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position);
-               $this->position += $whitespace;
-               return $whitespace;
-       }
-
-       /**
-        * Read value
-        */
-       function get_value()
-       {
-               $quote = substr($this->data, $this->position, 1);
-               if ($quote === '"' || $quote === "'")
-               {
-                       $this->position++;
-                       $len = strcspn($this->data, $quote, $this->position);
-                       if ($this->has_data())
-                       {
-                               $value = substr($this->data, $this->position, $len);
-                               $this->position += $len + 1;
-                               return $value;
-                       }
-               }
-               return false;
-       }
-
-       function before_version_name()
-       {
-               if ($this->skip_whitespace())
-               {
-                       $this->state = 'version_name';
-               }
-               else
-               {
-                       $this->state = false;
-               }
-       }
-
-       function version_name()
-       {
-               if (substr($this->data, $this->position, 7) === 'version')
-               {
-                       $this->position += 7;
-                       $this->skip_whitespace();
-                       $this->state = 'version_equals';
-               }
-               else
-               {
-                       $this->state = false;
-               }
-       }
-
-       function version_equals()
-       {
-               if (substr($this->data, $this->position, 1) === '=')
-               {
-                       $this->position++;
-                       $this->skip_whitespace();
-                       $this->state = 'version_value';
-               }
-               else
-               {
-                       $this->state = false;
-               }
-       }
-
-       function version_value()
-       {
-               if ($this->version = $this->get_value())
-               {
-                       $this->skip_whitespace();
-                       if ($this->has_data())
-                       {
-                               $this->state = 'encoding_name';
-                       }
-                       else
-                       {
-                               $this->state = 'emit';
-                       }
-               }
-               else
-               {
-                       $this->state = false;
-               }
-       }
-
-       function encoding_name()
-       {
-               if (substr($this->data, $this->position, 8) === 'encoding')
-               {
-                       $this->position += 8;
-                       $this->skip_whitespace();
-                       $this->state = 'encoding_equals';
-               }
-               else
-               {
-                       $this->state = 'standalone_name';
-               }
-       }
-
-       function encoding_equals()
-       {
-               if (substr($this->data, $this->position, 1) === '=')
-               {
-                       $this->position++;
-                       $this->skip_whitespace();
-                       $this->state = 'encoding_value';
-               }
-               else
-               {
-                       $this->state = false;
-               }
-       }
-
-       function encoding_value()
-       {
-               if ($this->encoding = $this->get_value())
-               {
-                       $this->skip_whitespace();
-                       if ($this->has_data())
-                       {
-                               $this->state = 'standalone_name';
-                       }
-                       else
-                       {
-                               $this->state = 'emit';
-                       }
-               }
-               else
-               {
-                       $this->state = false;
-               }
-       }
-
-       function standalone_name()
-       {
-               if (substr($this->data, $this->position, 10) === 'standalone')
-               {
-                       $this->position += 10;
-                       $this->skip_whitespace();
-                       $this->state = 'standalone_equals';
-               }
-               else
-               {
-                       $this->state = false;
-               }
-       }
-
-       function standalone_equals()
-       {
-               if (substr($this->data, $this->position, 1) === '=')
-               {
-                       $this->position++;
-                       $this->skip_whitespace();
-                       $this->state = 'standalone_value';
-               }
-               else
-               {
-                       $this->state = false;
-               }
-       }
-
-       function standalone_value()
-       {
-               if ($standalone = $this->get_value())
-               {
-                       switch ($standalone)
-                       {
-                               case 'yes':
-                                       $this->standalone = true;
-                                       break;
-
-                               case 'no':
-                                       $this->standalone = false;
-                                       break;
-
-                               default:
-                                       $this->state = false;
-                                       return;
-                       }
-
-                       $this->skip_whitespace();
-                       if ($this->has_data())
-                       {
-                               $this->state = false;
-                       }
-                       else
-                       {
-                               $this->state = 'emit';
-                       }
-               }
-               else
-               {
-                       $this->state = false;
-               }
-       }
-}
-
-class SimplePie_Locator
-{
-       var $useragent;
-       var $timeout;
-       var $file;
-       var $local = array();
-       var $elsewhere = array();
-       var $file_class = 'SimplePie_File';
-       var $cached_entities = array();
-       var $http_base;
-       var $base;
-       var $base_location = 0;
-       var $checked_feeds = 0;
-       var $max_checked_feeds = 10;
-       var $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer';
-
-       function SimplePie_Locator(&$file, $timeout = 10, $useragent = null, $file_class = 'SimplePie_File', $max_checked_feeds = 10, $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer')
-       {
-               $this->file =& $file;
-               $this->file_class = $file_class;
-               $this->useragent = $useragent;
-               $this->timeout = $timeout;
-               $this->max_checked_feeds = $max_checked_feeds;
-               $this->content_type_sniffer_class = $content_type_sniffer_class;
-       }
-
-       function find($type = SIMPLEPIE_LOCATOR_ALL, &$working)
-       {
-               if ($this->is_feed($this->file))
-               {
-                       return $this->file;
-               }
-
-               if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
-               {
-                       $sniffer = new $this->content_type_sniffer_class($this->file);
-                       if ($sniffer->get_type() !== 'text/html')
-                       {
-                               return null;
-                       }
-               }
-
-               if ($type & ~SIMPLEPIE_LOCATOR_NONE)
-               {
-                       $this->get_base();
-               }
-
-               if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery())
-               {
-                       return $working[0];
-               }
-
-               if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links())
-               {
-                       if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local))
-                       {
-                               return $working;
-                       }
-
-                       if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local))
-                       {
-                               return $working;
-                       }
-
-                       if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere))
-                       {
-                               return $working;
-                       }
-
-                       if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere))
-                       {
-                               return $working;
-                       }
-               }
-               return null;
-       }
-
-       function is_feed(&$file)
-       {
-               if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
-               {
-                       $sniffer = new $this->content_type_sniffer_class($file);
-                       $sniffed = $sniffer->get_type();
-                       if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'text/rdf', 'application/atom+xml', 'text/xml', 'application/xml')))
-                       {
-                               return true;
-                       }
-                       else
-                       {
-                               return false;
-                       }
-               }
-               elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL)
-               {
-                       return true;
-               }
-               else
-               {
-                       return false;
-               }
-       }
-
-       function get_base()
-       {
-               $this->http_base = $this->file->url;
-               $this->base = $this->http_base;
-               $elements = SimplePie_Misc::get_element('base', $this->file->body);
-               foreach ($elements as $element)
-               {
-                       if ($element['attribs']['href']['data'] !== '')
-                       {
-                               $this->base = SimplePie_Misc::absolutize_url(trim($element['attribs']['href']['data']), $this->http_base);
-                               $this->base_location = $element['offset'];
-                               break;
-                       }
-               }
-       }
-
-       function autodiscovery()
-       {
-               $links = array_merge(SimplePie_Misc::get_element('link', $this->file->body), SimplePie_Misc::get_element('a', $this->file->body), SimplePie_Misc::get_element('area', $this->file->body));
-               $done = array();
-               $feeds = array();
-               foreach ($links as $link)
-               {
-                       if ($this->checked_feeds === $this->max_checked_feeds)
-                       {
-                               break;
-                       }
-                       if (isset($link['attribs']['href']['data']) && isset($link['attribs']['rel']['data']))
-                       {
-                               $rel = array_unique(SimplePie_Misc::space_seperated_tokens(strtolower($link['attribs']['rel']['data'])));
-
-                               if ($this->base_location < $link['offset'])
-                               {
-                                       $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->base);
-                               }
-                               else
-                               {
-                                       $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->http_base);
-                               }
-
-                               if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !empty($link['attribs']['type']['data']) && in_array(strtolower(SimplePie_Misc::parse_mime($link['attribs']['type']['data'])), array('application/rss+xml', 'application/atom+xml'))) && !isset($feeds[$href]))
-                               {
-                                       $this->checked_feeds++;
-                                       $feed = new $this->file_class($href, $this->timeout, 5, null, $this->useragent);
-                                       if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
-                                       {
-                                               $feeds[$href] = $feed;
-                                       }
-                               }
-                               $done[] = $href;
-                       }
-               }
-
-               if (!empty($feeds))
-               {
-                       return array_values($feeds);
-               }
-               else {
-                       return null;
-               }
-       }
-
-       function get_links()
-       {
-               $links = SimplePie_Misc::get_element('a', $this->file->body);
-               foreach ($links as $link)
-               {
-                       if (isset($link['attribs']['href']['data']))
-                       {
-                               $href = trim($link['attribs']['href']['data']);
-                               $parsed = SimplePie_Misc::parse_url($href);
-                               if ($parsed['scheme'] === '' || preg_match('/^(http(s)|feed)?$/i', $parsed['scheme']))
-                               {
-                                       if ($this->base_location < $link['offset'])
-                                       {
-                                               $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->base);
-                                       }
-                                       else
-                                       {
-                                               $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->http_base);
-                                       }
-
-                                       $current = SimplePie_Misc::parse_url($this->file->url);
-
-                                       if ($parsed['authority'] === '' || $parsed['authority'] === $current['authority'])
-                                       {
-                                               $this->local[] = $href;
-                                       }
-                                       else
-                                       {
-                                               $this->elsewhere[] = $href;
-                                       }
-                               }
-                       }
-               }
-               $this->local = array_unique($this->local);
-               $this->elsewhere = array_unique($this->elsewhere);
-               if (!empty($this->local) || !empty($this->elsewhere))
-               {
-                       return true;
-               }
-               return null;
-       }
-
-       function extension(&$array)
-       {
-               foreach ($array as $key => $value)
-               {
-                       if ($this->checked_feeds === $this->max_checked_feeds)
-                       {
-                               break;
-                       }
-                       if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml')))
-                       {
-                               $this->checked_feeds++;
-                               $feed = new $this->file_class($value, $this->timeout, 5, null, $this->useragent);
-                               if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
-                               {
-                                       return $feed;
-                               }
-                               else
-                               {
-                                       unset($array[$key]);
-                               }
-                       }
-               }
-               return null;
-       }
-
-       function body(&$array)
-       {
-               foreach ($array as $key => $value)
-               {
-                       if ($this->checked_feeds === $this->max_checked_feeds)
-                       {
-                               break;
-                       }
-                       if (preg_match('/(rss|rdf|atom|xml)/i', $value))
-                       {
-                               $this->checked_feeds++;
-                               $feed = new $this->file_class($value, $this->timeout, 5, null, $this->useragent);
-                               if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
-                               {
-                                       return $feed;
-                               }
-                               else
-                               {
-                                       unset($array[$key]);
-                               }
-                       }
-               }
-               return null;
-       }
-}
-
-class SimplePie_Parser
-{
-       var $error_code;
-       var $error_string;
-       var $current_line;
-       var $current_column;
-       var $current_byte;
-       var $separator = ' ';
-       var $namespace = array('');
-       var $element = array('');
-       var $xml_base = array('');
-       var $xml_base_explicit = array(false);
-       var $xml_lang = array('');
-       var $data = array();
-       var $datas = array(array());
-       var $current_xhtml_construct = -1;
-       var $encoding;
-
-       function parse(&$data, $encoding)
-       {
-               // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character
-               if (strtoupper($encoding) === 'US-ASCII')
-               {
-                       $this->encoding = 'UTF-8';
-               }
-               else
-               {
-                       $this->encoding = $encoding;
-               }
-
-               // Strip BOM:
-               // UTF-32 Big Endian BOM
-               if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
-               {
-                       $data = substr($data, 4);
-               }
-               // UTF-32 Little Endian BOM
-               elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
-               {
-                       $data = substr($data, 4);
-               }
-               // UTF-16 Big Endian BOM
-               elseif (substr($data, 0, 2) === "\xFE\xFF")
-               {
-                       $data = substr($data, 2);
-               }
-               // UTF-16 Little Endian BOM
-               elseif (substr($data, 0, 2) === "\xFF\xFE")
-               {
-                       $data = substr($data, 2);
-               }
-               // UTF-8 BOM
-               elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
-               {
-                       $data = substr($data, 3);
-               }
-
-               if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false)
-               {
-                       $declaration = new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5));
-                       if ($declaration->parse())
-                       {
-                               $data = substr($data, $pos + 2);
-                               $data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data;
-                       }
-                       else
-                       {
-                               $this->error_string = 'SimplePie bug! Please report this!';
-                               return false;
-                       }
-               }
-
-               $return = true;
-
-               static $xml_is_sane = null;
-               if ($xml_is_sane === null)
-               {
-                       $parser_check = xml_parser_create();
-                       xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
-                       xml_parser_free($parser_check);
-                       $xml_is_sane = isset($values[0]['value']);
-               }
-
-               // Create the parser
-               if ($xml_is_sane)
-               {
-                       $xml = xml_parser_create_ns($this->encoding, $this->separator);
-                       xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1);
-                       xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0);
-                       xml_set_object($xml, $this);
-                       xml_set_character_data_handler($xml, 'cdata');
-                       xml_set_element_handler($xml, 'tag_open', 'tag_close');
-
-                       // Parse!
-                       if (!xml_parse($xml, $data, true))
-                       {
-                               $this->error_code = xml_get_error_code($xml);
-                               $this->error_string = xml_error_string($this->error_code);
-                               $return = false;
-                       }
-                       $this->current_line = xml_get_current_line_number($xml);
-                       $this->current_column = xml_get_current_column_number($xml);
-                       $this->current_byte = xml_get_current_byte_index($xml);
-                       xml_parser_free($xml);
-                       return $return;
-               }
-               else
-               {
-                       libxml_clear_errors();
-                       $xml = new XMLReader();
-                       $xml->xml($data);
-                       while (@$xml->read())
-                       {
-                               switch ($xml->nodeType)
-                               {
-
-                                       case constant('XMLReader::END_ELEMENT'):
-                                               if ($xml->namespaceURI !== '')
-                                               {
-                                                       $tagName = "{$xml->namespaceURI}{$this->separator}{$xml->localName}";
-                                               }
-                                               else
-                                               {
-                                                       $tagName = $xml->localName;
-                                               }
-                                               $this->tag_close(null, $tagName);
-                                               break;
-                                       case constant('XMLReader::ELEMENT'):
-                                               $empty = $xml->isEmptyElement;
-                                               if ($xml->namespaceURI !== '')
-                                               {
-                                                       $tagName = "{$xml->namespaceURI}{$this->separator}{$xml->localName}";
-                                               }
-                                               else
-                                               {
-                                                       $tagName = $xml->localName;
-                                               }
-                                               $attributes = array();
-                                               while ($xml->moveToNextAttribute())
-                                               {
-                                                       if ($xml->namespaceURI !== '')
-                                                       {
-                                                               $attrName = "{$xml->namespaceURI}{$this->separator}{$xml->localName}";
-                                                       }
-                                                       else
-                                                       {
-                                                               $attrName = $xml->localName;
-                                                       }
-                                                       $attributes[$attrName] = $xml->value;
-                                               }
-                                               $this->tag_open(null, $tagName, $attributes);
-                                               if ($empty)
-                                               {
-                                                       $this->tag_close(null, $tagName);
-                                               }
-                                               break;
-                                       case constant('XMLReader::TEXT'):
-
-                                       case constant('XMLReader::CDATA'):
-                                               $this->cdata(null, $xml->value);
-                                               break;
-                               }
-                       }
-                       if ($error = libxml_get_last_error())
-                       {
-                               $this->error_code = $error->code;
-                               $this->error_string = $error->message;
-                               $this->current_line = $error->line;
-                               $this->current_column = $error->column;
-                               return false;
-                       }
-                       else
-                       {
-                               return true;
-                       }
-               }
-       }
-
-       function get_error_code()
-       {
-               return $this->error_code;
-       }
-
-       function get_error_string()
-       {
-               return $this->error_string;
-       }
-
-       function get_current_line()
-       {
-               return $this->current_line;
-       }
-
-       function get_current_column()
-       {
-               return $this->current_column;
-       }
-
-       function get_current_byte()
-       {
-               return $this->current_byte;
-       }
-
-       function get_data()
-       {
-               return $this->data;
-       }
-
-       function tag_open($parser, $tag, $attributes)
-       {
-               list($this->namespace[], $this->element[]) = $this->split_ns($tag);
-
-               $attribs = array();
-               foreach ($attributes as $name => $value)
-               {
-                       list($attrib_namespace, $attribute) = $this->split_ns($name);
-                       $attribs[$attrib_namespace][$attribute] = $value;
-               }
-
-               if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['base']))
-               {
-                       $this->xml_base[] = SimplePie_Misc::absolutize_url($attribs[SIMPLEPIE_NAMESPACE_XML]['base'], end($this->xml_base));
-                       $this->xml_base_explicit[] = true;
-               }
-               else
-               {
-                       $this->xml_base[] = end($this->xml_base);
-                       $this->xml_base_explicit[] = end($this->xml_base_explicit);
-               }
-
-               if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['lang']))
-               {
-                       $this->xml_lang[] = $attribs[SIMPLEPIE_NAMESPACE_XML]['lang'];
-               }
-               else
-               {
-                       $this->xml_lang[] = end($this->xml_lang);
-               }
-
-               if ($this->current_xhtml_construct >= 0)
-               {
-                       $this->current_xhtml_construct++;
-                       if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML)
-                       {
-                               $this->data['data'] .= '<' . end($this->element);
-                               if (isset($attribs['']))
-                               {
-                                       foreach ($attribs[''] as $name => $value)
-                                       {
-                                               $this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"';
-                                       }
-                               }
-                               $this->data['data'] .= '>';
-                       }
-               }
-               else
-               {
-                       $this->datas[] =& $this->data;
-                       $this->data =& $this->data['child'][end($this->namespace)][end($this->element)][];
-                       $this->data = array('data' => '', 'attribs' => $attribs, 'xml_base' => end($this->xml_base), 'xml_base_explicit' => end($this->xml_base_explicit), 'xml_lang' => end($this->xml_lang));
-                       if ((end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_03 && in_array(end($this->element), array('title', 'tagline', 'copyright', 'info', 'summary', 'content')) && isset($attribs['']['mode']) && $attribs['']['mode'] === 'xml')
-                       || (end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_10 && in_array(end($this->element), array('rights', 'subtitle', 'summary', 'info', 'title', 'content')) && isset($attribs['']['type']) && $attribs['']['type'] === 'xhtml'))
-                       {
-                               $this->current_xhtml_construct = 0;
-                       }
-               }
-       }
-
-       function cdata($parser, $cdata)
-       {
-               if ($this->current_xhtml_construct >= 0)
-               {
-                       $this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding);
-               }
-               else
-               {
-                       $this->data['data'] .= $cdata;
-               }
-       }
-
-       function tag_close($parser, $tag)
-       {
-               if ($this->current_xhtml_construct >= 0)
-               {
-                       $this->current_xhtml_construct--;
-                       if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML && !in_array(end($this->element), array('area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param')))
-                       {
-                               $this->data['data'] .= '</' . end($this->element) . '>';
-                       }
-               }
-               if ($this->current_xhtml_construct === -1)
-               {
-                       $this->data =& $this->datas[count($this->datas) - 1];
-                       array_pop($this->datas);
-               }
-
-               array_pop($this->element);
-               array_pop($this->namespace);
-               array_pop($this->xml_base);
-               array_pop($this->xml_base_explicit);
-               array_pop($this->xml_lang);
-       }
-
-       function split_ns($string)
-       {
-               static $cache = array();
-               if (!isset($cache[$string]))
-               {
-                       if ($pos = strpos($string, $this->separator))
-                       {
-                               static $separator_length;
-                               if (!$separator_length)
-                               {
-                                       $separator_length = strlen($this->separator);
-                               }
-                               $namespace = substr($string, 0, $pos);
-                               $local_name = substr($string, $pos + $separator_length);
-                               if (strtolower($namespace) === SIMPLEPIE_NAMESPACE_ITUNES)
-                               {
-                                       $namespace = SIMPLEPIE_NAMESPACE_ITUNES;
-                               }
-
-                               // Normalize the Media RSS namespaces
-                               if ($namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG)
-                               {
-                                       $namespace = SIMPLEPIE_NAMESPACE_MEDIARSS;
-                               }
-                               $cache[$string] = array($namespace, $local_name);
-                       }
-                       else
-                       {
-                               $cache[$string] = array('', $string);
-                       }
-               }
-               return $cache[$string];
-       }
-}
-
-/**
- * @todo Move to using an actual HTML parser (this will allow tags to be properly stripped, and to switch between HTML and XHTML), this will also make it easier to shorten a string while preserving HTML tags
- */
-class SimplePie_Sanitize
-{
-       // Private vars
-       var $base;
-
-       // Options
-       var $remove_div = true;
-       var $image_handler = '';
-       var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style');
-       var $encode_instead_of_strip = false;
-       var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc');
-       var $strip_comments = false;
-       var $output_encoding = 'UTF-8';
-       var $enable_cache = true;
-       var $cache_location = './cache';
-       var $cache_name_function = 'md5';
-       var $cache_class = 'SimplePie_Cache';
-       var $file_class = 'SimplePie_File';
-       var $timeout = 10;
-       var $useragent = '';
-       var $force_fsockopen = false;
-
-       var $replace_url_attributes = array(
-               'a' => 'href',
-               'area' => 'href',
-               'blockquote' => 'cite',
-               'del' => 'cite',
-               'form' => 'action',
-               'img' => array('longdesc', 'src'),
-               'input' => 'src',
-               'ins' => 'cite',
-               'q' => 'cite'
-       );
-
-       function remove_div($enable = true)
-       {
-               $this->remove_div = (bool) $enable;
-       }
-
-       function set_image_handler($page = false)
-       {
-               if ($page)
-               {
-                       $this->image_handler = (string) $page;
-               }
-               else
-               {
-                       $this->image_handler = false;
-               }
-       }
-
-       function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie_Cache')
-       {
-               if (isset($enable_cache))
-               {
-                       $this->enable_cache = (bool) $enable_cache;
-               }
-
-               if ($cache_location)
-               {
-                       $this->cache_location = (string) $cache_location;
-               }
-
-               if ($cache_name_function)
-               {
-                       $this->cache_name_function = (string) $cache_name_function;
-               }
-
-               if ($cache_class)
-               {
-                       $this->cache_class = (string) $cache_class;
-               }
-       }
-
-       function pass_file_data($file_class = 'SimplePie_File', $timeout = 10, $useragent = '', $force_fsockopen = false)
-       {
-               if ($file_class)
-               {
-                       $this->file_class = (string) $file_class;
-               }
-
-               if ($timeout)
-               {
-                       $this->timeout = (string) $timeout;
-               }
-
-               if ($useragent)
-               {
-                       $this->useragent = (string) $useragent;
-               }
-
-               if ($force_fsockopen)
-               {
-                       $this->force_fsockopen = (string) $force_fsockopen;
-               }
-       }
-
-       function strip_htmltags($tags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'))
-       {
-               if ($tags)
-               {
-                       if (is_array($tags))
-                       {
-                               $this->strip_htmltags = $tags;
-                       }
-                       else
-                       {
-                               $this->strip_htmltags = explode(',', $tags);
-                       }
-               }
-               else
-               {
-                       $this->strip_htmltags = false;
-               }
-       }
-
-       function encode_instead_of_strip($encode = false)
-       {
-               $this->encode_instead_of_strip = (bool) $encode;
-       }
-
-       function strip_attributes($attribs = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'))
-       {
-               if ($attribs)
-               {
-                       if (is_array($attribs))
-                       {
-                               $this->strip_attributes = $attribs;
-                       }
-                       else
-                       {
-                               $this->strip_attributes = explode(',', $attribs);
-                       }
-               }
-               else
-               {
-                       $this->strip_attributes = false;
-               }
-       }
-
-       function strip_comments($strip = false)
-       {
-               $this->strip_comments = (bool) $strip;
-       }
-
-       function set_output_encoding($encoding = 'UTF-8')
-       {
-               $this->output_encoding = (string) $encoding;
-       }
-
-       /**
-        * Set element/attribute key/value pairs of HTML attributes
-        * containing URLs that need to be resolved relative to the feed
-        *
-        * @access public
-        * @since 1.0
-        * @param array $element_attribute Element/attribute key/value pairs
-        */
-       function set_url_replacements($element_attribute = array('a' => 'href', 'area' => 'href', 'blockquote' => 'cite', 'del' => 'cite', 'form' => 'action', 'img' => array('longdesc', 'src'), 'input' => 'src', 'ins' => 'cite', 'q' => 'cite'))
-       {
-               $this->replace_url_attributes = (array) $element_attribute;
-       }
-
-       function sanitize($data, $type, $base = '')
-       {
-               $data = trim($data);
-               if ($data !== '' || $type & SIMPLEPIE_CONSTRUCT_IRI)
-               {
-                       if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML)
-                       {
-                               if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>)/', $data))
-                               {
-                                       $type |= SIMPLEPIE_CONSTRUCT_HTML;
-                               }
-                               else
-                               {
-                                       $type |= SIMPLEPIE_CONSTRUCT_TEXT;
-                               }
-                       }
-
-                       if ($type & SIMPLEPIE_CONSTRUCT_BASE64)
-                       {
-                               $data = base64_decode($data);
-                       }
-
-                       if ($type & SIMPLEPIE_CONSTRUCT_XHTML)
-                       {
-                               if ($this->remove_div)
-                               {
-                                       $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '', $data);
-                                       $data = preg_replace('/<\/div>$/', '', $data);
-                               }
-                               else
-                               {
-                                       $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '<div>', $data);
-                               }
-                       }
-
-                       if ($type & (SIMPLEPIE_CONSTRUCT_HTML | SIMPLEPIE_CONSTRUCT_XHTML))
-                       {
-                               // Strip comments
-                               if ($this->strip_comments)
-                               {
-                                       $data = SimplePie_Misc::strip_comments($data);
-                               }
-
-                               // Strip out HTML tags and attributes that might cause various security problems.
-                               // Based on recommendations by Mark Pilgrim at:
-                               // http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely
-                               if ($this->strip_htmltags)
-                               {
-                                       foreach ($this->strip_htmltags as $tag)
-                                       {
-                                               $pcre = "/<($tag)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$tag" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>|(\/)?>)/siU';
-                                               while (preg_match($pcre, $data))
-                                               {
-                                                       $data = preg_replace_callback($pcre, array(&$this, 'do_strip_htmltags'), $data);
-                                               }
-                                       }
-                               }
-
-                               if ($this->strip_attributes)
-                               {
-                                       foreach ($this->strip_attributes as $attrib)
-                                       {
-                                               $data = preg_replace('/(<[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*)' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . trim($attrib) . '(?:\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>/', '\1\2\3>', $data);
-                                       }
-                               }
-
-                               // Replace relative URLs
-                               $this->base = $base;
-                               foreach ($this->replace_url_attributes as $element => $attributes)
-                               {
-                                       $data = $this->replace_urls($data, $element, $attributes);
-                               }
-
-                               // If image handling (caching, etc.) is enabled, cache and rewrite all the image tags.
-                               if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache)
-                               {
-                                       $images = SimplePie_Misc::get_element('img', $data);
-                                       foreach ($images as $img)
-                                       {
-                                               if (isset($img['attribs']['src']['data']))
-                                               {
-                                                       $image_url = call_user_func($this->cache_name_function, $img['attribs']['src']['data']);
-                                                       $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, $image_url, 'spi');
-
-                                                       if ($cache->load())
-                                                       {
-                                                               $img['attribs']['src']['data'] = $this->image_handler . $image_url;
-                                                               $data = str_replace($img['full'], SimplePie_Misc::element_implode($img), $data);
-                                                       }
-                                                       else
-                                                       {
-                                                               $file = new $this->file_class($img['attribs']['src']['data'], $this->timeout, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen);
-                                                               $headers = $file->headers;
-
-                                                               if ($file->success && ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
-                                                               {
-                                                                       if ($cache->save(array('headers' => $file->headers, 'body' => $file->body)))
-                                                                       {
-                                                                               $img['attribs']['src']['data'] = $this->image_handler . $image_url;
-                                                                               $data = str_replace($img['full'], SimplePie_Misc::element_implode($img), $data);
-                                                                       }
-                                                                       else
-                                                                       {
-                                                                               trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
-                                                                       }
-                                                               }
-                                                       }
-                                               }
-                                       }
-                               }
-
-                               // Having (possibly) taken stuff out, there may now be whitespace at the beginning/end of the data
-                               $data = trim($data);
-                       }
-
-                       if ($type & SIMPLEPIE_CONSTRUCT_IRI)
-                       {
-                               $data = SimplePie_Misc::absolutize_url($data, $base);
-                       }
-
-                       if ($type & (SIMPLEPIE_CONSTRUCT_TEXT | SIMPLEPIE_CONSTRUCT_IRI))
-                       {
-                               $data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8');
-                       }
-
-                       if ($this->output_encoding !== 'UTF-8')
-                       {
-                               $data = SimplePie_Misc::change_encoding($data, 'UTF-8', $this->output_encoding);
-                       }
-               }
-               return $data;
-       }
-
-       function replace_urls($data, $tag, $attributes)
-       {
-               if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags))
-               {
-                       $elements = SimplePie_Misc::get_element($tag, $data);
-                       foreach ($elements as $element)
-                       {
-                               if (is_array($attributes))
-                               {
-                                       foreach ($attributes as $attribute)
-                                       {
-                                               if (isset($element['attribs'][$attribute]['data']))
-                                               {
-                                                       $element['attribs'][$attribute]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attribute]['data'], $this->base);
-                                                       $new_element = SimplePie_Misc::element_implode($element);
-                                                       $data = str_replace($element['full'], $new_element, $data);
-                                                       $element['full'] = $new_element;
-                                               }
-                                       }
-                               }
-                               elseif (isset($element['attribs'][$attributes]['data']))
-                               {
-                                       $element['attribs'][$attributes]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attributes]['data'], $this->base);
-                                       $data = str_replace($element['full'], SimplePie_Misc::element_implode($element), $data);
-                               }
-                       }
-               }
-               return $data;
-       }
-
-       function do_strip_htmltags($match)
-       {
-               if ($this->encode_instead_of_strip)
-               {
-                       if (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style')))
-                       {
-                               $match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8');
-                               $match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8');
-                               return "&lt;$match[1]$match[2]&gt;$match[3]&lt;/$match[1]&gt;";
-                       }
-                       else
-                       {
-                               return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8');
-                       }
-               }
-               elseif (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style')))
-               {
-                       return $match[4];
-               }
-               else
-               {
-                       return '';
-               }
-       }
-}
-
-?>
index 6c33136f957a2576bf8a121404782aa23ece0f03..33070a1ecdf4dc071d65aabd94b67f530be47efe 100644 (file)
@@ -2,7 +2,6 @@
 require_once("mod/hostxrd.php");\r
 require_once("mod/nodeinfo.php");\r
 \r
-if(! function_exists('_well_known_init')) {\r
 function _well_known_init(&$a){\r
        if ($a->argc > 1) {\r
                switch($a->argv[1]) {\r
@@ -20,9 +19,7 @@ function _well_known_init(&$a){
        http_status_exit(404);\r
        killme();\r
 }\r
-}\r
 \r
-if(! function_exists('wk_social_relay')) {\r
 function wk_social_relay(&$a) {\r
 \r
        define('SR_SCOPE_ALL', 'all');\r
@@ -67,4 +64,3 @@ function wk_social_relay(&$a) {
        echo json_encode($relay, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);\r
        exit;\r
 }\r
-}\r
index a551e3dbd673f6f7d62f6f71482bc15c8e59824f..a2365803ac46047e1c1a2497520b21e46f9e0171 100644 (file)
@@ -2,8 +2,8 @@
 
 require_once('include/Scrape.php');
 
-if(! function_exists('acctlink_init')) {
 function acctlink_init(&$a) {
+
        if(x($_GET,'addr')) {
                $addr = trim($_GET['addr']);
                $res = probe_url($addr);
@@ -14,4 +14,3 @@ function acctlink_init(&$a) {
                }
        }
 }
-}
index 5666ccabb8112646f9c01217a9994d86edda8d3c..f5e04b96a75154ee76a042222925754164cfeaa4 100644 (file)
@@ -3,8 +3,8 @@
 
 require_once("include/acl_selectors.php");
 
-if(! function_exists('acl_init')) {
 function acl_init(&$a){
        acl_lookup($a);
 }
-}
+
+
index ff17c0b8c44b510f294f9e2322ea1cf68a13149a..2fc9c48a78c599e82bd3b426e7e0961018d47bda 100644 (file)
@@ -2,7 +2,7 @@
 
  /**
  * @file mod/admin.php
- *
+ * 
  * @brief Friendica admin
  */
 
@@ -23,7 +23,6 @@ require_once("include/text.php");
  * @param App $a
  *
  */
-if(! function_exists('admin_post')) {
 function admin_post(&$a){
 
 
@@ -56,13 +55,13 @@ function admin_post(&$a){
                                                        $func($a);
                                                }
                                }
-                               goaway($a->get_baseurl(true) . '/admin/plugins/' . $a->argv[2] );
+                               goaway('admin/plugins/'.$a->argv[2]);
                                return; // NOTREACHED
                                break;
                        case 'themes':
                                if($a->argc < 2) {
                                        if(is_ajax()) return;
-                                       goaway($a->get_baseurl(true) . '/admin/' );
+                                       goaway('admin/');
                                        return;
                                }
 
@@ -93,7 +92,7 @@ function admin_post(&$a){
                                info(t('Theme settings updated.'));
                                if(is_ajax()) return;
 
-                               goaway($a->get_baseurl(true) . '/admin/themes/' . $theme );
+                               goaway('admin/themes/'.$theme);
                                return;
                                break;
                        case 'features':
@@ -108,10 +107,9 @@ function admin_post(&$a){
                }
        }
 
-       goaway($a->get_baseurl(true) . '/admin' );
+       goaway('admin');
        return; // NOTREACHED
 }
-}
 
 /**
  * @brief Generates content of the admin panel pages
@@ -130,7 +128,6 @@ function admin_post(&$a){
  * @param App $a
  * @return string
  */
-if(! function_exists('admin_content')) {
 function admin_content(&$a) {
 
        if(!is_site_admin()) {
@@ -153,17 +150,17 @@ function admin_content(&$a) {
         * Side bar links
         */
        $aside_tools = array();
-       // array( url, name, extra css classes )
+       // array(url, name, extra css classes)
        // not part of $aside to make the template more adjustable
        $aside_sub = array(
-               'site'   =>     array($a->get_baseurl(true)."/admin/site/", t("Site") , "site"),
-               'users'  =>     array($a->get_baseurl(true)."/admin/users/", t("Users") , "users"),
-               'plugins'=>     array($a->get_baseurl(true)."/admin/plugins/", t("Plugins") , "plugins"),
-               'themes' =>     array($a->get_baseurl(true)."/admin/themes/", t("Themes") , "themes"),
-               'features' =>   array($a->get_baseurl(true)."/admin/features/", t("Additional features") , "features"),
-               'dbsync' =>     array($a->get_baseurl(true)."/admin/dbsync/", t('DB updates'), "dbsync"),
-               'queue'  =>     array($a->get_baseurl(true)."/admin/queue/", t('Inspect Queue'), "queue"),
-               'federation' => array($a->get_baseurl(true)."/admin/federation/", t('Federation Statistics'), "federation"),
+               'site'   =>     array("admin/site/", t("Site") , "site"),
+               'users'  =>     array("admin/users/", t("Users") , "users"),
+               'plugins'=>     array("admin/plugins/", t("Plugins") , "plugins"),
+               'themes' =>     array("admin/themes/", t("Themes") , "themes"),
+               'features' =>   array("admin/features/", t("Additional features") , "features"),
+               'dbsync' =>     array("admin/dbsync/", t('DB updates'), "dbsync"),
+               'queue'  =>     array("admin/queue/", t('Inspect Queue'), "queue"),
+               'federation' => array("admin/federation/", t('Federation Statistics'), "federation"),
        );
 
        /* get plugins admin page */
@@ -172,18 +169,18 @@ function admin_content(&$a) {
        $aside_tools['plugins_admin']=array();
        foreach ($r as $h){
                $plugin =$h['name'];
-               $aside_tools['plugins_admin'][] = array($a->get_baseurl(true)."/admin/plugins/".$plugin, $plugin, "plugin");
+               $aside_tools['plugins_admin'][] = array("admin/plugins/".$plugin, $plugin, "plugin");
                // temp plugins with admin
                $a->plugins_admin[] = $plugin;
        }
 
-       $aside_tools['logs'] = array($a->get_baseurl(true)."/admin/logs/", t("Logs"), "logs");
-       $aside_tools['viewlogs'] = array($a->get_baseurl(true)."/admin/viewlogs/", t("View Logs"), 'viewlogs');
-       $aside_tools['diagnostics_probe'] = array($a->get_baseurl(true).'/probe/', t('probe address'), 'probe');
-       $aside_tools['diagnostics_webfinger'] = array($a->get_baseurl(true).'/webfinger/', t('check webfinger'), 'webfinger');
+       $aside_tools['logs'] = array("admin/logs/", t("Logs"), "logs");
+       $aside_tools['viewlogs'] = array("admin/viewlogs/", t("View Logs"), 'viewlogs');
+       $aside_tools['diagnostics_probe'] = array('probe/', t('probe address'), 'probe');
+       $aside_tools['diagnostics_webfinger'] = array('webfinger/', t('check webfinger'), 'webfinger');
 
        $t = get_markup_template("admin_aside.tpl");
-       $a->page['aside'] .= replace_macros( $t, array(
+       $a->page['aside'] .= replace_macros($t, array(
                '$admin' => $aside_tools,
                '$subpages' => $aside_sub,
                '$admtxt' => t('Admin'),
@@ -191,7 +188,7 @@ function admin_content(&$a) {
                '$logtxt' => t('Logs'),
                '$diagnosticstxt' => t('diagnostics'),
                '$h_pending' => t('User registrations waiting for confirmation'),
-               '$admurl'=> $a->get_baseurl(true)."/admin/"
+               '$admurl'=> "admin/"
        ));
 
 
@@ -234,7 +231,7 @@ function admin_content(&$a) {
                                $o = admin_page_federation($a);
                                break;
                        default:
-                               notice( t("Item not found.") );
+                               notice(t("Item not found."));
                }
        } else {
                $o = admin_page_summary($a);
@@ -248,7 +245,6 @@ function admin_content(&$a) {
                return $o;
        }
 }
-}
 
 /**
  * @brief Subpage with some stats about "the federation" network
@@ -264,7 +260,6 @@ function admin_content(&$a) {
  * @param App $a
  * @return string
  */
-if(! function_exists('admin_page_federation')) {
 function admin_page_federation(&$a) {
        // get counts on active friendica, diaspora, redmatrix, hubzilla, gnu
        // social and statusnet nodes this node is knowing
@@ -275,6 +270,12 @@ function admin_page_federation(&$a) {
        // Add more platforms if you like, when one returns 0 known nodes it is not
        // displayed on the stats page.
        $platforms = array('Friendica', 'Diaspora', '%%red%%', 'Hubzilla', 'GNU Social', 'StatusNet');
+       $colors    = array('Friendica' => '#ffc018',     // orange from the logo
+                          'Diaspora'  => '#a1a1a1',     // logo is black and white, makes a gray
+                          '%%red%%'   => '#c50001',     // fire red from the logo
+                          'Hubzilla'  => '#43488a',     // blue from the logo
+                          'GNU Social'=> '#a22430',     // dark red from the logo
+                          'StatusNet' => '#789240');    // the green from the logo (red and blue have already others
        $counts = array();
        $total = 0;
 
@@ -282,14 +283,14 @@ function admin_page_federation(&$a) {
                // get a total count for the platform, the name and version of the
                // highest version and the protocol tpe
                $c = q('SELECT count(*) AS total, platform, network, version FROM gserver
-                       WHERE platform LIKE "%s" AND last_contact > last_failure
+                       WHERE platform LIKE "%s" AND last_contact > last_failure AND `version` != ""
                        ORDER BY version ASC;', $p);
                $total = $total + $c[0]['total'];
 
                // what versions for that platform do we know at all?
                // again only the active nodes
                $v = q('SELECT count(*) AS total, version FROM gserver
-                       WHERE last_contact > last_failure AND platform LIKE "%s"
+                       WHERE last_contact > last_failure AND platform LIKE "%s"  AND `version` != ""
                        GROUP BY version
                        ORDER BY version;', $p);
 
@@ -306,12 +307,12 @@ function admin_page_federation(&$a) {
                                $newVC = $vv['total'];
                                $newVV = $vv['version'];
                                $posDash = strpos($newVV, '-');
-                               if($posDash)
+                               if($posDash) 
                                        $newVV = substr($newVV, 0, $posDash);
                                if(isset($newV[$newVV]))
-                                       $newV[$newVV] += $newVC;
+                                       $newV[$newVV] += $newVC; 
                                else
-                                       $newV[$newVV] = $newVC;
+                                       $newV[$newVV] = $newVC; 
                        }
                        foreach ($newV as $key => $value) {
                                array_push($newVv, array('total'=>$value, 'version'=>$key));
@@ -343,9 +344,12 @@ function admin_page_federation(&$a) {
                        $v = $newVv;
                }
 
+               foreach ($v as $key => $vv)
+                       $v[$key]["version"] = trim(strip_tags($vv["version"]));
+
                // the 3rd array item is needed for the JavaScript graphs as JS does
                // not like some characters in the names of variables...
-               $counts[$p]=array($c[0], $v, str_replace(array(' ','%'),'',$p));
+               $counts[$p]=array($c[0], $v, str_replace(array(' ','%'),'',$p), $colors[$p]);
        }
 
        // some helpful text
@@ -366,7 +370,6 @@ function admin_page_federation(&$a) {
                '$baseurl' => $a->get_baseurl(),
        ));
 }
-}
 
 /**
  * @brief Admin Inspect Queue Page
@@ -381,7 +384,6 @@ function admin_page_federation(&$a) {
  * @param App $a
  * @return string
  */
-if(! function_exists('admin_page_queue')) {
 function admin_page_queue(&$a) {
        // get content from the queue table
        $r = q("SELECT c.name,c.nurl,q.id,q.network,q.created,q.last from queue as q, contact as c where c.id=q.cid order by q.cid, q.created;");
@@ -401,7 +403,6 @@ function admin_page_queue(&$a) {
                '$entries' => $r,
        ));
 }
-}
 
 /**
  * @brief Admin Summary Page
@@ -414,22 +415,21 @@ function admin_page_queue(&$a) {
  * @param App $a
  * @return string
  */
-if(! function_exists('admin_page_summary')) {
 function admin_page_summary(&$a) {
        $r = q("SELECT `page-flags`, COUNT(uid) as `count` FROM `user` GROUP BY `page-flags`");
        $accounts = array(
-               array( t('Normal Account'), 0),
-               array( t('Soapbox Account'), 0),
-               array( t('Community/Celebrity Account'), 0),
-               array( t('Automatic Friend Account'), 0),
-               array( t('Blog Account'), 0),
-               array( t('Private Forum'), 0)
+               array(t('Normal Account'), 0),
+               array(t('Soapbox Account'), 0),
+               array(t('Community/Celebrity Account'), 0),
+               array(t('Automatic Friend Account'), 0),
+               array(t('Blog Account'), 0),
+               array(t('Private Forum'), 0)
        );
 
        $users=0;
        foreach ($r as $u){ $accounts[$u['page-flags']][1] = $u['count']; $users+= $u['count']; }
 
-       logger('accounts: ' . print_r($accounts,true),LOGGER_DATA);
+       logger('accounts: '.print_r($accounts,true),LOGGER_DATA);
 
        $r = q("SELECT COUNT(id) as `count` FROM `register`");
        $pending = $r[0]['count'];
@@ -442,7 +442,7 @@ function admin_page_summary(&$a) {
 
        // We can do better, but this is a quick queue status
 
-       $queues = array( 'label' => t('Message queues'), 'deliverq' => $deliverq, 'queue' => $queue );
+       $queues = array('label' => t('Message queues'), 'deliverq' => $deliverq, 'queue' => $queue);
 
 
        $t = get_markup_template("admin_summary.tpl");
@@ -450,25 +450,23 @@ function admin_page_summary(&$a) {
                '$title' => t('Administration'),
                '$page' => t('Summary'),
                '$queues' => $queues,
-               '$users' => array( t('Registered users'), $users),
+               '$users' => array(t('Registered users'), $users),
                '$accounts' => $accounts,
-               '$pending' => array( t('Pending registrations'), $pending),
-               '$version' => array( t('Version'), FRIENDICA_VERSION),
+               '$pending' => array(t('Pending registrations'), $pending),
+               '$version' => array(t('Version'), FRIENDICA_VERSION),
                '$baseurl' => $a->get_baseurl(),
                '$platform' => FRIENDICA_PLATFORM,
                '$codename' => FRIENDICA_CODENAME,
                '$build' =>  get_config('system','build'),
-               '$plugins' => array( t('Active plugins'), $a->plugins )
+               '$plugins' => array(t('Active plugins'), $a->plugins)
        ));
 }
-}
 
 /**
  * @brief Process send data from Admin Site Page
- *
+ * 
  * @param App $a
  */
-if(! function_exists('admin_page_site_post')) {
 function admin_page_site_post(&$a) {
        if(!x($_POST,"page_site")) {
                return;
@@ -484,7 +482,7 @@ function admin_page_site_post(&$a) {
                $parsed = @parse_url($new_url);
                if(!$parsed || (!x($parsed,'host') || !x($parsed,'scheme'))) {
                        notice(t("Can not parse base url. Must have at least <scheme>://<domain>"));
-                       goaway($a->get_baseurl(true) . '/admin/site' );
+                       goaway('admin/site');
                }
 
                /* steps:
@@ -494,6 +492,10 @@ function admin_page_site_post(&$a) {
 
                $old_url = $a->get_baseurl(true);
 
+               // Generate host names for relocation the addresses in the format user@address.tld
+               $new_host = str_replace("http://", "@", normalise_link($new_url));
+               $old_host = str_replace("http://", "@", normalise_link($old_url));
+
                function update_table($table_name, $fields, $old_url, $new_url) {
                        global $db, $a;
 
@@ -512,17 +514,22 @@ function admin_page_site_post(&$a) {
                        $q = sprintf("UPDATE %s SET %s;", $table_name, $upds);
                        $r = q($q);
                        if(!$r) {
-                               notice( "Failed updating '$table_name': " . $db->error );
-                               goaway($a->get_baseurl(true) . '/admin/site' );
+                               notice("Failed updating '$table_name': ".$db->error);
+                               goaway('admin/site');
                        }
                }
 
                // update tables
+               // update profile links in the format "http://server.tld"
                update_table("profile", array('photo', 'thumb'), $old_url, $new_url);
                update_table("term", array('url'), $old_url, $new_url);
-               update_table("contact", array('photo','thumb','micro','url','nurl','request','notify','poll','confirm','poco'), $old_url, $new_url);
-               update_table("gcontact", array('photo','url','nurl','server_url'), $old_url, $new_url);
-               update_table("item", array('owner-link','owner-avatar','author-name','author-link','author-avatar','body','plink','tag'), $old_url, $new_url);
+               update_table("contact", array('photo','thumb','micro','url','nurl','alias','request','notify','poll','confirm','poco', 'avatar'), $old_url, $new_url);
+               update_table("gcontact", array('url','nurl','photo','server_url','notify','alias'), $old_url, $new_url);
+               update_table("item", array('owner-link','owner-avatar','author-link','author-avatar','body','plink','tag'), $old_url, $new_url);
+
+               // update profile addresses in the format "user@server.tld"
+               update_table("contact", array('addr'), $old_host, $new_host);
+               update_table("gcontact", array('connect','addr'), $old_host, $new_host);
 
                // update config
                $a->set_baseurl($new_url);
@@ -537,7 +544,7 @@ function admin_page_site_post(&$a) {
 
                info("Relocation started. Could take a while to complete.");
 
-               goaway($a->get_baseurl(true) . '/admin/site' );
+               goaway('admin/site');
        }
        // end relocate
 
@@ -600,6 +607,7 @@ function admin_page_site_post(&$a) {
        $dfrn_only              =       ((x($_POST,'dfrn_only'))                ? True                                          : False);
        $ostatus_disabled       =       !((x($_POST,'ostatus_disabled'))        ? True                                          : False);
        $ostatus_poll_interval  =       ((x($_POST,'ostatus_poll_interval'))    ? intval(trim($_POST['ostatus_poll_interval'])) :  0);
+       $ostatus_full_threads   =       ((x($_POST,'ostatus_full_threads'))     ? True                                          : False);
        $diaspora_enabled       =       ((x($_POST,'diaspora_enabled'))         ? True                                          : False);
        $ssl_policy             =       ((x($_POST,'ssl_policy'))               ? intval($_POST['ssl_policy'])                  : 0);
        $force_ssl              =       ((x($_POST,'force_ssl'))                ? True                                          : False);
@@ -620,6 +628,9 @@ function admin_page_site_post(&$a) {
        $only_tag_search        =       ((x($_POST,'only_tag_search'))          ? True                                          : False);
        $rino                   =       ((x($_POST,'rino'))                     ? intval($_POST['rino'])                        : 0);
        $embedly                =       ((x($_POST,'embedly'))                  ? notags(trim($_POST['embedly']))               : '');
+       $worker                 =       ((x($_POST,'worker'))                   ? True                                          : False);
+       $worker_queues          =       ((x($_POST,'worker_queues'))            ? intval($_POST['worker_queues'])               : 4);
+       $worker_dont_fork       =       ((x($_POST,'worker_dont_fork'))         ? True                                          : False);
 
        if($a->get_path() != "")
                $diaspora_enabled = false;
@@ -706,12 +717,12 @@ function admin_page_site_post(&$a) {
        set_config('system','language', $language);
        set_config('system','theme', $theme);
 
-       if( $theme_mobile === '---' ) {
+       if($theme_mobile === '---') {
                del_config('system','mobile-theme');
        } else {
                set_config('system','mobile-theme', $theme_mobile);
                }
-               if( $singleuser === '---' ) {
+               if($singleuser === '---') {
                        del_config('system','singleuser');
                } else {
                        set_config('system','singleuser', $singleuser);
@@ -748,6 +759,7 @@ function admin_page_site_post(&$a) {
        set_config('system','dfrn_only', $dfrn_only);
        set_config('system','ostatus_disabled', $ostatus_disabled);
        set_config('system','ostatus_poll_interval', $ostatus_poll_interval);
+       set_config('system','ostatus_full_threads', $ostatus_full_threads);
        set_config('system','diaspora_enabled', $diaspora_enabled);
 
        set_config('config','private_addons', $private_addons);
@@ -765,7 +777,9 @@ function admin_page_site_post(&$a) {
        set_config('system','proxy_disabled', $proxy_disabled);
        set_config('system','old_pager', $old_pager);
        set_config('system','only_tag_search', $only_tag_search);
-
+       set_config('system','worker', $worker);
+       set_config('system','worker_queues', $worker_queues);
+       set_config('system','worker_dont_fork', $worker_dont_fork);
 
        if($rino==2 and !function_exists('mcrypt_create_iv')) {
                notice(t("RINO2 needs mcrypt php extension to work."));
@@ -776,12 +790,11 @@ function admin_page_site_post(&$a) {
        set_config('system','embedly', $embedly);
 
 
-       info( t('Site settings updated.') . EOL);
-       goaway($a->get_baseurl(true) . '/admin/site' );
+       info(t('Site settings updated.').EOL);
+       goaway('admin/site');
        return; // NOTREACHED
 
 }
-}
 
 /**
  * @brief Generate Admin Site subpage
@@ -791,7 +804,6 @@ function admin_page_site_post(&$a) {
  * @param  App $a
  * @return string
  */
-if(! function_exists('admin_page_site')) {
 function admin_page_site(&$a) {
 
        /* Installed langs */
@@ -810,12 +822,12 @@ function admin_page_site(&$a) {
        $files = glob('view/theme/*');
        if($files) {
                foreach($files as $file) {
-                       if(intval(file_exists($file . '/unsupported')))
+                       if(intval(file_exists($file.'/unsupported')))
                                continue;
 
                        $f = basename($file);
-                       $theme_name = ((file_exists($file . '/experimental')) ?  sprintf("%s - \x28Experimental\x29", $f) : $f);
-                       if(file_exists($file . '/mobile')) {
+                       $theme_name = ((file_exists($file.'/experimental')) ?  sprintf("%s - \x28Experimental\x29", $f) : $f);
+                       if(file_exists($file.'/mobile')) {
                                $theme_choices_mobile[$f] = $theme_name;
                        } else {
                                $theme_choices[$f] = $theme_name;
@@ -906,6 +918,7 @@ function admin_page_site(&$a) {
                '$advanced' => t('Advanced'),
                '$portable_contacts' => t('Auto Discovered Contact Directory'),
                '$performance' => t('Performance'),
+               '$worker_title' => t('Worker'),
                '$relocate'=> t('Relocate - WARNING: advanced function. Could make this server unreachable.'),
                '$baseurl' => $a->get_baseurl(true),
                // name, label, value, help string, extra data...
@@ -951,6 +964,7 @@ function admin_page_site(&$a) {
                '$max_author_posts_community_page' => array('max_author_posts_community_page', t("Posts per user on community page"), get_config('system','max_author_posts_community_page'), t("The maximum number of posts per user on the community page. (Not valid for 'Global Community')")),
                '$ostatus_disabled'     => array('ostatus_disabled', t("Enable OStatus support"), !get_config('system','ostatus_disabled'), t("Provide built-in OStatus \x28StatusNet, GNU Social etc.\x29 compatibility. All communications in OStatus are public, so privacy warnings will be occasionally displayed.")),
                '$ostatus_poll_interval' => array('ostatus_poll_interval', t("OStatus conversation completion interval"), (string) intval(get_config('system','ostatus_poll_interval')), t("How often shall the poller check for new entries in OStatus conversations? This can be a very ressource task."), $ostatus_poll_choices),
+               '$ostatus_full_threads' => array('ostatus_full_threads', t("Only import OStatus threads from our contacts"), get_config('system','ostatus_full_threads'), t("Normally we import every content from our OStatus contacts. With this option we only store threads that are started by a contact that is known on our system.")),
                '$ostatus_not_able'     => t("OStatus support can only be enabled if threading is enabled."),
                '$diaspora_able'        => $diaspora_able,
                '$diaspora_not_able'    => t("Diaspora support can't be enabled because Friendica was installed into a sub directory."),
@@ -993,10 +1007,14 @@ function admin_page_site(&$a) {
                '$rino'                 => array('rino', t("RINO Encryption"), intval(get_config('system','rino_encrypt')), t("Encryption layer between nodes."), array("Disabled", "RINO1 (deprecated)", "RINO2")),
                '$embedly'              => array('embedly', t("Embedly API key"), get_config('system','embedly'), t("<a href='http://embed.ly'>Embedly</a> is used to fetch additional data for web pages. This is an optional parameter.")),
 
+               '$worker'               => array('worker', t("Enable 'worker' background processing"), get_config('system','worker'), t("The worker background processing limits the number of parallel background jobs to a maximum number and respects the system load.")),
+               '$worker_queues'        => array('worker_queues', t("Maximum number of parallel workers"), get_config('system','worker_queues'), t("On shared hosters set this to 2. On larger systems, values of 10 are great. Default value is 4.")),
+               '$worker_dont_fork'     => array('worker_dont_fork', t("Don't use 'proc_open' with the worker"), get_config('system','worker_dont_fork'), t("Enable this if your system doesn't allow the use of 'proc_open'. This can happen on shared hosters. If this is enabled you should increase the frequency of poller calls in your crontab.")),
+
                '$form_security_token'  => get_form_security_token("admin_site")
 
        ));
-}
+
 }
 
 /**
@@ -1011,18 +1029,17 @@ function admin_page_site(&$a) {
  * @param App $a
  * @return string
  **/
-if(! function_exists('admin_page_dbsync')) {
 function admin_page_dbsync(&$a) {
 
        $o = '';
 
        if($a->argc > 3 && intval($a->argv[3]) && $a->argv[2] === 'mark') {
-               set_config('database', 'update_' . intval($a->argv[3]), 'success');
+               set_config('database', 'update_'.intval($a->argv[3]), 'success');
                $curr = get_config('system','build');
                if(intval($curr) == intval($a->argv[3]))
                        set_config('system','build',intval($curr) + 1);
-               info( t('Update has been marked successful') . EOL);
-               goaway($a->get_baseurl(true) . '/admin/dbsync');
+               info(t('Update has been marked successful').EOL);
+               goaway('admin/dbsync');
        }
 
        if(($a->argc > 2) AND (intval($a->argv[2]) OR ($a->argv[2] === 'check'))) {
@@ -1040,7 +1057,7 @@ function admin_page_dbsync(&$a) {
 
        if($a->argc > 2 && intval($a->argv[2])) {
                require_once('update.php');
-               $func = 'update_' . intval($a->argv[2]);
+               $func = 'update_'.intval($a->argv[2]);
                if(function_exists($func)) {
                        $retval = $func();
                        if($retval === UPDATE_FAILED) {
@@ -1087,28 +1104,27 @@ function admin_page_dbsync(&$a) {
        }
 
        return $o;
-}
+
 }
 
 /**
  * @brief Process data send by Users admin page
- *
+ * 
  * @param App $a
  */
-if(! function_exists('admin_page_users_post')) {
 function admin_page_users_post(&$a){
-       $pending        =       ( x($_POST, 'pending')                  ? $_POST['pending']             : array() );
-       $users          =       ( x($_POST, 'user')                     ? $_POST['user']                : array() );
-       $nu_name        =       ( x($_POST, 'new_user_name')            ? $_POST['new_user_name']       : '');
-       $nu_nickname    =       ( x($_POST, 'new_user_nickname')        ? $_POST['new_user_nickname']   : '');
-       $nu_email       =       ( x($_POST, 'new_user_email')           ? $_POST['new_user_email']      : '');
+       $pending        =       (x($_POST, 'pending')                   ? $_POST['pending']             : array());
+       $users          =       (x($_POST, 'user')                      ? $_POST['user']                : array());
+       $nu_name        =       (x($_POST, 'new_user_name')             ? $_POST['new_user_name']       : '');
+       $nu_nickname    =       (x($_POST, 'new_user_nickname') ? $_POST['new_user_nickname']   : '');
+       $nu_email       =       (x($_POST, 'new_user_email')            ? $_POST['new_user_email']      : '');
 
        check_form_security_token_redirectOnErr('/admin/users', 'admin_users');
 
        if(!($nu_name==="") && !($nu_email==="") && !($nu_nickname==="")) {
                require_once('include/user.php');
 
-               $result = create_user( array('username'=>$nu_name, 'email'=>$nu_email, 'nickname'=>$nu_nickname, 'verified'=>1)  );
+               $result = create_user(array('username'=>$nu_name, 'email'=>$nu_email, 'nickname'=>$nu_nickname, 'verified'=>1));
                if(! $result['success']) {
                        notice($result['message']);
                        return;
@@ -1149,7 +1165,7 @@ function admin_page_users_post(&$a){
                notification(array(
                        'type' => "SYSTEM_EMAIL",
                        'to_email' => $nu['email'],
-                       'subject'=> sprintf( t('Registration details for %s'), $a->config['sitename']),
+                       'subject'=> sprintf(t('Registration details for %s'), $a->config['sitename']),
                        'preamble'=> $preamble,
                        'body' => $body));
 
@@ -1158,17 +1174,17 @@ function admin_page_users_post(&$a){
        if(x($_POST,'page_users_block')) {
                foreach($users as $uid){
                        q("UPDATE `user` SET `blocked`=1-`blocked` WHERE `uid`=%s",
-                               intval( $uid )
+                               intval($uid)
                        );
                }
-               notice( sprintf( tt("%s user blocked/unblocked", "%s users blocked/unblocked", count($users)), count($users)) );
+               notice(sprintf(tt("%s user blocked/unblocked", "%s users blocked/unblocked", count($users)), count($users)));
        }
        if(x($_POST,'page_users_delete')) {
                require_once("include/Contact.php");
                foreach($users as $uid){
                        user_remove($uid);
                }
-               notice( sprintf( tt("%s user deleted", "%s users deleted", count($users)), count($users)) );
+               notice(sprintf(tt("%s user deleted", "%s users deleted", count($users)), count($users)));
        }
 
        if(x($_POST,'page_users_approve')) {
@@ -1183,10 +1199,9 @@ function admin_page_users_post(&$a){
                        user_deny($hash);
                }
        }
-       goaway($a->get_baseurl(true) . '/admin/users' );
+       goaway('admin/users');
        return; // NOTREACHED
 }
-}
 
 /**
  * @brief Admin panel subpage for User management
@@ -1200,14 +1215,13 @@ function admin_page_users_post(&$a){
  * @param App $a
  * @return string
  */
-if(! function_exists('admin_page_users')) {
 function admin_page_users(&$a){
        if($a->argc>2) {
                $uid = $a->argv[3];
                $user = q("SELECT username, blocked FROM `user` WHERE `uid`=%d", intval($uid));
                if(count($user)==0) {
-                       notice( 'User not found' . EOL);
-                       goaway($a->get_baseurl(true) . '/admin/users' );
+                       notice('User not found'.EOL);
+                       goaway('admin/users');
                        return ''; // NOTREACHED
                }
                switch($a->argv[2]){
@@ -1217,18 +1231,18 @@ function admin_page_users(&$a){
                                require_once("include/Contact.php");
                                user_remove($uid);
 
-                               notice( sprintf(t("User '%s' deleted"), $user[0]['username']) . EOL);
+                               notice(sprintf(t("User '%s' deleted"), $user[0]['username']).EOL);
                        }; break;
                        case "block":{
                                check_form_security_token_redirectOnErr('/admin/users', 'admin_users', 't');
                                q("UPDATE `user` SET `blocked`=%d WHERE `uid`=%s",
-                                       intval( 1-$user[0]['blocked'] ),
-                                       intval( $uid )
+                                       intval(1-$user[0]['blocked']),
+                                       intval($uid)
                                );
-                               notice( sprintf( ($user[0]['blocked']?t("User '%s' unblocked"):t("User '%s' blocked")) , $user[0]['username']) . EOL);
+                               notice(sprintf(($user[0]['blocked']?t("User '%s' unblocked"):t("User '%s' blocked")) , $user[0]['username']).EOL);
                        }; break;
                }
-               goaway($a->get_baseurl(true) . '/admin/users' );
+               goaway('admin/users');
                return ''; // NOTREACHED
 
        }
@@ -1247,7 +1261,7 @@ function admin_page_users(&$a){
                $a->set_pager_itemspage(100);
        }
 
-       $users = q("SELECT `user` . * , `contact`.`name` , `contact`.`url` , `contact`.`micro`, `lastitem`.`lastitem_date`, `user`.`account_expired`
+       $users = q("SELECT `user`.* , `contact`.`name` , `contact`.`url` , `contact`.`micro`, `lastitem`.`lastitem_date`, `user`.`account_expired`
                                FROM
                                        (SELECT MAX(`item`.`changed`) as `lastitem_date`, `item`.`uid`
                                        FROM `item`
@@ -1294,7 +1308,7 @@ function admin_page_users(&$a){
 
        while(count($users)) {
                $new_user = array();
-               foreach( array_pop($users) as $k => $v) {
+               foreach(array_pop($users) as $k => $v) {
                        $k = str_replace('-','_',$k);
                        $new_user[$k] = $v;
                }
@@ -1320,7 +1334,7 @@ function admin_page_users(&$a){
                '$select_all' => t('select all'),
                '$h_pending' => t('User registrations waiting for confirm'),
                '$h_deleted' => t('User waiting for permanent deletion'),
-               '$th_pending' => array( t('Request date'), t('Name'), t('Email') ),
+               '$th_pending' => array(t('Request date'), t('Name'), t('Email')),
                '$no_pending' =>  t('No registrations.'),
                '$approve' => t('Approve'),
                '$deny' => t('Deny'),
@@ -1332,8 +1346,8 @@ function admin_page_users(&$a){
 
                '$h_users' => t('Users'),
                '$h_newuser' => t('New User'),
-               '$th_deleted' => array( t('Name'), t('Email'), t('Register date'), t('Last login'), t('Last item'), t('Deleted since') ),
-               '$th_users' => array( t('Name'), t('Email'), t('Register date'), t('Last login'), t('Last item'),  t('Account') ),
+               '$th_deleted' => array(t('Name'), t('Email'), t('Register date'), t('Last login'), t('Last item'), t('Deleted since')),
+               '$th_users' => array(t('Name'), t('Email'), t('Register date'), t('Last login'), t('Last item'),  t('Account')),
 
                '$confirm_delete_multi' => t('Selected users will be deleted!\n\nEverything these users had posted on this site will be permanently deleted!\n\nAre you sure?'),
                '$confirm_delete' => t('The user {0} will be deleted!\n\nEverything this user has posted on this site will be permanently deleted!\n\nAre you sure?'),
@@ -1353,7 +1367,7 @@ function admin_page_users(&$a){
        $o .= paginate($a);
        return $o;
 }
-}
+
 
 /**
  * @brief Plugins admin page
@@ -1371,7 +1385,6 @@ function admin_page_users(&$a){
  * @param App $a
  * @return string
  */
-if(! function_exists('admin_page_plugins')) {
 function admin_page_plugins(&$a){
 
        /*
@@ -1380,7 +1393,7 @@ function admin_page_plugins(&$a){
        if($a->argc == 3) {
                $plugin = $a->argv[2];
                if(!is_file("addon/$plugin/$plugin.php")) {
-                       notice( t("Item not found.") );
+                       notice(t("Item not found."));
                        return '';
                }
 
@@ -1392,14 +1405,14 @@ function admin_page_plugins(&$a){
                        if($idx !== false) {
                                unset($a->plugins[$idx]);
                                uninstall_plugin($plugin);
-                               info( sprintf( t("Plugin %s disabled."), $plugin ) );
+                               info(sprintf(t("Plugin %s disabled."), $plugin));
                        } else {
                                $a->plugins[] = $plugin;
                                install_plugin($plugin);
-                               info( sprintf( t("Plugin %s enabled."), $plugin ) );
+                               info(sprintf(t("Plugin %s enabled."), $plugin));
                        }
                        set_config("system","addon", implode(", ",$a->plugins));
-                       goaway($a->get_baseurl(true) . '/admin/plugins' );
+                       goaway('admin/plugins');
                        return ''; // NOTREACHED
                }
 
@@ -1497,19 +1510,17 @@ function admin_page_plugins(&$a){
                '$baseurl' => $a->get_baseurl(true),
                '$function' => 'plugins',
                '$plugins' => $plugins,
-               '$pcount' => count($plugins),
-               '$noplugshint' => sprintf( t('There are currently no plugins available on your node. You can find the official plugin repository at %1$s and might find other interesting plugins in the open plugin registry at %2$s'), 'https://github.com/friendica/friendica-addons', 'http://addons.friendi.ca'),
+               '$pcount' => count($plugins), 
+               '$noplugshint' => sprintf(t('There are currently no plugins available on your node. You can find the official plugin repository at %1$s and might find other interesting plugins in the open plugin registry at %2$s'), 'https://github.com/friendica/friendica-addons', 'http://addons.friendi.ca'),
                '$form_security_token' => get_form_security_token("admin_themes"),
        ));
 }
-}
 
 /**
  * @param array $themes
  * @param string $th
  * @param int $result
  */
-if(! function_exists('toggle_theme')) {
 function toggle_theme(&$themes,$th,&$result) {
        for($x = 0; $x < count($themes); $x ++) {
                if($themes[$x]['name'] === $th) {
@@ -1524,14 +1535,12 @@ function toggle_theme(&$themes,$th,&$result) {
                }
        }
 }
-}
 
 /**
  * @param array $themes
  * @param string $th
  * @return int
  */
-if(! function_exists('theme_status')) {
 function theme_status($themes,$th) {
        for($x = 0; $x < count($themes); $x ++) {
                if($themes[$x]['name'] === $th) {
@@ -1545,13 +1554,12 @@ function theme_status($themes,$th) {
        }
        return 0;
 }
-}
+
 
 /**
  * @param array $themes
  * @return string
  */
-if(! function_exists('rebuild_theme_table')) {
 function rebuild_theme_table($themes) {
        $o = '';
        if(count($themes)) {
@@ -1565,7 +1573,7 @@ function rebuild_theme_table($themes) {
        }
        return $o;
 }
-}
+
 
 /**
  * @brief Themes admin page
@@ -1583,7 +1591,6 @@ function rebuild_theme_table($themes) {
  * @param App $a
  * @return string
  */
-if(! function_exists('admin_page_themes')) {
 function admin_page_themes(&$a){
 
        $allowed_themes_str = get_config('system','allowed_themes');
@@ -1599,8 +1606,8 @@ function admin_page_themes(&$a){
        if($files) {
                foreach($files as $file) {
                        $f = basename($file);
-                       $is_experimental = intval(file_exists($file . '/experimental'));
-                       $is_supported = 1-(intval(file_exists($file . '/unsupported')));
+                       $is_experimental = intval(file_exists($file.'/experimental'));
+                       $is_supported = 1-(intval(file_exists($file.'/unsupported')));
                        $is_allowed = intval(in_array($f,$allowed_themes));
 
                        if($is_allowed OR $is_supported OR get_config("system", "show_unsupported_themes"))
@@ -1609,7 +1616,7 @@ function admin_page_themes(&$a){
        }
 
        if(! count($themes)) {
-               notice( t('No themes found.'));
+               notice(t('No themes found.'));
                return '';
        }
 
@@ -1620,7 +1627,7 @@ function admin_page_themes(&$a){
        if($a->argc == 3) {
                $theme = $a->argv[2];
                if(! is_dir("view/theme/$theme")) {
-                       notice( t("Item not found.") );
+                       notice(t("Item not found."));
                        return '';
                }
 
@@ -1633,15 +1640,15 @@ function admin_page_themes(&$a){
                        $s = rebuild_theme_table($themes);
                        if($result) {
                                install_theme($theme);
-                               info( sprintf('Theme %s enabled.',$theme));
+                               info(sprintf('Theme %s enabled.',$theme));
                        }
                        else {
                                uninstall_theme($theme);
-                               info( sprintf('Theme %s disabled.',$theme));
+                               info(sprintf('Theme %s disabled.',$theme));
                        }
 
                        set_config('system','allowed_themes',$s);
-                       goaway($a->get_baseurl(true) . '/admin/themes' );
+                       goaway('admin/themes');
                        return ''; // NOTREACHED
                }
 
@@ -1687,7 +1694,7 @@ function admin_page_themes(&$a){
                        $admin_form = __get_theme_admin_form($a, $theme);
                }
 
-               $screenshot = array( get_theme_screenshot($theme), t('Screenshot'));
+               $screenshot = array(get_theme_screenshot($theme), t('Screenshot'));
                if(! stristr($screenshot[0],$theme))
                        $screenshot = null;
 
@@ -1758,14 +1765,13 @@ function admin_page_themes(&$a){
                '$form_security_token' => get_form_security_token("admin_themes"),
        ));
 }
-}
+
 
 /**
  * @brief Prosesses data send by Logs admin page
- *
+ * 
  * @param App $a
  */
-if(! function_exists('admin_page_logs_post')) {
 function admin_page_logs_post(&$a) {
        if(x($_POST,"page_logs")) {
                check_form_security_token_redirectOnErr('/admin/logs', 'admin_logs');
@@ -1779,11 +1785,10 @@ function admin_page_logs_post(&$a) {
                set_config('system','loglevel', $loglevel);
        }
 
-       info( t("Log settings updated.") );
-       goaway($a->get_baseurl(true) . '/admin/logs' );
+       info(t("Log settings updated."));
+       goaway('admin/logs');
        return; // NOTREACHED
 }
-}
 
 /**
  * @brief Generates admin panel subpage for configuration of the logs
@@ -1801,7 +1806,6 @@ function admin_page_logs_post(&$a) {
  * @param App $a
  * @return string
  */
-if(! function_exists('admin_page_logs')) {
 function admin_page_logs(&$a){
 
        $log_choices = array(
@@ -1830,10 +1834,9 @@ function admin_page_logs(&$a){
                '$form_security_token' => get_form_security_token("admin_logs"),
                '$phpheader' => t("PHP logging"),
                '$phphint' => t("To enable logging of PHP errors and warnings you can add the following to the .htconfig.php file of your installation. The filename set in the 'error_log' line is relative to the friendica top-level directory and must be writeable by the web server. The option '1' for 'log_errors' and 'display_errors' is to enable these options, set to '0' to disable them."),
-               '$phplogcode' => "error_reporting(E_ERROR | E_WARNING | E_PARSE );\nini_set('error_log','php.out');\nini_set('log_errors','1');\nini_set('display_errors', '1');",
+               '$phplogcode' => "error_reporting(E_ERROR | E_WARNING | E_PARSE);\nini_set('error_log','php.out');\nini_set('log_errors','1');\nini_set('display_errors', '1');",
        ));
 }
-}
 
 /**
  * @brief Generates admin panel subpage to view the Friendica log
@@ -1853,7 +1856,6 @@ function admin_page_logs(&$a){
  * @param App $a
  * @return string
  */
-if(! function_exists('admin_page_viewlogs')) {
 function admin_page_viewlogs(&$a){
        $t = get_markup_template("admin_viewlogs.tpl");
        $f = get_config('system','logfile');
@@ -1890,19 +1892,17 @@ function admin_page_viewlogs(&$a){
                '$logname' =>  get_config('system','logfile')
        ));
 }
-}
 
 /**
  * @brief Prosesses data send by the features admin page
- *
+ * 
  * @param App $a
  */
-if(! function_exists('admin_page_features_post')) {
 function admin_page_features_post(&$a) {
 
        check_form_security_token_redirectOnErr('/admin/features', 'admin_manage_features');
 
-       logger('postvars: ' . print_r($_POST,true),LOGGER_DATA);
+       logger('postvars: '.print_r($_POST,true),LOGGER_DATA);
 
        $arr = array();
        $features = get_features(false);
@@ -1910,11 +1910,11 @@ function admin_page_features_post(&$a) {
        foreach($features as $fname => $fdata) {
                foreach(array_slice($fdata,1) as $f) {
                        $feature = $f[0];
-                       $feature_state = 'feature_' . $feature;
-                       $featurelock = 'featurelock_' . $feature;
+                       $feature_state = 'feature_'.$feature;
+                       $featurelock = 'featurelock_'.$feature;
 
                        if(x($_POST[$feature_state]))
-                               $val = intval($_POST['feature_' . $feature]);
+                               $val = intval($_POST['feature_'.$feature]);
                        else
                                $val = 0;
                        set_config('feature',$feature,$val);
@@ -1926,28 +1926,26 @@ function admin_page_features_post(&$a) {
                }
        }
 
-       goaway($a->get_baseurl(true) . '/admin/features' );
+       goaway('admin/features');
        return; // NOTREACHED
 }
-}
 
 /**
  * @brief Subpage for global additional feature management
- *
+ * 
  * This functin generates the subpage 'Manage Additional Features'
  * for the admin panel. At this page the admin can set preferences
- * for the user settings of the 'additional features'. If needed this
+ * for the user settings of the 'additional features'. If needed this 
  * preferences can be locked through the admin.
- *
+ * 
  * The returned string contains the HTML code of the subpage 'Manage
  * Additional Features'
- *
+ * 
  * @param App $a
  * @return string
  */
-if(! function_exists('admin_page_features')) {
 function admin_page_features(&$a) {
-
+       
        if((argc() > 1) && (argv(1) === 'features')) {
                $arr = array();
                $features = get_features(false);
@@ -1962,11 +1960,11 @@ function admin_page_features(&$a) {
                                        $set = $f[3];
                                $arr[$fname][1][] = array(
                                        array('feature_' .$f[0],$f[1],$set,$f[2],array(t('Off'),t('On'))),
-                                       array('featurelock_' .$f[0],sprintf( t('Lock feature %s'),$f[1]),(($f[4] !== false) ? "1" : ''),'',array(t('Off'),t('On')))
+                                       array('featurelock_' .$f[0],sprintf(t('Lock feature %s'),$f[1]),(($f[4] !== false) ? "1" : ''),'',array(t('Off'),t('On')))
                                );
                        }
                }
-
+               
                $tpl = get_markup_template("admin_settings_features.tpl");
                $o .= replace_macros($tpl, array(
                        '$form_security_token' => get_form_security_token("admin_manage_features"),
@@ -1978,4 +1976,3 @@ function admin_page_features(&$a) {
                return $o;
        }
 }
-}
index 8843265a99ed9a92a2637252b0911e69eabc2e58..356a389b83693ce291ae4b4afce726dcad7e0105 100644 (file)
@@ -5,7 +5,6 @@ require_once('include/Contact.php');
 require_once('include/contact_selectors.php');
 require_once('mod/contacts.php');
 
-if(! function_exists('allfriends_content')) {
 function allfriends_content(&$a) {
 
        $o = '';
@@ -98,4 +97,3 @@ function allfriends_content(&$a) {
 
        return $o;
 }
-}
index 141a80429887094774fab8bd894095b6a4559a6d..a2a1327e6df8b47c5452eb2fe0e1780294b26988 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-if(! function_exists('amcd_content')) {
+
 function amcd_content(&$a) {
 //header("Content-type: text/json");
 echo <<< EOT
@@ -46,5 +46,4 @@ echo <<< EOT
 }
 EOT;
 killme();
-}
-}
+}
\ No newline at end of file
index 67564836e8386b6a58abadc2d4fd51999fe5edbe..da2c40c3055a54b566cfd92bdcaf6a40680b65a0 100644 (file)
@@ -1,8 +1,10 @@
 <?php
+
 require_once('include/api.php');
 
-if(! function_exists('oauth_get_client')) {
 function oauth_get_client($request){
+
+
        $params = $request->get_parameters();
        $token = $params['oauth_token'];
 
@@ -17,10 +19,9 @@ function oauth_get_client($request){
 
        return $r[0];
 }
-}
 
-if(! function_exists('api_post')) {
 function api_post(&$a) {
+
        if(! local_user()) {
                notice( t('Permission denied.') . EOL);
                return;
@@ -30,10 +31,9 @@ function api_post(&$a) {
                notice( t('Permission denied.') . EOL);
                return;
        }
-}
+
 }
 
-if(! function_exists('api_content')) {
 function api_content(&$a) {
        if ($a->cmd=='api/oauth/authorize'){
                /*
@@ -114,4 +114,3 @@ function api_content(&$a) {
        echo api_call($a);
        killme();
 }
-}
index e807feae74e537c76110e33ccdf5f0ceb66bba63..a821ef5d5b89192b5534df7e8b78cd074e9a7964 100644 (file)
@@ -1,23 +1,25 @@
 <?php
-if(! function_exists('apps_content')) {
+
 function apps_content(&$a) {
-  $privateaddons = get_config('config','private_addons');
-  if ($privateaddons === "1") {
-    if((! (local_user()))) {
-      info( t("You must be logged in to use addons. "));
-      return;
-    }
-  }
+    $privateaddons = get_config('config','private_addons');
+      if ($privateaddons === "1") {
+       if((! (local_user())))  {
+       info( t("You must be logged in to use addons. "));
+      return;};
+}
 
-  $title = t('Applications');
+      $title = t('Applications');
 
-  if(count($a->apps)==0)
-    notice( t('No installed applications.') . EOL);
+       if(count($a->apps)==0)
+               notice( t('No installed applications.') . EOL);
+
+
+       $tpl = get_markup_template("apps.tpl");
+       return replace_macros($tpl, array(
+               '$title' => $title,
+               '$apps' => $a->apps,
+       ));
+
+       
 
-  $tpl = get_markup_template("apps.tpl");
-  return replace_macros($tpl, array(
-    '$title' => $title,
-    '$apps' => $a->apps,
-  ));
-}
 }
index 849faa26ec54ebc45fa9c6a32a476f2b162834ff..03f850f0d1749972d7e68e63de295007bf856cb5 100644 (file)
@@ -1,7 +1,7 @@
 <?php
+
 require_once('include/security.php');
 
-if(! function_exists('attach_init')) {
 function attach_init(&$a) {
 
        if($a->argc != 2) {
@@ -47,4 +47,3 @@ function attach_init(&$a) {
        killme();
        // NOTREACHED
 }
-}
index 56455bdb21dd342192c01cfe59efe26ed3455038..d31e090c554ee7f0b2c4eb6e716ed5b0d1f646ab 100644 (file)
@@ -9,56 +9,55 @@ function visible_lf($s) {
        return str_replace("\n",'<br />', $s);
 }
 
-if(! function_exists('babel_content')) {
 function babel_content(&$a) {
 
        $o .= '<h1>Babel Diagnostic</h1>';
 
        $o .= '<form action="babel" method="post">';
        $o .= t('Source (bbcode) text:') . EOL . '<textarea name="text" >' . htmlspecialchars($_REQUEST['text']) .'</textarea>' . EOL;
-       $o .= '<input type="submit" name="submit" value="Submit" /></form>';
+       $o .= '<input type="submit" name="submit" value="Submit" /></form>'; 
 
        $o .= '<br /><br />';
 
        $o .= '<form action="babel" method="post">';
        $o .= t('Source (Diaspora) text to convert to BBcode:') . EOL . '<textarea name="d2bbtext" >' . htmlspecialchars($_REQUEST['d2bbtext']) .'</textarea>' . EOL;
-       $o .= '<input type="submit" name="submit" value="Submit" /></form>';
+       $o .= '<input type="submit" name="submit" value="Submit" /></form>'; 
 
        $o .= '<br /><br />';
 
        if(x($_REQUEST,'text')) {
 
                $text = trim($_REQUEST['text']);
-               $o .= "<h2>" . t("Source input: ") . "</h2>" . EOL. EOL;
-               $o .= visible_lf($text) . EOL. EOL;
+               $o .= "<h2>" . t("Source input: ") . "</h2>" . EOL. EOL; 
+               $o .= visible_lf($text) . EOL. EOL; 
 
                $html = bbcode($text);
-               $o .= "<h2>" . t("bb2html (raw HTML): ") . "</h2>" . EOL. EOL;
-               $o .= htmlspecialchars($html). EOL. EOL;
+               $o .= "<h2>" . t("bb2html (raw HTML): ") . "</h2>" . EOL. EOL; 
+               $o .= htmlspecialchars($html). EOL. EOL; 
 
                //$html = bbcode($text);
-               $o .= "<h2>" . t("bb2html: ") . "</h2>" . EOL. EOL;
-               $o .= $html. EOL. EOL;
+               $o .= "<h2>" . t("bb2html: ") . "</h2>" . EOL. EOL; 
+               $o .= $html. EOL. EOL; 
 
                $bbcode = html2bbcode($html);
-               $o .= "<h2>" . t("bb2html2bb: ") . "</h2>" . EOL. EOL;
-               $o .= visible_lf($bbcode) . EOL. EOL;
+               $o .= "<h2>" . t("bb2html2bb: ") . "</h2>" . EOL. EOL; 
+               $o .= visible_lf($bbcode) . EOL. EOL; 
 
                $diaspora = bb2diaspora($text);
-               $o .= "<h2>" . t("bb2md: ") . "</h2>" . EOL. EOL;
-               $o .= visible_lf($diaspora) . EOL. EOL;
+               $o .= "<h2>" . t("bb2md: ") . "</h2>" . EOL. EOL; 
+               $o .= visible_lf($diaspora) . EOL. EOL; 
 
                $html = Markdown($diaspora);
-               $o .= "<h2>" . t("bb2md2html: ") . "</h2>" . EOL. EOL;
-               $o .= $html. EOL. EOL;
+               $o .= "<h2>" . t("bb2md2html: ") . "</h2>" . EOL. EOL; 
+               $o .= $html. EOL. EOL; 
 
                $bbcode = diaspora2bb($diaspora);
-               $o .= "<h2>" . t("bb2dia2bb: ") . "</h2>" . EOL. EOL;
-               $o .= visible_lf($bbcode) . EOL. EOL;
+               $o .= "<h2>" . t("bb2dia2bb: ") . "</h2>" . EOL. EOL; 
+               $o .= visible_lf($bbcode) . EOL. EOL; 
 
                $bbcode = html2bbcode($html);
-               $o .= "<h2>" . t("bb2md2html2bb: ") . "</h2>" . EOL. EOL;
-               $o .= visible_lf($bbcode) . EOL. EOL;
+               $o .= "<h2>" . t("bb2md2html2bb: ") . "</h2>" . EOL. EOL; 
+               $o .= visible_lf($bbcode) . EOL. EOL; 
 
 
 
@@ -67,15 +66,14 @@ function babel_content(&$a) {
        if(x($_REQUEST,'d2bbtext')) {
 
                $d2bbtext = trim($_REQUEST['d2bbtext']);
-               $o .= "<h2>" . t("Source input (Diaspora format): ") . "</h2>" . EOL. EOL;
-               $o .= visible_lf($d2bbtext) . EOL. EOL;
+               $o .= "<h2>" . t("Source input (Diaspora format): ") . "</h2>" . EOL. EOL; 
+               $o .= visible_lf($d2bbtext) . EOL. EOL; 
 
 
                $bb = diaspora2bb($d2bbtext);
-               $o .= "<h2>" . t("diaspora2bb: ") . "</h2>" . EOL. EOL;
-               $o .= visible_lf($bb) . EOL. EOL;
+               $o .= "<h2>" . t("diaspora2bb: ") . "</h2>" . EOL. EOL; 
+               $o .= visible_lf($bb) . EOL. EOL; 
        }
 
        return $o;
 }
-}
index 4db6bf401eef49d976e2f6e5f18c3abbb5590fb4..be8645c1fde901924032a4e88e48397d58f6a1c4 100644 (file)
@@ -1,14 +1,12 @@
 <?php
+
 require_once('include/conversation.php');
 require_once('include/items.php');
 
-if(! function_exists('bookmarklet_init')) {
 function bookmarklet_init(&$a) {
        $_GET["mode"] = "minimal";
 }
-}
 
-if(! function_exists('bookmarklet_content')) {
 function bookmarklet_content(&$a) {
        if(!local_user()) {
                $o = '<h2>'.t('Login').'</h2>';
@@ -46,4 +44,3 @@ function bookmarklet_content(&$a) {
 
        return $o;
 }
-}
index 04d01302c11504fff949580487ab7bc6af2950b0..6375d23984d708c989b81d53699843f9235fb076 100644 (file)
@@ -4,28 +4,21 @@
  * General purpose landing page for plugins/addons
  */
 
-if(! function_exists('cb_init')) {
+
 function cb_init(&$a) {
        call_hooks('cb_init');
 }
-}
 
-if(! function_exists('cb_post')) {
 function cb_post(&$a) {
        call_hooks('cb_post', $_POST);
 }
-}
 
-if(! function_exists('cb_afterpost')) {
 function cb_afterpost(&$a) {
        call_hooks('cb_afterpost');
 }
-}
 
-if(! function_exists('cb_content')) {
 function cb_content(&$a) {
        $o = '';
        call_hooks('cb_content', $o);
        return $o;
-}
-}
+}
\ No newline at end of file
index 4cdbe9641b3c006fd0b3c39bfaa3ef798f917ae0..62a5185fed84c4a00256bab9447fc56c64b530b2 100644 (file)
@@ -5,7 +5,6 @@ require_once('include/Contact.php');
 require_once('include/contact_selectors.php');
 require_once('mod/contacts.php');
 
-if(! function_exists('common_content')) {
 function common_content(&$a) {
 
        $o = '';
@@ -41,7 +40,7 @@ function common_content(&$a) {
                $vcard_widget .= replace_macros(get_markup_template("vcard-widget.tpl"),array(
                        '$name' => htmlentities($c[0]['name']),
                        '$photo' => $c[0]['photo'],
-                       'url' => z_root() . '/contacts/' . $cid
+                       'url' => 'contacts/' . $cid
                ));
 
                if(! x($a->page,'aside'))
@@ -145,4 +144,3 @@ function common_content(&$a) {
 
        return $o;
 }
-}
index c64c6216b167095ab2f24702938ff373e1900302..b6d72a35555a64f375b1402fc3188c93357c3579 100644 (file)
@@ -1,14 +1,15 @@
 <?php
-if(! function_exists('community_init')) {
+
 function community_init(&$a) {
        if(! local_user()) {
                unset($_SESSION['theme']);
                unset($_SESSION['mobile-theme']);
        }
+
+
 }
-}
 
-if(! function_exists('community_content')) {
+
 function community_content(&$a, $update = 0) {
 
        $o = '';
@@ -114,9 +115,7 @@ function community_content(&$a, $update = 0) {
 
        return $o;
 }
-}
 
-if(! function_exists('community_getitems')) {
 function community_getitems($start, $itemspage) {
        if (get_config('system','community_page_style') == CP_GLOBAL_COMMUNITY)
                return(community_getpublicitems($start, $itemspage));
@@ -141,10 +140,9 @@ function community_getitems($start, $itemspage) {
        );
 
        return($r);
-}
+
 }
 
-if(! function_exists('community_getpublicitems')) {
 function community_getpublicitems($start, $itemspage) {
        $r = q("SELECT `item`.`uri`, `item`.*, `item`.`id` AS `item_id`,
                        `author-name` AS `name`, `owner-avatar` AS `photo`,
@@ -159,4 +157,3 @@ function community_getpublicitems($start, $itemspage) {
 
        return($r);
 }
-}
index 0291350b21ed154dd5379253feb22c6d1f73ca47..bf81afe079118767cd83e3917871e6cf34e510ff 100644 (file)
@@ -2,7 +2,6 @@
 
 require_once('include/group.php');
 
-if(! function_exists('contactgroup_content')) {
 function contactgroup_content(&$a) {
 
 
@@ -48,5 +47,4 @@ function contactgroup_content(&$a) {
        }
 
        killme();
-}
-}
+}
\ No newline at end of file
index cef8bdb8978671ddac5cb1548ee799bcb49dc60a..4897663a051d92b2c3e208507de588ced76adfdd 100644 (file)
@@ -7,7 +7,6 @@ require_once('include/Scrape.php');
 require_once('mod/proxy.php');
 require_once('include/Photo.php');
 
-if(! function_exists('contacts_init')) {
 function contacts_init(&$a) {
        if(! local_user())
                return;
@@ -39,13 +38,13 @@ function contacts_init(&$a) {
 
                        if (($a->data['contact']['network'] != "") AND ($a->data['contact']['network'] != NETWORK_DFRN)) {
                                $networkname = format_network_name($a->data['contact']['network'],$a->data['contact']['url']);
-                       } else
+                       } else 
                                $networkname = '';
 
                        $vcard_widget = replace_macros(get_markup_template("vcard-widget.tpl"),array(
                                '$name' => htmlentities($a->data['contact']['name']),
                                '$photo' => $a->data['contact']['photo'],
-                               '$url' => ($a->data['contact']['network'] == NETWORK_DFRN) ? z_root()."/redir/".$a->data['contact']['id'] : $a->data['contact']['url'],
+                               '$url' => ($a->data['contact']['network'] == NETWORK_DFRN) ? "redir/".$a->data['contact']['id'] : $a->data['contact']['url'],
                                '$addr' => (($a->data['contact']['addr'] != "") ? ($a->data['contact']['addr']) : ""),
                                '$network_name' => $networkname,
                                '$network' => t('Network:'),
@@ -89,10 +88,9 @@ function contacts_init(&$a) {
                '$base' => $base
        ));
 
-}
+
 }
 
-if(! function_exists('contacts_batch_actions')) {
 function contacts_batch_actions(&$a){
        $contacts_id = $_POST['contact_batch'];
        if (!is_array($contacts_id)) return;
@@ -131,13 +129,13 @@ function contacts_batch_actions(&$a){
        }
 
        if(x($_SESSION,'return_url'))
-               goaway($a->get_baseurl(true) . '/' . $_SESSION['return_url']);
+               goaway('' . $_SESSION['return_url']);
        else
-               goaway($a->get_baseurl(true) . '/contacts');
-}
+               goaway('contacts');
+
 }
 
-if(! function_exists('contacts_post')) {
+
 function contacts_post(&$a) {
 
        if(! local_user())
@@ -159,7 +157,7 @@ function contacts_post(&$a) {
 
        if(! count($orig_record)) {
                notice( t('Could not access contact record.') . EOL);
-               goaway($a->get_baseurl(true) . '/contacts');
+               goaway('contacts');
                return; // NOTREACHED
        }
 
@@ -217,11 +215,10 @@ function contacts_post(&$a) {
                $a->data['contact'] = $r[0];
 
        return;
-}
+
 }
 
 /*contact actions*/
-if(! function_exists('_contact_update')) {
 function _contact_update($contact_id) {
        $r = q("SELECT `uid`, `url`, `network` FROM `contact` WHERE `id` = %d", intval($contact_id));
        if (!$r)
@@ -242,9 +239,7 @@ function _contact_update($contact_id) {
                // pull feed and consume it, which should subscribe to the hub.
                proc_run('php',"include/onepoll.php","$contact_id", "force");
 }
-}
 
-if(! function_exists('_contact_update_profile')) {
 function _contact_update_profile($contact_id) {
        $r = q("SELECT `uid`, `url`, `network` FROM `contact` WHERE `id` = %d", intval($contact_id));
        if (!$r)
@@ -304,9 +299,7 @@ function _contact_update_profile($contact_id) {
        // Update the entry in the gcontact table
        update_gcontact_from_probe($data["url"]);
 }
-}
 
-if(! function_exists('_contact_block')) {
 function _contact_block($contact_id, $orig_record) {
        $blocked = (($orig_record['blocked']) ? 0 : 1);
        $r = q("UPDATE `contact` SET `blocked` = %d WHERE `id` = %d AND `uid` = %d",
@@ -315,10 +308,8 @@ function _contact_block($contact_id, $orig_record) {
                intval(local_user())
        );
        return $r;
-}
-}
 
-if(! function_exists('_contact_ignore')) {
+}
 function _contact_ignore($contact_id, $orig_record) {
        $readonly = (($orig_record['readonly']) ? 0 : 1);
        $r = q("UPDATE `contact` SET `readonly` = %d WHERE `id` = %d AND `uid` = %d",
@@ -328,9 +319,6 @@ function _contact_ignore($contact_id, $orig_record) {
        );
        return $r;
 }
-}
-
-if(! function_exists('_contact_archive')) {
 function _contact_archive($contact_id, $orig_record) {
        $archived = (($orig_record['archive']) ? 0 : 1);
        $r = q("UPDATE `contact` SET `archive` = %d WHERE `id` = %d AND `uid` = %d",
@@ -343,18 +331,14 @@ function _contact_archive($contact_id, $orig_record) {
        }
        return $r;
 }
-}
-
-if(! function_exists('_contact_drop')) {
 function _contact_drop($contact_id, $orig_record) {
        $a = get_app();
 
        terminate_friendship($a->user,$a->contact,$orig_record);
        contact_remove($orig_record['id']);
 }
-}
 
-if(! function_exists('contacts_content')) {
+
 function contacts_content(&$a) {
 
        $sort_type = 0;
@@ -382,19 +366,19 @@ function contacts_content(&$a) {
 
                if(! count($orig_record)) {
                        notice( t('Could not access contact record.') . EOL);
-                       goaway($a->get_baseurl(true) . '/contacts');
+                       goaway('contacts');
                        return; // NOTREACHED
                }
 
                if($cmd === 'update') {
                        _contact_update($contact_id);
-                       goaway($a->get_baseurl(true) . '/contacts/' . $contact_id);
+                       goaway('contacts/' . $contact_id);
                        // NOTREACHED
                }
 
                if($cmd === 'updateprofile') {
                        _contact_update_profile($contact_id);
-                       goaway($a->get_baseurl(true) . '/crepair/' . $contact_id);
+                       goaway('crepair/' . $contact_id);
                        // NOTREACHED
                }
 
@@ -405,7 +389,7 @@ function contacts_content(&$a) {
                                info((($blocked) ? t('Contact has been blocked') : t('Contact has been unblocked')).EOL);
                        }
 
-                       goaway($a->get_baseurl(true) . '/contacts/' . $contact_id);
+                       goaway('contacts/' . $contact_id);
                        return; // NOTREACHED
                }
 
@@ -416,7 +400,7 @@ function contacts_content(&$a) {
                                info((($readonly) ? t('Contact has been ignored') : t('Contact has been unignored')).EOL);
                        }
 
-                       goaway($a->get_baseurl(true) . '/contacts/' . $contact_id);
+                       goaway('contacts/' . $contact_id);
                        return; // NOTREACHED
                }
 
@@ -428,7 +412,7 @@ function contacts_content(&$a) {
                                info((($archived) ? t('Contact has been archived') : t('Contact has been unarchived')).EOL);
                        }
 
-                       goaway($a->get_baseurl(true) . '/contacts/' . $contact_id);
+                       goaway('contacts/' . $contact_id);
                        return; // NOTREACHED
                }
 
@@ -463,17 +447,17 @@ function contacts_content(&$a) {
                        // Now check how the user responded to the confirmation query
                        if($_REQUEST['canceled']) {
                                if(x($_SESSION,'return_url'))
-                                       goaway($a->get_baseurl(true) . '/' . $_SESSION['return_url']);
+                                       goaway('' . $_SESSION['return_url']);
                                else
-                                       goaway($a->get_baseurl(true) . '/contacts');
+                                       goaway('contacts');
                        }
 
                        _contact_drop($contact_id, $orig_record[0]);
                        info( t('Contact has been removed.') . EOL );
                        if(x($_SESSION,'return_url'))
-                               goaway($a->get_baseurl(true) . '/' . $_SESSION['return_url']);
+                               goaway('' . $_SESSION['return_url']);
                        else
-                               goaway($a->get_baseurl(true) . '/contacts');
+                               goaway('contacts');
                        return; // NOTREACHED
                }
                if($cmd === 'posts') {
@@ -581,6 +565,9 @@ function contacts_content(&$a) {
                        ($contact['rel'] == CONTACT_IS_FOLLOWER))
                        $follow = $a->get_baseurl(true)."/follow?url=".urlencode($contact["url"]);
 
+               // Load contactact related actions like hide, suggest, delete and others
+               $contact_actions = contact_actions($contact);
+
 
                $o .= replace_macros($tpl, array(
                        //'$header' => t('Contact Editor'),
@@ -591,7 +578,7 @@ function contacts_content(&$a) {
                        '$lbl_info1' => t('Contact Information / Notes'),
                        '$infedit' => t('Edit contact notes'),
                        '$common_text' => $common_text,
-                       '$common_link' => $a->get_baseurl(true) . '/common/loc/' . local_user() . '/' . $contact['id'],
+                       '$common_link' => 'common/loc/' . local_user() . '/' . $contact['id'],
                        '$all_friends' => $all_friends,
                        '$relation_text' => $relation_text,
                        '$visit' => sprintf( t('Visit %s\'s profile [%s]'),$contact['name'],$contact['url']),
@@ -600,7 +587,7 @@ function contacts_content(&$a) {
                        '$lblcrepair' => t("Repair URL settings"),
                        '$lblrecent' => t('View conversations'),
                        '$lblsuggest' => $lblsuggest,
-                       '$delete' => t('Delete contact'),
+                       //'$delete' => t('Delete contact'),
                        '$nettype' => $nettype,
                        '$poll_interval' => $poll_interval,
                        '$poll_enabled' => $poll_enabled,
@@ -638,7 +625,11 @@ function contacts_content(&$a) {
                        '$about' => bbcode($contact["about"], false, false),
                        '$about_label' => t("About:"),
                        '$keywords' => $contact["keywords"],
-                       '$keywords_label' => t("Tags:")
+                       '$keywords_label' => t("Tags:"),
+                       '$contact_action_button' => t("Actions"),
+                       '$contact_actions' => $contact_actions,
+                       '$contact_status' => t("Status"),
+                       '$contact_settings_label' => t('Contact Settings'),
 
                ));
 
@@ -684,7 +675,7 @@ function contacts_content(&$a) {
        $tabs = array(
                array(
                        'label' => t('Suggestions'),
-                       'url'   => $a->get_baseurl(true) . '/suggest',
+                       'url'   => 'suggest',
                        'sel'   => '',
                        'title' => t('Suggest potential friends'),
                        'id'    => 'suggestions-tab',
@@ -692,7 +683,7 @@ function contacts_content(&$a) {
                ),
                array(
                        'label' => t('All Contacts'),
-                       'url'   => $a->get_baseurl(true) . '/contacts/all',
+                       'url'   => 'contacts/all',
                        'sel'   => ($all) ? 'active' : '',
                        'title' => t('Show all contacts'),
                        'id'    => 'showall-tab',
@@ -700,7 +691,7 @@ function contacts_content(&$a) {
                ),
                array(
                        'label' => t('Unblocked'),
-                       'url'   => $a->get_baseurl(true) . '/contacts',
+                       'url'   => 'contacts',
                        'sel'   => ((! $all) && (! $blocked) && (! $hidden) && (! $search) && (! $nets) && (! $ignored) && (! $archived)) ? 'active' : '',
                        'title' => t('Only show unblocked contacts'),
                        'id'    => 'showunblocked-tab',
@@ -709,7 +700,7 @@ function contacts_content(&$a) {
 
                array(
                        'label' => t('Blocked'),
-                       'url'   => $a->get_baseurl(true) . '/contacts/blocked',
+                       'url'   => 'contacts/blocked',
                        'sel'   => ($blocked) ? 'active' : '',
                        'title' => t('Only show blocked contacts'),
                        'id'    => 'showblocked-tab',
@@ -718,7 +709,7 @@ function contacts_content(&$a) {
 
                array(
                        'label' => t('Ignored'),
-                       'url'   => $a->get_baseurl(true) . '/contacts/ignored',
+                       'url'   => 'contacts/ignored',
                        'sel'   => ($ignored) ? 'active' : '',
                        'title' => t('Only show ignored contacts'),
                        'id'    => 'showignored-tab',
@@ -727,7 +718,7 @@ function contacts_content(&$a) {
 
                array(
                        'label' => t('Archived'),
-                       'url'   => $a->get_baseurl(true) . '/contacts/archived',
+                       'url'   => 'contacts/archived',
                        'sel'   => ($archived) ? 'active' : '',
                        'title' => t('Only show archived contacts'),
                        'id'    => 'showarchived-tab',
@@ -736,7 +727,7 @@ function contacts_content(&$a) {
 
                array(
                        'label' => t('Hidden'),
-                       'url'   => $a->get_baseurl(true) . '/contacts/hidden',
+                       'url'   => 'contacts/hidden',
                        'sel'   => ($hidden) ? 'active' : '',
                        'title' => t('Only show hidden contacts'),
                        'id'    => 'showhidden-tab',
@@ -815,9 +806,18 @@ function contacts_content(&$a) {
 
        return $o;
 }
-}
 
-if(! function_exists('contacts_tab')) {
+/**
+ * @brief List of pages for the Contact TabBar
+ * 
+ * Available Pages are 'Status', 'Profile', 'Contacts' and 'Common Friends'
+ * 
+ * @param app $a
+ * @param int $contact_id The ID of the contact
+ * @param int $active_tab 1 if tab should be marked as active
+ * 
+ * @return array with with contact TabBar data
+ */
 function contacts_tab($a, $contact_id, $active_tab) {
        // tabs
        $tabs = array(
@@ -839,6 +839,7 @@ function contacts_tab($a, $contact_id, $active_tab) {
                )
        );
 
+       // Show this tab only if there is visible friend list
        $x = count_all_friends(local_user(), $contact_id);
        if ($x)
                $tabs[] = array('label'=>t('Contacts'),
@@ -848,6 +849,7 @@ function contacts_tab($a, $contact_id, $active_tab) {
                                'id' => 'allfriends-tab',
                                'accesskey' => 't');
 
+       // Show this tab only if there is visible common friend list
        $common = count_common_friends(local_user(),$contact_id);
        if ($common)
                $tabs[] = array('label'=>t('Common Friends'),
@@ -857,43 +859,19 @@ function contacts_tab($a, $contact_id, $active_tab) {
                                'id' => 'common-loc-tab',
                                'accesskey' => 'd');
 
-       $tabs[] = array('label' => t('Repair'),
-                       'url'   => $a->get_baseurl(true) . '/crepair/' . $contact_id,
+       $tabs[] = array('label' => t('Advanced'),
+                       'url'   => 'crepair/' . $contact_id,
                        'sel' => (($active_tab == 5)?'active':''),
                        'title' => t('Advanced Contact Settings'),
-                       'id'    => 'repair-tab',
+                       'id'    => 'advanced-tab',
                        'accesskey' => 'r');
 
-
-       $tabs[] = array('label' => (($contact['blocked']) ? t('Unblock') : t('Block') ),
-                       'url'   => $a->get_baseurl(true) . '/contacts/' . $contact_id . '/block',
-                       'sel'   => '',
-                       'title' => t('Toggle Blocked status'),
-                       'id'    => 'toggle-block-tab',
-                       'accesskey' => 'b');
-
-       $tabs[] = array('label' => (($contact['readonly']) ? t('Unignore') : t('Ignore') ),
-                       'url'   => $a->get_baseurl(true) . '/contacts/' . $contact_id . '/ignore',
-                       'sel'   => '',
-                       'title' => t('Toggle Ignored status'),
-                       'id'    => 'toggle-ignore-tab',
-                       'accesskey' => 'i');
-
-       $tabs[] = array('label' => (($contact['archive']) ? t('Unarchive') : t('Archive') ),
-                       'url'   => $a->get_baseurl(true) . '/contacts/' . $contact_id . '/archive',
-                       'sel'   => '',
-                       'title' => t('Toggle Archive status'),
-                       'id'    => 'toggle-archive-tab',
-                       'accesskey' => 'v');
-
        $tab_tpl = get_markup_template('common_tabs.tpl');
        $tab_str = replace_macros($tab_tpl, array('$tabs' => $tabs));
 
        return $tab_str;
 }
-}
 
-if(! function_exists('contact_posts')) {
 function contact_posts($a, $contact_id) {
 
        $r = q("SELECT `url` FROM `contact` WHERE `id` = %d", intval($contact_id));
@@ -921,9 +899,7 @@ function contact_posts($a, $contact_id) {
 
        return $o;
 }
-}
 
-if(! function_exists('_contact_detail_for_template')) {
 function _contact_detail_for_template($rr){
 
        $community = '';
@@ -974,5 +950,74 @@ function _contact_detail_for_template($rr){
                'url' => $url,
                'network' => network_to_name($rr['network'], $rr['url']),
        );
+
 }
+
+/**
+ * @brief Gives a array with actions which can performed to a given contact
+ * 
+ * This includes actions like e.g. 'block', 'hide', 'archive', 'delete' and others
+ * 
+ * @param array $contact Data about the Contact
+ * @return array with contact related actions
+ */
+function contact_actions($contact) {
+
+       $poll_enabled = in_array($contact['network'], array(NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_FEED, NETWORK_MAIL, NETWORK_MAIL2));
+       $contact_action = array();
+
+       // Provide friend suggestion only for Friendica contacts
+       if($contact['network'] === NETWORK_DFRN) {
+               $contact_actions['suggest'] = array(
+                                                       'label' => t('Suggest friends'),
+                                                       'url'   => 'fsuggest/' . $contact['id'],
+                                                       'title' => '',
+                                                       'sel'   => '',
+                                                       'id'    =>  'suggest',
+                                       );
+       }
+
+       if($poll_enabled) {
+               $contact_actions['update'] = array(
+                                                       'label' => t('Update now'),
+                                                       'url'   => 'contacts/' . $contact['id'] . '/update',
+                                                       'title' => '',
+                                                       'sel'   => '',
+                                                       'id'    => 'update',
+                                       );
+       }
+
+       $contact_actions['block'] = array(
+                                               'label' => (intval($contact['blocked']) ? t('Unblock') : t('Block') ),
+                                               'url'   => 'contacts/' . $contact['id'] . '/block',
+                                               'title' => t('Toggle Blocked status'),
+                                               'sel'   => (intval($contact['blocked']) ? 'active' : ''),
+                                               'id'    => 'toggle-block',
+                               );
+
+       $contact_actions['ignore'] = array(
+                                               'label' => (intval($contact['readonly']) ? t('Unignore') : t('Ignore') ),
+                                               'url'   => 'contacts/' . $contact['id'] . '/ignore',
+                                               'title' => t('Toggle Ignored status'),
+                                               'sel'   => (intval($contact['readonly']) ? 'active' : ''),
+                                               'id'    => 'toggle-ignore',
+                               );
+
+       $contact_actions['archive'] = array(
+                                               'label' => (intval($contact['archive']) ? t('Unarchive') : t('Archive') ),
+                                               'url'   => 'contacts/' . $contact['id'] . '/archive',
+                                               'title' => t('Toggle Archive status'),
+                                               'sel'   => (intval($contact['archive']) ? 'active' : ''),
+                                               'id'    => 'toggle-archive',
+                               );
+
+       $contact_actions['delete'] = array(
+                                               'label' => t('Delete'),
+                                               'url'   => 'contacts/' . $contact['id'] . '/drop', 
+                                               'title' => t('Delete contact'),
+                                               'sel'   => '',
+                                               'id'    => 'delete',
+                               );
+
+       return $contact_actions;
 }
index ab0fe7e4bf2a37a884f4154f297b2ad05d6e4797..49cff74d2d9f4e4eac2ffcce3529f87fafcabc55 100644 (file)
@@ -15,7 +15,7 @@
 // fast - e.g. one or two milliseconds to fetch parent items for the current content,
 // and 10-20 milliseconds to fetch all the child items.
 
-if(! function_exists('content_content')) {
+
 function content_content(&$a, $update = 0) {
 
        require_once('include/conversation.php');
@@ -61,7 +61,7 @@ function content_content(&$a, $update = 0) {
 
        $o = '';
 
-
+       
 
        $contact_id = $a->cid;
 
@@ -100,7 +100,7 @@ function content_content(&$a, $update = 0) {
                        $def_acl = array('allow_cid' => $str);
        }
 
-
+       
        $sql_options  = (($star) ? " and starred = 1 " : '');
        $sql_options .= (($bmark) ? " and bookmark = 1 " : '');
 
@@ -137,7 +137,7 @@ function content_content(&$a, $update = 0) {
        }
        elseif($cid) {
 
-               $r = q("SELECT `id`,`name`,`network`,`writable`,`nurl` FROM `contact` WHERE `id` = %d
+               $r = q("SELECT `id`,`name`,`network`,`writable`,`nurl` FROM `contact` WHERE `id` = %d 
                                AND `blocked` = 0 AND `pending` = 0 LIMIT 1",
                        intval($cid)
                );
@@ -304,9 +304,9 @@ function content_content(&$a, $update = 0) {
        echo json_encode($o);
        killme();
 }
-}
 
-if(! function_exists('render_content')) {
+
+
 function render_content(&$a, $items, $mode, $update, $preview = false) {
 
        require_once('include/bbcode.php');
@@ -373,7 +373,7 @@ function render_content(&$a, $items, $mode, $update, $preview = false) {
 
                if($mode === 'network-new' || $mode === 'search' || $mode === 'community') {
 
-                       // "New Item View" on network page or search page results
+                       // "New Item View" on network page or search page results 
                        // - just loop through the items and format them minimally for display
 
                        //$tpl = get_markup_template('search_item.tpl');
@@ -389,7 +389,7 @@ function render_content(&$a, $items, $mode, $update, $preview = false) {
                                $sparkle     = '';
 
                                if($mode === 'search' || $mode === 'community') {
-                                       if(((activity_match($item['verb'],ACTIVITY_LIKE)) || (activity_match($item['verb'],ACTIVITY_DISLIKE)))
+                                       if(((activity_match($item['verb'],ACTIVITY_LIKE)) || (activity_match($item['verb'],ACTIVITY_DISLIKE))) 
                                                && ($item['id'] != $item['parent']))
                                                continue;
                                        $nickname = $item['nickname'];
@@ -420,7 +420,7 @@ function render_content(&$a, $items, $mode, $update, $preview = false) {
                                if(($normalised != 'mailbox') && (x($a->contacts[$normalised])))
                                        $profile_avatar = $a->contacts[$normalised]['thumb'];
                                else
-                                       $profile_avatar = ((strlen($item['author-avatar'])) ? $a->get_cached_avatar_image($item['author-avatar']) : $item['thumb']);
+                                       $profile_avatar = $a->remove_baseurl(((strlen($item['author-avatar'])) ? $item['author-avatar'] : $item['thumb']));
 
                                $locate = array('location' => $item['location'], 'coord' => $item['coord'], 'html' => '');
                                call_hooks('render_location',$locate);
@@ -436,7 +436,7 @@ function render_content(&$a, $items, $mode, $update, $preview = false) {
 
                                $drop = array(
                                        'dropping' => $dropping,
-                                       'select' => t('Select'),
+                                       'select' => t('Select'), 
                                        'delete' => t('Delete'),
                                );
 
@@ -526,11 +526,11 @@ function render_content(&$a, $items, $mode, $update, $preview = false) {
                                                $comments[$item['parent']] = 1;
                                        else
                                                $comments[$item['parent']] += 1;
-                               } elseif(! x($comments,$item['parent']))
+                               } elseif(! x($comments,$item['parent'])) 
                                        $comments[$item['parent']] = 0; // avoid notices later on
                        }
 
-                       // map all the like/dislike activities for each parent item
+                       // map all the like/dislike activities for each parent item 
                        // Store these in the $alike and $dlike arrays
 
                        foreach($items as $item) {
@@ -615,16 +615,16 @@ function render_content(&$a, $items, $mode, $update, $preview = false) {
                                        $comment_lastcollapsed = true;
                                }
 
-                               $redirect_url = $a->get_baseurl($ssl_state) . '/redir/' . $item['cid'] ;
+                               $redirect_url = 'redir/' . $item['cid'] ;
 
-                               $lock = ((($item['private'] == 1) || (($item['uid'] == local_user()) && (strlen($item['allow_cid']) || strlen($item['allow_gid'])
+                               $lock = ((($item['private'] == 1) || (($item['uid'] == local_user()) && (strlen($item['allow_cid']) || strlen($item['allow_gid']) 
                                        || strlen($item['deny_cid']) || strlen($item['deny_gid']))))
                                        ? t('Private Message')
                                        : false);
 
 
                                // Top-level wall post not written by the wall owner (wall-to-wall)
-                               // First figure out who owns it.
+                               // First figure out who owns it. 
 
                                $osparkle = '';
 
@@ -651,13 +651,13 @@ function render_content(&$a, $items, $mode, $update, $preview = false) {
                                                if((! $owner_linkmatch) && (! $alias_linkmatch) && (! $owner_namematch)) {
 
                                                        // The author url doesn't match the owner (typically the contact)
-                                                       // and also doesn't match the contact alias.
-                                                       // The name match is a hack to catch several weird cases where URLs are
+                                                       // and also doesn't match the contact alias. 
+                                                       // The name match is a hack to catch several weird cases where URLs are 
                                                        // all over the park. It can be tricked, but this prevents you from
                                                        // seeing "Bob Smith to Bob Smith via Wall-to-wall" and you know darn
-                                                       // well that it's the same Bob Smith.
+                                                       // well that it's the same Bob Smith. 
 
-                                                       // But it could be somebody else with the same name. It just isn't highly likely.
+                                                       // But it could be somebody else with the same name. It just isn't highly likely. 
 
 
                                                        $owner_url = $item['owner-link'];
@@ -666,7 +666,7 @@ function render_content(&$a, $items, $mode, $update, $preview = false) {
                                                        $template = $wallwall;
                                                        $commentww = 'ww';
                                                        // If it is our contact, use a friendly redirect link
-                                                       if((link_compare($item['owner-link'],$item['url']))
+                                                       if((link_compare($item['owner-link'],$item['url'])) 
                                                                && ($item['network'] === NETWORK_DFRN)) {
                                                                $owner_url = $redirect_url;
                                                                $osparkle = ' sparkle';
@@ -678,7 +678,7 @@ function render_content(&$a, $items, $mode, $update, $preview = false) {
                                }
 
                                $likebuttons = '';
-                               $shareable = ((($profile_owner == local_user()) && ($item['private'] != 1)) ? true : false);
+                               $shareable = ((($profile_owner == local_user()) && ($item['private'] != 1)) ? true : false); 
 
                                if($page_writeable) {
 /*                                     if($toplevelpost) {  */
@@ -698,7 +698,7 @@ function render_content(&$a, $items, $mode, $update, $preview = false) {
 
                                        if(($show_comment_box) || (($show_comment_box == false) && ($override_comment_box == false) && ($item['last-child']))) {
                                                $comment = replace_macros($cmnt_tpl,array(
-                                                       '$return_path' => '',
+                                                       '$return_path' => '', 
                                                        '$jsreload' => (($mode === 'display') ? $_SESSION['return_url'] : ''),
                                                        '$type' => (($mode === 'profile') ? 'wall-comment' : 'net-comment'),
                                                        '$id' => $item['item_id'],
@@ -739,7 +739,7 @@ function render_content(&$a, $items, $mode, $update, $preview = false) {
 
                                $drop = array(
                                        'dropping' => $dropping,
-                                       'select' => t('Select'),
+                                       'select' => t('Select'), 
                                        'delete' => t('Delete'),
                                );
 
@@ -791,7 +791,7 @@ function render_content(&$a, $items, $mode, $update, $preview = false) {
                                if(($normalised != 'mailbox') && (x($a->contacts,$normalised)))
                                        $profile_avatar = $a->contacts[$normalised]['thumb'];
                                else
-                                       $profile_avatar = (((strlen($item['author-avatar'])) && $diff_author) ? $item['author-avatar'] : $a->get_cached_avatar_image($thumb));
+                                       $profile_avatar = $a->remove_baseurl(((strlen($item['author-avatar']) && $diff_author) ? $item['author-avatar'] : $thumb));
 
                                $like    = ((x($alike,$item['uri'])) ? format_like($alike[$item['uri']],$alike[$item['uri'] . '-l'],'like',$item['uri']) : '');
                                $dislike = ((x($dlike,$item['uri'])) ? format_like($dlike[$item['uri']],$dlike[$item['uri'] . '-l'],'dislike',$item['uri']) : '');
@@ -805,9 +805,9 @@ function render_content(&$a, $items, $mode, $update, $preview = false) {
 
                                $shiny = "";
                                if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0)
-                                       $shiny = 'shiny';
+                                       $shiny = 'shiny'; 
 
-                               //
+                               // 
                                localize_item($item);
 
 
@@ -897,5 +897,5 @@ function render_content(&$a, $items, $mode, $update, $preview = false) {
 
 
        return $threads;
-}
+
 }
index 8e6321760b91b18d10aec000d9a70a105404750b..f8cfb03f3782230e51ded8a973fb7e0455f2233c 100644 (file)
@@ -5,7 +5,6 @@
  * addons repository will be listed though ATM)
  */
 
-if(! function_exists('credits_content')) {
 function credits_content (&$a) {
     /* fill the page with credits */
     $f = fopen('util/credits.txt','r');
@@ -19,4 +18,3 @@ function credits_content (&$a) {
        '$names'         => $arr,
     ));
 }
-}
index 50502b49875e976a68fcd342c1d7044cea0553b5..5b4db09dac1f16ab7a0500ccf6b8e90e9cd90dc5 100644 (file)
@@ -2,7 +2,6 @@
 require_once("include/contact_selectors.php");
 require_once("mod/contacts.php");
 
-if(! function_exists('crepair_init')) {
 function crepair_init(&$a) {
        if(! local_user())
                return;
@@ -29,9 +28,8 @@ function crepair_init(&$a) {
                profile_load($a, "", 0, get_contact_details_by_url($contact["url"]));
        }
 }
-}
 
-if(! function_exists('crepair_post')) {
+
 function crepair_post(&$a) {
        if(! local_user())
                return;
@@ -93,9 +91,9 @@ function crepair_post(&$a) {
 
        return;
 }
-}
 
-if(! function_exists('crepair_content')) {
+
+
 function crepair_content(&$a) {
 
        if(! local_user()) {
@@ -182,5 +180,5 @@ function crepair_content(&$a) {
        ));
 
        return $o;
-}
+
 }
index d421de37643791dcbf613850a5152560b415ee4e..20d2e605e0a2d91c90e1d664567037d6baf1902d 100644 (file)
@@ -1,13 +1,11 @@
 <?php
 require_once('mod/settings.php');
 
-if(! function_exists('delegate_init')) {
 function delegate_init(&$a) {
        return settings_init($a);
 }
-}
 
-if(! function_exists('delegate_content')) {
+
 function delegate_content(&$a) {
 
        if(! local_user()) {
@@ -92,12 +90,12 @@ function delegate_content(&$a) {
 
        // find every contact who might be a candidate for delegation
 
-       $r = q("select nurl from contact where substring_index(contact.nurl,'/',3) = '%s'
+       $r = q("select nurl from contact where substring_index(contact.nurl,'/',3) = '%s' 
                and contact.uid = %d and contact.self = 0 and network = '%s' ",
                dbesc(normalise_link($a->get_baseurl())),
                intval(local_user()),
                dbesc(NETWORK_DFRN)
-       );
+       ); 
 
        if(! count($r)) {
                notice( t('No potential page delegates located.') . EOL);
@@ -146,5 +144,5 @@ function delegate_content(&$a) {
 
        return $o;
 
-}
+
 }
index 00e215e334cb6d88b778d9dea3a3da7400f45149..5e0e5c85c5173197b6dd802ace0a6bc916cbedc2 100644 (file)
@@ -16,7 +16,6 @@
 
 require_once('include/enotify.php');
 
-if(! function_exists('dfrn_confirm_post')) {
 function dfrn_confirm_post(&$a,$handsfree = null) {
 
        if(is_array($handsfree)) {
@@ -428,8 +427,8 @@ function dfrn_confirm_post(&$a,$handsfree = null) {
 
                        if(($contact) && ($contact['network'] === NETWORK_DIASPORA)) {
                                require_once('include/diaspora.php');
-                               $ret = diaspora_share($user[0],$r[0]);
-                               logger('mod_follow: diaspora_share returns: ' . $ret);
+                               $ret = diaspora::send_share($user[0],$r[0]);
+                               logger('share returns: ' . $ret);
                        }
 
                        // Send a new friend post if we are allowed to...
@@ -449,6 +448,7 @@ function dfrn_confirm_post(&$a,$handsfree = null) {
                                if(count($self)) {
 
                                        $arr = array();
+                                       $arr['guid'] = get_guid(32);
                                        $arr['uri'] = $arr['parent-uri'] = item_new_uri($a->get_hostname(), $uid);
                                        $arr['uid'] = $uid;
                                        $arr['contact-id'] = $self[0]['id'];
@@ -467,7 +467,7 @@ function dfrn_confirm_post(&$a,$handsfree = null) {
                                        $BPhoto = '[url=' . $contact['url'] . ']' . '[img]' . $contact['thumb'] . '[/img][/url]';
 
                                        $arr['verb'] = ACTIVITY_FRIEND;
-                                   $arr['object-type'] = ACTIVITY_OBJ_PERSON;
+                                       $arr['object-type'] = ACTIVITY_OBJ_PERSON;
                                        $arr['body'] =  sprintf( t('%1$s is now friends with %2$s'), $A, $B)."\n\n\n".$BPhoto;
 
                                        $arr['object'] = '<object><type>' . ACTIVITY_OBJ_PERSON . '</type><title>' . $contact['name'] . '</title>'
@@ -490,13 +490,10 @@ function dfrn_confirm_post(&$a,$handsfree = null) {
                        }
                }
 
-
-               $g = q("select def_gid from user where uid = %d limit 1",
-                       intval($uid)
-               );
-               if($contact && $g && intval($g[0]['def_gid'])) {
+               $def_gid = get_default_group($uid, $contact["network"]);
+               if($contact && intval($def_gid)) {
                        require_once('include/group.php');
-                       group_add_member($uid,'',$contact['id'],$g[0]['def_gid']);
+                       group_add_member($uid, '', $contact['id'], $def_gid);
                }
 
                // Let's send our user to the contact editor in case they want to
@@ -802,5 +799,5 @@ function dfrn_confirm_post(&$a,$handsfree = null) {
 
        goaway(z_root());
        // NOTREACHED
-}
+
 }
index 7623b366cb92b9d24d6243b3fc92e7483f94653b..780fb456f5cd33f43e7bc6783196fd15e55570f1 100644 (file)
@@ -6,7 +6,6 @@ require_once('include/event.php');
 
 require_once('library/defuse/php-encryption-1.2.1/Crypto.php');
 
-if(! function_exists('dfrn_notify_post')) {
 function dfrn_notify_post(&$a) {
     logger(__function__, LOGGER_TRACE);
        $dfrn_id      = ((x($_POST,'dfrn_id'))      ? notags(trim($_POST['dfrn_id']))   : '');
@@ -215,9 +214,8 @@ function dfrn_notify_post(&$a) {
 
        // NOTREACHED
 }
-}
 
-if(! function_exists('dfrn_notify_content')) {
+
 function dfrn_notify_content(&$a) {
 
        if(x($_GET,'dfrn_id')) {
@@ -341,5 +339,5 @@ function dfrn_notify_content(&$a) {
 
                killme();
        }
-}
+
 }
index 82c75d28cfb28ac27035575a462b3d472e487bb9..ab6637607e564b2d71eb2ba1353b6944a6216c65 100644 (file)
@@ -3,7 +3,7 @@ require_once('include/items.php');
 require_once('include/auth.php');
 require_once('include/dfrn.php');
 
-if(! function_exists('dfrn_poll_init')) {
+
 function dfrn_poll_init(&$a) {
 
 
@@ -160,7 +160,7 @@ function dfrn_poll_init(&$a) {
 
                        if($final_dfrn_id != $orig_id) {
                                logger('profile_check: ' . $final_dfrn_id . ' != ' . $orig_id, LOGGER_DEBUG);
-                               // did not decode properly - cannot trust this site
+                               // did not decode properly - cannot trust this site 
                                xml_status(3, 'Bad decryption');
                        }
 
@@ -195,11 +195,11 @@ function dfrn_poll_init(&$a) {
                        return; // NOTREACHED
                }
        }
-}
+
 }
 
 
-if(! function_exists('dfrn_poll_post')) {
+
 function dfrn_poll_post(&$a) {
 
        $dfrn_id      = ((x($_POST,'dfrn_id'))      ? $_POST['dfrn_id']              : '');
@@ -257,7 +257,7 @@ function dfrn_poll_post(&$a) {
 
                        if($final_dfrn_id != $orig_id) {
                                logger('profile_check: ' . $final_dfrn_id . ' != ' . $orig_id, LOGGER_DEBUG);
-                               // did not decode properly - cannot trust this site
+                               // did not decode properly - cannot trust this site 
                                xml_status(3, 'Bad decryption');
                        }
 
@@ -377,9 +377,7 @@ function dfrn_poll_post(&$a) {
 
        }
 }
-}
 
-if(! function_exists('dfrn_poll_content')) {
 function dfrn_poll_content(&$a) {
 
        $dfrn_id         = ((x($_GET,'dfrn_id'))         ? $_GET['dfrn_id']              : '');
@@ -564,4 +562,3 @@ function dfrn_poll_content(&$a) {
                }
        }
 }
-}
index 2741ad59b4f2c1f3cc66eb4a064792df5120dd5c..837eec2dd244ef222e9013ccb8160439c00011f0 100644 (file)
@@ -42,8 +42,10 @@ function dfrn_request_init(&$a) {
 if(! function_exists('dfrn_request_post')) {
 function dfrn_request_post(&$a) {
 
-       if(($a->argc != 2) || (! count($a->profile)))
+       if(($a->argc != 2) || (! count($a->profile))) {
+               logger('Wrong count of argc or profiles: argc=' . $a->argc . ',profile()=' . count($a->profile));
                return;
+       }
 
 
        if(x($_POST, 'cancel')) {
@@ -172,18 +174,16 @@ function dfrn_request_post(&$a) {
                                        info( t("Introduction complete.") . EOL);
                                }
 
-                               $r = q("select id from contact where uid = %d and url = '%s' and `site-pubkey` = '%s' limit 1",
+                               $r = q("SELECT `id`, `network` FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `site-pubkey` = '%s' LIMIT 1",
                                        intval(local_user()),
                                        dbesc($dfrn_url),
                                        $parms['key'] // this was already escaped
                                );
                                if(count($r)) {
-                                       $g = q("select def_gid from user where uid = %d limit 1",
-                                               intval(local_user())
-                                       );
-                                       if($g && intval($g[0]['def_gid'])) {
+                                       $def_gid = get_default_group(local_user(), $r[0]["network"]);
+                                       if(intval($def_gid)) {
                                                require_once('include/group.php');
-                                               group_add_member(local_user(),'',$r[0]['id'],$g[0]['def_gid']);
+                                               group_add_member(local_user(), '', $r[0]['id'], $def_gid);
                                        }
                                        $forwardurl = $a->get_baseurl()."/contacts/".$r[0]['id'];
                                } else
@@ -386,19 +386,17 @@ function dfrn_request_post(&$a) {
                                intval($rel)
                        );
 
-                       $r = q("select id from contact where poll = '%s' and uid = %d limit 1",
+                       $r = q("SELECT `id`, `network` FROM `contact` WHERE `poll` = '%s' AND `uid` = %d LIMIT 1",
                                dbesc($poll),
                                intval($uid)
                        );
                        if(count($r)) {
                                $contact_id = $r[0]['id'];
 
-                               $g = q("select def_gid from user where uid = %d limit 1",
-                                       intval($uid)
-                               );
-                               if($g && intval($g[0]['def_gid'])) {
+                               $def_gid = get_default_group($uid, $r[0]["network"]);
+                               if (intval($def_gid)) {
                                        require_once('include/group.php');
-                                       group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
+                                       group_add_member($uid, '', $contact_id, $def_gid);
                                }
 
                                $photo = avatar_img($addr);
@@ -461,7 +459,7 @@ function dfrn_request_post(&$a) {
                                $network = NETWORK_DFRN;
                }
 
-               logger('dfrn_request: url: ' . $url);
+               logger('dfrn_request: url: ' . $url . ',network=' . $network, LOGGER_DEBUG);
 
                if($network === NETWORK_DFRN) {
                        $ret = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `self` = 0 LIMIT 1",
@@ -825,7 +823,7 @@ function dfrn_request_content(&$a) {
                else
                        $tpl = get_markup_template('auto_request.tpl');
 
-               $page_desc .= t("Please enter your 'Identity Address' from one of the following supported communications networks:");
+               $page_desc = t("Please enter your 'Identity Address' from one of the following supported communications networks:");
 
                // see if we are allowed to have NETWORK_MAIL2 contacts
 
@@ -850,7 +848,7 @@ function dfrn_request_content(&$a) {
                        get_server()
                );
 
-               $o .= replace_macros($tpl,array(
+               $o = replace_macros($tpl,array(
                        '$header' => t('Friend/Connection Request'),
                        '$desc' => t('Examples: jojo@demo.friendica.com, http://demo.friendica.com/profile/jojo, testuser@identi.ca'),
                        '$pls_answer' => t('Please answer the following:'),
index 7ce1530efc5aa36e879d565346f4428f7c87f634..625f6c95aca781eee24e1a3f895361f8c455e1b6 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-if(! function_exists('directory_init')) {
+
 function directory_init(&$a) {
        $a->set_pager_itemspage(60);
 
@@ -16,23 +16,23 @@ function directory_init(&$a) {
                unset($_SESSION['mobile-theme']);
        }
 
+
 }
-}
 
-if(! function_exists('directory_post')) {
+
 function directory_post(&$a) {
        if(x($_POST,'search'))
                $a->data['search'] = $_POST['search'];
 }
-}
 
-if(! function_exists('directory_content')) {
+
+
 function directory_content(&$a) {
        global $db;
 
        require_once("mod/proxy.php");
 
-       if((get_config('system','block_public')) && (! local_user()) && (! remote_user()) ||
+       if((get_config('system','block_public')) && (! local_user()) && (! remote_user()) || 
                (get_config('system','block_local_dir')) && (! local_user()) && (! remote_user())) {
                notice( t('Public access denied.') . EOL);
                return;
@@ -104,7 +104,7 @@ function directory_content(&$a) {
 
                        $itemurl = (($rr['addr'] != "") ? $rr['addr'] : $rr['profile_url']);
 
-                       $profile_link = z_root() . '/profile/' . ((strlen($rr['nickname'])) ? $rr['nickname'] : $rr['profile_uid']);
+                       $profile_link = 'profile/' . ((strlen($rr['nickname'])) ? $rr['nickname'] : $rr['profile_uid']);
 
                        $pdesc = (($rr['pdesc']) ? $rr['pdesc'] . '<br />' : '');
 
@@ -123,14 +123,14 @@ function directory_content(&$a) {
                        }
 //                     if(strlen($rr['dob'])) {
 //                             if(($years = age($rr['dob'],$rr['timezone'],'')) != 0)
-//                                     $details .= '<br />' . t('Age: ') . $years ;
+//                                     $details .= '<br />' . t('Age: ') . $years ; 
 //                     }
 //                     if(strlen($rr['gender']))
 //                             $details .= '<br />' . t('Gender: ') . $rr['gender'];
 
 
                        // show if account is a community account
-                       /// @TODO The other page types should be also respected, but first we need a good
+                       /// @TODO The other page types should be also respected, but first we need a good 
                        /// translatiion and systemwide consistency for displaying the page type
                        if((intval($rr['page-flags']) == PAGE_COMMUNITY) OR (intval($rr['page-flags']) == PAGE_PRVGROUP))
                                $community = true;
@@ -165,7 +165,7 @@ function directory_content(&$a) {
                                'id' => $rr['id'],
                                'url' => $profile_link,
                                'itemurl' => $itemurl,
-                               'thumb' => proxy_url($a->get_cached_avatar_image($rr[$photo]), false, PROXY_SIZE_THUMB),
+                               'thumb' => proxy_url($rr[$photo], false, PROXY_SIZE_THUMB),
                                'img_hover' => $rr['name'],
                                'name' => $rr['name'],
                                'details' => $details,
@@ -217,4 +217,3 @@ function directory_content(&$a) {
 
        return $o;
 }
-}
index f5e90705b760ebcdb3134fe4323caf6ef44d61b9..0dfe4d67a9b61960623d963f0f58718ccbabe2be 100644 (file)
@@ -5,7 +5,6 @@ require_once('include/Contact.php');
 require_once('include/contact_selectors.php');
 require_once('mod/contacts.php');
 
-if(! function_exists('dirfind_init')) {
 function dirfind_init(&$a) {
 
        if(! local_user()) {
@@ -20,9 +19,9 @@ function dirfind_init(&$a) {
 
        $a->page['aside'] .= follow_widget();
 }
-}
 
-if(! function_exists('dirfind_content')) {
+
+
 function dirfind_content(&$a, $prefix = "") {
 
        $community = false;
@@ -236,4 +235,3 @@ function dirfind_content(&$a, $prefix = "") {
 
        return $o;
 }
-}
index 9995a2b3efc2c0e0587a37d0dd2d1ca05b3c81e7..e53f9e20669d7ed70b93b579f85305494861abb0 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-if(! function_exists('display_init')) {
+
 function display_init(&$a) {
 
        if((get_config('system','block_public')) && (! local_user()) && (! remote_user())) {
@@ -17,7 +17,7 @@ function display_init(&$a) {
                // Does the local user have this item?
                if (local_user()) {
                        $r = q("SELECT `id`, `parent`, `author-name`, `author-link`, `author-avatar`, `network`, `body`, `uid` FROM `item`
-                               WHERE `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0
+                               WHERE `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated`
                                        AND `guid` = '%s' AND `uid` = %d", dbesc($a->argv[1]), local_user());
                        if (count($r)) {
                                $nick = $a->user["nickname"];
@@ -30,12 +30,12 @@ function display_init(&$a) {
                        $r = q("SELECT `user`.`nickname`, `item`.`id`, `item`.`parent`, `item`.`author-name`,
                                `item`.`author-link`, `item`.`author-avatar`, `item`.`network`, `item`.`uid`, `item`.`body`
                                FROM `item` INNER JOIN `user` ON `user`.`uid` = `item`.`uid`
-                               WHERE `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0
+                               WHERE `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated`
                                        AND `item`.`allow_cid` = ''  AND `item`.`allow_gid` = ''
                                        AND `item`.`deny_cid`  = '' AND `item`.`deny_gid`  = ''
-                                       AND `item`.`private` = 0 AND NOT `user`.`hidewall`
+                                       AND NOT `item`.`private` AND NOT `user`.`hidewall`
                                        AND `item`.`guid` = '%s'", dbesc($a->argv[1]));
-                               //      AND `item`.`private` = 0 AND `item`.`wall` = 1
+                               //      AND NOT `item`.`private` AND `item`.`wall`
                        if (count($r)) {
                                $nick = $r[0]["nickname"];
                                $itemuid = $r[0]["uid"];
@@ -46,17 +46,17 @@ function display_init(&$a) {
                if ($nick == "") {
                        $r = q("SELECT `item`.`id`, `item`.`parent`, `item`.`author-name`,
                                `item`.`author-link`, `item`.`author-avatar`, `item`.`network`, `item`.`uid`, `item`.`body`
-                               FROM `item` WHERE `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0
+                               FROM `item` WHERE `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated`
                                        AND `item`.`allow_cid` = ''  AND `item`.`allow_gid` = ''
                                        AND `item`.`deny_cid`  = '' AND `item`.`deny_gid`  = ''
-                                       AND `item`.`private` = 0 AND `item`.`uid` = 0
+                                       AND NOT `item`.`private` AND `item`.`uid` = 0
                                        AND `item`.`guid` = '%s'", dbesc($a->argv[1]));
-                               //      AND `item`.`private` = 0 AND `item`.`wall` = 1
+                               //      AND NOT `item`.`private` AND `item`.`wall`
                }
                if (count($r)) {
                        if ($r[0]["id"] != $r[0]["parent"])
                                $r = q("SELECT `id`, `author-name`, `author-link`, `author-avatar`, `network`, `body`, `uid` FROM `item`
-                                       WHERE `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0
+                                       WHERE `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated`
                                                AND `id` = %d", $r[0]["parent"]);
 
                        $profiledata = display_fetchauthor($a, $r[0]);
@@ -67,7 +67,7 @@ function display_init(&$a) {
                                if (($nickname != $a->user["nickname"])) {
                                        $r = q("SELECT `profile`.`uid` AS `profile_uid`, `profile`.* , `contact`.`avatar-date` AS picdate, `user`.* FROM `profile`
                                                INNER JOIN `contact` on `contact`.`uid` = `profile`.`uid` INNER JOIN `user` ON `profile`.`uid` = `user`.`uid`
-                                               WHERE `user`.`nickname` = '%s' AND `profile`.`is-default` = 1 and `contact`.`self` = 1 LIMIT 1",
+                                               WHERE `user`.`nickname` = '%s' AND `profile`.`is-default` AND `contact`.`self` LIMIT 1",
                                                dbesc($nickname)
                                        );
                                        if (count($r))
@@ -85,10 +85,9 @@ function display_init(&$a) {
        }
 
        profile_load($a, $nick, 0, $profiledata);
-}
+
 }
 
-if(! function_exists('display_fetchauthor')) {
 function display_fetchauthor($a, $item) {
 
        $profiledata = array();
@@ -121,27 +120,27 @@ function display_fetchauthor($a, $item) {
        }
 
        if (!$skip) {
-               $author = "";
-               preg_match("/author='(.*?)'/ism", $attributes, $matches);
-               if ($matches[1] != "")
+               $author = "";
+               preg_match("/author='(.*?)'/ism", $attributes, $matches);
+               if ($matches[1] != "")
                        $profiledata["name"] = html_entity_decode($matches[1],ENT_QUOTES,'UTF-8');
 
-               preg_match('/author="(.*?)"/ism', $attributes, $matches);
-               if ($matches[1] != "")
+               preg_match('/author="(.*?)"/ism', $attributes, $matches);
+               if ($matches[1] != "")
                        $profiledata["name"] = html_entity_decode($matches[1],ENT_QUOTES,'UTF-8');
 
-               $profile = "";
-               preg_match("/profile='(.*?)'/ism", $attributes, $matches);
-               if ($matches[1] != "")
+               $profile = "";
+               preg_match("/profile='(.*?)'/ism", $attributes, $matches);
+               if ($matches[1] != "")
                        $profiledata["url"] = $matches[1];
 
-               preg_match('/profile="(.*?)"/ism', $attributes, $matches);
-               if ($matches[1] != "")
+               preg_match('/profile="(.*?)"/ism', $attributes, $matches);
+               if ($matches[1] != "")
                        $profiledata["url"] = $matches[1];
 
-               $avatar = "";
-               preg_match("/avatar='(.*?)'/ism", $attributes, $matches);
-               if ($matches[1] != "")
+               $avatar = "";
+               preg_match("/avatar='(.*?)'/ism", $attributes, $matches);
+               if ($matches[1] != "")
                        $profiledata["photo"] = $matches[1];
 
                preg_match('/avatar="(.*?)"/ism', $attributes, $matches);
@@ -221,9 +220,7 @@ function display_fetchauthor($a, $item) {
 
        return($profiledata);
 }
-}
 
-if(! function_exists('display_content')) {
 function display_content(&$a, $update = 0) {
 
        if((get_config('system','block_public')) && (! local_user()) && (! remote_user())) {
@@ -260,7 +257,7 @@ function display_content(&$a, $update = 0) {
 
                        if (local_user()) {
                                $r = q("SELECT `id` FROM `item`
-                                       WHERE `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0
+                                       WHERE `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated`
                                                AND `guid` = '%s' AND `uid` = %d", dbesc($a->argv[1]), local_user());
                                if (count($r)) {
                                        $item_id = $r[0]["id"];
@@ -270,12 +267,12 @@ function display_content(&$a, $update = 0) {
 
                        if ($nick == "") {
                                $r = q("SELECT `user`.`nickname`, `item`.`id` FROM `item` INNER JOIN `user` ON `user`.`uid` = `item`.`uid`
-                                       WHERE `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0
+                                       WHERE `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated`
                                                AND `item`.`allow_cid` = ''  AND `item`.`allow_gid` = ''
                                                AND `item`.`deny_cid`  = '' AND `item`.`deny_gid`  = ''
-                                               AND `item`.`private` = 0  AND NOT `user`.`hidewall`
+                                               AND NOT `item`.`private` AND NOT `user`.`hidewall`
                                                AND `item`.`guid` = '%s'", dbesc($a->argv[1]));
-                                       //      AND `item`.`private` = 0 AND `item`.`wall` = 1
+                                       //      AND NOT `item`.`private` AND `item`.`wall`
                                if (count($r)) {
                                        $item_id = $r[0]["id"];
                                        $nick = $r[0]["nickname"];
@@ -283,12 +280,12 @@ function display_content(&$a, $update = 0) {
                        }
                        if ($nick == "") {
                                $r = q("SELECT `item`.`id` FROM `item`
-                                       WHERE `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0
+                                       WHERE `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated`
                                                AND `item`.`allow_cid` = ''  AND `item`.`allow_gid` = ''
                                                AND `item`.`deny_cid`  = '' AND `item`.`deny_gid`  = ''
-                                               AND `item`.`private` = 0  AND `item`.`uid` = 0
+                                               AND NOT `item`.`private` AND `item`.`uid` = 0
                                                AND `item`.`guid` = '%s'", dbesc($a->argv[1]));
-                                       //      AND `item`.`private` = 0 AND `item`.`wall` = 1
+                                       //      AND NOT `item`.`private` AND `item`.`wall`
                                if (count($r)) {
                                        $item_id = $r[0]["id"];
                                }
@@ -296,12 +293,22 @@ function display_content(&$a, $update = 0) {
                }
        }
 
-       if(! $item_id) {
+       if ($item_id AND !is_numeric($item_id)) {
+               $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
+                       dbesc($item_id), intval($a->profile['uid']));
+               if ($r)
+                       $item_id = $r[0]["id"];
+               else
+                       $item_id = false;
+       }
+
+       if (!$item_id) {
                $a->error = 404;
-               notice( t('Item not found.') . EOL);
+               notice(t('Item not found.').EOL);
                return;
        }
 
+
        $groups = array();
 
        $contact = null;
@@ -337,7 +344,7 @@ function display_content(&$a, $update = 0) {
                }
        }
 
-       $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` = 1 LIMIT 1",
+       $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1",
                intval($a->profile['uid'])
        );
        if(count($r))
@@ -350,10 +357,8 @@ function display_content(&$a, $update = 0) {
                return;
        }
 
-       // Why do we need this on the display page? We don't have the possibility to write new content here.
-       // Ad editing of posts work without this as well.
-       // We should remove this completely for the 3.5.1 release.
-       /*
+       // We need the editor here to be able to reshare an item.
+
        if ($is_owner) {
                $x = array(
                        'is_owner' => true,
@@ -369,66 +374,56 @@ function display_content(&$a, $update = 0) {
                );
                $o .= status_editor($a,$x,0,true);
        }
-       */
 
        $sql_extra = item_permissions_sql($a->profile['uid'],$remote_contact,$groups);
 
-       //              AND `item`.`parent` = ( SELECT `parent` FROM `item` FORCE INDEX (PRIMARY, `uri`) WHERE ( `id` = '%s' OR `uri` = '%s' ))
-
        if($update) {
 
-               $r = q("SELECT id FROM item WHERE item.uid = %d
-                       AND `item`.`parent` = (SELECT `parent` FROM `item` WHERE (`id` = '%s' OR `uri` = '%s'))
-                       $sql_extra AND unseen = 1",
-                       intval($a->profile['uid']),
-                       dbesc($item_id),
-                       dbesc($item_id)
+               $r = q("SELECT `id` FROM `item` WHERE `item`.`uid` = %d
+                       AND `item`.`parent` = (SELECT `parent` FROM `item` WHERE `id` = %d)
+                       $sql_extra AND `unseen`",
+                       intval($a->profile['uid']),
+                       intval($item_id)
                );
 
                if(!$r)
                        return '';
        }
 
-       //      AND `item`.`parent` = ( SELECT `parent` FROM `item` FORCE INDEX (PRIMARY, `uri`) WHERE ( `id` = '%s' OR `uri` = '%s' )
-
        $r = q("SELECT `item`.*, `item`.`id` AS `item_id`,  `item`.`network` AS `item_network`,
                `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`,
                `contact`.`network`, `contact`.`thumb`, `contact`.`self`, `contact`.`writable`,
                `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid`
                FROM `item` INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
-               AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0
-               WHERE `item`.`uid` = %d AND `item`.`visible` = 1 AND `item`.`deleted` = 0
-               and `item`.`moderated` = 0
-               AND `item`.`parent` = (SELECT `parent` FROM `item` WHERE (`id` = '%s' OR `uri` = '%s')
-               AND uid = %d)
+               AND NOT `contact`.`blocked` AND NOT `contact`.`pending`
+               WHERE `item`.`uid` = %d AND `item`.`visible` AND NOT `item`.`deleted`
+               AND NOT `item`.`moderated`
+               AND `item`.`parent` = (SELECT `parent` FROM `item` WHERE `id` = %d)
                $sql_extra
                ORDER BY `parent` DESC, `gravity` ASC, `id` ASC",
                intval($a->profile['uid']),
-               dbesc($item_id),
-               dbesc($item_id),
-               intval($a->profile['uid'])
+               intval($item_id)
        );
 
        if(!$r && local_user()) {
                // Check if this is another person's link to a post that we have
                $r = q("SELECT `item`.uri FROM `item`
-                       WHERE (`item`.`id` = '%s' OR `item`.`uri` = '%s' )
+                       WHERE (`item`.`id` = %d OR `item`.`uri` = '%s')
                        LIMIT 1",
-                       dbesc($item_id),
+                       intval($item_id),
                        dbesc($item_id)
                );
                if($r) {
                        $item_uri = $r[0]['uri'];
-                       //      AND `item`.`parent` = ( SELECT `parent` FROM `item` FORCE INDEX (PRIMARY, `uri`) WHERE `uri` = '%s' AND uid = %d )
 
                        $r = q("SELECT `item`.*, `item`.`id` AS `item_id`,  `item`.`network` AS `item_network`,
                                `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`,
                                `contact`.`network`, `contact`.`thumb`, `contact`.`self`, `contact`.`writable`,
                                `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid`
                                FROM `item` INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
-                               AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0
-                               WHERE `item`.`uid` = %d AND `item`.`visible` = 1 AND `item`.`deleted` = 0
-                               and `item`.`moderated` = 0
+                               AND NOT `contact`.`blocked` AND NOT `contact`.`pending`
+                               WHERE `item`.`uid` = %d AND `item`.`visible` AND NOT `item`.`deleted`
+                               AND NOT `item`.`moderated`
                                AND `item`.`parent` = (SELECT `parent` FROM `item` WHERE `uri` = '%s' AND uid = %d)
                                ORDER BY `parent` DESC, `gravity` ASC, `id` ASC ",
                                intval(local_user()),
@@ -443,7 +438,7 @@ function display_content(&$a, $update = 0) {
 
                if((local_user()) && (local_user() == $a->profile['uid'])) {
                        q("UPDATE `item` SET `unseen` = 0
-                               WHERE `parent` = %d AND `unseen` = 1",
+                               WHERE `parent` = %d AND `unseen`",
                                intval($r[0]['parent'])
                        );
                }
@@ -525,4 +520,4 @@ function display_content(&$a, $update = 0) {
 
        return $o;
 }
-}
+
index ee4d61e60a7c61182e177e77404fe16bbdcc1d22..9a80d0b2f4ba622badbaac42f617e973eeed926a 100644 (file)
@@ -2,7 +2,6 @@
 
 require_once('include/acl_selectors.php');
 
-if(! function_exists('editpost_content')) {
 function editpost_content(&$a) {
 
        $o = '';
@@ -151,5 +150,7 @@ function editpost_content(&$a) {
        ));
 
        return $o;
+
 }
-}
+
+
index 3dc20e535a2829b42262190573f7b68b4b25ce7f..653ae489b8f97c8022fe68fcd6f7513e4b5fe009 100644 (file)
@@ -5,7 +5,6 @@ require_once('include/datetime.php');
 require_once('include/event.php');
 require_once('include/items.php');
 
-if(! function_exists('events_post')) {
 function events_post(&$a) {
 
        logger('post: ' . print_r($_REQUEST,true));
@@ -157,9 +156,9 @@ function events_post(&$a) {
 
        goaway($_SESSION['return_url']);
 }
-}
 
-if(! function_exists('events_content')) {
+
+
 function events_content(&$a) {
 
        if(! local_user()) {
@@ -579,4 +578,3 @@ function events_content(&$a) {
                return $o;
        }
 }
-}
index 73510ef58a13c4355ea1d55d34dfb4b61aa6740f..5836efbe5290437741a04b9b201ecf84b5b6e58b 100644 (file)
@@ -10,7 +10,6 @@ require_once('include/Photo.php');
 /**
  * @param App $a
  */
-if(! function_exists('fbrowser_content')) {
 function fbrowser_content($a){
 
        if (!local_user())
@@ -75,10 +74,18 @@ function fbrowser_content($a){
                                        $filename_e = $rr['filename'];
                                }
 
+                               // Take the second largest picture as preview
+                               $p = q("SELECT `scale` FROM `photo` WHERE `resource-id` = '%s' AND `scale` > %d ORDER BY `resource-id`, `scale` LIMIT 1",
+                                       dbesc($rr['resource-id']), intval($rr['hiq']));
+                               if ($p)
+                                       $scale = $p[0]["scale"];
+                               else
+                                       $scale = $rr['loq'];
+
                                return array(
                                        $a->get_baseurl() . '/photos/' . $a->user['nickname'] . '/image/' . $rr['resource-id'],
                                        $filename_e,
-                                       $a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . $rr['loq'] . '.'. $ext
+                                       $a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . $scale . '.'. $ext
                                );
                        }
                        $files = array_map("_map_files1", $r);
@@ -142,5 +149,5 @@ function fbrowser_content($a){
                killme();
        }
 
-}
+
 }
index 02b8d68978f44897ae0b0d4716fec28a6f99a521..4e79f337dcff22502ac15d4c0673872253678312 100644 (file)
@@ -4,7 +4,7 @@ require_once('include/security.php');
 require_once('include/bbcode.php');
 require_once('include/items.php');
 
-if(! function_exists('filer_content')) {
+
 function filer_content(&$a) {
 
        if(! local_user()) {
@@ -30,9 +30,8 @@ function filer_content(&$a) {
                        '$field' => array('term', t("Save to Folder:"), '', '', $filetags, t('- select -')),
                        '$submit' => t('Save'),
                ));
-
+               
                echo $o;
        }
        killme();
 }
-}
index be3456b58dcebdb6a24d2127032fa84204942fbd..c266082c8ffe2e76a3fbe866df080e3a3159401d 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 
-if(! function_exists('filerm_content')) {
 function filerm_content(&$a) {
 
        if(! local_user()) {
@@ -26,4 +25,3 @@ function filerm_content(&$a) {
 
        killme();
 }
-}
index a8fdc31b5a2fd5775570fe8a0e21dc91ff0ab594..b92a0d980fb57e1150214c07aee7e5b590277c05 100644 (file)
@@ -5,7 +5,6 @@ require_once('include/follow.php');
 require_once('include/Contact.php');
 require_once('include/contact_selectors.php');
 
-if(! function_exists('follow_content')) {
 function follow_content(&$a) {
 
        if(! local_user()) {
@@ -149,9 +148,7 @@ function follow_content(&$a) {
 
        return $o;
 }
-}
 
-if(! function_exists('follow_post')) {
 function follow_post(&$a) {
 
        if(! local_user()) {
@@ -188,4 +185,3 @@ function follow_post(&$a) {
        goaway($return_url);
        // NOTREACHED
 }
-}
index 18d045f2d57d44244c605cf978dc14394388d241..aad5964baf6e5d1ddf737e88f46b636cb255d6d7 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 
-if(! function_exists('friendica_init')) {
 function friendica_init(&$a) {
        if ($a->argv[1]=="json"){
                $register_policy = Array('REGISTER_CLOSED', 'REGISTER_APPROVE', 'REGISTER_OPEN');
@@ -57,9 +56,9 @@ function friendica_init(&$a) {
                killme();
        }
 }
-}
 
-if(! function_exists('friendica_content')) {
+
+
 function friendica_content(&$a) {
 
        $o = '';
@@ -71,7 +70,7 @@ function friendica_content(&$a) {
        $o .= t('This is Friendica, version') . ' ' . FRIENDICA_VERSION . ' ';
        $o .= t('running at web location') . ' ' . z_root() . '</p><p>';
 
-       $o .= t('Please visit <a href="http://friendica.com">Friendica.com</a> to learn more about the Friendica project.') . '</p><p>';
+       $o .= t('Please visit <a href="http://friendica.com">Friendica.com</a> to learn more about the Friendica project.') . '</p><p>';        
 
        $o .= t('Bug reports and issues: please visit') . ' ' . '<a href="https://github.com/friendica/friendica/issues?state=open">'.t('the bugtracker at github').'</a></p><p>';
        $o .= t('Suggestions, praise, donations, etc. - please email "Info" at Friendica - dot com') . '</p>';
@@ -103,8 +102,8 @@ function friendica_content(&$a) {
        else
                $o .= '<p>' . t('No installed plugins/addons/apps') . '</p>';
 
-       call_hooks('about_hook', $o);
+       call_hooks('about_hook', $o);   
 
        return $o;
-}
+
 }
index 26a5e980635f5f788547f62b3385c0f875702c0d..6b1cbd7533be8ca44f6e9ce19c32609cdbf74490 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 
-if(! function_exists('fsuggest_post')) {
+
 function fsuggest_post(&$a) {
 
        if(! local_user()) {
@@ -39,11 +39,11 @@ function fsuggest_post(&$a) {
                                VALUES ( %d, %d, '%s','%s','%s','%s','%s','%s')",
                                intval(local_user()),
                                intval($contact_id),
-                               dbesc($r[0]['name']),
-                               dbesc($r[0]['url']),
-                               dbesc($r[0]['request']),
-                               dbesc($r[0]['photo']),
-                               dbesc($hash),
+                               dbesc($r[0]['name']), 
+                               dbesc($r[0]['url']), 
+                               dbesc($r[0]['request']), 
+                               dbesc($r[0]['photo']), 
+                               dbesc($hash), 
                                dbesc(datetime_convert())
                        );
                        $r = q("SELECT `id` FROM `fsuggest` WHERE `note` = '%s' AND `uid` = %d LIMIT 1",
@@ -65,11 +65,11 @@ function fsuggest_post(&$a) {
 
        }
 
+
 }
-}
 
 
-if(! function_exists('fsuggest_content')) {
+
 function fsuggest_content(&$a) {
 
        require_once('include/acl_selectors.php');
@@ -100,7 +100,7 @@ function fsuggest_content(&$a) {
 
        $o .= '<form id="fsuggest-form" action="fsuggest/' . $contact_id . '" method="post" >';
 
-       $o .= contact_selector('suggest','suggest-select', false,
+       $o .= contact_selector('suggest','suggest-select', false, 
                array('size' => 4, 'exclude' => $contact_id, 'networks' => 'DFRN_ONLY', 'single' => true));
 
 
@@ -109,4 +109,3 @@ function fsuggest_content(&$a) {
 
        return $o;
 }
-}
index 2f8053eefb8ed5bb717b528841aa91f3930c9a3b..5b28784f5663cc1b7056125b26619416e9e76b6d 100644 (file)
@@ -1,21 +1,18 @@
 <?php
 
-if(! function_exists('validate_members')) {
 function validate_members(&$item) {
        $item = intval($item);
 }
-}
 
-if(! function_exists('group_init')) {
 function group_init(&$a) {
        if(local_user()) {
                require_once('include/group.php');
                $a->page['aside'] = group_side('contacts','group','extended',(($a->argc > 1) ? intval($a->argv[1]) : 0));
        }
 }
-}
 
-if(! function_exists('group_post')) {
+
+
 function group_post(&$a) {
 
        if(! local_user()) {
@@ -67,9 +64,7 @@ function group_post(&$a) {
        }
        return;
 }
-}
 
-if(! function_exists('group_content')) {
 function group_content(&$a) {
        $change = false;
 
@@ -234,5 +229,5 @@ function group_content(&$a) {
        }
 
        return replace_macros($tpl, $context);
-}
+
 }
index af49423de38c408adaaf1ef30edbaa5e29225fef..6d2d9e2ebfc38f03761ec6b0595787db981184f9 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 
-if(! function_exists('hcard_init')) {
 function hcard_init(&$a) {
 
        $blocked = (((get_config('system','block_public')) && (! local_user()) && (! remote_user())) ? true : false);
@@ -16,7 +15,7 @@ function hcard_init(&$a) {
        $profile = 0;
        if((local_user()) && ($a->argc > 2) && ($a->argv[2] === 'view')) {
                $which = $a->user['nickname'];
-               $profile = $a->argv[1];
+               $profile = $a->argv[1];         
        }
 
        profile_load($a,$which,$profile);
@@ -24,7 +23,7 @@ function hcard_init(&$a) {
        if((x($a->profile,'page-flags')) && ($a->profile['page-flags'] == PAGE_COMMUNITY)) {
                $a->page['htmlhead'] .= '<meta name="friendica.community" content="true" />';
        }
-       if(x($a->profile,'openidserver'))
+       if(x($a->profile,'openidserver'))                               
                $a->page['htmlhead'] .= '<link rel="openid.server" href="' . $a->profile['openidserver'] . '" />' . "\r\n";
        if(x($a->profile,'openid')) {
                $delegate = ((strstr($a->profile['openid'],'://')) ? $a->profile['openid'] : 'http://' . $a->profile['openid']);
@@ -43,9 +42,10 @@ function hcard_init(&$a) {
        $uri = urlencode('acct:' . $a->profile['nickname'] . '@' . $a->get_hostname() . (($a->path) ? '/' . $a->path : ''));
        $a->page['htmlhead'] .= '<link rel="lrdd" type="application/xrd+xml" href="' . $a->get_baseurl() . '/xrd/?uri=' . $uri . '" />' . "\r\n";
        header('Link: <' . $a->get_baseurl() . '/xrd/?uri=' . $uri . '>; rel="lrdd"; type="application/xrd+xml"', false);
-
+       
        $dfrn_pages = array('request', 'confirm', 'notify', 'poll');
        foreach($dfrn_pages as $dfrn)
                $a->page['htmlhead'] .= "<link rel=\"dfrn-{$dfrn}\" href=\"".$a->get_baseurl()."/dfrn_{$dfrn}/{$which}\" />\r\n";
+
 }
-}
+
index 320e622fa51c06674787d54e7c4d3513f8b9467e..72225692793d5206d412d9e2a671f63ded4ab762 100644 (file)
@@ -18,7 +18,6 @@ if (!function_exists('load_doc_file')) {
 
 }
 
-if(! function_exists('help_content')) {
 function help_content(&$a) {
 
        nav_set_selected('help');
@@ -63,7 +62,7 @@ function help_content(&$a) {
        if ($filename !== "Home") {
                // create TOC but not for home
                $lines = explode("\n", $html);
-               $toc="<style>aside ul {padding-left: 1em;}</style><h2>TOC</h2><ul id='toc'>";
+               $toc="<style>aside ul {padding-left: 1em;}aside h1{font-size:2em}</style><h2>TOC</h2><ul id='toc'>";
                $lastlevel=1;
                $idnum = array(0,0,0,0,0,0,0);
                foreach($lines as &$line){
@@ -85,7 +84,7 @@ function help_content(&$a) {
                                }
                        }
                }
-               for($k=1;$k<$lastlevel; $k++) $toc.="</ul>";
+               for($k=0;$k<$lastlevel; $k++) $toc.="</ul>";
                $html = implode("\n",$lines);
 
                $a->page['aside'] = $toc.$a->page['aside'];
@@ -99,5 +98,5 @@ function help_content(&$a) {
                }
                </style>".$html;
        return $html;
-}
+
 }
index 5b178e9b8f668be56980435db6d75a3dc78592eb..4121764f1a526b340fc1c4805aa705a0a8a0f97a 100644 (file)
@@ -2,7 +2,6 @@
 
 require_once('include/crypto.php');
 
-if(! function_exists('hostxrd_init')) {
 function hostxrd_init(&$a) {
        header('Access-Control-Allow-Origin: *');
        header("Content-type: text/xml");
@@ -28,5 +27,5 @@ function hostxrd_init(&$a) {
        ));
        session_write_close();
        exit();
-}
+
 }
index 8a681a11543d053ca3ffeeaa0b7d2809164e6ce7..e876b4ef8b929ebb0ba661687b4baf04ea63697d 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 
-if(! function_exists('ignored_init')) {
+
 function ignored_init(&$a) {
 
        $ignored = 0;
@@ -43,4 +43,3 @@ function ignored_init(&$a) {
        echo json_encode($ignored);
        killme();
 }
-}
index be90acba109c26966254cc50c0195ddf94343ba9..8434b38e387f21e7953bc68b9c2163750284cd34 100755 (executable)
@@ -3,7 +3,7 @@ require_once "include/Photo.php";
 
 $install_wizard_pass=1;
 
-if(! function_exists('install_init')) {
+
 function install_init(&$a){
 
        // $baseurl/install/testrwrite to test if rewite in .htaccess is working
@@ -11,21 +11,20 @@ function install_init(&$a){
                echo "ok";
                killme();
        }
-
+       
        // We overwrite current theme css, because during install we could not have a working mod_rewrite
        // so we could not have a css at all. Here we set a static css file for the install procedure pages
        $a->config['system']['theme'] = "../install";
        $a->theme['stylesheet'] = $a->get_baseurl()."/view/install/style.css";
-
-
-
+       
+       
+       
        global $install_wizard_pass;
        if (x($_POST,'pass'))
                $install_wizard_pass = intval($_POST['pass']);
-}
+
 }
 
-if(! function_exists('install_post')) {
 function install_post(&$a) {
        global $install_wizard_pass, $db;
 
@@ -113,18 +112,14 @@ function install_post(&$a) {
                break;
        }
 }
-}
 
-if(! function_exists('get_db_errno')) {
 function get_db_errno() {
        if(class_exists('mysqli'))
                return mysqli_connect_errno();
        else
                return mysql_errno();
 }
-}
 
-if(! function_exists('install_content')) {
 function install_content(&$a) {
 
        global $install_wizard_pass, $db;
@@ -309,7 +304,6 @@ function install_content(&$a) {
 
        }
 }
-}
 
 /**
  * checks   : array passed to template
@@ -318,8 +312,7 @@ function install_content(&$a) {
  * required : boolean
  * help                : string optional
  */
-if(! function_exists('check_add')) {
-function check_add(&$checks, $title, $status, $required, $help) {
+function check_add(&$checks, $title, $status, $required, $help){
        $checks[] = array(
                'title' => $title,
                'status' => $status,
@@ -327,9 +320,7 @@ function check_add(&$checks, $title, $status, $required, $help) {
                'help'  => $help,
        );
 }
-}
 
-if(! function_exists('check_php')) {
 function check_php(&$phpath, &$checks) {
        $passed = $passed2 = $passed3 = false;
        if (strlen($phpath)){
@@ -379,10 +370,9 @@ function check_php(&$phpath, &$checks) {
                check_add($checks, t('PHP register_argc_argv'), $passed3, true, $help);
        }
 
-}
+
 }
 
-if(! function_exists('check_keys')) {
 function check_keys(&$checks) {
 
        $help = '';
@@ -402,10 +392,10 @@ function check_keys(&$checks) {
                $help .= t('If running under Windows, please see "http://www.php.net/manual/en/openssl.installation.php".');
        }
        check_add($checks, t('Generate encryption keys'), $res, true, $help);
-}
+
 }
 
-if(! function_exists('check_funcs')) {
+
 function check_funcs(&$checks) {
        $ck_funcs = array();
        check_add($ck_funcs, t('libCurl PHP module'), true, true, "");
@@ -467,9 +457,8 @@ function check_funcs(&$checks) {
        /*if((x($_SESSION,'sysmsg')) && is_array($_SESSION['sysmsg']) && count($_SESSION['sysmsg']))
                notice( t('Please see the file "INSTALL.txt".') . EOL);*/
 }
-}
 
-if(! function_exists('check_htconfig')) {
+
 function check_htconfig(&$checks) {
        $status = true;
        $help = "";
@@ -484,10 +473,9 @@ function check_htconfig(&$checks) {
        }
 
        check_add($checks, t('.htconfig.php is writable'), $status, false, $help);
-}
+
 }
 
-if(! function_exists('check_smarty3')) {
 function check_smarty3(&$checks) {
        $status = true;
        $help = "";
@@ -501,10 +489,9 @@ function check_smarty3(&$checks) {
        }
 
        check_add($checks, t('view/smarty3 is writable'), $status, true, $help);
-}
+
 }
 
-if(! function_exists('check_htaccess')) {
 function check_htaccess(&$checks) {
        $a = get_app();
        $status = true;
@@ -524,9 +511,7 @@ function check_htaccess(&$checks) {
                // cannot check modrewrite if libcurl is not installed
        }
 }
-}
 
-if(! function_exists('check_imagik')) {
 function check_imagik(&$checks) {
        $imagick = false;
        $gif = false;
@@ -543,18 +528,16 @@ function check_imagik(&$checks) {
                check_add($checks, t('ImageMagick supports GIF'), $gif, false, "");
        }
 }
-}
 
-if(! function_exists('manual_config')) {
+
+
 function manual_config(&$a) {
        $data = htmlentities($a->data['txt'],ENT_COMPAT,'UTF-8');
        $o = t('The database configuration file ".htconfig.php" could not be written. Please use the enclosed text to create a configuration file in your web server root.');
        $o .= "<textarea rows=\"24\" cols=\"80\" >$data</textarea>";
        return $o;
 }
-}
 
-if(! function_exists('load_database_rem')) {
 function load_database_rem($v, $i){
        $l = trim($i);
        if (strlen($l)>1 && ($l[0]=="-" || ($l[0]=="/" && $l[1]=="*"))){
@@ -563,9 +546,8 @@ function load_database_rem($v, $i){
                return $v."\n".$i;
        }
 }
-}
 
-if(! function_exists('load_database')) {
+
 function load_database($db) {
 
        require_once("include/dbstructure.php");
@@ -585,9 +567,7 @@ function load_database($db) {
 
        return $errors;
 }
-}
 
-if(! function_exists('what_next')) {
 function what_next() {
        $a = get_app();
        $baseurl = $a->get_baseurl();
@@ -599,4 +579,5 @@ function what_next() {
                .t("Go to your new Friendica node <a href='$baseurl/register'>registration page</a> and register as new user. Remember to use the same email you have entered as administrator email. This will allow you to enter the site admin panel.")
                ."</p>";
 }
-}
+
+
index 1f559dabc0445ac62b7cfd27d35f35035947fdf3..ccf876c7c0d1cfb400f40daa52aa2c563a32482b 100644 (file)
@@ -9,7 +9,6 @@
 
 require_once('include/email.php');
 
-if(! function_exists('invite_post')) {
 function invite_post(&$a) {
 
        if(! local_user()) {
@@ -50,7 +49,7 @@ function invite_post(&$a) {
                        notice(  sprintf( t('%s : Not a valid email address.'), $recip) . EOL);
                        continue;
                }
-
+               
                if($invonly && ($x || is_site_admin())) {
                        $code = autoname(8) . srand(1000,9999);
                        $nmessage = str_replace('$invite_code',$code,$message);
@@ -71,8 +70,8 @@ function invite_post(&$a) {
                else
                        $nmessage = $message;
 
-               $res = mail($recip, email_header_encode( t('Please join us on Friendica'),'UTF-8'),
-                       $nmessage,
+               $res = mail($recip, email_header_encode( t('Please join us on Friendica'),'UTF-8'), 
+                       $nmessage, 
                        "From: " . $a->user['email'] . "\n"
                        . 'Content-type: text/plain; charset=UTF-8' . "\n"
                        . 'Content-transfer-encoding: 8bit' );
@@ -94,9 +93,8 @@ function invite_post(&$a) {
        notice( sprintf( tt("%d message sent.", "%d messages sent.", $total) , $total) . EOL);
        return;
 }
-}
 
-if(! function_exists('invite_content')) {
+
 function invite_content(&$a) {
 
        if(! local_user()) {
@@ -136,7 +134,7 @@ function invite_content(&$a) {
                '$msg_text' => t('Your message:'),
                '$default_message' => t('You are cordially invited to join me and other close friends on Friendica - and help us to create a better social web.') . "\r\n" . "\r\n"
                        . $linktxt
-                       . "\r\n" . "\r\n" . (($invonly) ? t('You will need to supply this invitation code: $invite_code') . "\r\n" . "\r\n" : '') .t('Once you have registered, please connect with me via my profile page at:')
+                       . "\r\n" . "\r\n" . (($invonly) ? t('You will need to supply this invitation code: $invite_code') . "\r\n" . "\r\n" : '') .t('Once you have registered, please connect with me via my profile page at:') 
                        . "\r\n" . "\r\n" . $a->get_baseurl() . '/profile/' . $a->user['nickname']
                        . "\r\n" . "\r\n" . t('For more information about the Friendica project and why we feel it is important, please visit http://friendica.com') . "\r\n" . "\r\n"  ,
                '$submit' => t('Submit')
@@ -144,4 +142,3 @@ function invite_content(&$a) {
 
        return $o;
 }
-}
index f8f2e0fafe49f04a42f5f20c4cfd55e6907c9c52..14c8203c98e26f30d1e461f509c5954424b721b6 100644 (file)
@@ -24,8 +24,8 @@ require_once('include/threads.php');
 require_once('include/text.php');
 require_once('include/items.php');
 require_once('include/Scrape.php');
+require_once('include/diaspora.php');
 
-if(! function_exists('item_post')) {
 function item_post(&$a) {
 
        if((! local_user()) && (! remote_user()) && (! x($_REQUEST,'commenter')))
@@ -161,6 +161,9 @@ function item_post(&$a) {
                                logger('no contact found: '.print_r($thrparent, true), LOGGER_DEBUG);
                        } else
                                logger('parent contact: '.print_r($parent_contact, true), LOGGER_DEBUG);
+
+                       if ($parent_contact["nick"] == "")
+                               $parent_contact["nick"] = $parent_contact["name"];
                }
        }
 
@@ -845,9 +848,6 @@ function item_post(&$a) {
                // NOTREACHED
        }
 
-       // Store the guid and other relevant data
-       add_guid($datarray);
-
        $post_id = $r[0]['id'];
        logger('mod_item: saved item ' . $post_id);
 
@@ -901,7 +901,7 @@ function item_post(&$a) {
 
 
                // Store the comment signature information in case we need to relay to Diaspora
-               store_diaspora_comment_sig($datarray, $author, ($self ? $user['prvkey'] : false), $parent_item, $post_id);
+               diaspora::store_comment_signature($datarray, $author, ($self ? $user['prvkey'] : false), $post_id);
 
        } else {
                $parent = $post_id;
@@ -1018,9 +1018,7 @@ function item_post(&$a) {
        item_post_return($a->get_baseurl(), $api_source, $return_path);
        // NOTREACHED
 }
-}
 
-if(! function_exists('item_post_return')) {
 function item_post_return($baseurl, $api_source, $return_path) {
        // figure out how to return, depending on from whence we came
 
@@ -1040,9 +1038,9 @@ function item_post_return($baseurl, $api_source, $return_path) {
        echo json_encode($json);
        killme();
 }
-}
 
-if(! function_exists('item_content')) {
+
+
 function item_content(&$a) {
 
        if((! local_user()) && (! remote_user()))
@@ -1061,7 +1059,6 @@ function item_content(&$a) {
        }
        return $o;
 }
-}
 
 /**
  * This function removes the tag $tag from the text $body and replaces it with
@@ -1075,7 +1072,6 @@ function item_content(&$a) {
  *
  * @return boolean true if replaced, false if not replaced
  */
-if(! function_exists('handle_tag')) {
 function handle_tag($a, &$body, &$inform, &$str_tags, $profile_uid, $tag, $network = "") {
        require_once("include/Scrape.php");
        require_once("include/socgraph.php");
@@ -1250,44 +1246,3 @@ function handle_tag($a, &$body, &$inform, &$str_tags, $profile_uid, $tag, $netwo
 
        return array('replaced' => $replaced, 'contact' => $r[0]);
 }
-}
-
-if(! function_exists('store_diaspora_comment_sig')) {
-function store_diaspora_comment_sig($datarray, $author, $uprvkey, $parent_item, $post_id) {
-       // We won't be able to sign Diaspora comments for authenticated visitors - we don't have their private key
-
-       $enabled = intval(get_config('system','diaspora_enabled'));
-       if(! $enabled) {
-               logger('mod_item: diaspora support disabled, not storing comment signature', LOGGER_DEBUG);
-               return;
-       }
-
-
-       logger('mod_item: storing diaspora comment signature');
-
-       require_once('include/bb2diaspora.php');
-       $signed_body = html_entity_decode(bb2diaspora($datarray['body']));
-
-       // Only works for NETWORK_DFRN
-       $contact_baseurl_start = strpos($author['url'],'://') + 3;
-       $contact_baseurl_length = strpos($author['url'],'/profile') - $contact_baseurl_start;
-       $contact_baseurl = substr($author['url'], $contact_baseurl_start, $contact_baseurl_length);
-       $diaspora_handle = $author['nick'] . '@' . $contact_baseurl;
-
-       $signed_text = $datarray['guid'] . ';' . $parent_item['guid'] . ';' . $signed_body . ';' . $diaspora_handle;
-
-       if( $uprvkey !== false )
-               $authorsig = rsa_sign($signed_text,$uprvkey,'sha256');
-       else
-               $authorsig = '';
-
-       q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
-               intval($post_id),
-               dbesc($signed_text),
-               dbesc(base64_encode($authorsig)),
-               dbesc($diaspora_handle)
-       );
-
-       return;
-}
-}
index ef483a1f9eed3ca264086edc3d37d77b3380504d..8d383b9abe4ffad8726a3596eec1f2119dcda0d4 100755 (executable)
@@ -5,7 +5,6 @@ require_once('include/bbcode.php');
 require_once('include/items.php');
 require_once('include/like.php');
 
-if(! function_exists('like_content')) {
 function like_content(&$a) {
        if(! local_user() && ! remote_user()) {
                return false;
@@ -29,11 +28,11 @@ function like_content(&$a) {
        killme(); // NOTREACHED
 //     return; // NOTREACHED
 }
-}
+
 
 // Decide how to return. If we were called with a 'return' argument,
 // then redirect back to the calling page. If not, just quietly end
-if(! function_exists('like_content_return')) {
+
 function like_content_return($baseurl, $return_path) {
 
        if($return_path) {
@@ -46,4 +45,4 @@ function like_content_return($baseurl, $return_path) {
 
        killme();
 }
-}
+
index fc500f4dd9b0c6711206bcc5cacb0d9eaa81575c..d1453bc5272bd3cf2aa5ad8017853c75fdc44a85 100644 (file)
@@ -2,7 +2,7 @@
 
 require_once('include/datetime.php');
 
-if(! function_exists('localtime_post')) {
+
 function localtime_post(&$a) {
 
        $t = $_REQUEST['time'];
@@ -13,10 +13,9 @@ function localtime_post(&$a) {
 
        if($_POST['timezone'])
                $a->data['mod-localtime'] = datetime_convert('UTC',$_POST['timezone'],$t,$bd_format);
-}
+
 }
 
-if(! function_exists('localtime_content')) {
 function localtime_content(&$a) {
        $t = $_REQUEST['time'];
        if(! $t)
@@ -39,12 +38,12 @@ function localtime_content(&$a) {
 
        $o .= '<form action ="' . $a->get_baseurl() . '/localtime?f=&time=' . $t . '" method="post" >';
 
-       $o .= '<p>' . t('Please select your timezone:') . '</p>';
+       $o .= '<p>' . t('Please select your timezone:') . '</p>'; 
 
        $o .= select_timezone(($_REQUEST['timezone']) ? $_REQUEST['timezone'] : 'America/Los_Angeles');
 
        $o .= '<input type="submit" name="submit" value="' . t('Submit') . '" /></form>';
 
        return $o;
-}
-}
+
+}
\ No newline at end of file
index 82f93f498566e7be8b8fb5ef9cfdeb93483bb0d1..0ae54c8c125f92d07616113a7b52865e3d61fed1 100644 (file)
@@ -1,8 +1,8 @@
 <?php
 
-if(! function_exists('lockview_content')) {
-function lockview_content(&$a) {
 
+function lockview_content(&$a) {
+  
        $type = (($a->argc > 1) ? $a->argv[1] : 0);
        if (is_numeric($type)) {
                $item_id = intval($type);
@@ -10,13 +10,13 @@ function lockview_content(&$a) {
        } else {
                $item_id = (($a->argc > 2) ? intval($a->argv[2]) : 0);
        }
-
+  
        if(! $item_id)
                killme();
 
        if (!in_array($type, array('item','photo','event')))
                killme();
-
+     
        $r = q("SELECT * FROM `%s` WHERE `id` = %d LIMIT 1",
                dbesc($type),
                intval($item_id)
@@ -33,7 +33,7 @@ function lockview_content(&$a) {
        }
 
 
-       if(($item['private'] == 1) && (! strlen($item['allow_cid'])) && (! strlen($item['allow_gid']))
+       if(($item['private'] == 1) && (! strlen($item['allow_cid'])) && (! strlen($item['allow_gid'])) 
                && (! strlen($item['deny_cid'])) && (! strlen($item['deny_gid']))) {
 
                echo t('Remote privacy information not available.') . '<br />';
@@ -53,7 +53,7 @@ function lockview_content(&$a) {
                        dbesc(implode(', ', $allowed_groups))
                );
                if(count($r))
-                       foreach($r as $rr)
+                       foreach($r as $rr) 
                                $l[] = '<b>' . $rr['name'] . '</b>';
        }
        if(count($allowed_users)) {
@@ -61,7 +61,7 @@ function lockview_content(&$a) {
                        dbesc(implode(', ',$allowed_users))
                );
                if(count($r))
-                       foreach($r as $rr)
+                       foreach($r as $rr) 
                                $l[] = $rr['name'];
 
        }
@@ -71,7 +71,7 @@ function lockview_content(&$a) {
                        dbesc(implode(', ', $deny_groups))
                );
                if(count($r))
-                       foreach($r as $rr)
+                       foreach($r as $rr) 
                                $l[] = '<b><strike>' . $rr['name'] . '</strike></b>';
        }
        if(count($deny_users)) {
@@ -79,12 +79,12 @@ function lockview_content(&$a) {
                        dbesc(implode(', ',$deny_users))
                );
                if(count($r))
-                       foreach($r as $rr)
+                       foreach($r as $rr) 
                                $l[] = '<strike>' . $rr['name'] . '</strike>';
 
        }
 
        echo $o . implode(', ', $l);
        killme();
-}
+
 }
index 47c329eb63c75dd99efea1a254c17997064bf1b2..d09fc1868f55adb2525f734666abeae77c22be38 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-if(! function_exists('login_content')) {
+
 function login_content(&$a) {
        if(x($_SESSION,'theme'))
                unset($_SESSION['theme']);
@@ -9,5 +9,5 @@ function login_content(&$a) {
        if(local_user())
                goaway(z_root());
        return login(($a->config['register_policy'] == REGISTER_CLOSED) ? false : true);
-}
+
 }
index 0c4bb1a8335ffc38a9cce9a08a86df5bcc99b799..938d1cbb008ada25359cf6a68514760a2d98fc6f 100644 (file)
@@ -4,7 +4,6 @@ require_once('include/email.php');
 require_once('include/enotify.php');
 require_once('include/text.php');
 
-if(! function_exists('lostpass_post')) {
 function lostpass_post(&$a) {
 
        $loginame = notags(trim($_POST['login-name']));
@@ -75,10 +74,10 @@ function lostpass_post(&$a) {
                'body' => $body));
 
        goaway(z_root());
+
 }
-}
 
-if(! function_exists('lostpass_content')) {
+
 function lostpass_content(&$a) {
 
 
@@ -165,5 +164,5 @@ function lostpass_content(&$a) {
 
                return $o;
        }
-}
+
 }
index 02de29108f892fb3ea85c4b9b3c2b1206af97969..b50c94c9b93d9a6acb748cea3d3da6a680041ec1 100644 (file)
@@ -1,8 +1,7 @@
 <?php
-if(! function_exists('maintenance_content')) {
+
 function maintenance_content(&$a) {
        return replace_macros(get_markup_template('maintenance.tpl'), array(
                '$sysdown' => t('System down for maintenance')
        ));
 }
-}
index 6af3db99710bf598541fe346ac923b903c72d8d7..adcc3d787aac0d58563cbd5d38e82e50384498d2 100644 (file)
@@ -2,7 +2,7 @@
 
 require_once("include/text.php");
 
-if(! function_exists('manage_post')) {
+
 function manage_post(&$a) {
 
        if(! local_user())
@@ -87,9 +87,9 @@ function manage_post(&$a) {
        goaway( $a->get_baseurl() . "/profile/" . $a->user['nickname'] );
        // NOTREACHED
 }
-}
 
-if(! function_exists('manage_content')) {
+
+
 function manage_content(&$a) {
 
        if(! local_user()) {
@@ -144,5 +144,5 @@ function manage_content(&$a) {
        ));
 
        return $o;
-}
+
 }
index f4936b28dc95b5ca67cfd31a8e02000125d93b28..3b0367b4290e1efd02ccbd945eca34fb4e79bb4d 100644 (file)
@@ -13,7 +13,6 @@ require_once('mod/proxy.php');
  * @param App &$a
  * @return void|string
  */
-if(! function_exists('match_content')) {
 function match_content(&$a) {
 
        $o = '';
@@ -110,4 +109,3 @@ function match_content(&$a) {
 
        return $o;
 }
-}
index 1f11797d8b9ac6255c4e8a6b1a46e1f3d4c5489b..734bf3471017dd49560daa474f68a718ed3d466c 100644 (file)
@@ -3,7 +3,6 @@
 require_once('include/acl_selectors.php');
 require_once('include/message.php');
 
-if(! function_exists('message_init')) {
 function message_init(&$a) {
 
        $tabs = '';
@@ -14,7 +13,7 @@ function message_init(&$a) {
 
        $new = array(
                'label' => t('New Message'),
-               'url' => $a->get_baseurl(true) . '/message/new',
+               'url' => 'message/new',
                'sel'=> ($a->argv[1] == 'new'),
                'accesskey' => 'm',
        );
@@ -37,10 +36,9 @@ function message_init(&$a) {
                '$baseurl' => $a->get_baseurl(true),
                '$base' => $base
        ));
-}
+
 }
 
-if(! function_exists('message_post')) {
 function message_post(&$a) {
 
        if(! local_user()) {
@@ -92,8 +90,8 @@ function message_post(&$a) {
                $a->argv[1] = 'new';
        }
        else
-               goaway($a->get_baseurl(true) . '/' . $_SESSION['return_url']);
-}
+               goaway($_SESSION['return_url']);
+
 }
 
 // Note: the code in 'item_extract_images' and 'item_redir_and_replace_images'
@@ -173,7 +171,7 @@ function item_redir_and_replace_images($body, $images, $cid) {
 }}
 
 
-if(! function_exists('message_content')) {
+
 function message_content(&$a) {
 
        $o = '';
@@ -184,7 +182,7 @@ function message_content(&$a) {
                return;
        }
 
-       $myprofile = $a->get_baseurl(true) . '/profile/' . $a->user['nickname'];
+       $myprofile = 'profile/' . $a->user['nickname'];
 
        $tpl = get_markup_template('mail_head.tpl');
        $header = replace_macros($tpl, array(
@@ -223,7 +221,7 @@ function message_content(&$a) {
                }
                // Now check how the user responded to the confirmation query
                if($_REQUEST['canceled']) {
-                       goaway($a->get_baseurl(true) . '/' . $_SESSION['return_url']);
+                       goaway($_SESSION['return_url']);
                }
 
                $cmd = $a->argv[1];
@@ -236,7 +234,7 @@ function message_content(&$a) {
                                info( t('Message deleted.') . EOL );
                        }
                        //goaway($a->get_baseurl(true) . '/message' );
-                       goaway($a->get_baseurl(true) . '/' . $_SESSION['return_url']);
+                       goaway($_SESSION['return_url']);
                }
                else {
                        $r = q("SELECT `parent-uri`,`convid` FROM `mail` WHERE `id` = %d AND `uid` = %d LIMIT 1",
@@ -267,7 +265,7 @@ function message_content(&$a) {
                                        info( t('Conversation removed.') . EOL );
                        }
                        //goaway($a->get_baseurl(true) . '/message' );
-                       goaway($a->get_baseurl(true) . '/' . $_SESSION['return_url']);
+                       goaway($_SESSION['return_url']);
                }
 
        }
@@ -450,7 +448,7 @@ function message_content(&$a) {
                                $sparkle = '';
                        }
                        else {
-                               $from_url = $a->get_baseurl(true) . '/redir/' . $message['contact-id'];
+                               $from_url = 'redir/' . $message['contact-id'];
                                $sparkle = ' sparkle';
                        }
 
@@ -532,9 +530,7 @@ function message_content(&$a) {
                return $o;
        }
 }
-}
 
-if(! function_exists('get_messages')) {
 function get_messages($user, $lstart, $lend) {
 
        return q("SELECT max(`mail`.`created`) AS `mailcreated`, min(`mail`.`seen`) AS `mailseen`,
@@ -545,9 +541,7 @@ function get_messages($user, $lstart, $lend) {
                intval($user), intval($lstart), intval($lend)
        );
 }
-}
 
-if(! function_exists('render_messages')) {
 function render_messages($msg, $t) {
 
        $a = get_app();
@@ -555,7 +549,7 @@ function render_messages($msg, $t) {
        $tpl = get_markup_template($t);
        $rslt = '';
 
-       $myprofile = $a->get_baseurl(true) . '/profile/' . $a->user['nickname'];
+       $myprofile = 'profile/' . $a->user['nickname'];
 
        foreach($msg as $rr) {
 
@@ -583,7 +577,7 @@ function render_messages($msg, $t) {
                $rslt .= replace_macros($tpl, array(
                        '$id' => $rr['id'],
                        '$from_name' => $participants,
-                       '$from_url' => (($rr['network'] === NETWORK_DFRN) ? $a->get_baseurl(true) . '/redir/' . $rr['contact-id'] : $rr['url']),
+                       '$from_url' => (($rr['network'] === NETWORK_DFRN) ? 'redir/' . $rr['contact-id'] : $rr['url']),
                        '$sparkle' => ' sparkle',
                        '$from_photo' => (($rr['thumb']) ? $rr['thumb'] : $rr['from-photo']),
                        '$subject' => $subject_e,
@@ -599,4 +593,3 @@ function render_messages($msg, $t) {
 
        return $rslt;
 }
-}
index 282d55a24bdfac026c81d20b1a79ff95b0feac4a..bba2c2882d065e5fc2320f5ed8582718f5940b9a 100644 (file)
@@ -2,7 +2,6 @@
 
 require_once('library/asn1.php');
 
-if(! function_exists('modexp_init')) {
 function modexp_init(&$a) {
 
        if($a->argc != 2)
@@ -30,5 +29,6 @@ function modexp_init(&$a) {
        echo 'RSA' . '.' . $m . '.' . $e ;
 
        killme();
+
 }
-}
+
index 2476f06562e0f76af80b88d7122608511563b386..5e6ca0fcfc068533751908e8bc2e4d91ba7beed1 100644 (file)
@@ -4,7 +4,7 @@ require_once('include/security.php');
 require_once('include/bbcode.php');
 require_once('include/items.php');
 
-if(! function_exists('mood_init')) {
+
 function mood_init(&$a) {
 
        if(! local_user())
@@ -59,10 +59,10 @@ function mood_init(&$a) {
 
        $uri = item_new_uri($a->get_hostname(),$uid);
 
-       $action = sprintf( t('%1$s is currently %2$s'), '[url=' . $poster['url'] . ']' . $poster['name'] . '[/url]' , $verbs[$verb]);
+       $action = sprintf( t('%1$s is currently %2$s'), '[url=' . $poster['url'] . ']' . $poster['name'] . '[/url]' , $verbs[$verb]); 
 
        $arr = array();
-
+       $arr['guid']          = get_guid(32);
        $arr['uid']           = $uid;
        $arr['uri']           = $uri;
        $arr['parent-uri']    = (($parent_uri) ? $parent_uri : $uri);
@@ -105,9 +105,9 @@ function mood_init(&$a) {
 
        return;
 }
-}
 
-if(! function_exists('mood_content')) {
+
+
 function mood_content(&$a) {
 
        if(! local_user()) {
@@ -138,5 +138,5 @@ function mood_content(&$a) {
        ));
 
        return $o;
-}
+
 }
index 3b1b0b617ad185765d392e1c6b48d22d7bdc939b..89de5b70576f354653ad4daf8645d5aa5b843876 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 
-if(! function_exists('msearch_post')) {
 function msearch_post(&$a) {
 
        $perpage = (($_POST['n']) ? $_POST['n'] : 80);
@@ -27,8 +26,8 @@ function msearch_post(&$a) {
        if(count($r)) {
                foreach($r as $rr)
                        $results[] = array(
-                               'name' => $rr['name'],
-                               'url' => $a->get_baseurl() . '/profile/' . $rr['nickname'],
+                               'name' => $rr['name'], 
+                               'url' => $a->get_baseurl() . '/profile/' . $rr['nickname'], 
                                'photo' => $a->get_baseurl() . '/photo/avatar/' . $rr['uid'] . '.jpg',
                                'tags' => str_replace(array(',','  '),array(' ',' '),$rr['pub_keywords'])
                        );
@@ -39,5 +38,5 @@ function msearch_post(&$a) {
        echo json_encode($output);
 
        killme();
-}
-}
+
+}
\ No newline at end of file
index 8fbabfda96af63f5326697add93984a617b7fb38..5db69b171eed52f67fea8f65bd21ccb5df55c3ce 100644 (file)
@@ -2,7 +2,6 @@
 
 require_once("include/nav.php");
 
-if(! function_exists('navigation_content')) {
 function navigation_content(&$a) {
 
        $nav_info = nav_info($a);
@@ -23,5 +22,5 @@ function navigation_content(&$a) {
                '$apps' => $a->apps,
                '$clear_notifs' => t('Clear notifications')
        ));
-}
+
 }
index f9a0bec238e697c0b1eb5146a4bc4fa79c44c6fc..a9f369a894182fac5831c85d63438b3b04baa121 100644 (file)
@@ -1,6 +1,4 @@
 <?php
-
-if(! function_exists('network_init')) {
 function network_init(&$a) {
        if(! local_user()) {
                notice( t('Permission denied.') . EOL);
@@ -151,14 +149,13 @@ function network_init(&$a) {
 
        $a->page['aside'] .= (feature_enabled(local_user(),'groups') ? group_side('network/0','network','standard',$group_id) : '');
        $a->page['aside'] .= (feature_enabled(local_user(),'forumlist_widget') ? ForumManager::widget(local_user(),$cid) : '');
-       $a->page['aside'] .= posted_date_widget($a->get_baseurl() . '/network',local_user(),false);
-       $a->page['aside'] .= networks_widget($a->get_baseurl(true) . '/network',(x($_GET, 'nets') ? $_GET['nets'] : ''));
+       $a->page['aside'] .= posted_date_widget('network',local_user(),false);
+       $a->page['aside'] .= networks_widget('network',(x($_GET, 'nets') ? $_GET['nets'] : ''));
        $a->page['aside'] .= saved_searches($search);
-       $a->page['aside'] .= fileas_widget($a->get_baseurl(true) . '/network',(x($_GET, 'file') ? $_GET['file'] : ''));
-}
+       $a->page['aside'] .= fileas_widget('network',(x($_GET, 'file') ? $_GET['file'] : ''));
+
 }
 
-if(! function_exists('saved_searches')) {
 function saved_searches($search) {
 
        if(! feature_enabled(local_user(),'savedsearch'))
@@ -207,7 +204,7 @@ function saved_searches($search) {
        ));
 
        return $o;
-}
+
 }
 
 /**
@@ -225,7 +222,6 @@ function saved_searches($search) {
  *
  * @return Array ( $no_active, $comment_active, $postord_active, $conv_active, $new_active, $starred_active, $bookmarked_active, $spam_active );
  */
-if(! function_exists('network_query_get_sel_tab')) {
 function network_query_get_sel_tab($a) {
        $no_active='';
        $starred_active = '';
@@ -282,12 +278,10 @@ function network_query_get_sel_tab($a) {
 
        return array($no_active, $all_active, $postord_active, $conv_active, $new_active, $starred_active, $bookmarked_active, $spam_active);
 }
-}
 
 /**
  * Return selected network from query
  */
-if(! function_exists('network_query_get_sel_net')) {
 function network_query_get_sel_net() {
        $network = false;
 
@@ -297,9 +291,7 @@ function network_query_get_sel_net() {
 
        return $network;
 }
-}
 
-if(! function_exists('network_query_get_sel_group')) {
 function network_query_get_sel_group($a) {
        $group = false;
 
@@ -309,9 +301,8 @@ function network_query_get_sel_group($a) {
 
        return $group;
 }
-}
 
-if(! function_exists('network_content')) {
+
 function network_content(&$a, $update = 0) {
 
        require_once('include/conversation.php');
@@ -372,7 +363,7 @@ function network_content(&$a, $update = 0) {
        $tabs = array(
                array(
                        'label' => t('Commented Order'),
-                       'url'   => $a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . '?f=&order=comment' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : ''),
+                       'url'   => str_replace('/new', '', $cmd) . '?f=&order=comment' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : ''),
                        'sel'   => $all_active,
                        'title' => t('Sort by Comment Date'),
                        'id'    => 'commented-order-tab',
@@ -380,7 +371,7 @@ function network_content(&$a, $update = 0) {
                ),
                array(
                        'label' => t('Posted Order'),
-                       'url'   => $a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . '?f=&order=post' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : ''),
+                       'url'   => str_replace('/new', '', $cmd) . '?f=&order=post' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : ''),
                        'sel'   => $postord_active,
                        'title' => t('Sort by Post Date'),
                        'id'    => 'posted-order-tab',
@@ -391,7 +382,7 @@ function network_content(&$a, $update = 0) {
        if(feature_enabled(local_user(),'personal_tab')) {
                $tabs[] = array(
                        'label' => t('Personal'),
-                       'url'   => $a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&conv=1',
+                       'url'   => str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&conv=1',
                        'sel'   => $conv_active,
                        'title' => t('Posts that mention or involve you'),
                        'id'    => 'personal-tab',
@@ -402,7 +393,7 @@ function network_content(&$a, $update = 0) {
        if(feature_enabled(local_user(),'new_tab')) {
                $tabs[] = array(
                        'label' => t('New'),
-                       'url'   => $a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . ($len_naked_cmd ? '/' : '') . 'new' . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : ''),
+                       'url'   => str_replace('/new', '', $cmd) . ($len_naked_cmd ? '/' : '') . 'new' . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : ''),
                        'sel'   => $new_active,
                        'title' => t('Activity Stream - by date'),
                        'id'    => 'activitiy-by-date-tab',
@@ -413,7 +404,7 @@ function network_content(&$a, $update = 0) {
        if(feature_enabled(local_user(),'link_tab')) {
                $tabs[] = array(
                        'label' => t('Shared Links'),
-                       'url'   => $a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&bmark=1',
+                       'url'   => str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&bmark=1',
                        'sel'   => $bookmarked_active,
                        'title' => t('Interesting Links'),
                        'id'    => 'shared-links-tab',
@@ -424,7 +415,7 @@ function network_content(&$a, $update = 0) {
        if(feature_enabled(local_user(),'star_posts')) {
                $tabs[] = array(
                        'label' => t('Starred'),
-                       'url'   => $a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&star=1',
+                       'url'   => str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&star=1',
                        'sel'   => $starred_active,
                        'title' => t('Favourite Posts'),
                        'id'    => 'starred-posts-tab',
@@ -556,7 +547,7 @@ function network_content(&$a, $update = 0) {
                        if($update)
                                killme();
                        notice( t('No such group') . EOL );
-                       goaway($a->get_baseurl(true) . '/network/0');
+                       goaway('network/0');
                        // NOTREACHED
                }
 
@@ -620,7 +611,7 @@ function network_content(&$a, $update = 0) {
                }
                else {
                        notice( t('Invalid contact.') . EOL);
-                       goaway($a->get_baseurl(true) . '/network');
+                       goaway('network');
                        // NOTREACHED
                }
        }
@@ -895,4 +886,4 @@ function network_content(&$a, $update = 0) {
 
        return $o;
 }
-}
+
index ef253333028155c17a0e32f0dcd379dad5ae23f1..aa55c3a098804f7e77e696e07167ecf35c9c7ad1 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 
-if(! function_exists('newmember_content')) {
 function newmember_content(&$a) {
 
 
@@ -16,7 +15,7 @@ function newmember_content(&$a) {
 
        $o .= '<ul>';
 
-       $o .= '<li> ' . '<a target="newmember" href="help/guide">' . t('Friendica Walk-Through') . '</a><br />' . t('On your <em>Quick Start</em> page - find a brief introduction to your profile and network tabs, make some new connections, and find some groups to join.') . '</li>' . EOL;
+       $o .= '<li> ' . '<a target="newmember" href="help/guide">' . t('Friendica Walk-Through') . '</a><br />' . t('On your <em>Quick Start</em> page - find a brief introduction to your profile and network tabs, make some new connections, and find some groups to join.') . '</li>' . EOL; 
 
        $o .= '</ul>';
 
@@ -24,7 +23,7 @@ function newmember_content(&$a) {
 
        $o .= '<ul>';
 
-       $o .= '<li>' . '<a target="newmember" href="settings">' . t('Go to Your Settings') . '</a><br />' . t('On your <em>Settings</em> page -  change your initial password. Also make a note of your Identity Address. This looks just like an email address - and will be useful in making friends on the free social web.') . '</li>' . EOL;
+       $o .= '<li>' . '<a target="newmember" href="settings">' . t('Go to Your Settings') . '</a><br />' . t('On your <em>Settings</em> page -  change your initial password. Also make a note of your Identity Address. This looks just like an email address - and will be useful in making friends on the free social web.') . '</li>' . EOL; 
 
        $o .= '<li>' . t('Review the other settings, particularly the privacy settings. An unpublished directory listing is like having an unlisted phone number. In general, you should probably publish your listing - unless all of your friends and potential friends know exactly how to find you.') . '</li>' . EOL;
 
@@ -34,7 +33,7 @@ function newmember_content(&$a) {
 
        $o .= '<ul>';
 
-       $o .= '<li>' . '<a target="newmember" href="profile_photo">' . t('Upload Profile Photo') . '</a><br />' . t('Upload a profile photo if you have not done so already. Studies have shown that people with real photos of themselves are ten times more likely to make friends than people who do not.') . '</li>' . EOL;
+       $o .= '<li>' . '<a target="newmember" href="profile_photo">' . t('Upload Profile Photo') . '</a><br />' . t('Upload a profile photo if you have not done so already. Studies have shown that people with real photos of themselves are ten times more likely to make friends than people who do not.') . '</li>' . EOL;  
 
        $o .= '<li>' . '<a target="newmember" href="profiles">' . t('Edit Your Profile') . '</a><br />' . t('Edit your <strong>default</strong> profile to your liking. Review the settings for hiding your list of friends and hiding the profile from unknown visitors.') . '</li>' . EOL;
 
@@ -47,7 +46,7 @@ function newmember_content(&$a) {
        $o .= '<ul>';
 
     $mail_disabled = ((function_exists('imap_open') && (! get_config('system','imap_disabled'))) ? 0 : 1);
-
+       
        if(! $mail_disabled)
                $o .= '<li>' . '<a target="newmember" href="settings/connectors">' . t('Importing Emails') . '</a><br />' . t('Enter your email access information on your Connector Settings page if you wish to import and interact with friends or mailing lists from your email INBOX') . '</li>' . EOL;
 
@@ -83,4 +82,3 @@ function newmember_content(&$a) {
 
        return $o;
 }
-}
index 7f8939182e7fd52d88c454fc3084400e9ac3e826..ba310a10514151c6cac11a507709164a6b4046c6 100644 (file)
@@ -1,13 +1,12 @@
 <?php
 /**
  * @file mod/nodeinfo.php
- *
+ * 
  * Documentation: http://nodeinfo.diaspora.software/schema.html
 */
 
 require_once("include/plugin.php");
 
-if(! function_exists('nodeinfo_wellknown')) {
 function nodeinfo_wellknown(&$a) {
        if (!get_config("system", "nodeinfo")) {
                http_status_exit(404);
@@ -20,9 +19,7 @@ function nodeinfo_wellknown(&$a) {
        echo json_encode($nodeinfo, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
        exit;
 }
-}
 
-if(! function_exists('nodeinfo_init')) {
 function nodeinfo_init(&$a){
        if (!get_config("system", "nodeinfo")) {
                http_status_exit(404);
@@ -146,9 +143,9 @@ function nodeinfo_init(&$a){
        echo json_encode($nodeinfo, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
        exit;
 }
-}
 
-if(! function_exists('nodeinfo_cron')) {
+
+
 function nodeinfo_cron() {
 
        $a = get_app();
@@ -263,5 +260,5 @@ function nodeinfo_cron() {
         logger("cron_end");
        set_config('nodeinfo','last_calucation', time());
 }
-}
+
 ?>
index 818b0da77abbb59ad20a07eac225624ef35f6558..9f6e9784331851b702434245f24256313612d6e4 100644 (file)
@@ -4,7 +4,6 @@ require_once('include/Contact.php');
 require_once('include/socgraph.php');
 require_once('include/contact_selectors.php');
 
-if(! function_exists('nogroup_init')) {
 function nogroup_init(&$a) {
 
        if(! local_user())
@@ -18,9 +17,8 @@ function nogroup_init(&$a) {
 
        $a->page['aside'] .= group_side('contacts','group','extended',0,$contact_id);
 }
-}
 
-if(! function_exists('nogroup_content')) {
+
 function nogroup_content(&$a) {
 
        if(! local_user()) {
@@ -68,5 +66,5 @@ function nogroup_content(&$a) {
        ));
 
        return $o;
-}
+
 }
index 49fe2b9a37cb4ec02df1856b5ea1c53723fa1c90..4be1a740cd87b6eaaa4d135c256ad6b11d77bb80 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 
-if(! function_exists('noscrape_init')) {
 function noscrape_init(&$a) {
 
        if($a->argc > 1)
@@ -23,13 +22,17 @@ function noscrape_init(&$a) {
        $keywords = str_replace(array('#',',',' ',',,'),array('',' ',',',','),$keywords);
        $keywords = explode(',', $keywords);
 
+       $r = q("SELECT `photo` FROM `contact` WHERE `self` AND `uid` = %d",
+               intval($a->profile['uid']));
+
        $json_info = array(
                'fn' => $a->profile['name'],
                'addr' => $a->profile['addr'],
+               'nick' => $which,
                'key' => $a->profile['pubkey'],
                'homepage' => $a->get_baseurl()."/profile/{$which}",
                'comm' => (x($a->profile,'page-flags')) && ($a->profile['page-flags'] == PAGE_COMMUNITY),
-               'photo' => $a->profile['photo'],
+               'photo' => $r[0]["photo"],
                'tags' => $keywords
        );
 
@@ -63,5 +66,5 @@ function noscrape_init(&$a) {
        header('Content-type: application/json; charset=utf-8');
        echo json_encode($json_info);
        exit;
-}
+
 }
index 7817e25547f2a119294bf47395e0a991d691b4eb..73c1507e3e044d8ea62538192104eafdc9d1cd0f 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 
-if(! function_exists('notes_init')) {
 function notes_init(&$a) {
 
        if(! local_user())
@@ -13,10 +12,10 @@ function notes_init(&$a) {
        nav_set_selected('home');
 
 //     profile_load($a,$which,$profile);
+
 }
-}
 
-if(! function_exists('notes_content')) {
+
 function notes_content(&$a,$update = false) {
 
        if(! local_user()) {
@@ -70,12 +69,12 @@ function notes_content(&$a,$update = false) {
        // Construct permissions
 
        // default permissions - anonymous user
-
+       
        $sql_extra = " AND `allow_cid` = '<" . $a->contact['id'] . ">' ";
 
        $r = q("SELECT COUNT(*) AS `total`
                FROM `item` LEFT JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
-               WHERE `item`.`uid` = %d AND `item`.`visible` = 1 and `item`.`moderated` = 0
+               WHERE `item`.`uid` = %d AND `item`.`visible` = 1 and `item`.`moderated` = 0 
                AND `item`.`deleted` = 0 AND `item`.`type` = 'note'
                AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 AND `contact`.`self` = 1
                AND `item`.`id` = `item`.`parent` AND `item`.`wall` = 0
@@ -91,7 +90,7 @@ function notes_content(&$a,$update = false) {
 
        $r = q("SELECT `item`.`id` AS `item_id`, `contact`.`uid` AS `contact-uid`
                FROM `item` LEFT JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
-               WHERE `item`.`uid` = %d AND `item`.`visible` = 1 AND `item`.`deleted` = 0
+               WHERE `item`.`uid` = %d AND `item`.`visible` = 1 AND `item`.`deleted` = 0 
                and `item`.`moderated` = 0 AND `item`.`type` = 'note'
                AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 AND `contact`.`self` = 1
                AND `item`.`id` = `item`.`parent` AND `item`.`wall` = 0
@@ -110,10 +109,10 @@ function notes_content(&$a,$update = false) {
                foreach($r as $rr)
                        $parents_arr[] = $rr['item_id'];
                $parents_str = implode(', ', $parents_arr);
-
-               $r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
-                       `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`alias`, `contact`.`network`, `contact`.`rel`,
-                       `contact`.`thumb`, `contact`.`self`, `contact`.`writable`,
+               $r = q("SELECT `item`.*, `item`.`id` AS `item_id`, 
+                       `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`alias`, `contact`.`network`, `contact`.`rel`, 
+                       `contact`.`thumb`, `contact`.`self`, `contact`.`writable`, 
                        `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid`
                        FROM `item` LEFT JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
                        WHERE `item`.`uid` = %d AND `item`.`visible` = 1 and `item`.`moderated` = 0 AND `item`.`deleted` = 0
@@ -136,4 +135,3 @@ function notes_content(&$a,$update = false) {
        $o .= paginate($a);
        return $o;
 }
-}
index a42d60dd401f344e4d564229711366c6452ce0e3..19cf53189a1476dfb9795026ab9b23139ad0160f 100644 (file)
@@ -1,8 +1,7 @@
 <?php
-/* identi.ca -> friendica items permanent-url compatibility */
-
-if(! function_exists('notice_init')) {
-       function notice_init(&$a) {
+       /* identi.ca -> friendica items permanent-url compatibility */
+       
+       function notice_init(&$a){
                $id = $a->argv[1];
                $r = q("SELECT user.nickname FROM user LEFT JOIN item ON item.uid=user.uid WHERE item.id=%d",
                                intval($id)
@@ -17,5 +16,5 @@ if(! function_exists('notice_init')) {
 
                }
                return;
+
        }
-}
index c7421b2d42589e78872d625f5a2699c7d71c7813..f6c4e8f51fb128d7f3c719214e41a5d28d0eb58d 100644 (file)
@@ -3,7 +3,6 @@ include_once("include/bbcode.php");
 include_once("include/contact_selectors.php");
 include_once("include/Scrape.php");
 
-if(! function_exists('notifications_post')) {
 function notifications_post(&$a) {
 
        if(! local_user()) {
@@ -50,20 +49,20 @@ function notifications_post(&$a) {
                                        intval(local_user())
                                );
                        }
-                       goaway($a->get_baseurl(true) . '/notifications/intros');
+                       goaway('notifications/intros');
                }
                if($_POST['submit'] == t('Ignore')) {
                        $r = q("UPDATE `intro` SET `ignore` = 1 WHERE `id` = %d",
                                intval($intro_id));
-                       goaway($a->get_baseurl(true) . '/notifications/intros');
+                       goaway('notifications/intros');
                }
        }
 }
-}
 
 
 
-if(! function_exists('notifications_content')) {
+
+
 function notifications_content(&$a) {
 
        if(! local_user()) {
@@ -80,37 +79,37 @@ function notifications_content(&$a) {
        $tabs = array(
                array(
                        'label' => t('System'),
-                       'url'=>$a->get_baseurl(true) . '/notifications/system',
+                       'url'=>'notifications/system',
                        'sel'=> (($a->argv[1] == 'system') ? 'active' : ''),
                        'accesskey' => 'y',
                ),
                array(
                        'label' => t('Network'),
-                       'url'=>$a->get_baseurl(true) . '/notifications/network',
+                       'url'=>'notifications/network',
                        'sel'=> (($a->argv[1] == 'network') ? 'active' : ''),
                        'accesskey' => 'w',
                ),
                array(
                        'label' => t('Personal'),
-                       'url'=>$a->get_baseurl(true) . '/notifications/personal',
+                       'url'=>'notifications/personal',
                        'sel'=> (($a->argv[1] == 'personal') ? 'active' : ''),
                        'accesskey' => 'r',
                ),
                array(
                        'label' => t('Home'),
-                       'url' => $a->get_baseurl(true) . '/notifications/home',
+                       'url' => 'notifications/home',
                        'sel'=> (($a->argv[1] == 'home') ? 'active' : ''),
                        'accesskey' => 'h',
                ),
                array(
                        'label' => t('Introductions'),
-                       'url' => $a->get_baseurl(true) . '/notifications/intros',
+                       'url' => 'notifications/intros',
                        'sel'=> (($a->argv[1] == 'intros') ? 'active' : ''),
                        'accesskey' => 'i',
                ),
                /*array(
                        'label' => t('Messages'),
-                       'url' => $a->get_baseurl(true) . '/message',
+                       'url' => 'message',
                        'sel'=> '',
                ),*/ /*while I can have notifications for messages, this tablist is not place for message page link */
        );
@@ -580,4 +579,3 @@ function notifications_content(&$a) {
        $o .= paginate($a);
        return $o;
 }
-}
index 7acac1084af94805d692ddb8a0f2771a01939bcb..938e2ffbe35a908a79162b2a4c692a1d9a0d228c 100644 (file)
@@ -1,84 +1,72 @@
 <?php
+require_once('include/NotificationsManager.php');
 
-if(! function_exists('notify_init')) {
-function notify_init(&$a) {
-       if(! local_user())
-               return;
 
+function notify_init(&$a) {
+       if(! local_user()) return;
+       $nm = new NotificationsManager();
+               
        if($a->argc > 2 && $a->argv[1] === 'view' && intval($a->argv[2])) {
-               $r = q("select * from notify where id = %d and uid = %d limit 1",
-                       intval($a->argv[2]),
-                       intval(local_user())
-               );
-               if(count($r)) {
-                       q("update notify set seen = 1 where ( link = '%s' or ( parent != 0 and parent = %d and otype = '%s' )) and uid = %d",
-                               dbesc($r[0]['link']),
-                               intval($r[0]['parent']),
-                               dbesc($r[0]['otype']),
-                               intval(local_user())
-                       );
-
+               $note = $nm->getByID($a->argv[2]);
+               if ($note) {
+                       $nm->setSeen($note);
+               
                        // The friendica client has problems with the GUID. this is some workaround
                        if ($a->is_friendica_app()) {
                                require_once("include/items.php");
-                               $urldata = parse_url($r[0]['link']);
+                               $urldata = parse_url($note['link']);
                                $guid = basename($urldata["path"]);
                                $itemdata = get_item_id($guid, local_user());
                                if ($itemdata["id"] != 0)
-                                       $r[0]['link'] = $a->get_baseurl().'/display/'.$itemdata["nick"].'/'.$itemdata["id"];
+                                       $note['link'] = $a->get_baseurl().'/display/'.$itemdata["nick"].'/'.$itemdata["id"];
                        }
 
-                       goaway($r[0]['link']);
+                       goaway($note['link']);
                }
 
                goaway($a->get_baseurl(true));
        }
 
        if($a->argc > 2 && $a->argv[1] === 'mark' && $a->argv[2] === 'all' ) {
-               $r = q("update notify set seen = 1 where uid = %d",
-                       intval(local_user())
-               );
+               $r = $nm->setAllSeen();
                $j = json_encode(array('result' => ($r) ? 'success' : 'fail'));
                echo $j;
                killme();
        }
-}
+
 }
 
-if(! function_exists('notify_content')) {
 function notify_content(&$a) {
-       if(! local_user())
-               return login();
+       if(! local_user()) return login();
 
-               $notif_tpl = get_markup_template('notifications.tpl');
+       $nm = new NotificationsManager();
+       
+       $notif_tpl = get_markup_template('notifications.tpl');
 
-               $not_tpl = get_markup_template('notify.tpl');
-               require_once('include/bbcode.php');
+       $not_tpl = get_markup_template('notify.tpl');
+       require_once('include/bbcode.php');
 
-               $r = q("SELECT * from notify where uid = %d and seen = 0 order by date desc",
-                       intval(local_user())
-               );
-
-               if (count($r) > 0) {
-                       foreach ($r as $it) {
-                               $notif_content .= replace_macros($not_tpl,array(
-                                       '$item_link' => $a->get_baseurl(true).'/notify/view/'. $it['id'],
-                                       '$item_image' => $it['photo'],
-                                       '$item_text' => strip_tags(bbcode($it['msg'])),
-                                       '$item_when' => relative_date($it['date'])
-                               ));
-                       }
-               } else {
-                       $notif_content .= t('No more system notifications.');
+       $r = $nm->getAll(array('seen'=>0));
+       if ($r!==false && count($r) > 0) {
+               foreach ($r as $it) {
+                       $notif_content .= replace_macros($not_tpl,array(
+                               '$item_link' => $a->get_baseurl(true).'/notify/view/'. $it['id'],
+                               '$item_image' => $it['photo'],
+                               '$item_text' => strip_tags(bbcode($it['msg'])),
+                               '$item_when' => relative_date($it['date'])
+                       ));
                }
+       } else {
+               $notif_content .= t('No more system notifications.');
+       }
 
-               $o .= replace_macros($notif_tpl, array(
-                       '$notif_header' => t('System Notifications'),
-                       '$tabs' => '', // $tabs,
-                       '$notif_content' => $notif_content,
-               ));
+       $o .= replace_macros($notif_tpl, array(
+               '$notif_header' => t('System Notifications'),
+               '$tabs' => false, // $tabs,
+               '$notif_content' => $notif_content,
+       ));
 
        return $o;
 
-}
+
 }
index 021cbab6fdc8921dcd95727e31a70cc43bbfaead..cb478cb8605d7054133a6e9ab371e2dbb9187d9b 100644 (file)
@@ -1,8 +1,7 @@
 <?php
 require_once("include/oembed.php");
 
-if(! function_exists('oembed_content')) {
-function oembed_content(&$a) {
+function oembed_content(&$a){
        // logger('mod_oembed ' . $a->query_string, LOGGER_ALL);
 
        if ($a->argv[1]=='b2h'){
@@ -34,4 +33,3 @@ function oembed_content(&$a) {
        }
        killme();
 }
-}
index 1e7c9b23c9e412427fc225d8ce2bd29acabfb770..bbb436e7021ab3f0ad8088fedfde4772e46b0891 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 
-if(! function_exists('oexchange_init')) {
+
 function oexchange_init(&$a) {
 
        if(($a->argc > 1) && ($a->argv[1] === 'xrd')) {
@@ -11,10 +11,9 @@ function oexchange_init(&$a) {
                killme();
        }
 
-}
+
 }
 
-if(! function_exists('oexchange_content')) {
 function oexchange_content(&$a) {
 
        if(! local_user()) {
@@ -27,13 +26,13 @@ function oexchange_content(&$a) {
                return;
        }
 
-       $url = (((x($_REQUEST,'url')) && strlen($_REQUEST['url']))
+       $url = (((x($_REQUEST,'url')) && strlen($_REQUEST['url'])) 
                ? urlencode(notags(trim($_REQUEST['url']))) : '');
-       $title = (((x($_REQUEST,'title')) && strlen($_REQUEST['title']))
+       $title = (((x($_REQUEST,'title')) && strlen($_REQUEST['title'])) 
                ? '&title=' . urlencode(notags(trim($_REQUEST['title']))) : '');
-       $description = (((x($_REQUEST,'description')) && strlen($_REQUEST['description']))
+       $description = (((x($_REQUEST,'description')) && strlen($_REQUEST['description'])) 
                ? '&description=' . urlencode(notags(trim($_REQUEST['description']))) : '');
-       $tags = (((x($_REQUEST,'tags')) && strlen($_REQUEST['tags']))
+       $tags = (((x($_REQUEST,'tags')) && strlen($_REQUEST['tags'])) 
                ? '&tags=' . urlencode(notags(trim($_REQUEST['tags']))) : '');
 
        $s = fetch_url($a->get_baseurl() . '/parse_url?f=&url=' . $url . $title . $description . $tags);
@@ -53,5 +52,7 @@ function oexchange_content(&$a) {
        $_REQUEST = $post;
        require_once('mod/item.php');
        item_post($a);
+
 }
-}
+
+
index a92a124c0d06a8978dd72f5b9cbaede0cf43d20b..5d5539f00eb4e696429903179c1ae1b8022136d7 100644 (file)
@@ -1,8 +1,9 @@
 <?php
 
+
 require_once('library/openid.php');
 
-if(! function_exists('openid_content')) {
+
 function openid_content(&$a) {
 
        $noid = get_config('system','no_openid');
@@ -24,8 +25,8 @@ function openid_content(&$a) {
                                goaway(z_root());
                        }
 
-                       $r = q("SELECT `user`.*, `user`.`pubkey` as `upubkey`, `user`.`prvkey` as `uprvkey`
-                               FROM `user` WHERE `openid` = '%s' AND `blocked` = 0
+                       $r = q("SELECT `user`.*, `user`.`pubkey` as `upubkey`, `user`.`prvkey` as `uprvkey` 
+                               FROM `user` WHERE `openid` = '%s' AND `blocked` = 0 
                                AND `account_expired` = 0 AND `account_removed` = 0 AND `verified` = 1 LIMIT 1",
                                dbesc($authid)
                        );
@@ -39,7 +40,7 @@ function openid_content(&$a) {
                                require_once('include/security.php');
                                authenticate_success($r[0],true,true);
 
-                               // just in case there was no return url set
+                               // just in case there was no return url set 
                                // and we fell through
 
                                goaway(z_root());
@@ -93,4 +94,3 @@ function openid_content(&$a) {
        goaway(z_root());
        // NOTREACHED
 }
-}
index f3d55a102950673255158347af47db6c64937d1c..ff748d1c5360cbc2a0d28ade8d196a8a7b7fe5df 100644 (file)
@@ -1,18 +1,18 @@
 <?php
-if(! function_exists('opensearch_content')) {
-  function opensearch_content(&$a) {
+    function opensearch_content(&$a) {
+       
                $tpl = get_markup_template('opensearch.tpl');
-
+       
                header("Content-type: application/opensearchdescription+xml");
-
+       
                $o = replace_macros($tpl, array(
                        '$baseurl' => $a->get_baseurl(),
                        '$nodename' => $a->get_hostname(),
                ));
-
+               
                echo $o;
-
+               
                killme();
+               
        }
-}
-?>
+?>
\ No newline at end of file
index a21436db49f32e9418d4657ad59da9b8979e688e..6cca0bf67942537b75020a43008623e678cb734b 100644 (file)
@@ -3,7 +3,6 @@
 require_once('include/Scrape.php');
 require_once('include/follow.php');
 
-if(! function_exists('ostatus_subscribe_content')) {
 function ostatus_subscribe_content(&$a) {
 
        if(! local_user()) {
@@ -77,4 +76,3 @@ function ostatus_subscribe_content(&$a) {
 
        return $o;
 }
-}
index 225b831fea66c3751b5a8f444d180c37342cb201..20d6cfdbafeb181ca263aec232a33d53722dab8a 100644 (file)
--- a/mod/p.php
+++ b/mod/p.php
@@ -4,8 +4,7 @@ This file is part of the Diaspora protocol. It is used for fetching single publi
 */
 require_once("include/diaspora.php");
 
-if(! function_exists('p_init')) {
-function p_init($a) {
+function p_init($a){
        if ($a->argc != 2) {
                header($_SERVER["SERVER_PROTOCOL"].' 510 '.t('Not Extended'));
                killme();
@@ -29,14 +28,14 @@ function p_init($a) {
 
        $post = array();
 
-       $reshared = diaspora_is_reshare($item[0]["body"]);
+       $reshared = diaspora::is_reshare($item[0]["body"]);
 
        if ($reshared) {
                $nodename = "reshare";
                $post["root_diaspora_id"] = $reshared["root_handle"];
                $post["root_guid"] = $reshared["root_guid"];
                $post["guid"] = $item[0]["guid"];
-               $post["diaspora_handle"] = diaspora_handle_from_contact($item[0]["contact-id"]);
+               $post["diaspora_handle"] = diaspora::handle_from_contact($item[0]["contact-id"]);
                $post["public"] = (!$item[0]["private"] ? 'true':'false');
                $post["created_at"] = datetime_convert('UTC','UTC',$item[0]["created"]);
        } else {
@@ -49,7 +48,7 @@ function p_init($a) {
                $nodename = "status_message";
                $post["raw_message"] = str_replace("&", "&amp;", $body);
                $post["guid"] = $item[0]["guid"];
-               $post["diaspora_handle"] = diaspora_handle_from_contact($item[0]["contact-id"]);
+               $post["diaspora_handle"] = diaspora::handle_from_contact($item[0]["contact-id"]);
                $post["public"] = (!$item[0]["private"] ? 'true':'false');
                $post["created_at"] = datetime_convert('UTC','UTC',$item[0]["created"]);
                $post["provider_display_name"] = $item[0]["app"];
@@ -80,4 +79,3 @@ function p_init($a) {
 
        killme();
 }
-}
index 481cb89361b6f70be3b104891f923af8661499ef..a1ca5a3db5e2e1c831d35bada4b52a3502b54183 100644 (file)
@@ -1,14 +1,14 @@
 <?php
-/**
+/** 
  * @file mod/parse_url.php
- *
+ * 
  * @todo https://developers.google.com/+/plugins/snippet/
- *
+ * 
  * @verbatim
  * <meta itemprop="name" content="Toller Titel">
  * <meta itemprop="description" content="Eine tolle Beschreibung">
  * <meta itemprop="image" content="http://maple.libertreeproject.org/images/tree-icon.png">
- *
+ * 
  * <body itemscope itemtype="http://schema.org/Product">
  *   <h1 itemprop="name">Shiny Trinket</h1>
  *   <img itemprop="image" src="{image-url}" />
@@ -27,7 +27,6 @@ if(!function_exists('deletenode')) {
        }
 }
 
-if(! function_exists('completeurl')) {
 function completeurl($url, $scheme) {
        $urlarr = parse_url($url);
 
@@ -54,9 +53,7 @@ function completeurl($url, $scheme) {
 
        return($complete);
 }
-}
 
-if(! function_exists('parseurl_getsiteinfo_cached')) {
 function parseurl_getsiteinfo_cached($url, $no_guessing = false, $do_oembed = true) {
 
        if ($url == "")
@@ -80,9 +77,7 @@ function parseurl_getsiteinfo_cached($url, $no_guessing = false, $do_oembed = tr
 
        return $data;
 }
-}
 
-if(! function_exists('parseurl_getsiteinfo')) {
 function parseurl_getsiteinfo($url, $no_guessing = false, $do_oembed = true, $count = 1) {
        require_once("include/network.php");
        require_once("include/Photo.php");
@@ -405,15 +400,11 @@ function parseurl_getsiteinfo($url, $no_guessing = false, $do_oembed = true, $co
 
        return($siteinfo);
 }
-}
 
-if(! function_exists('arr_add_hashes')) {
 function arr_add_hashes(&$item,$k) {
        $item = '#' . $item;
 }
-}
 
-if(! function_exists('parse_url_content')) {
 function parse_url_content(&$a) {
 
        $text = null;
@@ -567,5 +558,4 @@ function parse_url_content(&$a) {
 
        killme();
 }
-}
 ?>
index 3baff13db5d7c03592d01bc3b8b73d7a0fa142cd..4166b4d53949d4f9cb5b1f9467c045b224ef98e0 100644 (file)
@@ -3,7 +3,6 @@
 require_once('include/security.php');
 require_once('include/Photo.php');
 
-if(! function_exists('photo_init')) {
 function photo_init(&$a) {
        global $_SERVER;
 
@@ -210,4 +209,3 @@ function photo_init(&$a) {
        killme();
        // NOTREACHED
 }
-}
index 9821918e5e19600b1a94de4a6e1a1d0c94f292e7..4761b627d8c51bb228ee071e887b5879ad72f807 100644 (file)
@@ -9,7 +9,6 @@ require_once('include/redir.php');
 require_once('include/tags.php');
 require_once('include/threads.php');
 
-if(! function_exists('photos_init')) {
 function photos_init(&$a) {
 
        if($a->argc > 1)
@@ -81,7 +80,7 @@ function photos_init(&$a) {
                                $entry = array(
                                        'text'      => $album['album'],
                                        'total'     => $album['total'],
-                                       'url'       => z_root() . '/photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album['album']),
+                                       'url'       => 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album['album']),
                                        'urlencode' => urlencode($album['album']),
                                        'bin2hex'   => bin2hex($album['album'])
                                );
@@ -101,7 +100,7 @@ function photos_init(&$a) {
                                '$recent'    => t('Recent Photos'),
                                '$albums'   => $albums['albums'],
                                '$baseurl'  => z_root(),
-                               '$upload'   => array( t('Upload New Photos'), $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/upload'),
+                               '$upload'   => array( t('Upload New Photos'), 'photos/' . $a->data['user']['nickname'] . '/upload'),
                                '$can_post' => $can_post
                        ));
                }
@@ -122,9 +121,9 @@ function photos_init(&$a) {
 
        return;
 }
-}
 
-if(! function_exists('photos_post')) {
+
+
 function photos_post(&$a) {
 
        logger('mod-photos: photos_post: begin' , LOGGER_DEBUG);
@@ -191,7 +190,7 @@ function photos_post(&$a) {
                $album = hex2bin($a->argv[3]);
 
                if($album === t('Profile Photos') || $album === 'Contact Photos' || $album === t('Contact Photos')) {
-                       goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']);
+                       goaway($_SESSION['photo_return']);
                        return; // NOTREACHED
                }
 
@@ -201,13 +200,13 @@ function photos_post(&$a) {
                );
                if(! count($r)) {
                        notice( t('Album not found.') . EOL);
-                       goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']);
+                       goaway($_SESSION['photo_return']);
                        return; // NOTREACHED
                }
 
                // Check if the user has responded to a delete confirmation query
                if($_REQUEST['canceled']) {
-                       goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']);
+                       goaway($_SESSION['photo_return']);
                }
 
                /*
@@ -222,7 +221,7 @@ function photos_post(&$a) {
                                intval($page_owner_uid)
                        );
                        $newurl = str_replace(bin2hex($album),bin2hex($newalbum),$_SESSION['photo_return']);
-                       goaway($a->get_baseurl() . '/' . $newurl);
+                       goaway($newurl);
                        return; // NOTREACHED
                }
 
@@ -274,7 +273,7 @@ function photos_post(&$a) {
                                }
                        }
                        else {
-                               goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']);
+                               goaway($_SESSION['photo_return']);
                                return; // NOTREACHED
                        }
 
@@ -310,14 +309,14 @@ function photos_post(&$a) {
                                }
                        }
                }
-               goaway($a->get_baseurl() . '/photos/' . $a->data['user']['nickname']);
+               goaway('photos/' . $a->data['user']['nickname']);
                return; // NOTREACHED
        }
 
 
        // Check if the user has responded to a delete confirmation query for a single photo
        if(($a->argc > 2) && $_REQUEST['canceled']) {
-               goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']);
+               goaway($_SESSION['photo_return']);
        }
 
        if(($a->argc > 2) && (x($_POST,'delete')) && ($_POST['delete'] == t('Delete Photo'))) {
@@ -380,7 +379,7 @@ function photos_post(&$a) {
                        }
                }
 
-               goaway($a->get_baseurl() . '/photos/' . $a->data['user']['nickname']);
+               goaway('photos/' . $a->data['user']['nickname']);
                return; // NOTREACHED
        }
 
@@ -489,7 +488,7 @@ function photos_post(&$a) {
                        $uri = item_new_uri($a->get_hostname(),$page_owner_uid);
 
                        $arr = array();
-
+                       $arr['guid']          = get_guid(32);
                        $arr['uid']           = $page_owner_uid;
                        $arr['uri']           = $uri;
                        $arr['parent-uri']    = $uri;
@@ -678,7 +677,7 @@ function photos_post(&$a) {
                                        $uri = item_new_uri($a->get_hostname(),$page_owner_uid);
 
                                        $arr = array();
-
+                                       $arr['guid']          = get_guid(32);
                                        $arr['uid']           = $page_owner_uid;
                                        $arr['uri']           = $uri;
                                        $arr['parent-uri']    = $uri;
@@ -719,12 +718,6 @@ function photos_post(&$a) {
 
                                        $item_id = item_store($arr);
                                        if($item_id) {
-                                               //q("UPDATE `item` SET `plink` = '%s' WHERE `uid` = %d AND `id` = %d",
-                                               //      dbesc($a->get_baseurl() . '/display/' . $owner_record['nickname'] . '/' . $item_id),
-                                               //      intval($page_owner_uid),
-                                               //      intval($item_id)
-                                               //);
-
                                                proc_run('php',"include/notifier.php","tag","$item_id");
                                        }
                                }
@@ -732,7 +725,7 @@ function photos_post(&$a) {
                        }
 
                }
-               goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']);
+               goaway($_SESSION['photo_return']);
                return; // NOTREACHED
        }
 
@@ -911,6 +904,7 @@ function photos_post(&$a) {
        if($lat && $lon)
                $arr['coord'] = $lat . ' ' . $lon;
 
+       $arr['guid']          = get_guid(32);
        $arr['uid']           = $page_owner_uid;
        $arr['uri']           = $uri;
        $arr['parent-uri']    = $uri;
@@ -939,14 +933,6 @@ function photos_post(&$a) {
 
        $item_id = item_store($arr);
 
-       //if($item_id) {
-       //      q("UPDATE `item` SET `plink` = '%s' WHERE `uid` = %d AND `id` = %d",
-       //              dbesc($a->get_baseurl() . '/display/' . $owner_record['nickname'] . '/' . $item_id),
-       //              intval($page_owner_uid),
-       //              intval($item_id)
-       //      );
-       //}
-
        if($visible)
                proc_run('php', "include/notifier.php", 'wall-new', $item_id);
 
@@ -955,12 +941,12 @@ function photos_post(&$a) {
        // addon uploaders should call "killme()" [e.g. exit] within the photo_post_end hook
        // if they do not wish to be redirected
 
-       goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']);
+       goaway($_SESSION['photo_return']);
        // NOTREACHED
 }
-}
 
-if(! function_exists('photos_content')) {
+
+
 function photos_content(&$a) {
 
        // URLs:
@@ -1126,7 +1112,7 @@ function photos_content(&$a) {
 
                $uploader = '';
 
-               $ret = array('post_url' => $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'],
+               $ret = array('post_url' => 'photos/' . $a->data['user']['nickname'],
                                'addon_text' => $uploader,
                                'default_upload' => true);
 
@@ -1268,15 +1254,15 @@ function photos_content(&$a) {
                else {
                        if(($album !== t('Profile Photos')) && ($album !== 'Contact Photos') && ($album !== t('Contact Photos'))) {
                                if($can_post) {
-                                       $edit = array(t('Edit Album'), $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album) . '/edit');
+                                       $edit = array(t('Edit Album'), 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album) . '/edit');
                                }
                        }
                }
 
                if($_GET['order'] === 'posted')
-                       $order =  array(t('Show Newest First'), $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album));
+                       $order =  array(t('Show Newest First'), 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album));
                else
-                       $order = array(t('Show Oldest First'), $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album) . '?f=&order=posted');
+                       $order = array(t('Show Oldest First'), 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album) . '?f=&order=posted');
 
                $photos = array();
 
@@ -1302,10 +1288,10 @@ function photos_content(&$a) {
                                $photos[] = array(
                                        'id' => $rr['id'],
                                        'twist' => ' ' . $twist . rand(2,4),
-                                       'link' => $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/image/' . $rr['resource-id']
+                                       'link' => 'photos/' . $a->data['user']['nickname'] . '/image/' . $rr['resource-id']
                                                . (($_GET['order'] === 'posted') ? '?f=&order=posted' : ''),
                                        'title' => t('View Photo'),
-                                       'src' => $a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . $rr['scale'] . '.' .$ext,
+                                       'src' => 'photo/' . $rr['resource-id'] . '-' . $rr['scale'] . '.' .$ext,
                                        'alt' => $imgalt_e,
                                        'desc'=> $desc_e,
                                        'ext' => $ext,
@@ -1318,7 +1304,7 @@ function photos_content(&$a) {
                                '$photos' => $photos,
                                '$album' => $album,
                                '$can_post' => $can_post,
-                               '$upload' => array(t('Upload New Photos'), $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/upload/' . bin2hex($album)),
+                               '$upload' => array(t('Upload New Photos'), 'photos/' . $a->data['user']['nickname'] . '/upload/' . bin2hex($album)),
                                '$order' => $order,
                                '$edit' => $edit
                        ));
@@ -1329,7 +1315,7 @@ function photos_content(&$a) {
 
        }
 
-       /**
+       /** 
         * Display one photo
         */
 
@@ -1385,8 +1371,8 @@ function photos_content(&$a) {
                                }
                        }
                        $edit_suffix = ((($cmd === 'edit') && ($can_post)) ? '/edit' : '');
-                       $prevlink = $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/image/' . $prvnxt[$prv]['resource-id'] . $edit_suffix . (($_GET['order'] === 'posted') ? '?f=&order=posted' : '');
-                       $nextlink = $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/image/' . $prvnxt[$nxt]['resource-id'] . $edit_suffix . (($_GET['order'] === 'posted') ? '?f=&order=posted' : '');
+                       $prevlink = 'photos/' . $a->data['user']['nickname'] . '/image/' . $prvnxt[$prv]['resource-id'] . $edit_suffix . (($_GET['order'] === 'posted') ? '?f=&order=posted' : '');
+                       $nextlink = 'photos/' . $a->data['user']['nickname'] . '/image/' . $prvnxt[$nxt]['resource-id'] . $edit_suffix . (($_GET['order'] === 'posted') ? '?f=&order=posted' : '');
                }
 
 
@@ -1403,14 +1389,14 @@ function photos_content(&$a) {
                        }
                }
 
-               $album_link = $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($ph[0]['album']);
+               $album_link = 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($ph[0]['album']);
                $tools = Null;
                $lock = Null;
 
                if($can_post && ($ph[0]['uid'] == $owner_uid)) {
                        $tools = array(
-                               'edit'  => array($a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/image/' . $datum . (($cmd === 'edit') ? '' : '/edit'), (($cmd === 'edit') ? t('View photo') : t('Edit photo'))),
-                               'profile'=>array($a->get_baseurl() . '/profile_photo/use/'.$ph[0]['resource-id'], t('Use as profile photo')),
+                               'edit'  => array('photos/' . $a->data['user']['nickname'] . '/image/' . $datum . (($cmd === 'edit') ? '' : '/edit'), (($cmd === 'edit') ? t('View photo') : t('Edit photo'))),
+                               'profile'=>array('profile_photo/use/'.$ph[0]['resource-id'], t('Use as profile photo')),
                        );
 
                        // lock
@@ -1434,9 +1420,9 @@ function photos_content(&$a) {
                        $prevlink = array($prevlink, '<div class="icon prev"></div>') ;
 
                $photo = array(
-                       'href' => $a->get_baseurl() . '/photo/' . $hires['resource-id'] . '-' . $hires['scale'] . '.' . $phototypes[$hires['type']],
+                       'href' => 'photo/' . $hires['resource-id'] . '-' . $hires['scale'] . '.' . $phototypes[$hires['type']],
                        'title'=> t('View Full Size'),
-                       'src'  => $a->get_baseurl() . '/photo/' . $lores['resource-id'] . '-' . $lores['scale'] . '.' . $phototypes[$lores['type']] . '?f=&_u=' . datetime_convert('','','','ymdhis'),
+                       'src'  => 'photo/' . $lores['resource-id'] . '-' . $lores['scale'] . '.' . $phototypes[$lores['type']] . '?f=&_u=' . datetime_convert('','','','ymdhis'),
                        'height' => $hires['height'],
                        'width' => $hires['width'],
                        'album' => $hires['album'],
@@ -1523,7 +1509,7 @@ function photos_content(&$a) {
                        }
                        $tags = array(t('Tags: '), $tag_str);
                        if($cmd === 'edit') {
-                               $tags[] = $a->get_baseurl() . '/tagrm/' . $link_item['id'];
+                               $tags[] = 'tagrm/' . $link_item['id'];
                                $tags[] = t('[Remove any tag]');
                        }
                }
@@ -1694,7 +1680,7 @@ function photos_content(&$a) {
                                        if(((activity_match($item['verb'],ACTIVITY_LIKE)) || (activity_match($item['verb'],ACTIVITY_DISLIKE))) && ($item['id'] != $item['parent']))
                                                continue;
 
-                                       $redirect_url = $a->get_baseurl() . '/redir/' . $item['cid'] ;
+                                       $redirect_url = 'redir/' . $item['cid'] ;
 
 
                                        if(local_user() && ($item['contact-uid'] == local_user())
@@ -1862,7 +1848,7 @@ function photos_content(&$a) {
                        //hide profile photos to others
                        if((! $is_owner) && (! remote_user()) && ($rr['album'] == t('Profile Photos')))
                                        continue;
-
+                       
                        if($twist == 'rotright')
                                $twist = 'rotleft';
                        else
@@ -1881,12 +1867,12 @@ function photos_content(&$a) {
                        $photos[] = array(
                                'id'            => $rr['id'],
                                'twist'         => ' ' . $twist . rand(2,4),
-                               'link'          => $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/image/' . $rr['resource-id'],
+                               'link'          => 'photos/' . $a->data['user']['nickname'] . '/image/' . $rr['resource-id'],
                                'title'         => t('View Photo'),
-                               'src'           => $a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . ((($rr['scale']) == 6) ? 4 : $rr['scale']) . '.' . $ext,
+                               'src'           => 'photo/' . $rr['resource-id'] . '-' . ((($rr['scale']) == 6) ? 4 : $rr['scale']) . '.' . $ext,
                                'alt'           => $alt_e,
                                'album' => array(
-                                       'link'  => $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($rr['album']),
+                                       'link'  => 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($rr['album']),
                                        'name'  => $name_e,
                                        'alt'   => t('View Album'),
                                ),
@@ -1899,7 +1885,7 @@ function photos_content(&$a) {
        $o .= replace_macros($tpl, array(
                '$title' => t('Recent Photos'),
                '$can_post' => $can_post,
-               '$upload' => array(t('Upload New Photos'), $a->get_baseurl().'/photos/'.$a->data['user']['nickname'].'/upload'),
+               '$upload' => array(t('Upload New Photos'), 'photos/'.$a->data['user']['nickname'].'/upload'),
                '$photos' => $photos,
        ));
 
@@ -1907,4 +1893,4 @@ function photos_content(&$a) {
        $o .= paginate($a);
        return $o;
 }
-}
+
index 9183dcbba2dcb5d7a65a0546692088548ca30962..544aa446bbb4d073e6256a44f0c286209d40e946 100644 (file)
@@ -5,7 +5,6 @@ require_once('include/ForumManager.php');
 require_once('include/group.php');
 require_once("mod/proxy.php");
 
-if(! function_exists('ping_init')) {
 function ping_init(&$a) {
 
        header("Content-type: text/xml");
@@ -206,9 +205,9 @@ function ping_init(&$a) {
                        $local_time = datetime_convert('UTC',date_default_timezone_get(),$n['date']);
 
                        call_hooks('ping_xmlize', $n);
-                       $notsxml = '<note href="%s" name="%s" url="%s" photo="%s" date="%s" seen="%s" timestamp="%s" >%s</note>'."\n";
-                       return sprintf ( $notsxml,
-                               xmlify($n['href']), xmlify($n['name']), xmlify($n['url']), xmlify($n['photo']),
+                       $notsxml = '<note id="%d" href="%s" name="%s" url="%s" photo="%s" date="%s" seen="%s" timestamp="%s" >%s</note>'."\n";
+                       return sprintf ( $notsxml, intval($n['id']),
+                               xmlify($n['href']), xmlify(xmlify($n['name'])), xmlify($n['url']), xmlify($n['photo']),
                                xmlify(relative_date($n['date'])), xmlify($n['seen']), xmlify(strtotime($local_time)),
                                xmlify($n['message'])
                        );
@@ -220,19 +219,21 @@ function ping_init(&$a) {
                                <home>$home</home>\r\n";
                if ($register!=0) echo "<register>$register</register>";
 
-               if ( count($groups_unseen) ) {
+               if (count($groups_unseen)) {
                        echo '<groups>';
-                       foreach ($groups_unseen as $it) {
-                               echo '<group id="' . $it['id'] . '">' . $it['count'] . "</group>";
-                       }
+                       foreach ($groups_unseen as $it)
+                               if ($it['count'] > 0)
+                                       echo '<group id="'.$it['id'].'">'.$it['count']."</group>";
+
                        echo "</groups>";
                }
 
-               if ( count($forums_unseen) ) {
+               if (count($forums_unseen)) {
                        echo '<forums>';
-                       foreach ($forums_unseen as $it) {
-                               echo '<forum id="' . $it['id'] . '">' . $it['count'] . "</forum>";
-                       }
+                       foreach ($forums_unseen as $it)
+                               if ($it['count'] > 0)
+                                       echo '<forum id="'.$it['id'].'">'.$it['count']."</forum>";
+
                        echo "</forums>";
                }
 
@@ -339,9 +340,7 @@ function ping_init(&$a) {
 
        killme();
 }
-}
 
-if(! function_exists('ping_get_notifications')) {
 function ping_get_notifications($uid) {
 
        $result = array();
@@ -413,4 +412,3 @@ function ping_get_notifications($uid) {
 
        return($result);
 }
-}
index 4b04d70138198851e02346ff7d91a45fa1c75bd9..0a1b392169b22dbf3e5748bb4ebbbebc8aa65a25 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 
-if(! function_exists('poco_init')) {
 function poco_init(&$a) {
        require_once("include/bbcode.php");
 
@@ -325,5 +324,5 @@ function poco_init(&$a) {
        else
                http_status_exit(500);
 
-}
+
 }
index 1af78b68edf2b85c6459ba7d9ec142f42e707ed2..4a643435be229acda46beee3fe352af8ed3e830a 100644 (file)
@@ -4,11 +4,11 @@
  *
  * Poke, prod, finger, or otherwise do unspeakable things to somebody - who must be a connection in your address book
  * This function can be invoked with the required arguments (verb and cid and private and possibly parent) silently via ajax or
- * other web request. You must be logged in and connected to a profile.
+ * other web request. You must be logged in and connected to a profile. 
  * If the required arguments aren't present, we'll display a simple form to choose a recipient and a verb.
  * parent is a special argument which let's you attach this activity as a comment to an existing conversation, which
  * may have started with somebody else poking (etc.) somebody, but this isn't necessary. This can be used in the more pokes
- * plugin version to have entire conversations where Alice poked Bob, Bob fingered Alice, Alice hugged Bob, etc.
+ * plugin version to have entire conversations where Alice poked Bob, Bob fingered Alice, Alice hugged Bob, etc.  
  *
  * private creates a private conversation with the recipient. Otherwise your profile's default post privacy is used.
  *
@@ -18,7 +18,7 @@ require_once('include/security.php');
 require_once('include/bbcode.php');
 require_once('include/items.php');
 
-if(! function_exists('poke_init')) {
+
 function poke_init(&$a) {
 
        if(! local_user())
@@ -91,6 +91,7 @@ function poke_init(&$a) {
 
        $arr = array();
 
+       $arr['guid']          = get_guid(32);
        $arr['uid']           = $uid;
        $arr['uri']           = $uri;
        $arr['parent-uri']    = (($parent_uri) ? $parent_uri : $uri);
@@ -140,9 +141,9 @@ function poke_init(&$a) {
 
        return;
 }
-}
 
-if(! function_exists('poke_content')) {
+
+
 function poke_content(&$a) {
 
        if(! local_user()) {
@@ -201,5 +202,5 @@ function poke_content(&$a) {
        ));
 
        return $o;
-}
+
 }
index 631bf0eba63f5d058e813fdd5306730dd021f111..c0e783a6aaded3cb6ea126bfd177eb50cb76e4df 100644 (file)
@@ -9,8 +9,7 @@ require_once('include/salmon.php');
 require_once('include/crypto.php');
 // not yet ready for prime time
 //require_once('include/zot.php');
-
-if(! function_exists('post_post')) {
+       
 function post_post(&$a) {
 
        $bulk_delivery = false;
@@ -20,7 +19,7 @@ function post_post(&$a) {
        }
        else {
                $nickname = $a->argv[2];
-               $r = q("SELECT * FROM `user` WHERE `nickname` = '%s'
+               $r = q("SELECT * FROM `user` WHERE `nickname` = '%s' 
                                AND `account_expired` = 0 AND `account_removed` = 0 LIMIT 1",
                        dbesc($nickname)
                );
@@ -49,4 +48,4 @@ function post_post(&$a) {
        http_status_exit(($ret) ? $ret : 200);
        // NOTREACHED
 }
-}
+
index 5d1c261fcb8dcceebdcf8b5a46e5c5db7e77a5f5..4584cb29e2564cd31963438d08b295d09c140ecb 100644 (file)
@@ -1,8 +1,7 @@
 <?php
 
-if(! function_exists('pretheme_init')) {
 function pretheme_init(&$a) {
-
+       
        if($_REQUEST['theme']) {
                $theme = $_REQUEST['theme'];
                $info = get_theme_info($theme);
@@ -21,4 +20,3 @@ function pretheme_init(&$a) {
        }
        killme();
 }
-}
index fcf83e76030cb4e2dcdaac90161ea73370433b90..c95db291b359a8410597ceb08d508826441e00cd 100644 (file)
@@ -2,14 +2,13 @@
 
 require_once('include/Scrape.php');
 
-if(! function_exists('probe_content')) {
 function probe_content(&$a) {
 
        $o .= '<h3>Probe Diagnostic</h3>';
 
        $o .= '<form action="probe" method="get">';
        $o .= 'Lookup address: <input type="text" style="width: 250px;" name="addr" value="' . $_GET['addr'] .'" />';
-       $o .= '<input type="submit" name="submit" value="Submit" /></form>';
+       $o .= '<input type="submit" name="submit" value="Submit" /></form>'; 
 
        $o .= '<br /><br />';
 
@@ -23,4 +22,3 @@ function probe_content(&$a) {
        }
        return $o;
 }
-}
index b02570d5afcca60960a6f25726225c052bd50167..26bd395230b8d937df35ceb1c5fcb10891116227 100644 (file)
@@ -3,7 +3,7 @@
 require_once('include/contact_widgets.php');
 require_once('include/redir.php');
 
-if(! function_exists('profile_init')) {
+
 function profile_init(&$a) {
 
        if(! x($a->page,'aside'))
@@ -65,10 +65,10 @@ function profile_init(&$a) {
        foreach($dfrn_pages as $dfrn)
                $a->page['htmlhead'] .= "<link rel=\"dfrn-{$dfrn}\" href=\"".$a->get_baseurl()."/dfrn_{$dfrn}/{$which}\" />\r\n";
        $a->page['htmlhead'] .= "<link rel=\"dfrn-poco\" href=\"".$a->get_baseurl()."/poco/{$which}\" />\r\n";
-}
+
 }
 
-if(! function_exists('profile_content')) {
+
 function profile_content(&$a, $update = 0) {
 
        $category = $datequery = $datequery2 = '';
@@ -350,4 +350,3 @@ function profile_content(&$a, $update = 0) {
 
        return $o;
 }
-}
index e3d6adb4914244dffe5564c64b4553ffb9e6dc34..4e8d279a972b7ce7ec54b03ee12416a8ec627659 100644 (file)
@@ -2,7 +2,6 @@
 
 require_once("include/Photo.php");
 
-if(! function_exists('profile_photo_init')) {
 function profile_photo_init(&$a) {
 
        if(! local_user()) {
@@ -10,10 +9,10 @@ function profile_photo_init(&$a) {
        }
 
        profile_load($a,$a->user['nickname']);
+
 }
-}
 
-if(! function_exists('profile_photo_post')) {
+
 function profile_photo_post(&$a) {
 
        if(! local_user()) {
@@ -144,7 +143,7 @@ function profile_photo_post(&$a) {
        $filesize = intval($_FILES['userfile']['size']);
        $filetype = $_FILES['userfile']['type'];
     if ($filetype=="") $filetype=guess_image_type($filename);
-
+    
        $maximagesize = get_config('system','maximagesize');
 
        if(($maximagesize) && ($filesize > $maximagesize)) {
@@ -165,7 +164,7 @@ function profile_photo_post(&$a) {
        $ph->orient($src);
        @unlink($src);
        return profile_photo_crop_ui_head($a, $ph);
-}
+       
 }
 
 
@@ -176,7 +175,7 @@ function profile_photo_content(&$a) {
                notice( t('Permission denied.') . EOL );
                return;
        }
-
+       
        $newuser = false;
 
        if($a->argc == 2 && $a->argv[1] === 'new')
@@ -187,9 +186,9 @@ function profile_photo_content(&$a) {
                        notice( t('Permission denied.') . EOL );
                        return;
                };
-
+               
 //             check_form_security_token_redirectOnErr('/profile_photo', 'profile_photo');
-
+        
                $resource_id = $a->argv[2];
                //die(":".local_user());
                $r=q("SELECT * FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s' ORDER BY `scale` ASC",
@@ -241,7 +240,7 @@ function profile_photo_content(&$a) {
 
 
        if(! x($a->config,'imagecrop')) {
-
+       
                $tpl = get_markup_template('profile_photo.tpl');
 
                $o .= replace_macros($tpl,array(
@@ -296,11 +295,11 @@ function profile_photo_crop_ui_head(&$a, $ph){
        }
 
        $hash = photo_new_resource();
-
+       
 
        $smallest = 0;
 
-       $r = $ph->store(local_user(), 0 , $hash, $filename, t('Profile Photos'), 0 );
+       $r = $ph->store(local_user(), 0 , $hash, $filename, t('Profile Photos'), 0 );   
 
        if($r)
                info( t('Image uploaded successfully.') . EOL );
@@ -309,8 +308,8 @@ function profile_photo_crop_ui_head(&$a, $ph){
 
        if($width > 640 || $height > 640) {
                $ph->scaleImage(640);
-               $r = $ph->store(local_user(), 0 , $hash, $filename, t('Profile Photos'), 1 );
-
+               $r = $ph->store(local_user(), 0 , $hash, $filename, t('Profile Photos'), 1 );   
+               
                if($r === false)
                        notice( sprintf(t('Image size reduction [%s] failed.'),"640") . EOL );
                else
@@ -324,3 +323,4 @@ function profile_photo_crop_ui_head(&$a, $ph){
        $a->page['end'] .= replace_macros(get_markup_template("cropend.tpl"), array());
        return;
 }}
+
index 9ce478ba198a375a80d664fcfea4c8935dab4f2c..39382fbdd5b6c14b671289b60680151c51531776 100644 (file)
@@ -1,7 +1,6 @@
 <?php
 require_once("include/Contact.php");
 
-if(! function_exists('profiles_init')) {
 function profiles_init(&$a) {
 
        nav_set_selected('profiles');
@@ -17,7 +16,7 @@ function profiles_init(&$a) {
                );
                if(! count($r)) {
                        notice( t('Profile not found.') . EOL);
-                       goaway($a->get_baseurl(true) . '/profiles');
+                       goaway('profiles');
                        return; // NOTREACHED
                }
 
@@ -35,9 +34,9 @@ function profiles_init(&$a) {
                        intval(local_user())
                );
                if($r)
-                       info( t('Profile deleted.') . EOL);
+                       info(t('Profile deleted.').EOL);
 
-               goaway($a->get_baseurl(true) . '/profiles');
+               goaway('profiles');
                return; // NOTREACHED
        }
 
@@ -74,9 +73,9 @@ function profiles_init(&$a) {
 
                info( t('New profile created.') . EOL);
                if(count($r3) == 1)
-                       goaway($a->get_baseurl(true) . '/profiles/' . $r3[0]['id']);
+                       goaway('profiles/'.$r3[0]['id']);
 
-               goaway($a->get_baseurl(true) . '/profiles');
+               goaway('profiles');
        }
 
        if(($a->argc > 2) && ($a->argv[1] === 'clone')) {
@@ -117,9 +116,9 @@ function profiles_init(&$a) {
                );
                info( t('New profile created.') . EOL);
                if(count($r3) == 1)
-                       goaway($a->get_baseurl(true) . '/profiles/' . $r3[0]['id']);
+                       goaway('profiles/'.$r3[0]['id']);
 
-               goaway($a->get_baseurl(true) . '/profiles');
+               goaway('profiles');
 
                return; // NOTREACHED
        }
@@ -140,10 +139,9 @@ function profiles_init(&$a) {
        }
 
 
-}
+
 }
 
-if(! function_exists('profile_clean_keywords')) {
 function profile_clean_keywords($keywords) {
        $keywords = str_replace(","," ",$keywords);
        $keywords = explode(" ", $keywords);
@@ -160,9 +158,7 @@ function profile_clean_keywords($keywords) {
 
        return $keywords;
 }
-}
 
-if(! function_exists('profiles_post')) {
 function profiles_post(&$a) {
 
        if(! local_user()) {
@@ -506,9 +502,8 @@ function profiles_post(&$a) {
                }
        }
 }
-}
 
-if(! function_exists('profile_activity')) {
+
 function profile_activity($changed, $value) {
        $a = get_app();
 
@@ -531,6 +526,8 @@ function profile_activity($changed, $value) {
                return;
 
        $arr = array();
+
+       $arr['guid'] = get_guid(32);
        $arr['uri'] = $arr['parent-uri'] = item_new_uri($a->get_hostname(), local_user());
        $arr['uid'] = local_user();
        $arr['contact-id'] = $self[0]['id'];
@@ -587,20 +584,11 @@ function profile_activity($changed, $value) {
 
        $i = item_store($arr);
        if($i) {
-
-               // give it a permanent link
-               //q("update item set plink = '%s' where id = %d",
-               //      dbesc($a->get_baseurl() . '/display/' . $a->user['nickname'] . '/' . $i),
-               //      intval($i)
-               //);
-
                proc_run('php',"include/notifier.php","activity","$i");
-
        }
 }
-}
 
-if(! function_exists('profiles_content')) {
+
 function profiles_content(&$a) {
 
        if(! local_user()) {
@@ -792,7 +780,7 @@ function profiles_content(&$a) {
                        );
                        if(count($r)){
                                //Go to the default profile.
-                               goaway($a->get_baseurl(true) . '/profiles/'.$r[0]['id']);
+                               goaway('profiles/'.$r[0]['id']);
                        }
                }
 
@@ -813,16 +801,16 @@ function profiles_content(&$a) {
 
                        foreach($r as $rr) {
                                $o .= replace_macros($tpl, array(
-                                       '$photo' => $a->get_cached_avatar_image($rr['thumb']),
+                                       '$photo' => $a->remove_baseurl($rr['thumb']),
                                        '$id' => $rr['id'],
                                        '$alt' => t('Profile Image'),
                                        '$profile_name' => $rr['profile-name'],
                                        '$visible' => (($rr['is-default']) ? '<strong>' . t('visible to everybody') . '</strong>'
-                                               : '<a href="' . $a->get_baseurl(true) . '/profperm/' . $rr['id'] . '" />' . t('Edit visibility') . '</a>')
+                                               : '<a href="'.'profperm/'.$rr['id'].'" />' . t('Edit visibility') . '</a>')
                                ));
                        }
                }
                return $o;
        }
-}
+
 }
index 6fb717294994ee0509a19aeef8bea83099c90577..077f695bea14c15be10f592258847bcd6e3d1198 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 
-if(! function_exists('profperm_init')) {
 function profperm_init(&$a) {
 
        if(! local_user())
@@ -10,10 +9,10 @@ function profperm_init(&$a) {
        $profile = $a->argv[1];
 
        profile_load($a,$which,$profile);
-}
+
 }
 
-if(! function_exists('profperm_content')) {
+
 function profperm_content(&$a) {
 
        if(! local_user()) {
@@ -109,9 +108,9 @@ function profperm_content(&$a) {
        }
 
        $o .= '<div id="prof-update-wrapper">';
-       if($change)
+       if($change) 
                $o = '';
-
+       
        $o .= '<div id="prof-members-title">';
        $o .= '<h3>' . t('Visible To') . '</h3>';
        $o .= '</div>';
@@ -157,5 +156,6 @@ function profperm_content(&$a) {
        }
        $o .= '</div>';
        return $o;
+
 }
-}
+
index 8e2a38925483f2533e81c1f8a9fcd13938f5bdac..abcaf4912798830546409ef6d16876bea9f3db96 100644 (file)
@@ -12,7 +12,6 @@ define("PROXY_SIZE_LARGE", "large");
 require_once('include/security.php');
 require_once("include/Photo.php");
 
-if(! function_exists('proxy_init')) {
 function proxy_init() {
        global $a, $_SERVER;
 
@@ -233,9 +232,7 @@ function proxy_init() {
 
        killme();
 }
-}
 
-if(! function_exists('proxy_url')) {
 function proxy_url($url, $writemode = false, $size = "") {
        global $_SERVER;
 
@@ -297,13 +294,11 @@ function proxy_url($url, $writemode = false, $size = "") {
        else
                return ($proxypath.$size);
 }
-}
 
 /**
  * @param $url string
  * @return boolean
  */
-if(! function_exists('proxy_is_local_image')) {
 function proxy_is_local_image($url) {
        if ($url[0] == '/') return true;
 
@@ -314,9 +309,7 @@ function proxy_is_local_image($url) {
        $url = normalise_link($url);
        return (substr($url, 0, strlen($baseurl)) == $baseurl);
 }
-}
 
-if(! function_exists('proxy_parse_query')) {
 function proxy_parse_query($var) {
         /**
          *  Use this function to parse out the query array element from
@@ -335,9 +328,7 @@ function proxy_parse_query($var) {
         unset($val, $x, $var);
         return $arr;
 }
-}
 
-if(! function_exists('proxy_img_cb')) {
 function proxy_img_cb($matches) {
 
        // if the picture seems to be from another picture cache then take the original source
@@ -351,13 +342,10 @@ function proxy_img_cb($matches) {
 
        return $matches[1].proxy_url(htmlspecialchars_decode($matches[2])).$matches[3];
 }
-}
 
-if(! function_exists('proxy_parse_html')) {
 function proxy_parse_html($html) {
        $a = get_app();
        $html = str_replace(normalise_link($a->get_baseurl())."/", $a->get_baseurl()."/", $html);
 
        return preg_replace_callback("/(<img [^>]*src *= *[\"'])([^\"']+)([\"'][^>]*>)/siU", "proxy_img_cb", $html);
 }
-}
index 15523e637a83da8d1c3e3c1e0d62385ef04317b9..6053ee2fbe1553e60d1fa182aff303fbd8e1dad0 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 
-if(! function_exists('hub_return')) {
 function hub_return($valid,$body) {
 
        if($valid) {
@@ -15,18 +14,18 @@ function hub_return($valid,$body) {
 
        // NOTREACHED
 }
-}
 
 // when receiving an XML feed, always return OK
-if(! function_exists('hub_post_return')) {
+
 function hub_post_return() {
+
        header($_SERVER["SERVER_PROTOCOL"] . ' 200 ' . 'OK');
        killme();
+
 }
-}
 
 
-if(! function_exists('pubsub_init')) {
+
 function pubsub_init(&$a) {
 
        $nick       = (($a->argc > 1) ? notags(trim($a->argv[1])) : '');
@@ -58,7 +57,7 @@ function pubsub_init(&$a) {
 
                $sql_extra = ((strlen($hub_verify)) ? sprintf(" AND `hub-verify` = '%s' ", dbesc($hub_verify)) : '');
 
-               $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d
+               $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d 
                        AND `blocked` = 0 AND `pending` = 0 $sql_extra LIMIT 1",
                        intval($contact_id),
                        intval($owner['uid'])
@@ -76,7 +75,7 @@ function pubsub_init(&$a) {
 
                $contact = $r[0];
 
-               // We must initiate an unsubscribe request with a verify_token.
+               // We must initiate an unsubscribe request with a verify_token. 
                // Don't allow outsiders to unsubscribe us.
 
                if($hub_mode === 'unsubscribe') {
@@ -96,11 +95,9 @@ function pubsub_init(&$a) {
                hub_return(true, $hub_challenge);
        }
 }
-}
 
 require_once('include/security.php');
 
-if(! function_exists('pubsub_post')) {
 function pubsub_post(&$a) {
 
        $xml = file_get_contents('php://input');
@@ -125,8 +122,8 @@ function pubsub_post(&$a) {
 
        $importer = $r[0];
 
-       $r = q("SELECT * FROM `contact` WHERE `subhub` = 1 AND `id` = %d AND `uid` = %d
-               AND ( `rel` = %d OR `rel` = %d OR network = '%s' ) AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
+       $r = q("SELECT * FROM `contact` WHERE `subhub` AND `id` = %d AND `uid` = %d
+               AND (`rel` = %d OR `rel` = %d OR network = '%s') AND NOT `blocked` LIMIT 1",
                intval($contact_id),
                intval($importer['uid']),
                intval(CONTACT_IS_SHARING),
@@ -158,5 +155,8 @@ function pubsub_post(&$a) {
        consume_feed($xml,$importer,$contact,$feedhub,1,2);
 
        hub_post_return();
+
 }
-}
+
+
+
index b0e3ef30998ec3b891291d7b5392bba1df1aac20..5d7621cc745fdf648cd1dc79d83342d09ccbe694 100644 (file)
@@ -1,12 +1,9 @@
 <?php
 
-if(! function_exists('post_var')) {
 function post_var($name) {
        return (x($_POST, $name)) ? notags(trim($_POST[$name])) : '';
 }
-}
 
-if(! function_exists('pubsubhubbub_init')) {
 function pubsubhubbub_init(&$a) {
        // PuSH subscription must be considered "public" so just block it
        // if public access isn't enabled.
@@ -161,5 +158,5 @@ function pubsubhubbub_init(&$a) {
 
        killme();
 }
-}
+
 ?>
index cffc3e50ba91256f4a854a03d83887b6a1c178c3..c35e253b670508c23771013c04187ca482f36a44 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 
-if(! function_exists('qsearch_init')) {
 function qsearch_init(&$a) {
 
        if(! local_user())
@@ -48,4 +47,4 @@ function qsearch_init(&$a) {
        echo json_encode((object) $results);
        killme();
 }
-}
+
index e9e0a8e7bb1e56e0dbb76f2a305fa960d4db5fe8..6713a81d9e4a2efe2f013feb6ceeb2c092a891fe 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 
-if(! function_exists('randprof_init')) {
+
 function randprof_init(&$a) {
        require_once('include/Contact.php');
        $x = random_profile();
@@ -8,4 +8,3 @@ function randprof_init(&$a) {
                goaway(zrl($x));
        goaway($a->get_baseurl() . '/profile');
 }
-}
index 3a30058cdc18fecbe0b30f2025a2fb3d7eadfaeb..4991ac47e81811b473597398a4c129df2c00b330 100644 (file)
@@ -9,7 +9,7 @@ require_once('include/salmon.php');
 require_once('include/crypto.php');
 require_once('include/diaspora.php');
 
-if(! function_exists('receive_post')) {
+
 function receive_post(&$a) {
 
 
@@ -53,7 +53,7 @@ function receive_post(&$a) {
 
        logger('mod-diaspora: message is okay', LOGGER_DEBUG);
 
-       $msg = diaspora_decode($importer,$xml);
+       $msg = diaspora::decode($importer,$xml);
 
        logger('mod-diaspora: decoded', LOGGER_DEBUG);
 
@@ -65,12 +65,13 @@ function receive_post(&$a) {
        logger('mod-diaspora: dispatching', LOGGER_DEBUG);
 
        $ret = 0;
-       if($public)
-               diaspora_dispatch_public($msg);
-       else
-               $ret = diaspora_dispatch($importer,$msg);
+       if($public) {
+               diaspora::dispatch_public($msg);
+       } else {
+               $ret = diaspora::dispatch($importer,$msg);
+       }
 
        http_status_exit(($ret) ? $ret : 200);
        // NOTREACHED
 }
-}
+
index 2dda0571b254c406fbf1a008cd9253fd305576fb..632c39578696807b3dfd20e6f6658df50656cc99 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 
-if(! function_exists('redir_init')) {
 function redir_init(&$a) {
 
        $url = ((x($_GET,'url')) ? $_GET['url'] : '');
@@ -58,9 +57,9 @@ function redir_init(&$a) {
                        intval(time() + 45)
                );
 
-               logger('mod_redir: ' . $r[0]['name'] . ' ' . $sec, LOGGER_DEBUG);
+               logger('mod_redir: ' . $r[0]['name'] . ' ' . $sec, LOGGER_DEBUG); 
                $dest = (($url) ? '&destination_url=' . $url : '');
-               goaway ($r[0]['poll'] . '?dfrn_id=' . $dfrn_id
+               goaway ($r[0]['poll'] . '?dfrn_id=' . $dfrn_id 
                        . '&dfrn_version=' . DFRN_PROTOCOL_VERSION . '&type=profile&sec=' . $sec . $dest . $quiet );
        }
 
@@ -76,4 +75,3 @@ function redir_init(&$a) {
 
        goaway(z_root());
 }
-}
index 2e3ca414e3408277425cea9477a2a9000e10b1fd..5a90db1f902d0b0265d26c1da3104bb1b5ce3e70 100644 (file)
@@ -3,7 +3,6 @@
 require_once('include/enotify.php');
 require_once('include/user.php');
 
-if(! function_exists('user_allow')) {
 function user_allow($hash) {
 
        $a = get_app();
@@ -56,14 +55,14 @@ function user_allow($hash) {
                info( t('Account approved.') . EOL );
                return true;
        }
-}
+
 }
 
 
 // This does not have to go through user_remove() and save the nickname
 // permanently against re-registration, as the person was not yet
 // allowed to have friends on this system
-if(! function_exists('user_deny')) {
+
 function user_deny($hash) {
 
        $register = q("SELECT * FROM `register` WHERE `hash` = '%s' LIMIT 1",
@@ -92,10 +91,9 @@ function user_deny($hash) {
        );
        notice( sprintf(t('Registration revoked for %s'), $user[0]['username']) . EOL);
        return true;
-}
+
 }
 
-if(! function_exists('regmod_content')) {
 function regmod_content(&$a) {
 
        global $lang;
@@ -133,4 +131,3 @@ function regmod_content(&$a) {
                killme();
        }
 }
-}
index 6c84c41892b9bdb1190cd6a944b53dfffd128ae8..904606fd57cb77b69a1a0b7ef48a9025ff012f25 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 
-if(! function_exists('removeme_post')) {
 function removeme_post(&$a) {
 
        if(! local_user())
@@ -25,10 +24,9 @@ function removeme_post(&$a) {
                user_remove($a->user['uid']);
                // NOTREACHED
        }
-}
+
 }
 
-if(! function_exists('removeme_content')) {
 function removeme_content(&$a) {
 
        if(! local_user())
@@ -52,5 +50,5 @@ function removeme_content(&$a) {
        ));
 
        return $o;
-}
+
 }
index e3956ba8cb4c50eec3f7241c0313ecbb6f62f892..2b1224f4233fa6bec55792ad3242278b541a795c 100755 (executable)
@@ -3,7 +3,6 @@
 require_once('include/Scrape.php');
 require_once('include/follow.php');
 
-if(! function_exists('repair_ostatus_content')) {
 function repair_ostatus_content(&$a) {
 
        if(! local_user()) {
@@ -56,4 +55,3 @@ function repair_ostatus_content(&$a) {
 
        return $o;
 }
-}
index 6f9c209fab5915e8f0f8c4e9dd0786f68f4dd0f3..f4984f0f0ffd12e8d911c73ae8f4d1b39b65adc7 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 
-if(! function_exists('rsd_xml_content')) {
+
+
 function rsd_xml_content(&$a) {
        header ("Content-Type: text/xml");
        echo '<?xml version="1.0" encoding="UTF-8"?>
@@ -20,5 +21,4 @@ function rsd_xml_content(&$a) {
  </rsd>
        ';
 die();
-}
-}
+}
\ No newline at end of file
index ee3826d8a8bb37a3c3b734fea775dd12804afeab..37230a557351ad70e9b4faada55a17a89f6ef69e 100644 (file)
@@ -6,7 +6,6 @@ require_once('include/crypto.php');
 require_once('include/items.php');
 require_once('include/follow.php');
 
-if(! function_exists('salmon_return')) {
 function salmon_return($val) {
 
        if($val >= 400)
@@ -17,10 +16,9 @@ function salmon_return($val) {
        logger('mod-salmon returns ' . $val);
        header($_SERVER["SERVER_PROTOCOL"] . ' ' . $val . ' ' . $err);
        killme();
-}
+
 }
 
-if(! function_exists('salmon_post')) {
 function salmon_post(&$a) {
 
        $xml = file_get_contents('php://input');
@@ -86,7 +84,7 @@ function salmon_post(&$a) {
        // decode the data
        $data = base64url_decode($data);
 
-       $author = ostatus_salmon_author($data,$importer);
+       $author = ostatus::salmon_author($data,$importer);
        $author_link = $author["author-link"];
 
        if(! $author_link) {
@@ -157,7 +155,7 @@ function salmon_post(&$a) {
                if(get_pconfig($importer['uid'],'system','ostatus_autofriend')) {
                        $result = new_contact($importer['uid'],$author_link);
                        if($result['success']) {
-                               $r = q("SELECT * FROM `contact` WHERE `network` = '%s' AND ( `url` = '%s' OR `alias` = '%s')
+                               $r = q("SELECT * FROM `contact` WHERE `network` = '%s' AND ( `url` = '%s' OR `alias` = '%s') 
                                        AND `uid` = %d LIMIT 1",
                                        dbesc(NETWORK_OSTATUS),
                                        dbesc($author_link),
@@ -183,8 +181,7 @@ function salmon_post(&$a) {
 
        $contact_rec = ((count($r)) ? $r[0] : null);
 
-       ostatus_import($data,$importer,$contact_rec, $hub);
+       ostatus::import($data,$importer,$contact_rec, $hub);
 
        http_status_exit(200);
 }
-}
index 431bd821d61478b020afa29f3e828e1c53e3c08c..790f577ba6e17511b6be30b39c3de963da5114a8 100644 (file)
@@ -4,7 +4,6 @@ require_once('include/security.php');
 require_once('include/conversation.php');
 require_once('mod/dirfind.php');
 
-if(! function_exists('search_saved_searches')) {
 function search_saved_searches() {
 
        $o = '';
@@ -40,10 +39,10 @@ function search_saved_searches() {
        }
 
        return $o;
+
 }
-}
 
-if(! function_exists('search_init')) {
+
 function search_init(&$a) {
 
        $search = ((x($_GET,'search')) ? notags(trim(rawurldecode($_GET['search']))) : '');
@@ -77,18 +76,17 @@ function search_init(&$a) {
        }
 
 
+
 }
-}
 
 
-if(! function_exists('search_post')) {
+
 function search_post(&$a) {
        if(x($_POST,'search'))
                $a->data['search'] = $_POST['search'];
 }
-}
 
-if(! function_exists('search_content')) {
+
 function search_content(&$a) {
 
        if((get_config('system','block_public')) && (! local_user()) && (! remote_user())) {
@@ -149,7 +147,7 @@ function search_content(&$a) {
        }
 
 
-       $o .= search($search,'search-box','/search',((local_user()) ? true : false), false);
+       $o .= search($search,'search-box','search',((local_user()) ? true : false), false);
 
        if(strpos($search,'#') === 0) {
                $tag = true;
@@ -219,11 +217,10 @@ function search_content(&$a) {
                        FROM `item`
                                INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND NOT `contact`.`blocked` AND NOT `contact`.`pending`
                        WHERE `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated`
-                               AND (`item`.`uid` = 0 OR (`item`.`uid` = %s AND (`item`.`private` OR NOT `item`.`network` IN ('%s', '%s', '%s'))))
+                               AND (`item`.`uid` = 0 OR (`item`.`uid` = %s AND NOT `item`.`global`))
                                $sql_extra
                        GROUP BY `item`.`uri` ORDER BY `item`.`id` DESC LIMIT %d , %d ",
-                               intval(local_user()), dbesc(NETWORK_DFRN), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DIASPORA),
-                               intval($a->pager['start']), intval($a->pager['itemspage']));
+                               intval(local_user()), intval($a->pager['start']), intval($a->pager['itemspage']));
        }
 
        if(! count($r)) {
@@ -250,4 +247,4 @@ function search_content(&$a) {
 
        return $o;
 }
-}
+
index ac3d885b63b9ba4ce81344f714995c08a68cc4b6..22c855edba6dcd0168608e98ff69f1889c2dbc5e 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 
-if(! function_exists('session_content')) {
 function session_content(&$a) {
-}
+
 }
index 1b62499c2276b40002843db3b704c6005fccda44..c7659212bff242359a337e3e88b4cc4ef699d4a1 100644 (file)
@@ -1,7 +1,8 @@
 <?php
 
-if(! function_exists('get_theme_config_file')) {
-function get_theme_config_file($theme) {
+require_once('include/group.php');
+
+function get_theme_config_file($theme){
        $a = get_app();
        $base_theme = $a->theme_info['extends'];
 
@@ -13,9 +14,7 @@ function get_theme_config_file($theme) {
        }
        return null;
 }
-}
 
-if(! function_exists('settings_init')) {
 function settings_init(&$a) {
 
        if(! local_user()) {
@@ -41,7 +40,7 @@ function settings_init(&$a) {
        $tabs = array(
                array(
                        'label' => t('Account'),
-                       'url'   => $a->get_baseurl(true).'/settings',
+                       'url'   => 'settings',
                        'selected'      =>  (($a->argc == 1) && ($a->argv[0] === 'settings')?'active':''),
                        'accesskey' => 'o',
                ),
@@ -50,7 +49,7 @@ function settings_init(&$a) {
        if(get_features()) {
                $tabs[] =       array(
                                        'label' => t('Additional features'),
-                                       'url'   => $a->get_baseurl(true).'/settings/features',
+                                       'url'   => 'settings/features',
                                        'selected'      => (($a->argc > 1) && ($a->argv[1] === 'features') ? 'active' : ''),
                                        'accesskey' => 't',
                                );
@@ -58,49 +57,49 @@ function settings_init(&$a) {
 
        $tabs[] =       array(
                'label' => t('Display'),
-               'url'   => $a->get_baseurl(true).'/settings/display',
+               'url'   => 'settings/display',
                'selected'      => (($a->argc > 1) && ($a->argv[1] === 'display')?'active':''),
                'accesskey' => 'i',
        );
 
        $tabs[] =       array(
                'label' => t('Social Networks'),
-               'url'   => $a->get_baseurl(true).'/settings/connectors',
+               'url'   => 'settings/connectors',
                'selected'      => (($a->argc > 1) && ($a->argv[1] === 'connectors')?'active':''),
                'accesskey' => 'w',
        );
 
        $tabs[] =       array(
                'label' => t('Plugins'),
-               'url'   => $a->get_baseurl(true).'/settings/addon',
+               'url'   => 'settings/addon',
                'selected'      => (($a->argc > 1) && ($a->argv[1] === 'addon')?'active':''),
                'accesskey' => 'l',
        );
 
        $tabs[] =       array(
                'label' => t('Delegations'),
-               'url'   => $a->get_baseurl(true).'/delegate',
+               'url'   => 'delegate',
                'selected'      => (($a->argc == 1) && ($a->argv[0] === 'delegate')?'active':''),
                'accesskey' => 'd',
        );
 
        $tabs[] =       array(
                'label' => t('Connected apps'),
-               'url' => $a->get_baseurl(true) . '/settings/oauth',
+               'url' => 'settings/oauth',
                'selected' => (($a->argc > 1) && ($a->argv[1] === 'oauth')?'active':''),
                'accesskey' => 'b',
        );
 
        $tabs[] =       array(
                'label' => t('Export personal data'),
-               'url' => $a->get_baseurl(true) . '/uexport',
+               'url' => 'uexport',
                'selected' => (($a->argc == 1) && ($a->argv[0] === 'uexport')?'active':''),
                'accesskey' => 'e',
        );
 
        $tabs[] =       array(
                'label' => t('Remove account'),
-               'url' => $a->get_baseurl(true) . '/removeme',
+               'url' => 'removeme',
                'selected' => (($a->argc == 1) && ($a->argv[0] === 'removeme')?'active':''),
                'accesskey' => 'r',
        );
@@ -112,10 +111,10 @@ function settings_init(&$a) {
                '$class' => 'settings-widget',
                '$items' => $tabs,
        ));
+
 }
-}
 
-if(! function_exists('settings_post')) {
+
 function settings_post(&$a) {
 
        if(! local_user())
@@ -201,6 +200,7 @@ function settings_post(&$a) {
                if(x($_POST, 'general-submit')) {
                        set_pconfig(local_user(), 'system', 'no_intelligent_shortening', intval($_POST['no_intelligent_shortening']));
                        set_pconfig(local_user(), 'system', 'ostatus_autofriend', intval($_POST['snautofollow']));
+                       set_pconfig(local_user(), 'ostatus', 'default_group', $_POST['group-selection']);
                        set_pconfig(local_user(), 'ostatus', 'legacy_contact', $_POST['legacy_contact']);
                } elseif(x($_POST, 'imap-submit')) {
 
@@ -344,7 +344,7 @@ function settings_post(&$a) {
                );
 
                call_hooks('display_settings_post', $_POST);
-               goaway($a->get_baseurl(true) . '/settings/display' );
+               goaway('settings/display' );
                return; // NOTREACHED
        }
 
@@ -353,7 +353,7 @@ function settings_post(&$a) {
        if (x($_POST,'resend_relocate')) {
                proc_run('php', 'include/notifier.php', 'relocate', local_user());
                info(t("Relocate message has been send to your contacts"));
-               goaway($a->get_baseurl(true) . '/settings');
+               goaway('settings');
        }
 
        call_hooks('settings_post', $_POST);
@@ -629,12 +629,11 @@ function settings_post(&$a) {
 
        }
 
-       goaway($a->get_baseurl(true) . '/settings' );
+       goaway('settings' );
        return; // NOTREACHED
 }
-}
 
-if(! function_exists('settings_content')) {
+
 function settings_content(&$a) {
 
        $o = '';
@@ -800,8 +799,11 @@ function settings_content(&$a) {
                $settings_connectors .= '<span class="field_help">'.t('If you receive a message from an unknown OStatus user, this option decides what to do. If it is checked, a new contact will be created for every unknown user.').'</span>';
                $settings_connectors .= '</div>';
 
+               $default_group = get_pconfig(local_user(), 'ostatus', 'default_group');
                $legacy_contact = get_pconfig(local_user(), 'ostatus', 'legacy_contact');
 
+               $settings_connectors .= mini_group_select(local_user(), $default_group, t("Default group for OStatus contacts"));
+
                if ($legacy_contact != "")
                        $a->page['htmlhead'] = '<meta http-equiv="refresh" content="0; URL='.$a->get_baseurl().'/ostatus_subscribe?url='.urlencode($legacy_contact).'">';
 
@@ -1155,7 +1157,7 @@ function settings_content(&$a) {
                info( t('Profile is <strong>not published</strong>.') . EOL );
 
 
-       //$subdir = ((strlen($a->get_path())) ? '<br />' . t('or') . ' ' . $a->get_baseurl(true) . '/profile/' . $nickname : '');
+       //$subdir = ((strlen($a->get_path())) ? '<br />' . t('or') . ' ' . 'profile/' . $nickname : '');
 
        $tpl_addr = get_markup_template("settings_nick_set.tpl");
 
@@ -1298,5 +1300,6 @@ function settings_content(&$a) {
        $o .= '</form>' . "\r\n";
 
        return $o;
+
 }
-}
+
index f3a221eb8ef4c985f9b8ad9e86a523efb8a4fa61..085da4e30d8a8b6f15e343a480d6743f733def15 100644 (file)
@@ -1,14 +1,12 @@
 <?php
-
-if(! function_exists('share_init')) {
 function share_init(&$a) {
 
        $post_id = (($a->argc > 1) ? intval($a->argv[1]) : 0);
        if((! $post_id) || (! local_user()))
                killme();
 
-       $r = q("SELECT item.*, contact.network FROM `item`
-               inner join contact on `item`.`contact-id` = `contact`.`id`
+       $r = q("SELECT item.*, contact.network FROM `item` 
+               inner join contact on `item`.`contact-id` = `contact`.`id` 
                WHERE `item`.`id` = %d AND `item`.`uid` = %d LIMIT 1",
 
                intval($post_id),
@@ -42,9 +40,7 @@ function share_init(&$a) {
        echo $o;
        killme();
 }
-}
 
-if(! function_exists('share_header')) {
 function share_header($author, $profile, $avatar, $guid, $posted, $link) {
        $header = "[share author='".str_replace(array("'", "[", "]"), array("&#x27;", "&#x5B;", "&#x5D;"),$author).
                "' profile='".str_replace(array("'", "[", "]"), array("&#x27;", "&#x5B;", "&#x5D;"),$profile).
@@ -60,4 +56,3 @@ function share_header($author, $profile, $avatar, $guid, $posted, $link) {
 
        return $header;
 }
-}
index 4d498b674679608cc33e599d0afb9bfbdce0985c..c47f95da76a5cfc7656aee72dcf7cb667402931d 100644 (file)
@@ -1,7 +1,3 @@
 <?php
 
-if(! function_exists('smilies_content')) {
-function smilies_content(&$a) {
-  return smilies('',true);
-}
-}
+function smilies_content(&$a) { return smilies('',true); }
index b4cc3267872de06d078f74938046721c895faf98..2a89ac768bae20cb3150c48b3d5a841347790611 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 
-if(! function_exists('starred_init')) {
+
 function starred_init(&$a) {
 
        require_once("include/threads.php");
@@ -47,4 +47,3 @@ function starred_init(&$a) {
        echo json_encode($starred);
        killme();
 }
-}
index 98cc708d260692355401679e20401232b3c2e5bf..21a9a0521c63d395267bfbe60f0431936ab3e071 100644 (file)
@@ -5,7 +5,6 @@
 
 require_once("include/plugin.php");
 
-if(! function_exists('statistics_json_init')) {
 function statistics_json_init(&$a) {
 
         if (!get_config("system", "nodeinfo")) {
@@ -58,4 +57,3 @@ function statistics_json_init(&$a) {
        logger("statistics_init: printed ".print_r($statistics, true), LOGGER_DATA);
        killme();
 }
-}
index 6cbaa1d2a744be3b466d6fcfba7adcda1b9e50a8..33cf7489c167764b716f3cee77ca7dae78011667 100644 (file)
@@ -4,7 +4,7 @@ require_once('include/security.php');
 require_once('include/bbcode.php');
 require_once('include/items.php');
 
-if(! function_exists('subthread_content')) {
+
 function subthread_content(&$a) {
 
        if(! local_user() && ! remote_user()) {
@@ -47,7 +47,7 @@ function subthread_content(&$a) {
                        $remote_owner = $r[0];
        }
 
-       // this represents the post owner on this system.
+       // this represents the post owner on this system. 
 
        $r = q("SELECT `contact`.*, `user`.`nickname` FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
                WHERE `contact`.`self` = 1 AND `contact`.`uid` = %d LIMIT 1",
@@ -107,6 +107,7 @@ EOT;
 
        $arr = array();
 
+       $arr['guid'] = get_guid(32);
        $arr['uri'] = $uri;
        $arr['uid'] = $owner_uid;
        $arr['contact-id'] = $contact['id'];
@@ -154,5 +155,7 @@ EOT;
        call_hooks('post_local_end', $arr);
 
        killme();
+
 }
-}
+
+
index 8f5f4f6a128b05cd20afea3083dc169ead318e5e..b73c2cd1b61eef34a248ff9e507d92b4182f419b 100644 (file)
@@ -3,7 +3,7 @@
 require_once('include/socgraph.php');
 require_once('include/contact_widgets.php');
 
-if(! function_exists('suggest_init')) {
+
 function suggest_init(&$a) {
        if(! local_user())
                return;
@@ -42,13 +42,13 @@ function suggest_init(&$a) {
                        );
                }
        }
-}
+
 }
 
 
 
 
-if(! function_exists('suggest_content')) {
+
 function suggest_content(&$a) {
 
        require_once("mod/proxy.php");
@@ -110,9 +110,8 @@ function suggest_content(&$a) {
        $o .= replace_macros($tpl,array(
                '$title' => t('Friend Suggestions'),
                '$contacts' => $entries,
-
+               
        ));
 
        return $o;
 }
-}
index bee37015ea464f3065a50ca32de5ccfe81db85b1..26166a3cc0458efb7885f3c8175521aaceb6bc72 100644 (file)
@@ -4,7 +4,7 @@ require_once('include/security.php');
 require_once('include/bbcode.php');
 require_once('include/items.php');
 
-if(! function_exists('tagger_content')) {
+
 function tagger_content(&$a) {
 
        if(! local_user() && ! remote_user()) {
@@ -101,6 +101,7 @@ EOT;
 
        $arr = array();
 
+       $arr['guid'] = get_guid(32);
        $arr['uri'] = $uri;
        $arr['uid'] = $owner_uid;
        $arr['contact-id'] = $contact['id'];
@@ -216,5 +217,5 @@ EOT;
 
        return; // NOTREACHED
 
-}
+
 }
index 70b3ef048f1be1bd2041beb4fbe1335ac51cf540..176986bc38e23cc195918b60b3f0e995e4b28fab 100644 (file)
@@ -2,7 +2,6 @@
 
 require_once('include/bbcode.php');
 
-if(! function_exists('tagrm_post')) {
 function tagrm_post(&$a) {
 
        if(! local_user())
@@ -41,13 +40,13 @@ function tagrm_post(&$a) {
 
        info( t('Tag removed') . EOL );
        goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']);
-
+       
        // NOTREACHED
-}
+
 }
 
 
-if(! function_exists('tagrm_content')) {
+
 function tagrm_content(&$a) {
 
        $o = '';
@@ -96,5 +95,5 @@ function tagrm_content(&$a) {
        $o .= '</form>';
 
        return $o;
-}
+       
 }
index dbf0996bba6766c1b2d0dd5677453f7da97bf9f6..00991e44ca04da87fb8b0c8664dedd55d30cf385 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 
-if(! function_exists('toggle_mobile_init')) {
 function toggle_mobile_init(&$a) {
 
        if(isset($_GET['off']))
@@ -15,4 +14,4 @@ function toggle_mobile_init(&$a) {
 
        goaway($address);
 }
-}
+
index eacf300f3f514baccffae6cfef0581f35a9144d9..3114add7e4a56ae325ce024b379fd232a053f9fe 100644 (file)
@@ -1,65 +1,14 @@
 <?php
 
-if(! function_exists('uexport_init')) {
-function uexport_init(&$a) {
+function uexport_init(&$a){
        if(! local_user())
                killme();
 
        require_once("mod/settings.php");
         settings_init($a);
-
-/*
-       $tabs = array(
-               array(
-                       'label' => t('Account settings'),
-                       'url'   => $a->get_baseurl(true).'/settings',
-                       'selected'      => '',
-               ),
-               array(
-                       'label' => t('Display settings'),
-                       'url'   => $a->get_baseurl(true).'/settings/display',
-                       'selected'      =>'',
-               ),
-
-               array(
-                       'label' => t('Connector settings'),
-                       'url'   => $a->get_baseurl(true).'/settings/connectors',
-                       'selected'      => '',
-               ),
-               array(
-                       'label' => t('Plugin settings'),
-                       'url'   => $a->get_baseurl(true).'/settings/addon',
-                       'selected'      => '',
-               ),
-               array(
-                       'label' => t('Connected apps'),
-                       'url' => $a->get_baseurl(true) . '/settings/oauth',
-                       'selected' => '',
-               ),
-               array(
-                       'label' => t('Export personal data'),
-                       'url' => $a->get_baseurl(true) . '/uexport',
-                       'selected' => 'active'
-               ),
-               array(
-                       'label' => t('Remove account'),
-                       'url' => $a->get_baseurl(true) . '/removeme',
-                       'selected' => ''
-               )
-       );
-
-       $tabtpl = get_markup_template("generic_links_widget.tpl");
-       $a->page['aside'] = replace_macros($tabtpl, array(
-               '$title' => t('Settings'),
-               '$class' => 'settings-widget',
-               '$items' => $tabs,
-       ));
-*/
-}
 }
 
-if(! function_exists('uexport_content')) {
-function uexport_content(&$a) {
+function uexport_content(&$a){
 
     if ($a->argc > 1) {
         header("Content-type: application/json");
@@ -77,8 +26,8 @@ function uexport_content(&$a) {
       * list of array( 'link url', 'link text', 'help text' )
       */
     $options = array(
-            array('/uexport/account',t('Export account'),t('Export your account info and contacts. Use this to make a backup of your account and/or to move it to another server.')),
-            array('/uexport/backup',t('Export all'),t('Export your accout info, contacts and all your items as json. Could be a very big file, and could take a lot of time. Use this to make a full backup of your account (photos are not exported)')),
+            array('uexport/account',t('Export account'),t('Export your account info and contacts. Use this to make a backup of your account and/or to move it to another server.')),
+            array('uexport/backup',t('Export all'),t('Export your accout info, contacts and all your items as json. Could be a very big file, and could take a lot of time. Use this to make a full backup of your account (photos are not exported)')),
     );
     call_hooks('uexport_options', $options);
 
@@ -89,10 +38,9 @@ function uexport_content(&$a) {
         '$options' => $options
     ));
 
-}
+
 }
 
-if(! function_exists('_uexport_multirow')) {
 function _uexport_multirow($query) {
        $result = array();
        $r = q($query);
@@ -107,9 +55,7 @@ function _uexport_multirow($query) {
        }
     return $result;
 }
-}
 
-if(! function_exists('_uexport_row')) {
 function _uexport_row($query) {
        $result = array();
        $r = q($query);
@@ -121,10 +67,9 @@ function _uexport_row($query) {
        }
     return $result;
 }
-}
 
-if(! function_exists('uexport_account')) {
-function uexport_account($a) {
+
+function uexport_account($a){
 
        $user = _uexport_row(
         sprintf( "SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", intval(local_user()) )
@@ -171,16 +116,15 @@ function uexport_account($a) {
 
     //echo "<pre>"; var_dump(json_encode($output)); killme();
        echo json_encode($output);
-}
+
 }
 
 /**
  * echoes account data and items as separated json, one per line
  */
-if(! function_exists('uexport_all')) {
 function uexport_all(&$a) {
 
-    uexport_account($a);
+       uexport_account($a);
        echo "\n";
 
        $r = q("SELECT count(*) as `total` FROM `item` WHERE `uid` = %d ",
@@ -207,5 +151,5 @@ function uexport_all(&$a) {
                $output = array('item' => $r);
                echo json_encode($output)."\n";
        }
-}
+
 }
index 942268b0ef0a89ac0cd523a70a74ef92c9658320..7ed5648d9e2d360de0cef3b85bb885ed9a903d66 100644 (file)
@@ -5,7 +5,6 @@
 \r
 require_once("include/uimport.php");\r
 \r
-if(! function_exists('uimport_post')) {\r
 function uimport_post(&$a) {\r
        switch($a->config['register_policy']) {\r
         case REGISTER_OPEN:\r
@@ -28,18 +27,16 @@ function uimport_post(&$a) {
             $verified = 0;\r
             break;\r
        }\r
-\r
+    \r
     if (x($_FILES,'accountfile')){\r
         /// @TODO Pass $blocked / $verified, send email to admin on REGISTER_APPROVE\r
         import_account($a, $_FILES['accountfile']);\r
         return;\r
     }\r
 }\r
-}\r
 \r
-if(! function_exists('uimport_content')) {\r
 function uimport_content(&$a) {\r
-\r
+       \r
        if((! local_user()) && ($a->config['register_policy'] == REGISTER_CLOSED)) {\r
                notice("Permission denied." . EOL);\r
                return;\r
@@ -54,8 +51,8 @@ function uimport_content(&$a) {
                        return;\r
                }\r
        }\r
-\r
-\r
+       \r
+       \r
        if(x($_SESSION,'theme'))\r
                unset($_SESSION['theme']);\r
        if(x($_SESSION,'mobile-theme'))\r
@@ -74,4 +71,3 @@ function uimport_content(&$a) {
         ),\r
     ));\r
 }\r
-}\r
index 396f4234c01e907761d79dae7a5268b1df7b9e55..512629b0052955fe647aa4b6f8678ee72eadccad 100644 (file)
@@ -4,7 +4,6 @@
 
 require_once('mod/community.php');
 
-if(! function_exists('update_community_content')) {
 function update_community_content(&$a) {
 
        header("Content-type: text/html");
@@ -30,5 +29,5 @@ function update_community_content(&$a) {
        echo "</section>";
        echo "</body></html>\r\n";
        killme();
-}
-}
+
+}
\ No newline at end of file
index 9400cb39a6f02ba01094d6a4f2e5730cca82cbd0..25b0f7792663a6cfdb0748c1503253e281ec7e2d 100644 (file)
@@ -5,7 +5,6 @@
 require_once('mod/display.php');
 require_once('include/group.php');
 
-if(! function_exists('update_display_content')) {
 function update_display_content(&$a) {
 
        $profile_uid = intval($_GET['p']);
@@ -35,5 +34,5 @@ function update_display_content(&$a) {
        echo "</section>";
        echo "</body></html>\r\n";
        killme();
-}
+
 }
index b2e7abc90cccabd23e64c724a7ecd6f70d17a22c..1bf374657573b5785e38622f0b716ff8bfcffd7b 100644 (file)
@@ -5,7 +5,6 @@
 require_once('mod/network.php');
 require_once('include/group.php');
 
-if(! function_exists('update_network_content')) {
 function update_network_content(&$a) {
 
        $profile_uid = intval($_GET['p']);
@@ -38,5 +37,5 @@ function update_network_content(&$a) {
        echo "</section>";
        echo "</body></html>\r\n";
        killme();
-}
+
 }
index e1e4f1d79550ef7496a01ff63dd38e9aabcd7724..6b8fff5115aa56ba62fbb70b60b330b5b3a70f74 100644 (file)
@@ -9,7 +9,6 @@
 
 require_once('mod/notes.php');
 
-if(! function_exists('update_notes_content')) {
 function update_notes_content(&$a) {
 
        $profile_uid = intval($_GET['p']);
@@ -21,8 +20,8 @@ function update_notes_content(&$a) {
 
        /**
         *
-        * Grab the page inner contents by calling the content function from the profile module directly,
-        * but move any image src attributes to another attribute name. This is because
+        * Grab the page inner contents by calling the content function from the profile module directly, 
+        * but move any image src attributes to another attribute name. This is because 
         * some browsers will prefetch all the images for the page even if we don't need them.
         * The only ones we need to fetch are those for new page additions, which we'll discover
         * on the client side and then swap the image back.
@@ -53,5 +52,5 @@ function update_notes_content(&$a) {
        echo "</section>";
        echo "</body></html>\r\n";
        killme();
-}
-}
+
+}
\ No newline at end of file
index 93a94ae0d8721928ecc9b4bee53ae2727cc4afd0..2492a48ee496d6714ce2513c5a68102fb21f76a4 100644 (file)
@@ -9,7 +9,6 @@
 
 require_once('mod/profile.php');
 
-if(! function_exists('update_profile_content')) {
 function update_profile_content(&$a) {
 
        $profile_uid = intval($_GET['p']);
@@ -25,8 +24,8 @@ function update_profile_content(&$a) {
 
        /**
         *
-        * Grab the page inner contents by calling the content function from the profile module directly,
-        * but move any image src attributes to another attribute name. This is because
+        * Grab the page inner contents by calling the content function from the profile module directly, 
+        * but move any image src attributes to another attribute name. This is because 
         * some browsers will prefetch all the images for the page even if we don't need them.
         * The only ones we need to fetch are those for new page additions, which we'll discover
         * on the client side and then swap the image back.
@@ -57,5 +56,5 @@ function update_profile_content(&$a) {
        echo "</section>";
        echo "</body></html>\r\n";
        killme();
-}
-}
+
+}
\ No newline at end of file
index f9db7b05b19fe5e5c526f36e2d7e9e2d9bbfacb7..bf8d696b60f90cceef1b14d825ba70151cde932f 100644 (file)
@@ -5,7 +5,7 @@ require_once('include/bbcode.php');
 require_once('include/security.php');
 require_once('include/redir.php');
 
-if(! function_exists('videos_init')) {
+
 function videos_init(&$a) {
 
        if($a->argc > 1)
@@ -102,9 +102,9 @@ function videos_init(&$a) {
 
        return;
 }
-}
 
-if(! function_exists('videos_post')) {
+
+
 function videos_post(&$a) {
 
        $owner_uid = $a->data['user']['uid'];
@@ -176,11 +176,11 @@ function videos_post(&$a) {
        }
 
     goaway($a->get_baseurl() . '/videos/' . $a->data['user']['nickname']);
+
 }
-}
 
 
-if(! function_exists('videos_content')) {
+
 function videos_content(&$a) {
 
        // URLs (most aren't currently implemented):
@@ -407,4 +407,4 @@ function videos_content(&$a) {
        $o .= paginate($a);
        return $o;
 }
-}
+
index a270baeaa1425f2faa850d1d21c662875412adf6..15b3733b3fc974f45dcb21d2c2bec7a3728b6ef3 100644 (file)
@@ -2,18 +2,16 @@
 /**
  * load view/theme/$current_theme/style.php with friendica contex
  */
-
-if(! function_exists('view_init')) {
-function view_init($a) {
+function view_init($a){
        header("Content-Type: text/css");
-
+               
        if ($a->argc == 4){
                $theme = $a->argv[2];
                $THEMEPATH = "view/theme/$theme";
                if(file_exists("view/theme/$theme/style.php"))
                        require_once("view/theme/$theme/style.php");
        }
-
+       
        killme();
 }
-}
index acb51f0cb4c53bf134ce2f0222b59dbb63eca6d8..04520e0d9309cc163ffe86c165625ef0e96233cd 100644 (file)
@@ -2,7 +2,6 @@
 require_once('include/Contact.php');
 require_once('include/contact_selectors.php');
 
-if(! function_exists('viewcontacts_init')) {
 function viewcontacts_init(&$a) {
 
        if((get_config('system','block_public')) && (! local_user()) && (! remote_user())) {
@@ -27,9 +26,8 @@ function viewcontacts_init(&$a) {
                profile_load($a,$a->argv[1]);
        }
 }
-}
 
-if(! function_exists('viewcontacts_content')) {
+
 function viewcontacts_content(&$a) {
        require_once("mod/proxy.php");
 
@@ -123,4 +121,3 @@ function viewcontacts_content(&$a) {
 
        return $o;
 }
-}
index 1203d18fc9dc2597bf2c1d6a64ce59e9a35c318c..3fa4eaed53bad2b53686a59aef0af30acf1f5645 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 
-if(! function_exists('viewsrc_content')) {
+
 function viewsrc_content(&$a) {
 
        if(! local_user()) {
@@ -16,7 +16,7 @@ function viewsrc_content(&$a) {
                return;
        }
 
-       $r = q("SELECT `item`.`body` FROM `item`
+       $r = q("SELECT `item`.`body` FROM `item` 
                WHERE `item`.`uid` = %d AND `item`.`visible` = 1 AND `item`.`deleted` = 0
                and `item`.`moderated` = 0
                AND `item`.`id` = '%s' LIMIT 1",
@@ -33,4 +33,4 @@ function viewsrc_content(&$a) {
                }
        return $o;
 }
-}
+
index 20e646cb9a0f59d0ba10f3fe311996252a056952..68752a0e1f659082e51ec6df90add56663d5ba1d 100644 (file)
@@ -3,7 +3,6 @@
 require_once('include/attach.php');
 require_once('include/datetime.php');
 
-if(! function_exists('wall_attach_post')) {
 function wall_attach_post(&$a) {
 
        $r_json = (x($_GET,'response') && $_GET['response']=='json');
@@ -191,4 +190,3 @@ function wall_attach_post(&$a) {
        killme();
        // NOTREACHED
 }
-}
index 2851807d572041af980aa64c57e33677fea6ef9e..b815348c7010baddd103553dc653f83da416ce86 100644 (file)
@@ -2,7 +2,6 @@
 
 require_once('include/Photo.php');
 
-if(! function_exists('wall_upload_post')) {
 function wall_upload_post(&$a, $desktopmode = true) {
 
        logger("wall upload: starting new upload", LOGGER_DEBUG);
@@ -298,4 +297,3 @@ function wall_upload_post(&$a, $desktopmode = true) {
        killme();
        // NOTREACHED
 }
-}
index a01dfd2b9f7fde6f4f518899456be6fe4b8fb10b..b8859badd3d30504f6ea7c57f914e3a9be31ad5d 100644 (file)
@@ -2,7 +2,6 @@
 
 require_once('include/message.php');
 
-if(! function_exists('wallmessage_post')) {
 function wallmessage_post(&$a) {
 
        $replyto = get_my_url();
@@ -49,7 +48,7 @@ function wallmessage_post(&$a) {
        $body = str_replace("\r\n","\n",$body);
        $body = str_replace("\n\n","\n",$body);
 
-
+       
        $ret = send_wallmessage($user, $body, $subject, $replyto);
 
        switch($ret){
@@ -70,10 +69,10 @@ function wallmessage_post(&$a) {
        }
 
 //     goaway($a->get_baseurl() . '/profile/' . $user['nickname']);
+       
 }
-}
 
-if(! function_exists('wallmessage_content')) {
+
 function wallmessage_content(&$a) {
 
        if(! get_my_url()) {
@@ -135,9 +134,9 @@ function wallmessage_content(&$a) {
                '$nickname' => $user['nickname'],
                '$linkurl' => t('Please enter a link URL:')
        ));
+       
 
-
-
+       
        $tpl = get_markup_template('wallmessage.tpl');
        $o .= replace_macros($tpl,array(
                '$header' => t('Send Private Message'),
@@ -159,4 +158,3 @@ function wallmessage_content(&$a) {
 
        return $o;
 }
-}
index 4024671b02f271c5a31956b58d54bb98afaf2259..74bd2c9543c6ceda125719b075f1fe08f7845c08 100644 (file)
@@ -1,13 +1,14 @@
 <?php
 
-if(! function_exists('webfinger_content')) {
+
+
 function webfinger_content(&$a) {
 
        $o .= '<h3>Webfinger Diagnostic</h3>';
 
        $o .= '<form action="webfinger" method="get">';
        $o .= 'Lookup address: <input type="text" style="width: 250px;" name="addr" value="' . $_GET['addr'] .'" />';
-       $o .= '<input type="submit" name="submit" value="Submit" /></form>';
+       $o .= '<input type="submit" name="submit" value="Submit" /></form>'; 
 
        $o .= '<br /><br />';
 
@@ -23,4 +24,3 @@ function webfinger_content(&$a) {
        }
        return $o;
 }
-}
index f8e0a9c4098fdf98d8265b7f9f9733f5a30762af..c23119145ca4d0685f0aab07ed549dd7df997f3d 100644 (file)
@@ -2,7 +2,6 @@
 
 require_once('include/crypto.php');
 
-if(! function_exists('xrd_init')) {
 function xrd_init(&$a) {
 
        $uri = urldecode(notags(trim($_GET['uri'])));
@@ -78,5 +77,5 @@ function xrd_init(&$a) {
 
        echo $arr['xml'];
        killme();
-}
+
 }
index 04c1a707e3a6e3eb6fef18ac4006dbe3252200c2..59659cdaff2e4816df0f42bcdfc4447ef10d51a3 100644 (file)
@@ -50,7 +50,7 @@ class Item extends BaseObject {
                $this->writable = ($this->get_data_value('writable') || $this->get_data_value('self'));
 
                $ssl_state = ((local_user()) ? true : false);
-               $this->redirect_url = $a->get_baseurl($ssl_state) . '/redir/' . $this->get_data_value('cid') ;
+               $this->redirect_url = 'redir/' . $this->get_data_value('cid') ;
 
                if(get_config('system','thread_allow') && $a->theme_thread_allow && !$this->is_toplevel())
                        $this->threaded = true;
@@ -119,9 +119,9 @@ class Item extends BaseObject {
                $shareable = ((($conv->get_profile_owner() == local_user()) && ($item['private'] != 1)) ? true : false);
                if(local_user() && link_compare($a->contact['url'],$item['author-link'])) {
                        if ($item["event-id"] != 0)
-                               $edpost = array($a->get_baseurl($ssl_state)."/events/event/".$item['event-id'], t("Edit"));
+                               $edpost = array("events/event/".$item['event-id'], t("Edit"));
                        else
-                               $edpost = array($a->get_baseurl($ssl_state)."/editpost/".$item['id'], t("Edit"));
+                               $edpost = array("editpost/".$item['id'], t("Edit"));
                } else
                        $edpost = false;
                if(($this->get_data_value('uid') == local_user()) || $this->is_visiting())
@@ -154,13 +154,13 @@ class Item extends BaseObject {
                if(($normalised != 'mailbox') && (x($a->contacts,$normalised)))
                        $profile_avatar = $a->contacts[$normalised]['thumb'];
                else
-                       $profile_avatar = (((strlen($item['author-avatar'])) && $diff_author) ? $item['author-avatar'] : $a->get_cached_avatar_image($this->get_data_value('thumb')));
+                       $profile_avatar = (((strlen($item['author-avatar'])) && $diff_author) ? $item['author-avatar'] : $a->remove_baseurl($this->get_data_value('thumb')));
 
                $locate = array('location' => $item['location'], 'coord' => $item['coord'], 'html' => '');
                call_hooks('render_location',$locate);
                $location = ((strlen($locate['html'])) ? $locate['html'] : render_location_dummy($locate));
 
-               $searchpath = $a->get_baseurl()."/search?tag=";
+               $searchpath = "search?tag=";
                $tags=array();
                $hashtags = array();
                $mentions = array();
@@ -324,7 +324,7 @@ class Item extends BaseObject {
 
                // Diaspora isn't able to do likes on comments - but red does
                if (($item["item_network"] == NETWORK_DIASPORA) AND ($indent == 'comment') AND
-                       !diaspora_is_redmatrix($item["owner-link"]) AND isset($buttons["like"]))
+                       !diaspora::is_redmatrix($item["owner-link"]) AND isset($buttons["like"]))
                        unset($buttons["like"]);
 
                // Diaspora doesn't has multithreaded comments
@@ -703,9 +703,9 @@ class Item extends BaseObject {
                                '$parent' => $this->get_id(),
                                '$qcomment' => $qcomment,
                                '$profile_uid' =>  $conv->get_profile_owner(),
-                               '$mylink' => $a->contact['url'],
+                               '$mylink' => $a->remove_baseurl($a->contact['url']),
                                '$mytitle' => t('This is you'),
-                               '$myphoto' => $a->contact['thumb'],
+                               '$myphoto' => $a->remove_baseurl($a->contact['thumb']),
                                '$comment' => t('Comment'),
                                '$submit' => t('Submit'),
                                '$edbold' => t('Bold'),
index 20a539215e3a7fa6d7a720a9acd2d4e191d7d7f4..fad1270d473275f1fdb0abcd4776a98fb50244e7 100644 (file)
@@ -10,7 +10,7 @@ Internationalisation
 
 extract.php - extracts translatable strings from our project files. It 
 currently doesn't pick up strings in other libraries we might be using such as 
-tinymce, simplepie, and the HTML parsers.
+tinymce and the HTML parsers.
 
 In order for extract to do its job, every use of the t() translation function 
 must be preceded by one space. The string also can not contain parentheses. If
diff --git a/util/createdoxygen.php b/util/createdoxygen.php
new file mode 100644 (file)
index 0000000..163c94b
--- /dev/null
@@ -0,0 +1,94 @@
+#!/usr/bin/php
+<?php
+/**
+ * @file util/createdoxygen.php
+ * @brief Adds a doxygen header to functions
+ */
+
+if (count($_SERVER["argv"]) < 2)
+       die("usage: createdoxygen.php file\n");
+
+$file = $_SERVER["argv"][1];
+$data = file_get_contents($file);
+
+$lines = explode("\n", $data);
+
+$previous = "";
+
+foreach ($lines AS $line) {
+       $line = rtrim(trim($line, "\r"));
+
+       if (strstr(strtolower($line), "function")) {
+               $detect = strtolower(trim($line));
+               $detect = implode(" ", explode(" ", $detect));
+
+               $found = false;
+
+               if (substr($detect, 0, 9) == "function ")
+                       $found = true;
+
+               if (substr($detect, 0, 17) == "private function ")
+                       $found = true;
+
+               if (substr($detect, 0, 23) == "public static function ")
+                       $found = true;
+
+               if (substr($detect, 0, 10) == "function (")
+                       $found = false;
+
+               if ($found and (trim($previous) == "*/"))
+                       $found = false;
+
+               if ($found and !strstr($detect, "{"))
+                       $found = false;
+
+               if ($found) {
+                       echo add_documentation($line);
+               }
+       }
+       echo $line."\n";
+       $previous = $line;
+}
+
+/**
+ * @brief Adds a doxygen header
+ *
+ * @param string $line The current line of the document
+ *
+ * @return string added doxygen header
+ */
+function add_documentation($line) {
+
+       $trimmed = ltrim($line);
+       $length = strlen($line) - strlen($trimmed);
+       $space = substr($line, 0, $length);
+
+       $block = $space."/**\n".
+               $space." * @brief \n".
+               $space." *\n"; /**/
+
+
+       $left = strpos($line, "(");
+       $line = substr($line, $left + 1);
+
+       $right = strpos($line, ")");
+       $line = trim(substr($line, 0, $right));
+
+       if ($line != "") {
+               $parameters = explode(",", $line);
+               foreach ($parameters AS $parameter) {
+                       $parameter = trim($parameter);
+                       $splitted = explode("=", $parameter);
+
+                       $block .= $space." * @param ".trim($splitted[0], "& ")."\n";
+               }
+               if (count($parameters) > 0)
+                       $block .= $space." *\n";
+       }
+
+       $block .= $space." * @return \n".
+               $space." */\n";
+
+       return $block;
+}
+?>
index 404b059133f8542fde7652eae157a275b1c53a3f..eacb8707f463eaff31d41bfdcf30f25e572d5ddf 100755 (executable)
@@ -46,17 +46,20 @@ for i in c:
 n1 = len(contributors)
 print('  > found %d contributors' % n1)
 #  get the contributors to the addons
-os.chdir(path+'/addon')
-#  get the contributors
-print('> getting contributors to the addons')
-p = subprocess.Popen(['git', 'shortlog', '--no-merges', '-s'],
-                         stdout=subprocess.PIPE,
-                         stderr=subprocess.STDOUT)
-c = iter(p.stdout.readline, b'')
-for i in c:
-    name = i.decode().split('\t')[1].split('\n')[0]
-    if not name in contributors and name not in dontinclude:
-        contributors.append(name)
+try:
+    os.chdir(path+'/addon')
+    #  get the contributors
+    print('> getting contributors to the addons')
+    p = subprocess.Popen(['git', 'shortlog', '--no-merges', '-s'],
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT)
+    c = iter(p.stdout.readline, b'')
+    for i in c:
+        name = i.decode().split('\t')[1].split('\n')[0]
+        if not name in contributors and name not in dontinclude:
+            contributors.append(name)
+except FileNotFoundError:
+    print('  > no addon directory found ( THE LIST OF CONTRIBUTORS WILL BE INCOMPLETE )')
 n2 = len(contributors)
 print('  > found %d new contributors' % (n2-n1))
 print('> total of %d contributors to the repositories of friendica' % n2)
index 46a9913f45cf6236838dc099fe60d6808f7309cd..df1dedd68ae84e44053fdbebfe97f5c25e7d6710 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-01-24 06:49+0100\n"
+"POT-Creation-Date: 2016-03-12 07:34+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,15 +18,15 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 
 
-#: mod/contacts.php:50 include/identity.php:395
+#: mod/contacts.php:50 include/identity.php:396
 msgid "Network:"
 msgstr ""
 
-#: mod/contacts.php:51 mod/contacts.php:961 mod/videos.php:37
+#: mod/contacts.php:51 mod/contacts.php:947 mod/videos.php:37
 #: mod/viewcontacts.php:105 mod/dirfind.php:214 mod/network.php:598
 #: mod/allfriends.php:77 mod/match.php:82 mod/directory.php:172
 #: mod/common.php:123 mod/suggest.php:95 mod/photos.php:41
-#: include/identity.php:298
+#: include/identity.php:299
 msgid "Forum"
 msgstr ""
 
@@ -37,7 +37,7 @@ msgid_plural "%d contacts edited."
 msgstr[0] ""
 msgstr[1] ""
 
-#: mod/contacts.php:159 mod/contacts.php:383
+#: mod/contacts.php:159 mod/contacts.php:368
 msgid "Could not access contact record."
 msgstr ""
 
@@ -49,150 +49,150 @@ msgstr ""
 msgid "Contact updated."
 msgstr ""
 
-#: mod/contacts.php:208 mod/dfrn_request.php:575
+#: mod/contacts.php:208 mod/dfrn_request.php:573
 msgid "Failed to update contact record."
 msgstr ""
 
-#: mod/contacts.php:365 mod/manage.php:96 mod/display.php:509
+#: mod/contacts.php:350 mod/manage.php:96 mod/display.php:513
 #: mod/profile_photo.php:19 mod/profile_photo.php:175 mod/profile_photo.php:186
 #: mod/profile_photo.php:199 mod/ostatus_subscribe.php:9 mod/follow.php:11
-#: mod/follow.php:73 mod/follow.php:155 mod/item.php:180 mod/item.php:192
+#: mod/follow.php:73 mod/follow.php:155 mod/item.php:183 mod/item.php:195
 #: mod/group.php:19 mod/dfrn_confirm.php:55 mod/fsuggest.php:78
 #: mod/wall_upload.php:77 mod/wall_upload.php:80 mod/viewcontacts.php:40
 #: mod/notifications.php:69 mod/message.php:45 mod/message.php:181
-#: mod/crepair.php:117 mod/dirfind.php:11 mod/nogroup.php:25 mod/network.php:4
+#: mod/crepair.php:100 mod/dirfind.php:11 mod/nogroup.php:25 mod/network.php:4
 #: mod/allfriends.php:12 mod/events.php:165 mod/wallmessage.php:9
 #: mod/wallmessage.php:33 mod/wallmessage.php:79 mod/wallmessage.php:103
 #: mod/wall_attach.php:67 mod/wall_attach.php:70 mod/settings.php:20
-#: mod/settings.php:126 mod/settings.php:646 mod/register.php:42
+#: mod/settings.php:126 mod/settings.php:647 mod/register.php:42
 #: mod/delegate.php:12 mod/common.php:18 mod/mood.php:114 mod/suggest.php:58
-#: mod/profiles.php:165 mod/profiles.php:615 mod/editpost.php:10 mod/api.php:26
+#: mod/profiles.php:165 mod/profiles.php:593 mod/editpost.php:10 mod/api.php:26
 #: mod/api.php:31 mod/notes.php:22 mod/poke.php:149 mod/repair_ostatus.php:9
-#: mod/invite.php:15 mod/invite.php:101 mod/photos.php:171 mod/photos.php:1105
+#: mod/invite.php:15 mod/invite.php:101 mod/photos.php:171 mod/photos.php:1091
 #: mod/regmod.php:110 mod/uimport.php:23 mod/attach.php:33
-#: include/items.php:5096 index.php:383
+#: include/items.php:2002 index.php:384
 msgid "Permission denied."
 msgstr ""
 
-#: mod/contacts.php:404
+#: mod/contacts.php:389
 msgid "Contact has been blocked"
 msgstr ""
 
-#: mod/contacts.php:404
+#: mod/contacts.php:389
 msgid "Contact has been unblocked"
 msgstr ""
 
-#: mod/contacts.php:415
+#: mod/contacts.php:400
 msgid "Contact has been ignored"
 msgstr ""
 
-#: mod/contacts.php:415
+#: mod/contacts.php:400
 msgid "Contact has been unignored"
 msgstr ""
 
-#: mod/contacts.php:427
+#: mod/contacts.php:412
 msgid "Contact has been archived"
 msgstr ""
 
-#: mod/contacts.php:427
+#: mod/contacts.php:412
 msgid "Contact has been unarchived"
 msgstr ""
 
-#: mod/contacts.php:454 mod/contacts.php:802
+#: mod/contacts.php:439 mod/contacts.php:794
 msgid "Do you really want to delete this contact?"
 msgstr ""
 
-#: mod/contacts.php:456 mod/follow.php:110 mod/message.php:216
-#: mod/settings.php:1103 mod/settings.php:1109 mod/settings.php:1117
-#: mod/settings.php:1121 mod/settings.php:1126 mod/settings.php:1132
-#: mod/settings.php:1138 mod/settings.php:1144 mod/settings.php:1170
-#: mod/settings.php:1171 mod/settings.php:1172 mod/settings.php:1173
-#: mod/settings.php:1174 mod/dfrn_request.php:857 mod/register.php:238
-#: mod/suggest.php:29 mod/profiles.php:658 mod/profiles.php:661
-#: mod/profiles.php:687 mod/api.php:105 include/items.php:4928
+#: mod/contacts.php:441 mod/follow.php:110 mod/message.php:216
+#: mod/settings.php:1107 mod/settings.php:1113 mod/settings.php:1121
+#: mod/settings.php:1125 mod/settings.php:1130 mod/settings.php:1136
+#: mod/settings.php:1142 mod/settings.php:1148 mod/settings.php:1174
+#: mod/settings.php:1175 mod/settings.php:1176 mod/settings.php:1177
+#: mod/settings.php:1178 mod/dfrn_request.php:855 mod/register.php:238
+#: mod/suggest.php:29 mod/profiles.php:636 mod/profiles.php:639
+#: mod/profiles.php:665 mod/api.php:105 include/items.php:1834
 msgid "Yes"
 msgstr ""
 
-#: mod/contacts.php:459 mod/tagrm.php:11 mod/tagrm.php:94 mod/follow.php:121
+#: mod/contacts.php:444 mod/tagrm.php:11 mod/tagrm.php:94 mod/follow.php:121
 #: mod/videos.php:131 mod/message.php:219 mod/fbrowser.php:93
-#: mod/fbrowser.php:128 mod/settings.php:660 mod/settings.php:686
-#: mod/dfrn_request.php:871 mod/suggest.php:32 mod/editpost.php:148
+#: mod/fbrowser.php:128 mod/settings.php:661 mod/settings.php:687
+#: mod/dfrn_request.php:869 mod/suggest.php:32 mod/editpost.php:148
 #: mod/photos.php:247 mod/photos.php:336 include/conversation.php:1220
-#: include/items.php:4931
+#: include/items.php:1837
 msgid "Cancel"
 msgstr ""
 
-#: mod/contacts.php:471
+#: mod/contacts.php:456
 msgid "Contact has been removed."
 msgstr ""
 
-#: mod/contacts.php:512
+#: mod/contacts.php:497
 #, php-format
 msgid "You are mutual friends with %s"
 msgstr ""
 
-#: mod/contacts.php:516
+#: mod/contacts.php:501
 #, php-format
 msgid "You are sharing with %s"
 msgstr ""
 
-#: mod/contacts.php:521
+#: mod/contacts.php:506
 #, php-format
 msgid "%s is sharing with you"
 msgstr ""
 
-#: mod/contacts.php:541
+#: mod/contacts.php:526
 msgid "Private communications are not available for this contact."
 msgstr ""
 
-#: mod/contacts.php:544 mod/admin.php:822
+#: mod/contacts.php:529 mod/admin.php:838
 msgid "Never"
 msgstr ""
 
-#: mod/contacts.php:548
+#: mod/contacts.php:533
 msgid "(Update was successful)"
 msgstr ""
 
-#: mod/contacts.php:548
+#: mod/contacts.php:533
 msgid "(Update was not successful)"
 msgstr ""
 
-#: mod/contacts.php:550
+#: mod/contacts.php:535 mod/contacts.php:972
 msgid "Suggest friends"
 msgstr ""
 
-#: mod/contacts.php:554
+#: mod/contacts.php:539
 #, php-format
 msgid "Network type: %s"
 msgstr ""
 
-#: mod/contacts.php:567
+#: mod/contacts.php:552
 msgid "Communications lost with this contact!"
 msgstr ""
 
-#: mod/contacts.php:570
+#: mod/contacts.php:555
 msgid "Fetch further information for feeds"
 msgstr ""
 
-#: mod/contacts.php:571 mod/admin.php:831
+#: mod/contacts.php:556 mod/admin.php:847
 msgid "Disabled"
 msgstr ""
 
-#: mod/contacts.php:571
+#: mod/contacts.php:556
 msgid "Fetch information"
 msgstr ""
 
-#: mod/contacts.php:571
+#: mod/contacts.php:556
 msgid "Fetch information and keywords"
 msgstr ""
 
-#: mod/contacts.php:587 mod/manage.php:143 mod/fsuggest.php:107
-#: mod/message.php:342 mod/message.php:525 mod/crepair.php:196
+#: mod/contacts.php:575 mod/manage.php:143 mod/fsuggest.php:107
+#: mod/message.php:342 mod/message.php:525 mod/crepair.php:179
 #: mod/events.php:574 mod/content.php:712 mod/install.php:261
-#: mod/install.php:299 mod/mood.php:137 mod/profiles.php:696
-#: mod/localtime.php:45 mod/poke.php:198 mod/invite.php:140 mod/photos.php:1137
-#: mod/photos.php:1261 mod/photos.php:1579 mod/photos.php:1630
-#: mod/photos.php:1678 mod/photos.php:1766 object/Item.php:710
+#: mod/install.php:299 mod/mood.php:137 mod/profiles.php:674
+#: mod/localtime.php:45 mod/poke.php:198 mod/invite.php:140 mod/photos.php:1123
+#: mod/photos.php:1247 mod/photos.php:1565 mod/photos.php:1616
+#: mod/photos.php:1664 mod/photos.php:1752 object/Item.php:710
 #: view/theme/cleanzero/config.php:80 view/theme/dispy/config.php:70
 #: view/theme/quattro/config.php:64 view/theme/diabook/config.php:148
 #: view/theme/diabook/theme.php:633 view/theme/vier/config.php:107
@@ -200,304 +200,314 @@ msgstr ""
 msgid "Submit"
 msgstr ""
 
-#: mod/contacts.php:588
+#: mod/contacts.php:576
 msgid "Profile Visibility"
 msgstr ""
 
-#: mod/contacts.php:589
+#: mod/contacts.php:577
 #, php-format
 msgid ""
 "Please choose the profile you would like to display to %s when viewing your "
 "profile securely."
 msgstr ""
 
-#: mod/contacts.php:590
+#: mod/contacts.php:578
 msgid "Contact Information / Notes"
 msgstr ""
 
-#: mod/contacts.php:591
+#: mod/contacts.php:579
 msgid "Edit contact notes"
 msgstr ""
 
-#: mod/contacts.php:596 mod/contacts.php:952 mod/viewcontacts.php:97
+#: mod/contacts.php:584 mod/contacts.php:938 mod/viewcontacts.php:97
 #: mod/nogroup.php:41
 #, php-format
 msgid "Visit %s's profile [%s]"
 msgstr ""
 
-#: mod/contacts.php:597
+#: mod/contacts.php:585
 msgid "Block/Unblock contact"
 msgstr ""
 
-#: mod/contacts.php:598
+#: mod/contacts.php:586
 msgid "Ignore contact"
 msgstr ""
 
-#: mod/contacts.php:599
+#: mod/contacts.php:587
 msgid "Repair URL settings"
 msgstr ""
 
-#: mod/contacts.php:600
+#: mod/contacts.php:588
 msgid "View conversations"
 msgstr ""
 
-#: mod/contacts.php:602
-msgid "Delete contact"
-msgstr ""
-
-#: mod/contacts.php:606
+#: mod/contacts.php:594
 msgid "Last update:"
 msgstr ""
 
-#: mod/contacts.php:608
+#: mod/contacts.php:596
 msgid "Update public posts"
 msgstr ""
 
-#: mod/contacts.php:610
+#: mod/contacts.php:598 mod/contacts.php:982
 msgid "Update now"
 msgstr ""
 
-#: mod/contacts.php:612 mod/follow.php:103 mod/dirfind.php:196
+#: mod/contacts.php:600 mod/follow.php:103 mod/dirfind.php:196
 #: mod/allfriends.php:65 mod/match.php:71 mod/suggest.php:82
-#: include/contact_widgets.php:32 include/Contact.php:297
+#: include/contact_widgets.php:32 include/Contact.php:299
 #: include/conversation.php:924
 msgid "Connect/Follow"
 msgstr ""
 
-#: mod/contacts.php:615 mod/contacts.php:806 mod/contacts.php:865
-#: mod/admin.php:1312
+#: mod/contacts.php:603 mod/contacts.php:798 mod/contacts.php:991
+#: mod/admin.php:1334
 msgid "Unblock"
 msgstr ""
 
-#: mod/contacts.php:615 mod/contacts.php:806 mod/contacts.php:865
-#: mod/admin.php:1311
+#: mod/contacts.php:603 mod/contacts.php:798 mod/contacts.php:991
+#: mod/admin.php:1333
 msgid "Block"
 msgstr ""
 
-#: mod/contacts.php:616 mod/contacts.php:807 mod/contacts.php:872
+#: mod/contacts.php:604 mod/contacts.php:799 mod/contacts.php:999
 msgid "Unignore"
 msgstr ""
 
-#: mod/contacts.php:616 mod/contacts.php:807 mod/contacts.php:872
+#: mod/contacts.php:604 mod/contacts.php:799 mod/contacts.php:999
 #: mod/notifications.php:54 mod/notifications.php:179 mod/notifications.php:259
 msgid "Ignore"
 msgstr ""
 
-#: mod/contacts.php:619
+#: mod/contacts.php:607
 msgid "Currently blocked"
 msgstr ""
 
-#: mod/contacts.php:620
+#: mod/contacts.php:608
 msgid "Currently ignored"
 msgstr ""
 
-#: mod/contacts.php:621
+#: mod/contacts.php:609
 msgid "Currently archived"
 msgstr ""
 
-#: mod/contacts.php:622 mod/notifications.php:172 mod/notifications.php:251
+#: mod/contacts.php:610 mod/notifications.php:172 mod/notifications.php:251
 msgid "Hide this contact from others"
 msgstr ""
 
-#: mod/contacts.php:622
+#: mod/contacts.php:610
 msgid ""
 "Replies/likes to your public posts <strong>may</strong> still be visible"
 msgstr ""
 
-#: mod/contacts.php:623
+#: mod/contacts.php:611
 msgid "Notification for new posts"
 msgstr ""
 
-#: mod/contacts.php:623
+#: mod/contacts.php:611
 msgid "Send a notification of every new post of this contact"
 msgstr ""
 
-#: mod/contacts.php:626
+#: mod/contacts.php:614
 msgid "Blacklisted keywords"
 msgstr ""
 
-#: mod/contacts.php:626
+#: mod/contacts.php:614
 msgid ""
 "Comma separated list of keywords that should not be converted to hashtags, "
 "when \"Fetch information and keywords\" is selected"
 msgstr ""
 
-#: mod/contacts.php:633 mod/follow.php:126 mod/notifications.php:255
+#: mod/contacts.php:621 mod/follow.php:126 mod/notifications.php:255
 msgid "Profile URL"
 msgstr ""
 
-#: mod/contacts.php:636 mod/notifications.php:244 mod/events.php:566
-#: mod/directory.php:145 include/identity.php:308 include/bb2diaspora.php:170
+#: mod/contacts.php:624 mod/notifications.php:244 mod/events.php:566
+#: mod/directory.php:145 include/identity.php:309 include/bb2diaspora.php:170
 #: include/event.php:36 include/event.php:60
 msgid "Location:"
 msgstr ""
 
-#: mod/contacts.php:638 mod/notifications.php:246 mod/directory.php:153
-#: include/identity.php:317 include/identity.php:631
+#: mod/contacts.php:626 mod/notifications.php:246 mod/directory.php:153
+#: include/identity.php:318 include/identity.php:632
 msgid "About:"
 msgstr ""
 
-#: mod/contacts.php:640 mod/follow.php:134 mod/notifications.php:248
-#: include/identity.php:625
+#: mod/contacts.php:628 mod/follow.php:134 mod/notifications.php:248
+#: include/identity.php:626
 msgid "Tags:"
 msgstr ""
 
-#: mod/contacts.php:685
+#: mod/contacts.php:629
+msgid "Actions"
+msgstr ""
+
+#: mod/contacts.php:631 mod/contacts.php:825 include/identity.php:687
+#: include/nav.php:75
+msgid "Status"
+msgstr ""
+
+#: mod/contacts.php:632
+msgid "Contact Settings"
+msgstr ""
+
+#: mod/contacts.php:677
 msgid "Suggestions"
 msgstr ""
 
-#: mod/contacts.php:688
+#: mod/contacts.php:680
 msgid "Suggest potential friends"
 msgstr ""
 
-#: mod/contacts.php:693 mod/group.php:192
+#: mod/contacts.php:685 mod/group.php:192
 msgid "All Contacts"
 msgstr ""
 
-#: mod/contacts.php:696
+#: mod/contacts.php:688
 msgid "Show all contacts"
 msgstr ""
 
-#: mod/contacts.php:701
+#: mod/contacts.php:693
 msgid "Unblocked"
 msgstr ""
 
-#: mod/contacts.php:704
+#: mod/contacts.php:696
 msgid "Only show unblocked contacts"
 msgstr ""
 
-#: mod/contacts.php:710
+#: mod/contacts.php:702
 msgid "Blocked"
 msgstr ""
 
-#: mod/contacts.php:713
+#: mod/contacts.php:705
 msgid "Only show blocked contacts"
 msgstr ""
 
-#: mod/contacts.php:719
+#: mod/contacts.php:711
 msgid "Ignored"
 msgstr ""
 
-#: mod/contacts.php:722
+#: mod/contacts.php:714
 msgid "Only show ignored contacts"
 msgstr ""
 
-#: mod/contacts.php:728
+#: mod/contacts.php:720
 msgid "Archived"
 msgstr ""
 
-#: mod/contacts.php:731
+#: mod/contacts.php:723
 msgid "Only show archived contacts"
 msgstr ""
 
-#: mod/contacts.php:737
+#: mod/contacts.php:729
 msgid "Hidden"
 msgstr ""
 
-#: mod/contacts.php:740
+#: mod/contacts.php:732
 msgid "Only show hidden contacts"
 msgstr ""
 
-#: mod/contacts.php:793 mod/contacts.php:841 mod/viewcontacts.php:116
-#: include/identity.php:741 include/identity.php:744 include/text.php:1012
+#: mod/contacts.php:785 mod/contacts.php:845 mod/viewcontacts.php:116
+#: include/identity.php:742 include/identity.php:745 include/text.php:983
 #: include/nav.php:123 include/nav.php:187 view/theme/diabook/theme.php:125
 msgid "Contacts"
 msgstr ""
 
-#: mod/contacts.php:797
+#: mod/contacts.php:789
 msgid "Search your contacts"
 msgstr ""
 
-#: mod/contacts.php:798
+#: mod/contacts.php:790
 msgid "Finding: "
 msgstr ""
 
-#: mod/contacts.php:799 mod/directory.php:210 include/contact_widgets.php:34
+#: mod/contacts.php:791 mod/directory.php:210 include/contact_widgets.php:34
 msgid "Find"
 msgstr ""
 
-#: mod/contacts.php:805 mod/settings.php:156 mod/settings.php:685
+#: mod/contacts.php:797 mod/settings.php:156 mod/settings.php:686
 msgid "Update"
 msgstr ""
 
-#: mod/contacts.php:808 mod/contacts.php:879
+#: mod/contacts.php:800 mod/contacts.php:1007
 msgid "Archive"
 msgstr ""
 
-#: mod/contacts.php:808 mod/contacts.php:879
+#: mod/contacts.php:800 mod/contacts.php:1007
 msgid "Unarchive"
 msgstr ""
 
-#: mod/contacts.php:809 mod/group.php:171 mod/admin.php:1310
-#: mod/content.php:440 mod/content.php:743 mod/settings.php:722
-#: mod/photos.php:1723 object/Item.php:134 include/conversation.php:635
+#: mod/contacts.php:801 mod/contacts.php:1015 mod/group.php:171
+#: mod/admin.php:1332 mod/content.php:440 mod/content.php:743
+#: mod/settings.php:723 mod/photos.php:1709 object/Item.php:134
+#: include/conversation.php:635
 msgid "Delete"
 msgstr ""
 
-#: mod/contacts.php:822 include/identity.php:686 include/nav.php:75
-msgid "Status"
-msgstr ""
-
-#: mod/contacts.php:825 mod/follow.php:143 include/identity.php:689
+#: mod/contacts.php:828 mod/follow.php:143 include/identity.php:690
 msgid "Status Messages and Posts"
 msgstr ""
 
-#: mod/contacts.php:830 mod/profperm.php:104 mod/newmember.php:32
-#: include/identity.php:579 include/identity.php:665 include/identity.php:694
+#: mod/contacts.php:833 mod/profperm.php:104 mod/newmember.php:32
+#: include/identity.php:580 include/identity.php:666 include/identity.php:695
 #: include/nav.php:76 view/theme/diabook/theme.php:124
 msgid "Profile"
 msgstr ""
 
-#: mod/contacts.php:833 include/identity.php:697
+#: mod/contacts.php:836 include/identity.php:698
 msgid "Profile Details"
 msgstr ""
 
-#: mod/contacts.php:844
+#: mod/contacts.php:848
 msgid "View all contacts"
 msgstr ""
 
-#: mod/contacts.php:850 mod/common.php:134
+#: mod/contacts.php:855 mod/common.php:134
 msgid "Common Friends"
 msgstr ""
 
-#: mod/contacts.php:853
+#: mod/contacts.php:858
 msgid "View all common friends"
 msgstr ""
 
-#: mod/contacts.php:857
-msgid "Repair"
+#: mod/contacts.php:862 mod/admin.php:909
+msgid "Advanced"
 msgstr ""
 
-#: mod/contacts.php:860
+#: mod/contacts.php:865
 msgid "Advanced Contact Settings"
 msgstr ""
 
-#: mod/contacts.php:868
-msgid "Toggle Blocked status"
+#: mod/contacts.php:910
+msgid "Mutual Friendship"
 msgstr ""
 
-#: mod/contacts.php:875
-msgid "Toggle Ignored status"
+#: mod/contacts.php:914
+msgid "is a fan of yours"
 msgstr ""
 
-#: mod/contacts.php:882
-msgid "Toggle Archive status"
+#: mod/contacts.php:918
+msgid "you are a fan of"
 msgstr ""
 
-#: mod/contacts.php:924
-msgid "Mutual Friendship"
+#: mod/contacts.php:939 mod/nogroup.php:42
+msgid "Edit contact"
 msgstr ""
 
-#: mod/contacts.php:928
-msgid "is a fan of yours"
+#: mod/contacts.php:993
+msgid "Toggle Blocked status"
 msgstr ""
 
-#: mod/contacts.php:932
-msgid "you are a fan of"
+#: mod/contacts.php:1001
+msgid "Toggle Ignored status"
 msgstr ""
 
-#: mod/contacts.php:953 mod/nogroup.php:42
-msgid "Edit contact"
+#: mod/contacts.php:1009
+msgid "Toggle Archive status"
+msgstr ""
+
+#: mod/contacts.php:1017
+msgid "Delete contact"
 msgstr ""
 
 #: mod/hcard.php:10
@@ -522,7 +532,7 @@ msgstr ""
 msgid "Post successful."
 msgstr ""
 
-#: mod/profperm.php:19 mod/group.php:72 index.php:382
+#: mod/profperm.php:19 mod/group.php:72 index.php:383
 msgid "Permission denied"
 msgstr ""
 
@@ -546,23 +556,23 @@ msgstr ""
 msgid "All Contacts (with secure profile access)"
 msgstr ""
 
-#: mod/display.php:82 mod/display.php:291 mod/display.php:513
-#: mod/viewsrc.php:15 mod/admin.php:234 mod/admin.php:1365 mod/admin.php:1599
-#: mod/notice.php:15 include/items.php:4887
+#: mod/display.php:82 mod/display.php:298 mod/display.php:517
+#: mod/viewsrc.php:15 mod/admin.php:234 mod/admin.php:1387 mod/admin.php:1621
+#: mod/notice.php:15 include/items.php:1793
 msgid "Item not found."
 msgstr ""
 
-#: mod/display.php:220 mod/videos.php:197 mod/viewcontacts.php:35
-#: mod/community.php:22 mod/dfrn_request.php:786 mod/search.php:93
-#: mod/search.php:99 mod/directory.php:37 mod/photos.php:976
+#: mod/display.php:227 mod/videos.php:197 mod/viewcontacts.php:35
+#: mod/community.php:22 mod/dfrn_request.php:784 mod/search.php:93
+#: mod/search.php:99 mod/directory.php:37 mod/photos.php:962
 msgid "Public access denied."
 msgstr ""
 
-#: mod/display.php:339 mod/profile.php:155
+#: mod/display.php:346 mod/profile.php:155
 msgid "Access to this profile has been restricted."
 msgstr ""
 
-#: mod/display.php:506
+#: mod/display.php:510
 msgid "Item has been removed."
 msgstr ""
 
@@ -597,7 +607,7 @@ msgid ""
 "join."
 msgstr ""
 
-#: mod/newmember.php:22 mod/admin.php:1418 mod/admin.php:1676
+#: mod/newmember.php:22 mod/admin.php:1440 mod/admin.php:1698
 #: mod/settings.php:109 include/nav.php:182 view/theme/diabook/theme.php:544
 #: view/theme/diabook/theme.php:648
 msgid "Settings"
@@ -622,7 +632,7 @@ msgid ""
 "potential friends know exactly how to find you."
 msgstr ""
 
-#: mod/newmember.php:36 mod/profile_photo.php:250 mod/profiles.php:709
+#: mod/newmember.php:36 mod/profile_photo.php:250 mod/profiles.php:687
 msgid "Upload Profile Photo"
 msgstr ""
 
@@ -705,7 +715,7 @@ msgid ""
 "hours."
 msgstr ""
 
-#: mod/newmember.php:61 include/group.php:283
+#: mod/newmember.php:61 include/group.php:286
 msgid "Groups"
 msgstr ""
 
@@ -765,8 +775,8 @@ msgstr ""
 #: mod/profile_photo.php:74 mod/profile_photo.php:81 mod/profile_photo.php:88
 #: mod/profile_photo.php:210 mod/profile_photo.php:302
 #: mod/profile_photo.php:311 mod/photos.php:78 mod/photos.php:192
-#: mod/photos.php:775 mod/photos.php:1245 mod/photos.php:1268
-#: mod/photos.php:1862 include/user.php:345 include/user.php:352
+#: mod/photos.php:769 mod/photos.php:1231 mod/photos.php:1254
+#: mod/photos.php:1848 include/user.php:345 include/user.php:352
 #: include/user.php:359 view/theme/diabook/theme.php:500
 msgid "Profile Photos"
 msgstr ""
@@ -787,12 +797,12 @@ msgstr ""
 msgid "Unable to process image"
 msgstr ""
 
-#: mod/profile_photo.php:150 mod/wall_upload.php:151 mod/photos.php:811
+#: mod/profile_photo.php:150 mod/wall_upload.php:151 mod/photos.php:805
 #, php-format
 msgid "Image exceeds size limit of %s"
 msgstr ""
 
-#: mod/profile_photo.php:159 mod/wall_upload.php:183 mod/photos.php:851
+#: mod/profile_photo.php:159 mod/wall_upload.php:188 mod/photos.php:845
 msgid "Unable to process image."
 msgstr ""
 
@@ -836,13 +846,13 @@ msgstr ""
 msgid "Image uploaded successfully."
 msgstr ""
 
-#: mod/profile_photo.php:307 mod/wall_upload.php:216 mod/photos.php:878
+#: mod/profile_photo.php:307 mod/wall_upload.php:221 mod/photos.php:872
 msgid "Image upload failed."
 msgstr ""
 
 #: mod/subthread.php:87 mod/tagger.php:62 include/like.php:165
 #: include/conversation.php:130 include/conversation.php:266
-#: include/text.php:2000 include/diaspora.php:2169
+#: include/text.php:1923 include/diaspora.php:2117
 #: view/theme/diabook/theme.php:471
 msgid "photo"
 msgstr ""
@@ -850,7 +860,7 @@ msgstr ""
 #: mod/subthread.php:87 mod/tagger.php:62 include/like.php:165
 #: include/like.php:334 include/conversation.php:125
 #: include/conversation.php:134 include/conversation.php:261
-#: include/conversation.php:270 include/diaspora.php:2169
+#: include/conversation.php:270 include/diaspora.php:2117
 #: view/theme/diabook/theme.php:466 view/theme/diabook/theme.php:475
 msgid "status"
 msgstr ""
@@ -920,11 +930,11 @@ msgstr ""
 msgid "- select -"
 msgstr ""
 
-#: mod/filer.php:31 mod/editpost.php:109 mod/notes.php:61 include/text.php:1004
+#: mod/filer.php:31 mod/editpost.php:109 mod/notes.php:61 include/text.php:975
 msgid "Save"
 msgstr ""
 
-#: mod/follow.php:19 mod/dfrn_request.php:870
+#: mod/follow.php:19 mod/dfrn_request.php:868
 msgid "Submit Request"
 msgstr ""
 
@@ -944,30 +954,30 @@ msgstr ""
 msgid "The network type couldn't be detected. Contact can't be added."
 msgstr ""
 
-#: mod/follow.php:109 mod/dfrn_request.php:856
+#: mod/follow.php:109 mod/dfrn_request.php:854
 msgid "Please answer the following:"
 msgstr ""
 
-#: mod/follow.php:110 mod/dfrn_request.php:857
+#: mod/follow.php:110 mod/dfrn_request.php:855
 #, php-format
 msgid "Does %s know you?"
 msgstr ""
 
-#: mod/follow.php:110 mod/settings.php:1103 mod/settings.php:1109
-#: mod/settings.php:1117 mod/settings.php:1121 mod/settings.php:1126
-#: mod/settings.php:1132 mod/settings.php:1138 mod/settings.php:1144
-#: mod/settings.php:1170 mod/settings.php:1171 mod/settings.php:1172
-#: mod/settings.php:1173 mod/settings.php:1174 mod/dfrn_request.php:857
-#: mod/register.php:239 mod/profiles.php:658 mod/profiles.php:662
-#: mod/profiles.php:687 mod/api.php:106
+#: mod/follow.php:110 mod/settings.php:1107 mod/settings.php:1113
+#: mod/settings.php:1121 mod/settings.php:1125 mod/settings.php:1130
+#: mod/settings.php:1136 mod/settings.php:1142 mod/settings.php:1148
+#: mod/settings.php:1174 mod/settings.php:1175 mod/settings.php:1176
+#: mod/settings.php:1177 mod/settings.php:1178 mod/dfrn_request.php:855
+#: mod/register.php:239 mod/profiles.php:636 mod/profiles.php:640
+#: mod/profiles.php:665 mod/api.php:106
 msgid "No"
 msgstr ""
 
-#: mod/follow.php:111 mod/dfrn_request.php:861
+#: mod/follow.php:111 mod/dfrn_request.php:859
 msgid "Add a personal note:"
 msgstr ""
 
-#: mod/follow.php:117 mod/dfrn_request.php:867
+#: mod/follow.php:117 mod/dfrn_request.php:865
 msgid "Your Identity Address:"
 msgstr ""
 
@@ -979,17 +989,17 @@ msgstr ""
 msgid "Unable to locate original post."
 msgstr ""
 
-#: mod/item.php:329
+#: mod/item.php:332
 msgid "Empty post discarded."
 msgstr ""
 
-#: mod/item.php:467 mod/wall_upload.php:213 mod/wall_upload.php:227
-#: mod/wall_upload.php:234 include/Photo.php:958 include/Photo.php:973
-#: include/Photo.php:980 include/Photo.php:1002 include/message.php:145
+#: mod/item.php:470 mod/wall_upload.php:218 mod/wall_upload.php:232
+#: mod/wall_upload.php:239 include/Photo.php:994 include/Photo.php:1009
+#: include/Photo.php:1016 include/Photo.php:1038 include/message.php:145
 msgid "Wall Photos"
 msgstr ""
 
-#: mod/item.php:842
+#: mod/item.php:845
 msgid "System error. Post not saved."
 msgstr ""
 
@@ -1039,7 +1049,7 @@ msgstr ""
 msgid "Create a group of contacts/friends."
 msgstr ""
 
-#: mod/group.php:94 mod/group.php:178 include/group.php:289
+#: mod/group.php:94 mod/group.php:178 include/group.php:292
 msgid "Group Name: "
 msgstr ""
 
@@ -1063,7 +1073,7 @@ msgstr ""
 msgid "Group is empty"
 msgstr ""
 
-#: mod/apps.php:7 index.php:226
+#: mod/apps.php:7 index.php:227
 msgid "You must be logged in to use addons. "
 msgstr ""
 
@@ -1076,12 +1086,12 @@ msgid "No installed applications."
 msgstr ""
 
 #: mod/dfrn_confirm.php:64 mod/profiles.php:18 mod/profiles.php:133
-#: mod/profiles.php:179 mod/profiles.php:627
+#: mod/profiles.php:179 mod/profiles.php:605
 msgid "Profile not found."
 msgstr ""
 
 #: mod/dfrn_confirm.php:120 mod/fsuggest.php:20 mod/fsuggest.php:92
-#: mod/crepair.php:131
+#: mod/crepair.php:114
 msgid "Contact not found."
 msgstr ""
 
@@ -1115,57 +1125,57 @@ msgstr ""
 msgid "Introduction failed or was revoked."
 msgstr ""
 
-#: mod/dfrn_confirm.php:430
+#: mod/dfrn_confirm.php:413
 msgid "Unable to set contact photo."
 msgstr ""
 
-#: mod/dfrn_confirm.php:487 include/conversation.php:185
-#: include/diaspora.php:637
+#: mod/dfrn_confirm.php:470 include/conversation.php:185
+#: include/diaspora.php:638
 #, php-format
 msgid "%1$s is now friends with %2$s"
 msgstr ""
 
-#: mod/dfrn_confirm.php:572
+#: mod/dfrn_confirm.php:552
 #, php-format
 msgid "No user record found for '%s' "
 msgstr ""
 
-#: mod/dfrn_confirm.php:582
+#: mod/dfrn_confirm.php:562
 msgid "Our site encryption key is apparently messed up."
 msgstr ""
 
-#: mod/dfrn_confirm.php:593
+#: mod/dfrn_confirm.php:573
 msgid "Empty site URL was provided or URL could not be decrypted by us."
 msgstr ""
 
-#: mod/dfrn_confirm.php:614
+#: mod/dfrn_confirm.php:594
 msgid "Contact record was not found for you on our site."
 msgstr ""
 
-#: mod/dfrn_confirm.php:628
+#: mod/dfrn_confirm.php:608
 #, php-format
 msgid "Site public key not available in contact record for URL %s."
 msgstr ""
 
-#: mod/dfrn_confirm.php:648
+#: mod/dfrn_confirm.php:628
 msgid ""
 "The ID provided by your system is a duplicate on our system. It should work "
 "if you try again."
 msgstr ""
 
-#: mod/dfrn_confirm.php:659
+#: mod/dfrn_confirm.php:639
 msgid "Unable to set your contact credentials on our system."
 msgstr ""
 
-#: mod/dfrn_confirm.php:726
+#: mod/dfrn_confirm.php:698
 msgid "Unable to update your contact profile details on our system"
 msgstr ""
 
-#: mod/dfrn_confirm.php:753 mod/dfrn_request.php:741 include/items.php:4299
+#: mod/dfrn_confirm.php:725 mod/dfrn_request.php:739 include/items.php:1434
 msgid "[Name Withheld]"
 msgstr ""
 
-#: mod/dfrn_confirm.php:798
+#: mod/dfrn_confirm.php:770
 #, php-format
 msgid "%1$s has joined %2$s"
 msgstr ""
@@ -1190,15 +1200,15 @@ msgstr ""
 msgid "No videos selected"
 msgstr ""
 
-#: mod/videos.php:308 mod/photos.php:1087
+#: mod/videos.php:308 mod/photos.php:1073
 msgid "Access to this item is restricted."
 msgstr ""
 
-#: mod/videos.php:383 include/text.php:1472
+#: mod/videos.php:383 include/text.php:1443
 msgid "View Video"
 msgstr ""
 
-#: mod/videos.php:390 mod/photos.php:1890
+#: mod/videos.php:390 mod/photos.php:1876
 msgid "View Album"
 msgstr ""
 
@@ -1230,7 +1240,7 @@ msgstr ""
 
 #: mod/wall_upload.php:20 mod/wall_upload.php:33 mod/wall_upload.php:86
 #: mod/wall_upload.php:122 mod/wall_upload.php:125 mod/wall_attach.php:17
-#: mod/wall_attach.php:25 mod/wall_attach.php:76 include/api.php:1781
+#: mod/wall_attach.php:25 mod/wall_attach.php:76
 msgid "Invalid request."
 msgstr ""
 
@@ -1288,7 +1298,7 @@ msgid ""
 "Password reset failed."
 msgstr ""
 
-#: mod/lostpass.php:109 boot.php:1444
+#: mod/lostpass.php:109 boot.php:1534
 msgid "Password Reset"
 msgstr ""
 
@@ -1364,15 +1374,15 @@ msgstr ""
 msgid "Reset"
 msgstr ""
 
-#: mod/ping.php:265
+#: mod/ping.php:267
 msgid "{0} wants to be your friend"
 msgstr ""
 
-#: mod/ping.php:280
+#: mod/ping.php:282
 msgid "{0} sent you a message"
 msgstr ""
 
-#: mod/ping.php:295
+#: mod/ping.php:297
 msgid "{0} requested registration"
 msgstr ""
 
@@ -1392,7 +1402,7 @@ msgstr ""
 msgid "System"
 msgstr ""
 
-#: mod/notifications.php:87 mod/admin.php:390 include/nav.php:154
+#: mod/notifications.php:87 mod/admin.php:399 include/nav.php:154
 msgid "Network"
 msgstr ""
 
@@ -1438,7 +1448,7 @@ msgstr ""
 msgid "if applicable"
 msgstr ""
 
-#: mod/notifications.php:176 mod/notifications.php:257 mod/admin.php:1308
+#: mod/notifications.php:176 mod/notifications.php:257 mod/admin.php:1330
 msgid "Approve"
 msgstr ""
 
@@ -1488,8 +1498,8 @@ msgstr ""
 msgid "New Follower"
 msgstr ""
 
-#: mod/notifications.php:250 mod/directory.php:147 include/identity.php:310
-#: include/identity.php:590
+#: mod/notifications.php:250 mod/directory.php:147 include/identity.php:311
+#: include/identity.php:591
 msgid "Gender:"
 msgstr ""
 
@@ -1538,11 +1548,11 @@ msgstr ""
 msgid "Network Notifications"
 msgstr ""
 
-#: mod/notifications.php:385 mod/notify.php:72
+#: mod/notifications.php:385 mod/notify.php:60
 msgid "No more system notifications."
 msgstr ""
 
-#: mod/notifications.php:389 mod/notify.php:76
+#: mod/notifications.php:389 mod/notify.php:64
 msgid "System Notifications"
 msgstr ""
 
@@ -1693,7 +1703,7 @@ msgstr ""
 
 #: mod/message.php:341 mod/message.php:526 mod/content.php:501
 #: mod/content.php:885 mod/wallmessage.php:156 mod/editpost.php:124
-#: mod/photos.php:1610 object/Item.php:396 include/conversation.php:713
+#: mod/photos.php:1596 object/Item.php:396 include/conversation.php:713
 #: include/conversation.php:1201
 msgid "Please wait"
 msgstr ""
@@ -1755,98 +1765,98 @@ msgstr[1] ""
 msgid "[Embedded content - reload page to view]"
 msgstr ""
 
-#: mod/crepair.php:104
+#: mod/crepair.php:87
 msgid "Contact settings applied."
 msgstr ""
 
-#: mod/crepair.php:106
+#: mod/crepair.php:89
 msgid "Contact update failed."
 msgstr ""
 
-#: mod/crepair.php:137
+#: mod/crepair.php:120
 msgid ""
 "<strong>WARNING: This is highly advanced</strong> and if you enter incorrect "
 "information your communications with this contact may stop working."
 msgstr ""
 
-#: mod/crepair.php:138
+#: mod/crepair.php:121
 msgid ""
 "Please use your browser 'Back' button <strong>now</strong> if you are "
 "uncertain what to do on this page."
 msgstr ""
 
-#: mod/crepair.php:151 mod/crepair.php:153
+#: mod/crepair.php:134 mod/crepair.php:136
 msgid "No mirroring"
 msgstr ""
 
-#: mod/crepair.php:151
+#: mod/crepair.php:134
 msgid "Mirror as forwarded posting"
 msgstr ""
 
-#: mod/crepair.php:151 mod/crepair.php:153
+#: mod/crepair.php:134 mod/crepair.php:136
 msgid "Mirror as my own posting"
 msgstr ""
 
-#: mod/crepair.php:167
+#: mod/crepair.php:150
 msgid "Return to contact editor"
 msgstr ""
 
-#: mod/crepair.php:169
+#: mod/crepair.php:152
 msgid "Refetch contact data"
 msgstr ""
 
-#: mod/crepair.php:170 mod/admin.php:1306 mod/admin.php:1318 mod/admin.php:1319
-#: mod/admin.php:1332 mod/settings.php:661 mod/settings.php:687
+#: mod/crepair.php:153 mod/admin.php:1328 mod/admin.php:1340 mod/admin.php:1341
+#: mod/admin.php:1354 mod/settings.php:662 mod/settings.php:688
 msgid "Name"
 msgstr ""
 
-#: mod/crepair.php:171
+#: mod/crepair.php:154
 msgid "Account Nickname"
 msgstr ""
 
-#: mod/crepair.php:172
+#: mod/crepair.php:155
 msgid "@Tagname - overrides Name/Nickname"
 msgstr ""
 
-#: mod/crepair.php:173
+#: mod/crepair.php:156
 msgid "Account URL"
 msgstr ""
 
-#: mod/crepair.php:174
+#: mod/crepair.php:157
 msgid "Friend Request URL"
 msgstr ""
 
-#: mod/crepair.php:175
+#: mod/crepair.php:158
 msgid "Friend Confirm URL"
 msgstr ""
 
-#: mod/crepair.php:176
+#: mod/crepair.php:159
 msgid "Notification Endpoint URL"
 msgstr ""
 
-#: mod/crepair.php:177
+#: mod/crepair.php:160
 msgid "Poll/Feed URL"
 msgstr ""
 
-#: mod/crepair.php:178
+#: mod/crepair.php:161
 msgid "New photo from this URL"
 msgstr ""
 
-#: mod/crepair.php:179
+#: mod/crepair.php:162
 msgid "Remote Self"
 msgstr ""
 
-#: mod/crepair.php:182
+#: mod/crepair.php:165
 msgid "Mirror postings from this contact"
 msgstr ""
 
-#: mod/crepair.php:184
+#: mod/crepair.php:167
 msgid ""
 "Mark this contact as remote_self, this will cause friendica to repost new "
 "entries from this contact."
 msgstr ""
 
-#: mod/bookmarklet.php:12 boot.php:1430 include/nav.php:91
+#: mod/bookmarklet.php:12 boot.php:1520 include/nav.php:91
 msgid "Login"
 msgstr ""
 
@@ -1864,8 +1874,8 @@ msgid "Connect"
 msgstr ""
 
 #: mod/dirfind.php:195 mod/allfriends.php:64 mod/match.php:70
-#: mod/directory.php:162 mod/suggest.php:81 include/Contact.php:283
-#: include/Contact.php:296 include/Contact.php:338 include/conversation.php:912
+#: mod/directory.php:162 mod/suggest.php:81 include/Contact.php:285
+#: include/Contact.php:298 include/Contact.php:340 include/conversation.php:912
 #: include/conversation.php:926
 msgid "View Profile"
 msgstr ""
@@ -1879,14 +1889,14 @@ msgstr ""
 msgid "No matches"
 msgstr ""
 
-#: mod/fbrowser.php:32 include/identity.php:702 include/nav.php:77
+#: mod/fbrowser.php:32 include/identity.php:703 include/nav.php:77
 #: view/theme/diabook/theme.php:126
 msgid "Photos"
 msgstr ""
 
 #: mod/fbrowser.php:41 mod/fbrowser.php:62 mod/photos.php:62 mod/photos.php:192
-#: mod/photos.php:1119 mod/photos.php:1245 mod/photos.php:1268
-#: mod/photos.php:1838 mod/photos.php:1850 view/theme/diabook/theme.php:499
+#: mod/photos.php:1105 mod/photos.php:1231 mod/photos.php:1254
+#: mod/photos.php:1824 mod/photos.php:1836 view/theme/diabook/theme.php:499
 msgid "Contact Photos"
 msgstr ""
 
@@ -1902,19 +1912,19 @@ msgstr ""
 msgid "Theme settings updated."
 msgstr ""
 
-#: mod/admin.php:156 mod/admin.php:888
+#: mod/admin.php:156 mod/admin.php:904
 msgid "Site"
 msgstr ""
 
-#: mod/admin.php:157 mod/admin.php:832 mod/admin.php:1301 mod/admin.php:1316
+#: mod/admin.php:157 mod/admin.php:848 mod/admin.php:1323 mod/admin.php:1338
 msgid "Users"
 msgstr ""
 
-#: mod/admin.php:158 mod/admin.php:1416 mod/admin.php:1476 mod/settings.php:72
+#: mod/admin.php:158 mod/admin.php:1438 mod/admin.php:1498 mod/settings.php:72
 msgid "Plugins"
 msgstr ""
 
-#: mod/admin.php:159 mod/admin.php:1674 mod/admin.php:1724
+#: mod/admin.php:159 mod/admin.php:1696 mod/admin.php:1746
 msgid "Themes"
 msgstr ""
 
@@ -1926,19 +1936,19 @@ msgstr ""
 msgid "DB updates"
 msgstr ""
 
-#: mod/admin.php:162 mod/admin.php:385
+#: mod/admin.php:162 mod/admin.php:394
 msgid "Inspect Queue"
 msgstr ""
 
-#: mod/admin.php:163 mod/admin.php:354
+#: mod/admin.php:163 mod/admin.php:363
 msgid "Federation Statistics"
 msgstr ""
 
-#: mod/admin.php:177 mod/admin.php:188 mod/admin.php:1792
+#: mod/admin.php:177 mod/admin.php:188 mod/admin.php:1814
 msgid "Logs"
 msgstr ""
 
-#: mod/admin.php:178 mod/admin.php:1859
+#: mod/admin.php:178 mod/admin.php:1881
 msgid "View Logs"
 msgstr ""
 
@@ -1966,739 +1976,750 @@ msgstr ""
 msgid "User registrations waiting for confirmation"
 msgstr ""
 
-#: mod/admin.php:347
+#: mod/admin.php:356
 msgid ""
 "This page offers you some numbers to the known part of the federated social "
 "network your Friendica node is part of. These numbers are not complete but "
 "only reflect the part of the network your node is aware of."
 msgstr ""
 
-#: mod/admin.php:348
+#: mod/admin.php:357
 msgid ""
 "The <em>Auto Discovered Contact Directory</em> feature is not enabled, it "
 "will improve the data displayed here."
 msgstr ""
 
-#: mod/admin.php:353 mod/admin.php:384 mod/admin.php:441 mod/admin.php:887
-#: mod/admin.php:1300 mod/admin.php:1415 mod/admin.php:1475 mod/admin.php:1673
-#: mod/admin.php:1723 mod/admin.php:1791 mod/admin.php:1858
+#: mod/admin.php:362 mod/admin.php:393 mod/admin.php:450 mod/admin.php:903
+#: mod/admin.php:1322 mod/admin.php:1437 mod/admin.php:1497 mod/admin.php:1695
+#: mod/admin.php:1745 mod/admin.php:1813 mod/admin.php:1880
 msgid "Administration"
 msgstr ""
 
-#: mod/admin.php:360
+#: mod/admin.php:369
 #, php-format
 msgid "Currently this node is aware of %d nodes from the following platforms:"
 msgstr ""
 
-#: mod/admin.php:387
+#: mod/admin.php:396
 msgid "ID"
 msgstr ""
 
-#: mod/admin.php:388
+#: mod/admin.php:397
 msgid "Recipient Name"
 msgstr ""
 
-#: mod/admin.php:389
+#: mod/admin.php:398
 msgid "Recipient Profile"
 msgstr ""
 
-#: mod/admin.php:391
+#: mod/admin.php:400
 msgid "Created"
 msgstr ""
 
-#: mod/admin.php:392
+#: mod/admin.php:401
 msgid "Last Tried"
 msgstr ""
 
-#: mod/admin.php:393
+#: mod/admin.php:402
 msgid ""
 "This page lists the content of the queue for outgoing postings. These are "
 "postings the initial delivery failed for. They will be resend later and "
 "eventually deleted if the delivery fails permanently."
 msgstr ""
 
-#: mod/admin.php:412 mod/admin.php:1254
+#: mod/admin.php:421 mod/admin.php:1276
 msgid "Normal Account"
 msgstr ""
 
-#: mod/admin.php:413 mod/admin.php:1255
+#: mod/admin.php:422 mod/admin.php:1277
 msgid "Soapbox Account"
 msgstr ""
 
-#: mod/admin.php:414 mod/admin.php:1256
+#: mod/admin.php:423 mod/admin.php:1278
 msgid "Community/Celebrity Account"
 msgstr ""
 
-#: mod/admin.php:415 mod/admin.php:1257
+#: mod/admin.php:424 mod/admin.php:1279
 msgid "Automatic Friend Account"
 msgstr ""
 
-#: mod/admin.php:416
+#: mod/admin.php:425
 msgid "Blog Account"
 msgstr ""
 
-#: mod/admin.php:417
+#: mod/admin.php:426
 msgid "Private Forum"
 msgstr ""
 
-#: mod/admin.php:436
+#: mod/admin.php:445
 msgid "Message queues"
 msgstr ""
 
-#: mod/admin.php:442
+#: mod/admin.php:451
 msgid "Summary"
 msgstr ""
 
-#: mod/admin.php:444
+#: mod/admin.php:453
 msgid "Registered users"
 msgstr ""
 
-#: mod/admin.php:446
+#: mod/admin.php:455
 msgid "Pending registrations"
 msgstr ""
 
-#: mod/admin.php:447
+#: mod/admin.php:456
 msgid "Version"
 msgstr ""
 
-#: mod/admin.php:452
+#: mod/admin.php:461
 msgid "Active plugins"
 msgstr ""
 
-#: mod/admin.php:475
+#: mod/admin.php:484
 msgid "Can not parse base url. Must have at least <scheme>://<domain>"
 msgstr ""
 
-#: mod/admin.php:760
+#: mod/admin.php:776
 msgid "RINO2 needs mcrypt php extension to work."
 msgstr ""
 
-#: mod/admin.php:768
+#: mod/admin.php:784
 msgid "Site settings updated."
 msgstr ""
 
-#: mod/admin.php:796 mod/settings.php:912
+#: mod/admin.php:812 mod/settings.php:916
 msgid "No special theme for mobile devices"
 msgstr ""
 
-#: mod/admin.php:815
+#: mod/admin.php:831
 msgid "No community page"
 msgstr ""
 
-#: mod/admin.php:816
+#: mod/admin.php:832
 msgid "Public postings from users of this site"
 msgstr ""
 
-#: mod/admin.php:817
+#: mod/admin.php:833
 msgid "Global community page"
 msgstr ""
 
-#: mod/admin.php:823
+#: mod/admin.php:839
 msgid "At post arrival"
 msgstr ""
 
-#: mod/admin.php:824 include/contact_selectors.php:56
+#: mod/admin.php:840 include/contact_selectors.php:56
 msgid "Frequently"
 msgstr ""
 
-#: mod/admin.php:825 include/contact_selectors.php:57
+#: mod/admin.php:841 include/contact_selectors.php:57
 msgid "Hourly"
 msgstr ""
 
-#: mod/admin.php:826 include/contact_selectors.php:58
+#: mod/admin.php:842 include/contact_selectors.php:58
 msgid "Twice daily"
 msgstr ""
 
-#: mod/admin.php:827 include/contact_selectors.php:59
+#: mod/admin.php:843 include/contact_selectors.php:59
 msgid "Daily"
 msgstr ""
 
-#: mod/admin.php:833
+#: mod/admin.php:849
 msgid "Users, Global Contacts"
 msgstr ""
 
-#: mod/admin.php:834
+#: mod/admin.php:850
 msgid "Users, Global Contacts/fallback"
 msgstr ""
 
-#: mod/admin.php:838
+#: mod/admin.php:854
 msgid "One month"
 msgstr ""
 
-#: mod/admin.php:839
+#: mod/admin.php:855
 msgid "Three months"
 msgstr ""
 
-#: mod/admin.php:840
+#: mod/admin.php:856
 msgid "Half a year"
 msgstr ""
 
-#: mod/admin.php:841
+#: mod/admin.php:857
 msgid "One year"
 msgstr ""
 
-#: mod/admin.php:846
+#: mod/admin.php:862
 msgid "Multi user instance"
 msgstr ""
 
-#: mod/admin.php:869
+#: mod/admin.php:885
 msgid "Closed"
 msgstr ""
 
-#: mod/admin.php:870
+#: mod/admin.php:886
 msgid "Requires approval"
 msgstr ""
 
-#: mod/admin.php:871
+#: mod/admin.php:887
 msgid "Open"
 msgstr ""
 
-#: mod/admin.php:875
+#: mod/admin.php:891
 msgid "No SSL policy, links will track page SSL state"
 msgstr ""
 
-#: mod/admin.php:876
+#: mod/admin.php:892
 msgid "Force all links to use SSL"
 msgstr ""
 
-#: mod/admin.php:877
+#: mod/admin.php:893
 msgid "Self-signed certificate, use SSL for local links only (discouraged)"
 msgstr ""
 
-#: mod/admin.php:889 mod/admin.php:1477 mod/admin.php:1725 mod/admin.php:1793
-#: mod/admin.php:1942 mod/settings.php:659 mod/settings.php:769
-#: mod/settings.php:813 mod/settings.php:882 mod/settings.php:969
-#: mod/settings.php:1204
+#: mod/admin.php:905 mod/admin.php:1499 mod/admin.php:1747 mod/admin.php:1815
+#: mod/admin.php:1964 mod/settings.php:660 mod/settings.php:770
+#: mod/settings.php:817 mod/settings.php:886 mod/settings.php:973
+#: mod/settings.php:1208
 msgid "Save Settings"
 msgstr ""
 
-#: mod/admin.php:890 mod/register.php:263
+#: mod/admin.php:906 mod/register.php:263
 msgid "Registration"
 msgstr ""
 
-#: mod/admin.php:891
+#: mod/admin.php:907
 msgid "File upload"
 msgstr ""
 
-#: mod/admin.php:892
+#: mod/admin.php:908
 msgid "Policies"
 msgstr ""
 
-#: mod/admin.php:893
-msgid "Advanced"
-msgstr ""
-
-#: mod/admin.php:894
+#: mod/admin.php:910
 msgid "Auto Discovered Contact Directory"
 msgstr ""
 
-#: mod/admin.php:895
+#: mod/admin.php:911
 msgid "Performance"
 msgstr ""
 
-#: mod/admin.php:896
+#: mod/admin.php:912
+msgid "Worker"
+msgstr ""
+
+#: mod/admin.php:913
 msgid ""
 "Relocate - WARNING: advanced function. Could make this server unreachable."
 msgstr ""
 
-#: mod/admin.php:899
+#: mod/admin.php:916
 msgid "Site name"
 msgstr ""
 
-#: mod/admin.php:900
+#: mod/admin.php:917
 msgid "Host name"
 msgstr ""
 
-#: mod/admin.php:901
+#: mod/admin.php:918
 msgid "Sender Email"
 msgstr ""
 
-#: mod/admin.php:901
+#: mod/admin.php:918
 msgid ""
 "The email address your server shall use to send notification emails from."
 msgstr ""
 
-#: mod/admin.php:902
+#: mod/admin.php:919
 msgid "Banner/Logo"
 msgstr ""
 
-#: mod/admin.php:903
+#: mod/admin.php:920
 msgid "Shortcut icon"
 msgstr ""
 
-#: mod/admin.php:903
+#: mod/admin.php:920
 msgid "Link to an icon that will be used for browsers."
 msgstr ""
 
-#: mod/admin.php:904
+#: mod/admin.php:921
 msgid "Touch icon"
 msgstr ""
 
-#: mod/admin.php:904
+#: mod/admin.php:921
 msgid "Link to an icon that will be used for tablets and mobiles."
 msgstr ""
 
-#: mod/admin.php:905
+#: mod/admin.php:922
 msgid "Additional Info"
 msgstr ""
 
-#: mod/admin.php:905
+#: mod/admin.php:922
 #, php-format
 msgid ""
 "For public servers: you can add additional information here that will be "
 "listed at %s/siteinfo."
 msgstr ""
 
-#: mod/admin.php:906
+#: mod/admin.php:923
 msgid "System language"
 msgstr ""
 
-#: mod/admin.php:907
+#: mod/admin.php:924
 msgid "System theme"
 msgstr ""
 
-#: mod/admin.php:907
+#: mod/admin.php:924
 msgid ""
 "Default system theme - may be over-ridden by user profiles - <a href='#' "
 "id='cnftheme'>change theme settings</a>"
 msgstr ""
 
-#: mod/admin.php:908
+#: mod/admin.php:925
 msgid "Mobile system theme"
 msgstr ""
 
-#: mod/admin.php:908
+#: mod/admin.php:925
 msgid "Theme for mobile devices"
 msgstr ""
 
-#: mod/admin.php:909
+#: mod/admin.php:926
 msgid "SSL link policy"
 msgstr ""
 
-#: mod/admin.php:909
+#: mod/admin.php:926
 msgid "Determines whether generated links should be forced to use SSL"
 msgstr ""
 
-#: mod/admin.php:910
+#: mod/admin.php:927
 msgid "Force SSL"
 msgstr ""
 
-#: mod/admin.php:910
+#: mod/admin.php:927
 msgid ""
 "Force all Non-SSL requests to SSL - Attention: on some systems it could lead "
 "to endless loops."
 msgstr ""
 
-#: mod/admin.php:911
+#: mod/admin.php:928
 msgid "Old style 'Share'"
 msgstr ""
 
-#: mod/admin.php:911
+#: mod/admin.php:928
 msgid "Deactivates the bbcode element 'share' for repeating items."
 msgstr ""
 
-#: mod/admin.php:912
+#: mod/admin.php:929
 msgid "Hide help entry from navigation menu"
 msgstr ""
 
-#: mod/admin.php:912
+#: mod/admin.php:929
 msgid ""
 "Hides the menu entry for the Help pages from the navigation menu. You can "
 "still access it calling /help directly."
 msgstr ""
 
-#: mod/admin.php:913
+#: mod/admin.php:930
 msgid "Single user instance"
 msgstr ""
 
-#: mod/admin.php:913
+#: mod/admin.php:930
 msgid "Make this instance multi-user or single-user for the named user"
 msgstr ""
 
-#: mod/admin.php:914
+#: mod/admin.php:931
 msgid "Maximum image size"
 msgstr ""
 
-#: mod/admin.php:914
+#: mod/admin.php:931
 msgid ""
 "Maximum size in bytes of uploaded images. Default is 0, which means no "
 "limits."
 msgstr ""
 
-#: mod/admin.php:915
+#: mod/admin.php:932
 msgid "Maximum image length"
 msgstr ""
 
-#: mod/admin.php:915
+#: mod/admin.php:932
 msgid ""
 "Maximum length in pixels of the longest side of uploaded images. Default is "
 "-1, which means no limits."
 msgstr ""
 
-#: mod/admin.php:916
+#: mod/admin.php:933
 msgid "JPEG image quality"
 msgstr ""
 
-#: mod/admin.php:916
+#: mod/admin.php:933
 msgid ""
 "Uploaded JPEGS will be saved at this quality setting [0-100]. Default is "
 "100, which is full quality."
 msgstr ""
 
-#: mod/admin.php:918
+#: mod/admin.php:935
 msgid "Register policy"
 msgstr ""
 
-#: mod/admin.php:919
+#: mod/admin.php:936
 msgid "Maximum Daily Registrations"
 msgstr ""
 
-#: mod/admin.php:919
+#: mod/admin.php:936
 msgid ""
 "If registration is permitted above, this sets the maximum number of new user "
 "registrations to accept per day.  If register is set to closed, this setting "
 "has no effect."
 msgstr ""
 
-#: mod/admin.php:920
+#: mod/admin.php:937
 msgid "Register text"
 msgstr ""
 
-#: mod/admin.php:920
+#: mod/admin.php:937
 msgid "Will be displayed prominently on the registration page."
 msgstr ""
 
-#: mod/admin.php:921
+#: mod/admin.php:938
 msgid "Accounts abandoned after x days"
 msgstr ""
 
-#: mod/admin.php:921
+#: mod/admin.php:938
 msgid ""
 "Will not waste system resources polling external sites for abandonded "
 "accounts. Enter 0 for no time limit."
 msgstr ""
 
-#: mod/admin.php:922
+#: mod/admin.php:939
 msgid "Allowed friend domains"
 msgstr ""
 
-#: mod/admin.php:922
+#: mod/admin.php:939
 msgid ""
 "Comma separated list of domains which are allowed to establish friendships "
 "with this site. Wildcards are accepted. Empty to allow any domains"
 msgstr ""
 
-#: mod/admin.php:923
+#: mod/admin.php:940
 msgid "Allowed email domains"
 msgstr ""
 
-#: mod/admin.php:923
+#: mod/admin.php:940
 msgid ""
 "Comma separated list of domains which are allowed in email addresses for "
 "registrations to this site. Wildcards are accepted. Empty to allow any "
 "domains"
 msgstr ""
 
-#: mod/admin.php:924
+#: mod/admin.php:941
 msgid "Block public"
 msgstr ""
 
-#: mod/admin.php:924
+#: mod/admin.php:941
 msgid ""
 "Check to block public access to all otherwise public personal pages on this "
 "site unless you are currently logged in."
 msgstr ""
 
-#: mod/admin.php:925
+#: mod/admin.php:942
 msgid "Force publish"
 msgstr ""
 
-#: mod/admin.php:925
+#: mod/admin.php:942
 msgid ""
 "Check to force all profiles on this site to be listed in the site directory."
 msgstr ""
 
-#: mod/admin.php:926
+#: mod/admin.php:943
 msgid "Global directory URL"
 msgstr ""
 
-#: mod/admin.php:926
+#: mod/admin.php:943
 msgid ""
 "URL to the global directory. If this is not set, the global directory is "
 "completely unavailable to the application."
 msgstr ""
 
-#: mod/admin.php:927
+#: mod/admin.php:944
 msgid "Allow threaded items"
 msgstr ""
 
-#: mod/admin.php:927
+#: mod/admin.php:944
 msgid "Allow infinite level threading for items on this site."
 msgstr ""
 
-#: mod/admin.php:928
+#: mod/admin.php:945
 msgid "Private posts by default for new users"
 msgstr ""
 
-#: mod/admin.php:928
+#: mod/admin.php:945
 msgid ""
 "Set default post permissions for all new members to the default privacy "
 "group rather than public."
 msgstr ""
 
-#: mod/admin.php:929
+#: mod/admin.php:946
 msgid "Don't include post content in email notifications"
 msgstr ""
 
-#: mod/admin.php:929
+#: mod/admin.php:946
 msgid ""
 "Don't include the content of a post/comment/private message/etc. in the "
 "email notifications that are sent out from this site, as a privacy measure."
 msgstr ""
 
-#: mod/admin.php:930
+#: mod/admin.php:947
 msgid "Disallow public access to addons listed in the apps menu."
 msgstr ""
 
-#: mod/admin.php:930
+#: mod/admin.php:947
 msgid ""
 "Checking this box will restrict addons listed in the apps menu to members "
 "only."
 msgstr ""
 
-#: mod/admin.php:931
+#: mod/admin.php:948
 msgid "Don't embed private images in posts"
 msgstr ""
 
-#: mod/admin.php:931
+#: mod/admin.php:948
 msgid ""
 "Don't replace locally-hosted private photos in posts with an embedded copy "
 "of the image. This means that contacts who receive posts containing private "
 "photos will have to authenticate and load each image, which may take a while."
 msgstr ""
 
-#: mod/admin.php:932
+#: mod/admin.php:949
 msgid "Allow Users to set remote_self"
 msgstr ""
 
-#: mod/admin.php:932
+#: mod/admin.php:949
 msgid ""
 "With checking this, every user is allowed to mark every contact as a "
 "remote_self in the repair contact dialog. Setting this flag on a contact "
 "causes mirroring every posting of that contact in the users stream."
 msgstr ""
 
-#: mod/admin.php:933
+#: mod/admin.php:950
 msgid "Block multiple registrations"
 msgstr ""
 
-#: mod/admin.php:933
+#: mod/admin.php:950
 msgid "Disallow users to register additional accounts for use as pages."
 msgstr ""
 
-#: mod/admin.php:934
+#: mod/admin.php:951
 msgid "OpenID support"
 msgstr ""
 
-#: mod/admin.php:934
+#: mod/admin.php:951
 msgid "OpenID support for registration and logins."
 msgstr ""
 
-#: mod/admin.php:935
+#: mod/admin.php:952
 msgid "Fullname check"
 msgstr ""
 
-#: mod/admin.php:935
+#: mod/admin.php:952
 msgid ""
 "Force users to register with a space between firstname and lastname in Full "
 "name, as an antispam measure"
 msgstr ""
 
-#: mod/admin.php:936
+#: mod/admin.php:953
 msgid "UTF-8 Regular expressions"
 msgstr ""
 
-#: mod/admin.php:936
+#: mod/admin.php:953
 msgid "Use PHP UTF8 regular expressions"
 msgstr ""
 
-#: mod/admin.php:937
+#: mod/admin.php:954
 msgid "Community Page Style"
 msgstr ""
 
-#: mod/admin.php:937
+#: mod/admin.php:954
 msgid ""
 "Type of community page to show. 'Global community' shows every public "
 "posting from an open distributed network that arrived on this server."
 msgstr ""
 
-#: mod/admin.php:938
+#: mod/admin.php:955
 msgid "Posts per user on community page"
 msgstr ""
 
-#: mod/admin.php:938
+#: mod/admin.php:955
 msgid ""
 "The maximum number of posts per user on the community page. (Not valid for "
 "'Global Community')"
 msgstr ""
 
-#: mod/admin.php:939
+#: mod/admin.php:956
 msgid "Enable OStatus support"
 msgstr ""
 
-#: mod/admin.php:939
+#: mod/admin.php:956
 msgid ""
 "Provide built-in OStatus (StatusNet, GNU Social etc.) compatibility. All "
 "communications in OStatus are public, so privacy warnings will be "
 "occasionally displayed."
 msgstr ""
 
-#: mod/admin.php:940
+#: mod/admin.php:957
 msgid "OStatus conversation completion interval"
 msgstr ""
 
-#: mod/admin.php:940
+#: mod/admin.php:957
 msgid ""
 "How often shall the poller check for new entries in OStatus conversations? "
 "This can be a very ressource task."
 msgstr ""
 
-#: mod/admin.php:941
+#: mod/admin.php:958
+msgid "Only import OStatus threads from our contacts"
+msgstr ""
+
+#: mod/admin.php:958
+msgid ""
+"Normally we import every content from our OStatus contacts. With this option "
+"we only store threads that are started by a contact that is known on our "
+"system."
+msgstr ""
+
+#: mod/admin.php:959
 msgid "OStatus support can only be enabled if threading is enabled."
 msgstr ""
 
-#: mod/admin.php:943
+#: mod/admin.php:961
 msgid ""
 "Diaspora support can't be enabled because Friendica was installed into a sub "
 "directory."
 msgstr ""
 
-#: mod/admin.php:944
+#: mod/admin.php:962
 msgid "Enable Diaspora support"
 msgstr ""
 
-#: mod/admin.php:944
+#: mod/admin.php:962
 msgid "Provide built-in Diaspora network compatibility."
 msgstr ""
 
-#: mod/admin.php:945
+#: mod/admin.php:963
 msgid "Only allow Friendica contacts"
 msgstr ""
 
-#: mod/admin.php:945
+#: mod/admin.php:963
 msgid ""
 "All contacts must use Friendica protocols. All other built-in communication "
 "protocols disabled."
 msgstr ""
 
-#: mod/admin.php:946
+#: mod/admin.php:964
 msgid "Verify SSL"
 msgstr ""
 
-#: mod/admin.php:946
+#: mod/admin.php:964
 msgid ""
 "If you wish, you can turn on strict certificate checking. This will mean you "
 "cannot connect (at all) to self-signed SSL sites."
 msgstr ""
 
-#: mod/admin.php:947
+#: mod/admin.php:965
 msgid "Proxy user"
 msgstr ""
 
-#: mod/admin.php:948
+#: mod/admin.php:966
 msgid "Proxy URL"
 msgstr ""
 
-#: mod/admin.php:949
+#: mod/admin.php:967
 msgid "Network timeout"
 msgstr ""
 
-#: mod/admin.php:949
+#: mod/admin.php:967
 msgid "Value is in seconds. Set to 0 for unlimited (not recommended)."
 msgstr ""
 
-#: mod/admin.php:950
+#: mod/admin.php:968
 msgid "Delivery interval"
 msgstr ""
 
-#: mod/admin.php:950
+#: mod/admin.php:968
 msgid ""
 "Delay background delivery processes by this many seconds to reduce system "
 "load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 "
 "for large dedicated servers."
 msgstr ""
 
-#: mod/admin.php:951
+#: mod/admin.php:969
 msgid "Poll interval"
 msgstr ""
 
-#: mod/admin.php:951
+#: mod/admin.php:969
 msgid ""
 "Delay background polling processes by this many seconds to reduce system "
 "load. If 0, use delivery interval."
 msgstr ""
 
-#: mod/admin.php:952
+#: mod/admin.php:970
 msgid "Maximum Load Average"
 msgstr ""
 
-#: mod/admin.php:952
+#: mod/admin.php:970
 msgid ""
 "Maximum system load before delivery and poll processes are deferred - "
 "default 50."
 msgstr ""
 
-#: mod/admin.php:953
+#: mod/admin.php:971
 msgid "Maximum Load Average (Frontend)"
 msgstr ""
 
-#: mod/admin.php:953
+#: mod/admin.php:971
 msgid "Maximum system load before the frontend quits service - default 50."
 msgstr ""
 
-#: mod/admin.php:954
+#: mod/admin.php:972
 msgid "Maximum table size for optimization"
 msgstr ""
 
-#: mod/admin.php:954
+#: mod/admin.php:972
 msgid ""
 "Maximum table size (in MB) for the automatic optimization - default 100 MB. "
 "Enter -1 to disable it."
 msgstr ""
 
-#: mod/admin.php:955
+#: mod/admin.php:973
 msgid "Minimum level of fragmentation"
 msgstr ""
 
-#: mod/admin.php:955
+#: mod/admin.php:973
 msgid ""
 "Minimum fragmenation level to start the automatic optimization - default "
 "value is 30%."
 msgstr ""
 
-#: mod/admin.php:957
+#: mod/admin.php:975
 msgid "Periodical check of global contacts"
 msgstr ""
 
-#: mod/admin.php:957
+#: mod/admin.php:975
 msgid ""
 "If enabled, the global contacts are checked periodically for missing or "
 "outdated data and the vitality of the contacts and servers."
 msgstr ""
 
-#: mod/admin.php:958
+#: mod/admin.php:976
 msgid "Days between requery"
 msgstr ""
 
-#: mod/admin.php:958
+#: mod/admin.php:976
 msgid "Number of days after which a server is requeried for his contacts."
 msgstr ""
 
-#: mod/admin.php:959
+#: mod/admin.php:977
 msgid "Discover contacts from other servers"
 msgstr ""
 
-#: mod/admin.php:959
+#: mod/admin.php:977
 msgid ""
 "Periodically query other servers for contacts. You can choose between "
 "'users': the users on the remote system, 'Global Contacts': active contacts "
@@ -2708,32 +2729,32 @@ msgid ""
 "Global Contacts'."
 msgstr ""
 
-#: mod/admin.php:960
+#: mod/admin.php:978
 msgid "Timeframe for fetching global contacts"
 msgstr ""
 
-#: mod/admin.php:960
+#: mod/admin.php:978
 msgid ""
 "When the discovery is activated, this value defines the timeframe for the "
 "activity of the global contacts that are fetched from other servers."
 msgstr ""
 
-#: mod/admin.php:961
+#: mod/admin.php:979
 msgid "Search the local directory"
 msgstr ""
 
-#: mod/admin.php:961
+#: mod/admin.php:979
 msgid ""
 "Search the local directory instead of the global directory. When searching "
 "locally, every search will be executed on the global directory in the "
 "background. This improves the search results when the search is repeated."
 msgstr ""
 
-#: mod/admin.php:963
+#: mod/admin.php:981
 msgid "Publish server information"
 msgstr ""
 
-#: mod/admin.php:963
+#: mod/admin.php:981
 msgid ""
 "If enabled, general server and usage data will be published. The data "
 "contains the name and version of the server, number of users with public "
@@ -2741,204 +2762,235 @@ msgid ""
 "href='http://the-federation.info/'>the-federation.info</a> for details."
 msgstr ""
 
-#: mod/admin.php:965
+#: mod/admin.php:983
 msgid "Use MySQL full text engine"
 msgstr ""
 
-#: mod/admin.php:965
+#: mod/admin.php:983
 msgid ""
 "Activates the full text engine. Speeds up search - but can only search for "
 "four and more characters."
 msgstr ""
 
-#: mod/admin.php:966
+#: mod/admin.php:984
 msgid "Suppress Language"
 msgstr ""
 
-#: mod/admin.php:966
+#: mod/admin.php:984
 msgid "Suppress language information in meta information about a posting."
 msgstr ""
 
-#: mod/admin.php:967
+#: mod/admin.php:985
 msgid "Suppress Tags"
 msgstr ""
 
-#: mod/admin.php:967
+#: mod/admin.php:985
 msgid "Suppress showing a list of hashtags at the end of the posting."
 msgstr ""
 
-#: mod/admin.php:968
+#: mod/admin.php:986
 msgid "Path to item cache"
 msgstr ""
 
-#: mod/admin.php:968
+#: mod/admin.php:986
 msgid "The item caches buffers generated bbcode and external images."
 msgstr ""
 
-#: mod/admin.php:969
+#: mod/admin.php:987
 msgid "Cache duration in seconds"
 msgstr ""
 
-#: mod/admin.php:969
+#: mod/admin.php:987
 msgid ""
 "How long should the cache files be hold? Default value is 86400 seconds (One "
 "day). To disable the item cache, set the value to -1."
 msgstr ""
 
-#: mod/admin.php:970
+#: mod/admin.php:988
 msgid "Maximum numbers of comments per post"
 msgstr ""
 
-#: mod/admin.php:970
+#: mod/admin.php:988
 msgid "How much comments should be shown for each post? Default value is 100."
 msgstr ""
 
-#: mod/admin.php:971
+#: mod/admin.php:989
 msgid "Path for lock file"
 msgstr ""
 
-#: mod/admin.php:971
+#: mod/admin.php:989
 msgid ""
 "The lock file is used to avoid multiple pollers at one time. Only define a "
 "folder here."
 msgstr ""
 
-#: mod/admin.php:972
+#: mod/admin.php:990
 msgid "Temp path"
 msgstr ""
 
-#: mod/admin.php:972
+#: mod/admin.php:990
 msgid ""
 "If you have a restricted system where the webserver can't access the system "
 "temp path, enter another path here."
 msgstr ""
 
-#: mod/admin.php:973
+#: mod/admin.php:991
 msgid "Base path to installation"
 msgstr ""
 
-#: mod/admin.php:973
+#: mod/admin.php:991
 msgid ""
 "If the system cannot detect the correct path to your installation, enter the "
 "correct path here. This setting should only be set if you are using a "
 "restricted system and symbolic links to your webroot."
 msgstr ""
 
-#: mod/admin.php:974
+#: mod/admin.php:992
 msgid "Disable picture proxy"
 msgstr ""
 
-#: mod/admin.php:974
+#: mod/admin.php:992
 msgid ""
 "The picture proxy increases performance and privacy. It shouldn't be used on "
 "systems with very low bandwith."
 msgstr ""
 
-#: mod/admin.php:975
+#: mod/admin.php:993
 msgid "Enable old style pager"
 msgstr ""
 
-#: mod/admin.php:975
+#: mod/admin.php:993
 msgid ""
 "The old style pager has page numbers but slows down massively the page speed."
 msgstr ""
 
-#: mod/admin.php:976
+#: mod/admin.php:994
 msgid "Only search in tags"
 msgstr ""
 
-#: mod/admin.php:976
+#: mod/admin.php:994
 msgid "On large systems the text search can slow down the system extremely."
 msgstr ""
 
-#: mod/admin.php:978
-msgid "New base url"
+#: mod/admin.php:996
+msgid "New base url"
+msgstr ""
+
+#: mod/admin.php:996
+msgid ""
+"Change base url for this server. Sends relocate message to all DFRN contacts "
+"of all users."
+msgstr ""
+
+#: mod/admin.php:998
+msgid "RINO Encryption"
+msgstr ""
+
+#: mod/admin.php:998
+msgid "Encryption layer between nodes."
+msgstr ""
+
+#: mod/admin.php:999
+msgid "Embedly API key"
+msgstr ""
+
+#: mod/admin.php:999
+msgid ""
+"<a href='http://embed.ly'>Embedly</a> is used to fetch additional data for "
+"web pages. This is an optional parameter."
+msgstr ""
+
+#: mod/admin.php:1001
+msgid "Enable 'worker' background processing"
 msgstr ""
 
-#: mod/admin.php:978
+#: mod/admin.php:1001
 msgid ""
-"Change base url for this server. Sends relocate message to all DFRN contacts "
-"of all users."
+"The worker background processing limits the number of parallel background "
+"jobs to a maximum number and respects the system load."
 msgstr ""
 
-#: mod/admin.php:980
-msgid "RINO Encryption"
+#: mod/admin.php:1002
+msgid "Maximum number of parallel workers"
 msgstr ""
 
-#: mod/admin.php:980
-msgid "Encryption layer between nodes."
+#: mod/admin.php:1002
+msgid ""
+"On shared hosters set this to 2. On larger systems, values of 10 are great. "
+"Default value is 4."
 msgstr ""
 
-#: mod/admin.php:981
-msgid "Embedly API key"
+#: mod/admin.php:1003
+msgid "Don't use 'proc_open' with the worker"
 msgstr ""
 
-#: mod/admin.php:981
+#: mod/admin.php:1003
 msgid ""
-"<a href='http://embed.ly'>Embedly</a> is used to fetch additional data for "
-"web pages. This is an optional parameter."
+"Enable this if your system doesn't allow the use of 'proc_open'. This can "
+"happen on shared hosters. If this is enabled you should increase the "
+"frequency of poller calls in your crontab."
 msgstr ""
 
-#: mod/admin.php:1010
+#: mod/admin.php:1032
 msgid "Update has been marked successful"
 msgstr ""
 
-#: mod/admin.php:1018
+#: mod/admin.php:1040
 #, php-format
 msgid "Database structure update %s was successfully applied."
 msgstr ""
 
-#: mod/admin.php:1021
+#: mod/admin.php:1043
 #, php-format
 msgid "Executing of database structure update %s failed with error: %s"
 msgstr ""
 
-#: mod/admin.php:1033
+#: mod/admin.php:1055
 #, php-format
 msgid "Executing %s failed with error: %s"
 msgstr ""
 
-#: mod/admin.php:1036
+#: mod/admin.php:1058
 #, php-format
 msgid "Update %s was successfully applied."
 msgstr ""
 
-#: mod/admin.php:1040
+#: mod/admin.php:1062
 #, php-format
 msgid "Update %s did not return a status. Unknown if it succeeded."
 msgstr ""
 
-#: mod/admin.php:1042
+#: mod/admin.php:1064
 #, php-format
 msgid "There was no additional update function %s that needed to be called."
 msgstr ""
 
-#: mod/admin.php:1061
+#: mod/admin.php:1083
 msgid "No failed updates."
 msgstr ""
 
-#: mod/admin.php:1062
+#: mod/admin.php:1084
 msgid "Check database structure"
 msgstr ""
 
-#: mod/admin.php:1067
+#: mod/admin.php:1089
 msgid "Failed Updates"
 msgstr ""
 
-#: mod/admin.php:1068
+#: mod/admin.php:1090
 msgid ""
 "This does not include updates prior to 1139, which did not return a status."
 msgstr ""
 
-#: mod/admin.php:1069
+#: mod/admin.php:1091
 msgid "Mark success (if update was manually applied)"
 msgstr ""
 
-#: mod/admin.php:1070
+#: mod/admin.php:1092
 msgid "Attempt to execute this update step automatically"
 msgstr ""
 
-#: mod/admin.php:1102
+#: mod/admin.php:1124
 #, php-format
 msgid ""
 "\n"
@@ -2946,7 +2998,7 @@ msgid ""
 "\t\t\t\tthe administrator of %2$s has set up an account for you."
 msgstr ""
 
-#: mod/admin.php:1105
+#: mod/admin.php:1127
 #, php-format
 msgid ""
 "\n"
@@ -2982,168 +3034,168 @@ msgid ""
 "\t\t\tThank you and welcome to %4$s."
 msgstr ""
 
-#: mod/admin.php:1137 include/user.php:423
+#: mod/admin.php:1159 include/user.php:423
 #, php-format
 msgid "Registration details for %s"
 msgstr ""
 
-#: mod/admin.php:1149
+#: mod/admin.php:1171
 #, php-format
 msgid "%s user blocked/unblocked"
 msgid_plural "%s users blocked/unblocked"
 msgstr[0] ""
 msgstr[1] ""
 
-#: mod/admin.php:1156
+#: mod/admin.php:1178
 #, php-format
 msgid "%s user deleted"
 msgid_plural "%s users deleted"
 msgstr[0] ""
 msgstr[1] ""
 
-#: mod/admin.php:1203
+#: mod/admin.php:1225
 #, php-format
 msgid "User '%s' deleted"
 msgstr ""
 
-#: mod/admin.php:1211
+#: mod/admin.php:1233
 #, php-format
 msgid "User '%s' unblocked"
 msgstr ""
 
-#: mod/admin.php:1211
+#: mod/admin.php:1233
 #, php-format
 msgid "User '%s' blocked"
 msgstr ""
 
-#: mod/admin.php:1302
+#: mod/admin.php:1324
 msgid "Add User"
 msgstr ""
 
-#: mod/admin.php:1303
+#: mod/admin.php:1325
 msgid "select all"
 msgstr ""
 
-#: mod/admin.php:1304
+#: mod/admin.php:1326
 msgid "User registrations waiting for confirm"
 msgstr ""
 
-#: mod/admin.php:1305
+#: mod/admin.php:1327
 msgid "User waiting for permanent deletion"
 msgstr ""
 
-#: mod/admin.php:1306
+#: mod/admin.php:1328
 msgid "Request date"
 msgstr ""
 
-#: mod/admin.php:1306 mod/admin.php:1318 mod/admin.php:1319 mod/admin.php:1334
+#: mod/admin.php:1328 mod/admin.php:1340 mod/admin.php:1341 mod/admin.php:1356
 #: include/contact_selectors.php:79 include/contact_selectors.php:86
 msgid "Email"
 msgstr ""
 
-#: mod/admin.php:1307
+#: mod/admin.php:1329
 msgid "No registrations."
 msgstr ""
 
-#: mod/admin.php:1309
+#: mod/admin.php:1331
 msgid "Deny"
 msgstr ""
 
-#: mod/admin.php:1313
+#: mod/admin.php:1335
 msgid "Site admin"
 msgstr ""
 
-#: mod/admin.php:1314
+#: mod/admin.php:1336
 msgid "Account expired"
 msgstr ""
 
-#: mod/admin.php:1317
+#: mod/admin.php:1339
 msgid "New User"
 msgstr ""
 
-#: mod/admin.php:1318 mod/admin.php:1319
+#: mod/admin.php:1340 mod/admin.php:1341
 msgid "Register date"
 msgstr ""
 
-#: mod/admin.php:1318 mod/admin.php:1319
+#: mod/admin.php:1340 mod/admin.php:1341
 msgid "Last login"
 msgstr ""
 
-#: mod/admin.php:1318 mod/admin.php:1319
+#: mod/admin.php:1340 mod/admin.php:1341
 msgid "Last item"
 msgstr ""
 
-#: mod/admin.php:1318
+#: mod/admin.php:1340
 msgid "Deleted since"
 msgstr ""
 
-#: mod/admin.php:1319 mod/settings.php:41
+#: mod/admin.php:1341 mod/settings.php:41
 msgid "Account"
 msgstr ""
 
-#: mod/admin.php:1321
+#: mod/admin.php:1343
 msgid ""
 "Selected users will be deleted!\\n\\nEverything these users had posted on "
 "this site will be permanently deleted!\\n\\nAre you sure?"
 msgstr ""
 
-#: mod/admin.php:1322
+#: mod/admin.php:1344
 msgid ""
 "The user {0} will be deleted!\\n\\nEverything this user has posted on this "
 "site will be permanently deleted!\\n\\nAre you sure?"
 msgstr ""
 
-#: mod/admin.php:1332
+#: mod/admin.php:1354
 msgid "Name of the new user."
 msgstr ""
 
-#: mod/admin.php:1333
+#: mod/admin.php:1355
 msgid "Nickname"
 msgstr ""
 
-#: mod/admin.php:1333
+#: mod/admin.php:1355
 msgid "Nickname of the new user."
 msgstr ""
 
-#: mod/admin.php:1334
+#: mod/admin.php:1356
 msgid "Email address of the new user."
 msgstr ""
 
-#: mod/admin.php:1377
+#: mod/admin.php:1399
 #, php-format
 msgid "Plugin %s disabled."
 msgstr ""
 
-#: mod/admin.php:1381
+#: mod/admin.php:1403
 #, php-format
 msgid "Plugin %s enabled."
 msgstr ""
 
-#: mod/admin.php:1392 mod/admin.php:1628
+#: mod/admin.php:1414 mod/admin.php:1650
 msgid "Disable"
 msgstr ""
 
-#: mod/admin.php:1394 mod/admin.php:1630
+#: mod/admin.php:1416 mod/admin.php:1652
 msgid "Enable"
 msgstr ""
 
-#: mod/admin.php:1417 mod/admin.php:1675
+#: mod/admin.php:1439 mod/admin.php:1697
 msgid "Toggle"
 msgstr ""
 
-#: mod/admin.php:1425 mod/admin.php:1684
+#: mod/admin.php:1447 mod/admin.php:1706
 msgid "Author: "
 msgstr ""
 
-#: mod/admin.php:1426 mod/admin.php:1685
+#: mod/admin.php:1448 mod/admin.php:1707
 msgid "Maintainer: "
 msgstr ""
 
-#: mod/admin.php:1478
+#: mod/admin.php:1500
 msgid "Reload active plugins"
 msgstr ""
 
-#: mod/admin.php:1483
+#: mod/admin.php:1505
 #, php-format
 msgid ""
 "There are currently no plugins available on your node. You can find the "
@@ -3151,62 +3203,62 @@ msgid ""
 "in the open plugin registry at %2$s"
 msgstr ""
 
-#: mod/admin.php:1588
+#: mod/admin.php:1610
 msgid "No themes found."
 msgstr ""
 
-#: mod/admin.php:1666
+#: mod/admin.php:1688
 msgid "Screenshot"
 msgstr ""
 
-#: mod/admin.php:1726
+#: mod/admin.php:1748
 msgid "Reload active themes"
 msgstr ""
 
-#: mod/admin.php:1731
+#: mod/admin.php:1753
 #, php-format
 msgid "No themes found on the system. They should be paced in %1$s"
 msgstr ""
 
-#: mod/admin.php:1732
+#: mod/admin.php:1754
 msgid "[Experimental]"
 msgstr ""
 
-#: mod/admin.php:1733
+#: mod/admin.php:1755
 msgid "[Unsupported]"
 msgstr ""
 
-#: mod/admin.php:1757
+#: mod/admin.php:1779
 msgid "Log settings updated."
 msgstr ""
 
-#: mod/admin.php:1794
+#: mod/admin.php:1816
 msgid "Clear"
 msgstr ""
 
-#: mod/admin.php:1799
+#: mod/admin.php:1821
 msgid "Enable Debugging"
 msgstr ""
 
-#: mod/admin.php:1800
+#: mod/admin.php:1822
 msgid "Log file"
 msgstr ""
 
-#: mod/admin.php:1800
+#: mod/admin.php:1822
 msgid ""
 "Must be writable by web server. Relative to your Friendica top-level "
 "directory."
 msgstr ""
 
-#: mod/admin.php:1801
+#: mod/admin.php:1823
 msgid "Log level"
 msgstr ""
 
-#: mod/admin.php:1804
+#: mod/admin.php:1826
 msgid "PHP logging"
 msgstr ""
 
-#: mod/admin.php:1805
+#: mod/admin.php:1827
 msgid ""
 "To enable logging of PHP errors and warnings you can add the following to "
 "the .htconfig.php file of your installation. The filename set in the "
@@ -3215,20 +3267,20 @@ msgid ""
 "'display_errors' is to enable these options, set to '0' to disable them."
 msgstr ""
 
-#: mod/admin.php:1931 mod/admin.php:1932 mod/settings.php:759
+#: mod/admin.php:1953 mod/admin.php:1954 mod/settings.php:760
 msgid "Off"
 msgstr ""
 
-#: mod/admin.php:1931 mod/admin.php:1932 mod/settings.php:759
+#: mod/admin.php:1953 mod/admin.php:1954 mod/settings.php:760
 msgid "On"
 msgstr ""
 
-#: mod/admin.php:1932
+#: mod/admin.php:1954
 #, php-format
 msgid "Lock feature %s"
 msgstr ""
 
-#: mod/admin.php:1940
+#: mod/admin.php:1962
 msgid "Manage Additional Features"
 msgstr ""
 
@@ -3245,7 +3297,7 @@ msgstr ""
 msgid "Saved Searches"
 msgstr ""
 
-#: mod/network.php:201 include/group.php:293
+#: mod/network.php:201 include/group.php:296
 msgid "add"
 msgstr ""
 
@@ -3362,31 +3414,31 @@ msgstr ""
 msgid "Sat"
 msgstr ""
 
-#: mod/events.php:208 mod/settings.php:948 include/text.php:1274
+#: mod/events.php:208 mod/settings.php:952 include/text.php:1245
 msgid "Sunday"
 msgstr ""
 
-#: mod/events.php:209 mod/settings.php:948 include/text.php:1274
+#: mod/events.php:209 mod/settings.php:952 include/text.php:1245
 msgid "Monday"
 msgstr ""
 
-#: mod/events.php:210 include/text.php:1274
+#: mod/events.php:210 include/text.php:1245
 msgid "Tuesday"
 msgstr ""
 
-#: mod/events.php:211 include/text.php:1274
+#: mod/events.php:211 include/text.php:1245
 msgid "Wednesday"
 msgstr ""
 
-#: mod/events.php:212 include/text.php:1274
+#: mod/events.php:212 include/text.php:1245
 msgid "Thursday"
 msgstr ""
 
-#: mod/events.php:213 include/text.php:1274
+#: mod/events.php:213 include/text.php:1245
 msgid "Friday"
 msgstr ""
 
-#: mod/events.php:214 include/text.php:1274
+#: mod/events.php:214 include/text.php:1245
 msgid "Saturday"
 msgstr ""
 
@@ -3406,7 +3458,7 @@ msgstr ""
 msgid "Apr"
 msgstr ""
 
-#: mod/events.php:219 mod/events.php:231 include/text.php:1278
+#: mod/events.php:219 mod/events.php:231 include/text.php:1249
 msgid "May"
 msgstr ""
 
@@ -3438,47 +3490,47 @@ msgstr ""
 msgid "Dec"
 msgstr ""
 
-#: mod/events.php:227 include/text.php:1278
+#: mod/events.php:227 include/text.php:1249
 msgid "January"
 msgstr ""
 
-#: mod/events.php:228 include/text.php:1278
+#: mod/events.php:228 include/text.php:1249
 msgid "February"
 msgstr ""
 
-#: mod/events.php:229 include/text.php:1278
+#: mod/events.php:229 include/text.php:1249
 msgid "March"
 msgstr ""
 
-#: mod/events.php:230 include/text.php:1278
+#: mod/events.php:230 include/text.php:1249
 msgid "April"
 msgstr ""
 
-#: mod/events.php:232 include/text.php:1278
+#: mod/events.php:232 include/text.php:1249
 msgid "June"
 msgstr ""
 
-#: mod/events.php:233 include/text.php:1278
+#: mod/events.php:233 include/text.php:1249
 msgid "July"
 msgstr ""
 
-#: mod/events.php:234 include/text.php:1278
+#: mod/events.php:234 include/text.php:1249
 msgid "August"
 msgstr ""
 
-#: mod/events.php:235 include/text.php:1278
+#: mod/events.php:235 include/text.php:1249
 msgid "September"
 msgstr ""
 
-#: mod/events.php:236 include/text.php:1278
+#: mod/events.php:236 include/text.php:1249
 msgid "October"
 msgstr ""
 
-#: mod/events.php:237 include/text.php:1278
+#: mod/events.php:237 include/text.php:1249
 msgid "November"
 msgstr ""
 
-#: mod/events.php:238 include/text.php:1278
+#: mod/events.php:238 include/text.php:1249
 msgid "December"
 msgstr ""
 
@@ -3486,15 +3538,15 @@ msgstr ""
 msgid "today"
 msgstr ""
 
-#: mod/events.php:240 include/datetime.php:288
+#: mod/events.php:240 include/datetime.php:344
 msgid "month"
 msgstr ""
 
-#: mod/events.php:241 include/datetime.php:289
+#: mod/events.php:241 include/datetime.php:345
 msgid "week"
 msgstr ""
 
-#: mod/events.php:242 include/datetime.php:290
+#: mod/events.php:242 include/datetime.php:346
 msgid "day"
 msgstr ""
 
@@ -3506,11 +3558,11 @@ msgstr ""
 msgid "Edit event"
 msgstr ""
 
-#: mod/events.php:421 include/text.php:1728 include/text.php:1735
+#: mod/events.php:421 include/text.php:1651 include/text.php:1658
 msgid "link to source"
 msgstr ""
 
-#: mod/events.php:456 include/identity.php:722 include/nav.php:79
+#: mod/events.php:456 include/identity.php:723 include/nav.php:79
 #: include/nav.php:140 view/theme/diabook/theme.php:127
 msgid "Events"
 msgstr ""
@@ -3568,7 +3620,7 @@ msgid "Share this event"
 msgstr ""
 
 #: mod/events.php:572 mod/content.php:721 mod/editpost.php:145
-#: mod/photos.php:1631 mod/photos.php:1679 mod/photos.php:1767
+#: mod/photos.php:1617 mod/photos.php:1665 mod/photos.php:1753
 #: object/Item.php:719 include/conversation.php:1216
 msgid "Preview"
 msgstr ""
@@ -3584,7 +3636,7 @@ msgid ""
 "code or the translation of Friendica. Thank you all!"
 msgstr ""
 
-#: mod/content.php:439 mod/content.php:742 mod/photos.php:1722
+#: mod/content.php:439 mod/content.php:742 mod/photos.php:1708
 #: object/Item.php:133 include/conversation.php:634
 msgid "Select"
 msgstr ""
@@ -3613,23 +3665,23 @@ msgstr[0] ""
 msgstr[1] ""
 
 #: mod/content.php:607 object/Item.php:421 object/Item.php:434
-#: include/text.php:2004
+#: include/text.php:1927
 msgid "comment"
 msgid_plural "comments"
 msgstr[0] ""
 msgstr[1] ""
 
-#: mod/content.php:608 boot.php:870 object/Item.php:422
-#: include/contact_widgets.php:242 include/forums.php:110
-#: include/items.php:5207 view/theme/vier/theme.php:264
+#: mod/content.php:608 boot.php:872 object/Item.php:422
+#: include/contact_widgets.php:242 include/ForumManager.php:117
+#: include/items.php:2113 view/theme/vier/theme.php:260
 msgid "show more"
 msgstr ""
 
-#: mod/content.php:622 mod/photos.php:1418 object/Item.php:117
+#: mod/content.php:622 mod/photos.php:1404 object/Item.php:117
 msgid "Private Message"
 msgstr ""
 
-#: mod/content.php:686 mod/photos.php:1607 object/Item.php:253
+#: mod/content.php:686 mod/photos.php:1593 object/Item.php:253
 msgid "I like this (toggle)"
 msgstr ""
 
@@ -3637,7 +3689,7 @@ msgstr ""
 msgid "like"
 msgstr ""
 
-#: mod/content.php:687 mod/photos.php:1608 object/Item.php:254
+#: mod/content.php:687 mod/photos.php:1594 object/Item.php:254
 msgid "I don't like this (toggle)"
 msgstr ""
 
@@ -3653,13 +3705,13 @@ msgstr ""
 msgid "share"
 msgstr ""
 
-#: mod/content.php:709 mod/photos.php:1627 mod/photos.php:1675
-#: mod/photos.php:1763 object/Item.php:707
+#: mod/content.php:709 mod/photos.php:1613 mod/photos.php:1661
+#: mod/photos.php:1749 object/Item.php:707
 msgid "This is you"
 msgstr ""
 
-#: mod/content.php:711 mod/photos.php:1629 mod/photos.php:1677
-#: mod/photos.php:1765 boot.php:869 object/Item.php:393 object/Item.php:709
+#: mod/content.php:711 mod/photos.php:1615 mod/photos.php:1663
+#: mod/photos.php:1751 boot.php:871 object/Item.php:393 object/Item.php:709
 msgid "Comment"
 msgstr ""
 
@@ -3695,7 +3747,7 @@ msgstr ""
 msgid "Video"
 msgstr ""
 
-#: mod/content.php:730 mod/settings.php:721 object/Item.php:122
+#: mod/content.php:730 mod/settings.php:722 object/Item.php:122
 #: object/Item.php:124
 msgid "Edit"
 msgstr ""
@@ -4094,19 +4146,19 @@ msgstr ""
 msgid "Help:"
 msgstr ""
 
-#: mod/help.php:47 include/nav.php:113 view/theme/vier/theme.php:302
+#: mod/help.php:47 include/nav.php:113 view/theme/vier/theme.php:298
 msgid "Help"
 msgstr ""
 
-#: mod/help.php:53 mod/p.php:16 mod/p.php:25 index.php:270
+#: mod/help.php:53 mod/p.php:16 mod/p.php:25 index.php:271
 msgid "Not Found"
 msgstr ""
 
-#: mod/help.php:56 index.php:273
+#: mod/help.php:56 index.php:274
 msgid "Page not found."
 msgstr ""
 
-#: mod/dfrn_poll.php:103 mod/dfrn_poll.php:536
+#: mod/dfrn_poll.php:101 mod/dfrn_poll.php:534
 #, php-format
 msgid "%1$s welcomes %2$s"
 msgstr ""
@@ -4170,7 +4222,7 @@ msgstr ""
 msgid "Display"
 msgstr ""
 
-#: mod/settings.php:65 mod/settings.php:864
+#: mod/settings.php:65 mod/settings.php:868
 msgid "Social Networks"
 msgstr ""
 
@@ -4182,7 +4234,7 @@ msgstr ""
 msgid "Connected apps"
 msgstr ""
 
-#: mod/settings.php:93 mod/uexport.php:85
+#: mod/settings.php:93 mod/uexport.php:37
 msgid "Export personal data"
 msgstr ""
 
@@ -4194,685 +4246,689 @@ msgstr ""
 msgid "Missing some important data!"
 msgstr ""
 
-#: mod/settings.php:266
+#: mod/settings.php:267
 msgid "Failed to connect with email account using the settings provided."
 msgstr ""
 
-#: mod/settings.php:271
+#: mod/settings.php:272
 msgid "Email settings updated."
 msgstr ""
 
-#: mod/settings.php:286
+#: mod/settings.php:287
 msgid "Features updated"
 msgstr ""
 
-#: mod/settings.php:353
+#: mod/settings.php:354
 msgid "Relocate message has been send to your contacts"
 msgstr ""
 
-#: mod/settings.php:367 include/user.php:39
+#: mod/settings.php:368 include/user.php:39
 msgid "Passwords do not match. Password unchanged."
 msgstr ""
 
-#: mod/settings.php:372
+#: mod/settings.php:373
 msgid "Empty passwords are not allowed. Password unchanged."
 msgstr ""
 
-#: mod/settings.php:380
+#: mod/settings.php:381
 msgid "Wrong password."
 msgstr ""
 
-#: mod/settings.php:391
+#: mod/settings.php:392
 msgid "Password changed."
 msgstr ""
 
-#: mod/settings.php:393
+#: mod/settings.php:394
 msgid "Password update failed. Please try again."
 msgstr ""
 
-#: mod/settings.php:462
+#: mod/settings.php:463
 msgid " Please use a shorter name."
 msgstr ""
 
-#: mod/settings.php:464
+#: mod/settings.php:465
 msgid " Name too short."
 msgstr ""
 
-#: mod/settings.php:473
+#: mod/settings.php:474
 msgid "Wrong Password"
 msgstr ""
 
-#: mod/settings.php:478
+#: mod/settings.php:479
 msgid " Not valid email."
 msgstr ""
 
-#: mod/settings.php:484
+#: mod/settings.php:485
 msgid " Cannot change to that email."
 msgstr ""
 
-#: mod/settings.php:540
+#: mod/settings.php:541
 msgid "Private forum has no privacy permissions. Using default privacy group."
 msgstr ""
 
-#: mod/settings.php:544
+#: mod/settings.php:545
 msgid "Private forum has no privacy permissions and no default privacy group."
 msgstr ""
 
-#: mod/settings.php:583
+#: mod/settings.php:584
 msgid "Settings updated."
 msgstr ""
 
-#: mod/settings.php:658 mod/settings.php:684 mod/settings.php:720
+#: mod/settings.php:659 mod/settings.php:685 mod/settings.php:721
 msgid "Add application"
 msgstr ""
 
-#: mod/settings.php:662 mod/settings.php:688
+#: mod/settings.php:663 mod/settings.php:689
 msgid "Consumer Key"
 msgstr ""
 
-#: mod/settings.php:663 mod/settings.php:689
+#: mod/settings.php:664 mod/settings.php:690
 msgid "Consumer Secret"
 msgstr ""
 
-#: mod/settings.php:664 mod/settings.php:690
+#: mod/settings.php:665 mod/settings.php:691
 msgid "Redirect"
 msgstr ""
 
-#: mod/settings.php:665 mod/settings.php:691
+#: mod/settings.php:666 mod/settings.php:692
 msgid "Icon url"
 msgstr ""
 
-#: mod/settings.php:676
+#: mod/settings.php:677
 msgid "You can't edit this application."
 msgstr ""
 
-#: mod/settings.php:719
+#: mod/settings.php:720
 msgid "Connected Apps"
 msgstr ""
 
-#: mod/settings.php:723
+#: mod/settings.php:724
 msgid "Client key starts with"
 msgstr ""
 
-#: mod/settings.php:724
+#: mod/settings.php:725
 msgid "No name"
 msgstr ""
 
-#: mod/settings.php:725
+#: mod/settings.php:726
 msgid "Remove authorization"
 msgstr ""
 
-#: mod/settings.php:737
+#: mod/settings.php:738
 msgid "No Plugin settings configured"
 msgstr ""
 
-#: mod/settings.php:745
+#: mod/settings.php:746
 msgid "Plugin Settings"
 msgstr ""
 
-#: mod/settings.php:767
+#: mod/settings.php:768
 msgid "Additional Features"
 msgstr ""
 
-#: mod/settings.php:777 mod/settings.php:781
+#: mod/settings.php:778 mod/settings.php:782
 msgid "General Social Media Settings"
 msgstr ""
 
-#: mod/settings.php:787
+#: mod/settings.php:788
 msgid "Disable intelligent shortening"
 msgstr ""
 
-#: mod/settings.php:789
+#: mod/settings.php:790
 msgid ""
 "Normally the system tries to find the best link to add to shortened posts. "
 "If this option is enabled then every shortened post will always point to the "
 "original friendica post."
 msgstr ""
 
-#: mod/settings.php:795
+#: mod/settings.php:796
 msgid "Automatically follow any GNU Social (OStatus) followers/mentioners"
 msgstr ""
 
-#: mod/settings.php:797
+#: mod/settings.php:798
 msgid ""
 "If you receive a message from an unknown OStatus user, this option decides "
 "what to do. If it is checked, a new contact will be created for every "
 "unknown user."
 msgstr ""
 
-#: mod/settings.php:806
+#: mod/settings.php:804
+msgid "Default group for OStatus contacts"
+msgstr ""
+
+#: mod/settings.php:810
 msgid "Your legacy GNU Social account"
 msgstr ""
 
-#: mod/settings.php:808
+#: mod/settings.php:812
 msgid ""
 "If you enter your old GNU Social/Statusnet account name here (in the format "
 "user@domain.tld), your contacts will be added automatically. The field will "
 "be emptied when done."
 msgstr ""
 
-#: mod/settings.php:811
+#: mod/settings.php:815
 msgid "Repair OStatus subscriptions"
 msgstr ""
 
-#: mod/settings.php:820 mod/settings.php:821
+#: mod/settings.php:824 mod/settings.php:825
 #, php-format
 msgid "Built-in support for %s connectivity is %s"
 msgstr ""
 
-#: mod/settings.php:820 mod/dfrn_request.php:865
+#: mod/settings.php:824 mod/dfrn_request.php:863
 #: include/contact_selectors.php:80
 msgid "Diaspora"
 msgstr ""
 
-#: mod/settings.php:820 mod/settings.php:821
+#: mod/settings.php:824 mod/settings.php:825
 msgid "enabled"
 msgstr ""
 
-#: mod/settings.php:820 mod/settings.php:821
+#: mod/settings.php:824 mod/settings.php:825
 msgid "disabled"
 msgstr ""
 
-#: mod/settings.php:821
+#: mod/settings.php:825
 msgid "GNU Social (OStatus)"
 msgstr ""
 
-#: mod/settings.php:857
+#: mod/settings.php:861
 msgid "Email access is disabled on this site."
 msgstr ""
 
-#: mod/settings.php:869
+#: mod/settings.php:873
 msgid "Email/Mailbox Setup"
 msgstr ""
 
-#: mod/settings.php:870
+#: mod/settings.php:874
 msgid ""
 "If you wish to communicate with email contacts using this service "
 "(optional), please specify how to connect to your mailbox."
 msgstr ""
 
-#: mod/settings.php:871
+#: mod/settings.php:875
 msgid "Last successful email check:"
 msgstr ""
 
-#: mod/settings.php:873
+#: mod/settings.php:877
 msgid "IMAP server name:"
 msgstr ""
 
-#: mod/settings.php:874
+#: mod/settings.php:878
 msgid "IMAP port:"
 msgstr ""
 
-#: mod/settings.php:875
+#: mod/settings.php:879
 msgid "Security:"
 msgstr ""
 
-#: mod/settings.php:875 mod/settings.php:880
+#: mod/settings.php:879 mod/settings.php:884
 msgid "None"
 msgstr ""
 
-#: mod/settings.php:876
+#: mod/settings.php:880
 msgid "Email login name:"
 msgstr ""
 
-#: mod/settings.php:877
+#: mod/settings.php:881
 msgid "Email password:"
 msgstr ""
 
-#: mod/settings.php:878
+#: mod/settings.php:882
 msgid "Reply-to address:"
 msgstr ""
 
-#: mod/settings.php:879
+#: mod/settings.php:883
 msgid "Send public posts to all email contacts:"
 msgstr ""
 
-#: mod/settings.php:880
+#: mod/settings.php:884
 msgid "Action after import:"
 msgstr ""
 
-#: mod/settings.php:880
+#: mod/settings.php:884
 msgid "Mark as seen"
 msgstr ""
 
-#: mod/settings.php:880
+#: mod/settings.php:884
 msgid "Move to folder"
 msgstr ""
 
-#: mod/settings.php:881
+#: mod/settings.php:885
 msgid "Move to folder:"
 msgstr ""
 
-#: mod/settings.php:967
+#: mod/settings.php:971
 msgid "Display Settings"
 msgstr ""
 
-#: mod/settings.php:973 mod/settings.php:991
+#: mod/settings.php:977 mod/settings.php:995
 msgid "Display Theme:"
 msgstr ""
 
-#: mod/settings.php:974
+#: mod/settings.php:978
 msgid "Mobile Theme:"
 msgstr ""
 
-#: mod/settings.php:975
+#: mod/settings.php:979
 msgid "Update browser every xx seconds"
 msgstr ""
 
-#: mod/settings.php:975
+#: mod/settings.php:979
 msgid "Minimum of 10 seconds. Enter -1 to disable it."
 msgstr ""
 
-#: mod/settings.php:976
+#: mod/settings.php:980
 msgid "Number of items to display per page:"
 msgstr ""
 
-#: mod/settings.php:976 mod/settings.php:977
+#: mod/settings.php:980 mod/settings.php:981
 msgid "Maximum of 100 items"
 msgstr ""
 
-#: mod/settings.php:977
+#: mod/settings.php:981
 msgid "Number of items to display per page when viewed from mobile device:"
 msgstr ""
 
-#: mod/settings.php:978
+#: mod/settings.php:982
 msgid "Don't show emoticons"
 msgstr ""
 
-#: mod/settings.php:979
+#: mod/settings.php:983
 msgid "Calendar"
 msgstr ""
 
-#: mod/settings.php:980
+#: mod/settings.php:984
 msgid "Beginning of week:"
 msgstr ""
 
-#: mod/settings.php:981
+#: mod/settings.php:985
 msgid "Don't show notices"
 msgstr ""
 
-#: mod/settings.php:982
+#: mod/settings.php:986
 msgid "Infinite scroll"
 msgstr ""
 
-#: mod/settings.php:983
+#: mod/settings.php:987
 msgid "Automatic updates only at the top of the network page"
 msgstr ""
 
-#: mod/settings.php:985 view/theme/cleanzero/config.php:82
+#: mod/settings.php:989 view/theme/cleanzero/config.php:82
 #: view/theme/dispy/config.php:72 view/theme/quattro/config.php:66
 #: view/theme/diabook/config.php:150 view/theme/vier/config.php:109
 #: view/theme/duepuntozero/config.php:61
 msgid "Theme settings"
 msgstr ""
 
-#: mod/settings.php:1062
+#: mod/settings.php:1066
 msgid "User Types"
 msgstr ""
 
-#: mod/settings.php:1063
+#: mod/settings.php:1067
 msgid "Community Types"
 msgstr ""
 
-#: mod/settings.php:1064
+#: mod/settings.php:1068
 msgid "Normal Account Page"
 msgstr ""
 
-#: mod/settings.php:1065
+#: mod/settings.php:1069
 msgid "This account is a normal personal profile"
 msgstr ""
 
-#: mod/settings.php:1068
+#: mod/settings.php:1072
 msgid "Soapbox Page"
 msgstr ""
 
-#: mod/settings.php:1069
+#: mod/settings.php:1073
 msgid "Automatically approve all connection/friend requests as read-only fans"
 msgstr ""
 
-#: mod/settings.php:1072
+#: mod/settings.php:1076
 msgid "Community Forum/Celebrity Account"
 msgstr ""
 
-#: mod/settings.php:1073
+#: mod/settings.php:1077
 msgid "Automatically approve all connection/friend requests as read-write fans"
 msgstr ""
 
-#: mod/settings.php:1076
+#: mod/settings.php:1080
 msgid "Automatic Friend Page"
 msgstr ""
 
-#: mod/settings.php:1077
+#: mod/settings.php:1081
 msgid "Automatically approve all connection/friend requests as friends"
 msgstr ""
 
-#: mod/settings.php:1080
+#: mod/settings.php:1084
 msgid "Private Forum [Experimental]"
 msgstr ""
 
-#: mod/settings.php:1081
+#: mod/settings.php:1085
 msgid "Private forum - approved members only"
 msgstr ""
 
-#: mod/settings.php:1093
+#: mod/settings.php:1097
 msgid "OpenID:"
 msgstr ""
 
-#: mod/settings.php:1093
+#: mod/settings.php:1097
 msgid "(Optional) Allow this OpenID to login to this account."
 msgstr ""
 
-#: mod/settings.php:1103
+#: mod/settings.php:1107
 msgid "Publish your default profile in your local site directory?"
 msgstr ""
 
-#: mod/settings.php:1109
+#: mod/settings.php:1113
 msgid "Publish your default profile in the global social directory?"
 msgstr ""
 
-#: mod/settings.php:1117
+#: mod/settings.php:1121
 msgid "Hide your contact/friend list from viewers of your default profile?"
 msgstr ""
 
-#: mod/settings.php:1121 include/acl_selectors.php:331
+#: mod/settings.php:1125 include/acl_selectors.php:331
 msgid "Hide your profile details from unknown viewers?"
 msgstr ""
 
-#: mod/settings.php:1121
+#: mod/settings.php:1125
 msgid ""
 "If enabled, posting public messages to Diaspora and other networks isn't "
 "possible."
 msgstr ""
 
-#: mod/settings.php:1126
+#: mod/settings.php:1130
 msgid "Allow friends to post to your profile page?"
 msgstr ""
 
-#: mod/settings.php:1132
+#: mod/settings.php:1136
 msgid "Allow friends to tag your posts?"
 msgstr ""
 
-#: mod/settings.php:1138
+#: mod/settings.php:1142
 msgid "Allow us to suggest you as a potential friend to new members?"
 msgstr ""
 
-#: mod/settings.php:1144
+#: mod/settings.php:1148
 msgid "Permit unknown people to send you private mail?"
 msgstr ""
 
-#: mod/settings.php:1152
+#: mod/settings.php:1156
 msgid "Profile is <strong>not published</strong>."
 msgstr ""
 
-#: mod/settings.php:1160
+#: mod/settings.php:1164
 #, php-format
 msgid "Your Identity Address is <strong>'%s'</strong> or '%s'."
 msgstr ""
 
-#: mod/settings.php:1167
+#: mod/settings.php:1171
 msgid "Automatically expire posts after this many days:"
 msgstr ""
 
-#: mod/settings.php:1167
+#: mod/settings.php:1171
 msgid "If empty, posts will not expire. Expired posts will be deleted"
 msgstr ""
 
-#: mod/settings.php:1168
+#: mod/settings.php:1172
 msgid "Advanced expiration settings"
 msgstr ""
 
-#: mod/settings.php:1169
+#: mod/settings.php:1173
 msgid "Advanced Expiration"
 msgstr ""
 
-#: mod/settings.php:1170
+#: mod/settings.php:1174
 msgid "Expire posts:"
 msgstr ""
 
-#: mod/settings.php:1171
+#: mod/settings.php:1175
 msgid "Expire personal notes:"
 msgstr ""
 
-#: mod/settings.php:1172
+#: mod/settings.php:1176
 msgid "Expire starred posts:"
 msgstr ""
 
-#: mod/settings.php:1173
+#: mod/settings.php:1177
 msgid "Expire photos:"
 msgstr ""
 
-#: mod/settings.php:1174
+#: mod/settings.php:1178
 msgid "Only expire posts by others:"
 msgstr ""
 
-#: mod/settings.php:1202
+#: mod/settings.php:1206
 msgid "Account Settings"
 msgstr ""
 
-#: mod/settings.php:1210
+#: mod/settings.php:1214
 msgid "Password Settings"
 msgstr ""
 
-#: mod/settings.php:1211 mod/register.php:274
+#: mod/settings.php:1215 mod/register.php:274
 msgid "New Password:"
 msgstr ""
 
-#: mod/settings.php:1212 mod/register.php:275
+#: mod/settings.php:1216 mod/register.php:275
 msgid "Confirm:"
 msgstr ""
 
-#: mod/settings.php:1212
+#: mod/settings.php:1216
 msgid "Leave password fields blank unless changing"
 msgstr ""
 
-#: mod/settings.php:1213
+#: mod/settings.php:1217
 msgid "Current Password:"
 msgstr ""
 
-#: mod/settings.php:1213 mod/settings.php:1214
+#: mod/settings.php:1217 mod/settings.php:1218
 msgid "Your current password to confirm the changes"
 msgstr ""
 
-#: mod/settings.php:1214
+#: mod/settings.php:1218
 msgid "Password:"
 msgstr ""
 
-#: mod/settings.php:1218
+#: mod/settings.php:1222
 msgid "Basic Settings"
 msgstr ""
 
-#: mod/settings.php:1219 include/identity.php:588
+#: mod/settings.php:1223 include/identity.php:589
 msgid "Full Name:"
 msgstr ""
 
-#: mod/settings.php:1220
+#: mod/settings.php:1224
 msgid "Email Address:"
 msgstr ""
 
-#: mod/settings.php:1221
+#: mod/settings.php:1225
 msgid "Your Timezone:"
 msgstr ""
 
-#: mod/settings.php:1222
+#: mod/settings.php:1226
 msgid "Your Language:"
 msgstr ""
 
-#: mod/settings.php:1222
+#: mod/settings.php:1226
 msgid ""
 "Set the language we use to show you friendica interface and to send you "
 "emails"
 msgstr ""
 
-#: mod/settings.php:1223
+#: mod/settings.php:1227
 msgid "Default Post Location:"
 msgstr ""
 
-#: mod/settings.php:1224
+#: mod/settings.php:1228
 msgid "Use Browser Location:"
 msgstr ""
 
-#: mod/settings.php:1227
+#: mod/settings.php:1231
 msgid "Security and Privacy Settings"
 msgstr ""
 
-#: mod/settings.php:1229
+#: mod/settings.php:1233
 msgid "Maximum Friend Requests/Day:"
 msgstr ""
 
-#: mod/settings.php:1229 mod/settings.php:1259
+#: mod/settings.php:1233 mod/settings.php:1263
 msgid "(to prevent spam abuse)"
 msgstr ""
 
-#: mod/settings.php:1230
+#: mod/settings.php:1234
 msgid "Default Post Permissions"
 msgstr ""
 
-#: mod/settings.php:1231
+#: mod/settings.php:1235
 msgid "(click to open/close)"
 msgstr ""
 
-#: mod/settings.php:1240 mod/photos.php:1199 mod/photos.php:1584
+#: mod/settings.php:1244 mod/photos.php:1185 mod/photos.php:1570
 msgid "Show to Groups"
 msgstr ""
 
-#: mod/settings.php:1241 mod/photos.php:1200 mod/photos.php:1585
+#: mod/settings.php:1245 mod/photos.php:1186 mod/photos.php:1571
 msgid "Show to Contacts"
 msgstr ""
 
-#: mod/settings.php:1242
+#: mod/settings.php:1246
 msgid "Default Private Post"
 msgstr ""
 
-#: mod/settings.php:1243
+#: mod/settings.php:1247
 msgid "Default Public Post"
 msgstr ""
 
-#: mod/settings.php:1247
+#: mod/settings.php:1251
 msgid "Default Permissions for New Posts"
 msgstr ""
 
-#: mod/settings.php:1259
+#: mod/settings.php:1263
 msgid "Maximum private messages per day from unknown people:"
 msgstr ""
 
-#: mod/settings.php:1262
+#: mod/settings.php:1266
 msgid "Notification Settings"
 msgstr ""
 
-#: mod/settings.php:1263
+#: mod/settings.php:1267
 msgid "By default post a status message when:"
 msgstr ""
 
-#: mod/settings.php:1264
+#: mod/settings.php:1268
 msgid "accepting a friend request"
 msgstr ""
 
-#: mod/settings.php:1265
+#: mod/settings.php:1269
 msgid "joining a forum/community"
 msgstr ""
 
-#: mod/settings.php:1266
+#: mod/settings.php:1270
 msgid "making an <em>interesting</em> profile change"
 msgstr ""
 
-#: mod/settings.php:1267
+#: mod/settings.php:1271
 msgid "Send a notification email when:"
 msgstr ""
 
-#: mod/settings.php:1268
+#: mod/settings.php:1272
 msgid "You receive an introduction"
 msgstr ""
 
-#: mod/settings.php:1269
+#: mod/settings.php:1273
 msgid "Your introductions are confirmed"
 msgstr ""
 
-#: mod/settings.php:1270
+#: mod/settings.php:1274
 msgid "Someone writes on your profile wall"
 msgstr ""
 
-#: mod/settings.php:1271
+#: mod/settings.php:1275
 msgid "Someone writes a followup comment"
 msgstr ""
 
-#: mod/settings.php:1272
+#: mod/settings.php:1276
 msgid "You receive a private message"
 msgstr ""
 
-#: mod/settings.php:1273
+#: mod/settings.php:1277
 msgid "You receive a friend suggestion"
 msgstr ""
 
-#: mod/settings.php:1274
+#: mod/settings.php:1278
 msgid "You are tagged in a post"
 msgstr ""
 
-#: mod/settings.php:1275
+#: mod/settings.php:1279
 msgid "You are poked/prodded/etc. in a post"
 msgstr ""
 
-#: mod/settings.php:1277
+#: mod/settings.php:1281
 msgid "Activate desktop notifications"
 msgstr ""
 
-#: mod/settings.php:1277
+#: mod/settings.php:1281
 msgid "Show desktop popup on new notifications"
 msgstr ""
 
-#: mod/settings.php:1279
+#: mod/settings.php:1283
 msgid "Text-only notification emails"
 msgstr ""
 
-#: mod/settings.php:1281
+#: mod/settings.php:1285
 msgid "Send text only notification emails, without the html part"
 msgstr ""
 
-#: mod/settings.php:1283
+#: mod/settings.php:1287
 msgid "Advanced Account/Page Type Settings"
 msgstr ""
 
-#: mod/settings.php:1284
+#: mod/settings.php:1288
 msgid "Change the behaviour of this account for special situations"
 msgstr ""
 
-#: mod/settings.php:1287
+#: mod/settings.php:1291
 msgid "Relocate"
 msgstr ""
 
-#: mod/settings.php:1288
+#: mod/settings.php:1292
 msgid ""
 "If you have moved this profile from another server, and some of your "
 "contacts don't receive your updates, try pushing this button."
 msgstr ""
 
-#: mod/settings.php:1289
+#: mod/settings.php:1293
 msgid "Resend relocate message to contacts"
 msgstr ""
 
-#: mod/dfrn_request.php:96
+#: mod/dfrn_request.php:98
 msgid "This introduction has already been accepted."
 msgstr ""
 
-#: mod/dfrn_request.php:119 mod/dfrn_request.php:516
+#: mod/dfrn_request.php:121 mod/dfrn_request.php:514
 msgid "Profile location is not valid or does not contain profile information."
 msgstr ""
 
-#: mod/dfrn_request.php:124 mod/dfrn_request.php:521
+#: mod/dfrn_request.php:126 mod/dfrn_request.php:519
 msgid "Warning: profile location has no identifiable owner name."
 msgstr ""
 
-#: mod/dfrn_request.php:126 mod/dfrn_request.php:523
+#: mod/dfrn_request.php:128 mod/dfrn_request.php:521
 msgid "Warning: profile location has no profile photo."
 msgstr ""
 
-#: mod/dfrn_request.php:129 mod/dfrn_request.php:526
+#: mod/dfrn_request.php:131 mod/dfrn_request.php:524
 #, php-format
 msgid "%d required parameter was not found at the given location"
 msgid_plural "%d required parameters were not found at the given location"
 msgstr[0] ""
 msgstr[1] ""
 
-#: mod/dfrn_request.php:172
+#: mod/dfrn_request.php:174
 msgid "Introduction complete."
 msgstr ""
 
@@ -4909,93 +4965,93 @@ msgstr ""
 msgid "This account has not been configured for email. Request failed."
 msgstr ""
 
-#: mod/dfrn_request.php:474
+#: mod/dfrn_request.php:472
 msgid "You have already introduced yourself here."
 msgstr ""
 
-#: mod/dfrn_request.php:478
+#: mod/dfrn_request.php:476
 #, php-format
 msgid "Apparently you are already friends with %s."
 msgstr ""
 
-#: mod/dfrn_request.php:499
+#: mod/dfrn_request.php:497
 msgid "Invalid profile URL."
 msgstr ""
 
-#: mod/dfrn_request.php:505 include/follow.php:72
+#: mod/dfrn_request.php:503 include/follow.php:76
 msgid "Disallowed profile URL."
 msgstr ""
 
-#: mod/dfrn_request.php:596
+#: mod/dfrn_request.php:594
 msgid "Your introduction has been sent."
 msgstr ""
 
-#: mod/dfrn_request.php:636
+#: mod/dfrn_request.php:634
 msgid ""
 "Remote subscription can't be done for your network. Please subscribe "
 "directly on your system."
 msgstr ""
 
-#: mod/dfrn_request.php:659
+#: mod/dfrn_request.php:657
 msgid "Please login to confirm introduction."
 msgstr ""
 
-#: mod/dfrn_request.php:669
+#: mod/dfrn_request.php:667
 msgid ""
 "Incorrect identity currently logged in. Please login to <strong>this</"
 "strong> profile."
 msgstr ""
 
-#: mod/dfrn_request.php:683 mod/dfrn_request.php:700
+#: mod/dfrn_request.php:681 mod/dfrn_request.php:698
 msgid "Confirm"
 msgstr ""
 
-#: mod/dfrn_request.php:695
+#: mod/dfrn_request.php:693
 msgid "Hide this contact"
 msgstr ""
 
-#: mod/dfrn_request.php:698
+#: mod/dfrn_request.php:696
 #, php-format
 msgid "Welcome home %s."
 msgstr ""
 
-#: mod/dfrn_request.php:699
+#: mod/dfrn_request.php:697
 #, php-format
 msgid "Please confirm your introduction/connection request to %s."
 msgstr ""
 
-#: mod/dfrn_request.php:828
+#: mod/dfrn_request.php:826
 msgid ""
 "Please enter your 'Identity Address' from one of the following supported "
 "communications networks:"
 msgstr ""
 
-#: mod/dfrn_request.php:849
+#: mod/dfrn_request.php:847
 #, php-format
 msgid ""
 "If you are not yet a member of the free social web, <a href=\"%s/siteinfo"
 "\">follow this link to find a public Friendica site and join us today</a>."
 msgstr ""
 
-#: mod/dfrn_request.php:854
+#: mod/dfrn_request.php:852
 msgid "Friend/Connection Request"
 msgstr ""
 
-#: mod/dfrn_request.php:855
+#: mod/dfrn_request.php:853
 msgid ""
 "Examples: jojo@demo.friendica.com, http://demo.friendica.com/profile/jojo, "
 "testuser@identi.ca"
 msgstr ""
 
-#: mod/dfrn_request.php:863 include/contact_selectors.php:76
+#: mod/dfrn_request.php:861 include/contact_selectors.php:76
 msgid "Friendica"
 msgstr ""
 
-#: mod/dfrn_request.php:864
+#: mod/dfrn_request.php:862
 msgid "StatusNet/Federated Social Web"
 msgstr ""
 
-#: mod/dfrn_request.php:866
+#: mod/dfrn_request.php:864
 #, php-format
 msgid ""
 " - please do not use this form.  Instead, enter %s into your Diaspora search "
@@ -5083,7 +5139,7 @@ msgstr ""
 msgid "Choose a nickname: "
 msgstr ""
 
-#: mod/register.php:280 boot.php:1405 include/nav.php:108
+#: mod/register.php:280 boot.php:1495 include/nav.php:108
 msgid "Register"
 msgstr ""
 
@@ -5111,7 +5167,7 @@ msgstr ""
 msgid "Only one search per minute is permitted for not logged in users."
 msgstr ""
 
-#: mod/search.php:136 include/text.php:1003 include/nav.php:118
+#: mod/search.php:136 include/text.php:974 include/nav.php:118
 msgid "Search"
 msgstr ""
 
@@ -5125,16 +5181,16 @@ msgstr ""
 msgid "Search results for: %s"
 msgstr ""
 
-#: mod/directory.php:149 include/identity.php:313 include/identity.php:610
+#: mod/directory.php:149 include/identity.php:314 include/identity.php:611
 msgid "Status:"
 msgstr ""
 
-#: mod/directory.php:151 include/identity.php:315 include/identity.php:621
+#: mod/directory.php:151 include/identity.php:316 include/identity.php:622
 msgid "Homepage:"
 msgstr ""
 
 #: mod/directory.php:203 view/theme/diabook/theme.php:525
-#: view/theme/vier/theme.php:205
+#: view/theme/vier/theme.php:201
 msgid "Global Directory"
 msgstr ""
 
@@ -5193,21 +5249,21 @@ msgstr ""
 msgid "No contacts in common."
 msgstr ""
 
-#: mod/uexport.php:77
+#: mod/uexport.php:29
 msgid "Export account"
 msgstr ""
 
-#: mod/uexport.php:77
+#: mod/uexport.php:29
 msgid ""
 "Export your account info and contacts. Use this to make a backup of your "
 "account and/or to move it to another server."
 msgstr ""
 
-#: mod/uexport.php:78
+#: mod/uexport.php:30
 msgid "Export all"
 msgstr ""
 
-#: mod/uexport.php:78
+#: mod/uexport.php:30
 msgid ""
 "Export your accout info, contacts and all your items as json. Could be a "
 "very big file, and could take a lot of time. Use this to make a full backup "
@@ -5242,7 +5298,7 @@ msgid "Ignore/Hide"
 msgstr ""
 
 #: mod/suggest.php:111 include/contact_widgets.php:35
-#: view/theme/diabook/theme.php:527 view/theme/vier/theme.php:207
+#: view/theme/diabook/theme.php:527 view/theme/vier/theme.php:203
 msgid "Friend Suggestions"
 msgstr ""
 
@@ -5274,11 +5330,11 @@ msgstr ""
 msgid "Romantic Partner"
 msgstr ""
 
-#: mod/profiles.php:344 mod/photos.php:1647 include/conversation.php:508
+#: mod/profiles.php:344 mod/photos.php:1633 include/conversation.php:508
 msgid "Likes"
 msgstr ""
 
-#: mod/profiles.php:348 mod/photos.php:1647 include/conversation.php:508
+#: mod/profiles.php:348 mod/photos.php:1633 include/conversation.php:508
 msgid "Dislikes"
 msgstr ""
 
@@ -5306,7 +5362,7 @@ msgstr ""
 msgid "Homepage"
 msgstr ""
 
-#: mod/profiles.php:375 mod/profiles.php:708
+#: mod/profiles.php:375 mod/profiles.php:686
 msgid "Interests"
 msgstr ""
 
@@ -5314,7 +5370,7 @@ msgstr ""
 msgid "Address"
 msgstr ""
 
-#: mod/profiles.php:386 mod/profiles.php:704
+#: mod/profiles.php:386 mod/profiles.php:682
 msgid "Location"
 msgstr ""
 
@@ -5322,260 +5378,260 @@ msgstr ""
 msgid "Profile updated."
 msgstr ""
 
-#: mod/profiles.php:565
+#: mod/profiles.php:551
 msgid " and "
 msgstr ""
 
-#: mod/profiles.php:573
+#: mod/profiles.php:559
 msgid "public profile"
 msgstr ""
 
-#: mod/profiles.php:576
+#: mod/profiles.php:562
 #, php-format
 msgid "%1$s changed %2$s to &ldquo;%3$s&rdquo;"
 msgstr ""
 
-#: mod/profiles.php:577
+#: mod/profiles.php:563
 #, php-format
 msgid " - Visit %1$s's %2$s"
 msgstr ""
 
-#: mod/profiles.php:580
+#: mod/profiles.php:566
 #, php-format
 msgid "%1$s has an updated %2$s, changing %3$s."
 msgstr ""
 
-#: mod/profiles.php:655
+#: mod/profiles.php:633
 msgid "Hide contacts and friends:"
 msgstr ""
 
-#: mod/profiles.php:660
+#: mod/profiles.php:638
 msgid "Hide your contact/friend list from viewers of this profile?"
 msgstr ""
 
-#: mod/profiles.php:684
+#: mod/profiles.php:662
 msgid "Show more profile fields:"
 msgstr ""
 
-#: mod/profiles.php:695
+#: mod/profiles.php:673
 msgid "Edit Profile Details"
 msgstr ""
 
-#: mod/profiles.php:697
+#: mod/profiles.php:675
 msgid "Change Profile Photo"
 msgstr ""
 
-#: mod/profiles.php:698
+#: mod/profiles.php:676
 msgid "View this profile"
 msgstr ""
 
-#: mod/profiles.php:699
+#: mod/profiles.php:677
 msgid "Create a new profile using these settings"
 msgstr ""
 
-#: mod/profiles.php:700
+#: mod/profiles.php:678
 msgid "Clone this profile"
 msgstr ""
 
-#: mod/profiles.php:701
+#: mod/profiles.php:679
 msgid "Delete this profile"
 msgstr ""
 
-#: mod/profiles.php:702
+#: mod/profiles.php:680
 msgid "Basic information"
 msgstr ""
 
-#: mod/profiles.php:703
+#: mod/profiles.php:681
 msgid "Profile picture"
 msgstr ""
 
-#: mod/profiles.php:705
+#: mod/profiles.php:683
 msgid "Preferences"
 msgstr ""
 
-#: mod/profiles.php:706
+#: mod/profiles.php:684
 msgid "Status information"
 msgstr ""
 
-#: mod/profiles.php:707
+#: mod/profiles.php:685
 msgid "Additional information"
 msgstr ""
 
-#: mod/profiles.php:710
+#: mod/profiles.php:688
 msgid "Profile Name:"
 msgstr ""
 
-#: mod/profiles.php:711
+#: mod/profiles.php:689
 msgid "Your Full Name:"
 msgstr ""
 
-#: mod/profiles.php:712
+#: mod/profiles.php:690
 msgid "Title/Description:"
 msgstr ""
 
-#: mod/profiles.php:713
+#: mod/profiles.php:691
 msgid "Your Gender:"
 msgstr ""
 
-#: mod/profiles.php:714
+#: mod/profiles.php:692
 msgid "Birthday :"
 msgstr ""
 
-#: mod/profiles.php:715
+#: mod/profiles.php:693
 msgid "Street Address:"
 msgstr ""
 
-#: mod/profiles.php:716
+#: mod/profiles.php:694
 msgid "Locality/City:"
 msgstr ""
 
-#: mod/profiles.php:717
+#: mod/profiles.php:695
 msgid "Postal/Zip Code:"
 msgstr ""
 
-#: mod/profiles.php:718
+#: mod/profiles.php:696
 msgid "Country:"
 msgstr ""
 
-#: mod/profiles.php:719
+#: mod/profiles.php:697
 msgid "Region/State:"
 msgstr ""
 
-#: mod/profiles.php:720
+#: mod/profiles.php:698
 msgid "<span class=\"heart\">&hearts;</span> Marital Status:"
 msgstr ""
 
-#: mod/profiles.php:721
+#: mod/profiles.php:699
 msgid "Who: (if applicable)"
 msgstr ""
 
-#: mod/profiles.php:722
+#: mod/profiles.php:700
 msgid "Examples: cathy123, Cathy Williams, cathy@example.com"
 msgstr ""
 
-#: mod/profiles.php:723
+#: mod/profiles.php:701
 msgid "Since [date]:"
 msgstr ""
 
-#: mod/profiles.php:724 include/identity.php:619
+#: mod/profiles.php:702 include/identity.php:620
 msgid "Sexual Preference:"
 msgstr ""
 
-#: mod/profiles.php:725
+#: mod/profiles.php:703
 msgid "Homepage URL:"
 msgstr ""
 
-#: mod/profiles.php:726 include/identity.php:623
+#: mod/profiles.php:704 include/identity.php:624
 msgid "Hometown:"
 msgstr ""
 
-#: mod/profiles.php:727 include/identity.php:627
+#: mod/profiles.php:705 include/identity.php:628
 msgid "Political Views:"
 msgstr ""
 
-#: mod/profiles.php:728
+#: mod/profiles.php:706
 msgid "Religious Views:"
 msgstr ""
 
-#: mod/profiles.php:729
+#: mod/profiles.php:707
 msgid "Public Keywords:"
 msgstr ""
 
-#: mod/profiles.php:730
+#: mod/profiles.php:708
 msgid "Private Keywords:"
 msgstr ""
 
-#: mod/profiles.php:731 include/identity.php:635
+#: mod/profiles.php:709 include/identity.php:636
 msgid "Likes:"
 msgstr ""
 
-#: mod/profiles.php:732 include/identity.php:637
+#: mod/profiles.php:710 include/identity.php:638
 msgid "Dislikes:"
 msgstr ""
 
-#: mod/profiles.php:733
+#: mod/profiles.php:711
 msgid "Example: fishing photography software"
 msgstr ""
 
-#: mod/profiles.php:734
+#: mod/profiles.php:712
 msgid "(Used for suggesting potential friends, can be seen by others)"
 msgstr ""
 
-#: mod/profiles.php:735
+#: mod/profiles.php:713
 msgid "(Used for searching profiles, never shown to others)"
 msgstr ""
 
-#: mod/profiles.php:736
+#: mod/profiles.php:714
 msgid "Tell us about yourself..."
 msgstr ""
 
-#: mod/profiles.php:737
+#: mod/profiles.php:715
 msgid "Hobbies/Interests"
 msgstr ""
 
-#: mod/profiles.php:738
+#: mod/profiles.php:716
 msgid "Contact information and Social Networks"
 msgstr ""
 
-#: mod/profiles.php:739
+#: mod/profiles.php:717
 msgid "Musical interests"
 msgstr ""
 
-#: mod/profiles.php:740
+#: mod/profiles.php:718
 msgid "Books, literature"
 msgstr ""
 
-#: mod/profiles.php:741
+#: mod/profiles.php:719
 msgid "Television"
 msgstr ""
 
-#: mod/profiles.php:742
+#: mod/profiles.php:720
 msgid "Film/dance/culture/entertainment"
 msgstr ""
 
-#: mod/profiles.php:743
+#: mod/profiles.php:721
 msgid "Love/romance"
 msgstr ""
 
-#: mod/profiles.php:744
+#: mod/profiles.php:722
 msgid "Work/employment"
 msgstr ""
 
-#: mod/profiles.php:745
+#: mod/profiles.php:723
 msgid "School/education"
 msgstr ""
 
-#: mod/profiles.php:750
+#: mod/profiles.php:728
 msgid ""
 "This is your <strong>public</strong> profile.<br />It <strong>may</strong> "
 "be visible to anybody using the internet."
 msgstr ""
 
-#: mod/profiles.php:760
+#: mod/profiles.php:738
 msgid "Age: "
 msgstr ""
 
-#: mod/profiles.php:813
+#: mod/profiles.php:791
 msgid "Edit/Manage Profiles"
 msgstr ""
 
-#: mod/profiles.php:814 include/identity.php:260 include/identity.php:286
+#: mod/profiles.php:792 include/identity.php:261 include/identity.php:287
 msgid "Change profile photo"
 msgstr ""
 
-#: mod/profiles.php:815 include/identity.php:261
+#: mod/profiles.php:793 include/identity.php:262
 msgid "Create New Profile"
 msgstr ""
 
-#: mod/profiles.php:826 include/identity.php:271
+#: mod/profiles.php:804 include/identity.php:272
 msgid "Profile Image"
 msgstr ""
 
-#: mod/profiles.php:828 include/identity.php:274
+#: mod/profiles.php:806 include/identity.php:275
 msgid "visible to everybody"
 msgstr ""
 
-#: mod/profiles.php:829 include/identity.php:275
+#: mod/profiles.php:807 include/identity.php:276
 msgid "Edit visibility"
 msgstr ""
 
@@ -5721,7 +5777,7 @@ msgstr ""
 msgid "Visible to:"
 msgstr ""
 
-#: mod/notes.php:46 include/identity.php:730
+#: mod/notes.php:46 include/identity.php:731
 msgid "Personal Notes"
 msgstr ""
 
@@ -5878,15 +5934,15 @@ msgid ""
 "important, please visit http://friendica.com"
 msgstr ""
 
-#: mod/photos.php:99 include/identity.php:705
+#: mod/photos.php:99 include/identity.php:706
 msgid "Photo Albums"
 msgstr ""
 
-#: mod/photos.php:100 mod/photos.php:1899
+#: mod/photos.php:100 mod/photos.php:1885
 msgid "Recent Photos"
 msgstr ""
 
-#: mod/photos.php:103 mod/photos.php:1320 mod/photos.php:1901
+#: mod/photos.php:103 mod/photos.php:1306 mod/photos.php:1887
 msgid "Upload New Photos"
 msgstr ""
 
@@ -5898,7 +5954,7 @@ msgstr ""
 msgid "Album not found."
 msgstr ""
 
-#: mod/photos.php:232 mod/photos.php:244 mod/photos.php:1262
+#: mod/photos.php:232 mod/photos.php:244 mod/photos.php:1248
 msgid "Delete Album"
 msgstr ""
 
@@ -5906,7 +5962,7 @@ msgstr ""
 msgid "Do you really want to delete this photo album and all its photos?"
 msgstr ""
 
-#: mod/photos.php:322 mod/photos.php:333 mod/photos.php:1580
+#: mod/photos.php:322 mod/photos.php:333 mod/photos.php:1566
 msgid "Delete Photo"
 msgstr ""
 
@@ -5923,151 +5979,151 @@ msgstr ""
 msgid "a photo"
 msgstr ""
 
-#: mod/photos.php:819
+#: mod/photos.php:813
 msgid "Image file is empty."
 msgstr ""
 
-#: mod/photos.php:986
+#: mod/photos.php:972
 msgid "No photos selected"
 msgstr ""
 
-#: mod/photos.php:1147
+#: mod/photos.php:1133
 #, php-format
 msgid "You have used %1$.2f Mbytes of %2$.2f Mbytes photo storage."
 msgstr ""
 
-#: mod/photos.php:1182
+#: mod/photos.php:1168
 msgid "Upload Photos"
 msgstr ""
 
-#: mod/photos.php:1186 mod/photos.php:1257
+#: mod/photos.php:1172 mod/photos.php:1243
 msgid "New album name: "
 msgstr ""
 
-#: mod/photos.php:1187
+#: mod/photos.php:1173
 msgid "or existing album name: "
 msgstr ""
 
-#: mod/photos.php:1188
+#: mod/photos.php:1174
 msgid "Do not show a status post for this upload"
 msgstr ""
 
-#: mod/photos.php:1190 mod/photos.php:1575 include/acl_selectors.php:347
+#: mod/photos.php:1176 mod/photos.php:1561 include/acl_selectors.php:347
 msgid "Permissions"
 msgstr ""
 
-#: mod/photos.php:1201
+#: mod/photos.php:1187
 msgid "Private Photo"
 msgstr ""
 
-#: mod/photos.php:1202
+#: mod/photos.php:1188
 msgid "Public Photo"
 msgstr ""
 
-#: mod/photos.php:1270
+#: mod/photos.php:1256
 msgid "Edit Album"
 msgstr ""
 
-#: mod/photos.php:1276
+#: mod/photos.php:1262
 msgid "Show Newest First"
 msgstr ""
 
-#: mod/photos.php:1278
+#: mod/photos.php:1264
 msgid "Show Oldest First"
 msgstr ""
 
-#: mod/photos.php:1306 mod/photos.php:1884
+#: mod/photos.php:1292 mod/photos.php:1870
 msgid "View Photo"
 msgstr ""
 
-#: mod/photos.php:1353
+#: mod/photos.php:1339
 msgid "Permission denied. Access to this item may be restricted."
 msgstr ""
 
-#: mod/photos.php:1355
+#: mod/photos.php:1341
 msgid "Photo not available"
 msgstr ""
 
-#: mod/photos.php:1411
+#: mod/photos.php:1397
 msgid "View photo"
 msgstr ""
 
-#: mod/photos.php:1411
+#: mod/photos.php:1397
 msgid "Edit photo"
 msgstr ""
 
-#: mod/photos.php:1412
+#: mod/photos.php:1398
 msgid "Use as profile photo"
 msgstr ""
 
-#: mod/photos.php:1437
+#: mod/photos.php:1423
 msgid "View Full Size"
 msgstr ""
 
-#: mod/photos.php:1523
+#: mod/photos.php:1509
 msgid "Tags: "
 msgstr ""
 
-#: mod/photos.php:1526
+#: mod/photos.php:1512
 msgid "[Remove any tag]"
 msgstr ""
 
-#: mod/photos.php:1566
+#: mod/photos.php:1552
 msgid "New album name"
 msgstr ""
 
-#: mod/photos.php:1567
+#: mod/photos.php:1553
 msgid "Caption"
 msgstr ""
 
-#: mod/photos.php:1568
+#: mod/photos.php:1554
 msgid "Add a Tag"
 msgstr ""
 
-#: mod/photos.php:1568
+#: mod/photos.php:1554
 msgid "Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping"
 msgstr ""
 
-#: mod/photos.php:1569
+#: mod/photos.php:1555
 msgid "Do not rotate"
 msgstr ""
 
-#: mod/photos.php:1570
+#: mod/photos.php:1556
 msgid "Rotate CW (right)"
 msgstr ""
 
-#: mod/photos.php:1571
+#: mod/photos.php:1557
 msgid "Rotate CCW (left)"
 msgstr ""
 
-#: mod/photos.php:1586
+#: mod/photos.php:1572
 msgid "Private photo"
 msgstr ""
 
-#: mod/photos.php:1587
+#: mod/photos.php:1573
 msgid "Public photo"
 msgstr ""
 
-#: mod/photos.php:1609 include/conversation.php:1182
+#: mod/photos.php:1595 include/conversation.php:1182
 msgid "Share"
 msgstr ""
 
-#: mod/photos.php:1648 include/conversation.php:509
+#: mod/photos.php:1634 include/conversation.php:509
 #: include/conversation.php:1413
 msgid "Attending"
 msgid_plural "Attending"
 msgstr[0] ""
 msgstr[1] ""
 
-#: mod/photos.php:1648 include/conversation.php:509
+#: mod/photos.php:1634 include/conversation.php:509
 msgid "Not attending"
 msgstr ""
 
-#: mod/photos.php:1648 include/conversation.php:509
+#: mod/photos.php:1634 include/conversation.php:509
 msgid "Might attend"
 msgstr ""
 
-#: mod/photos.php:1813
+#: mod/photos.php:1799
 msgid "Map"
 msgstr ""
 
@@ -6127,60 +6183,60 @@ msgstr ""
 msgid "Item was not found."
 msgstr ""
 
-#: boot.php:868
+#: boot.php:870
 msgid "Delete this item?"
 msgstr ""
 
-#: boot.php:871
+#: boot.php:873
 msgid "show fewer"
 msgstr ""
 
-#: boot.php:1292
+#: boot.php:1382
 #, php-format
 msgid "Update %s failed. See error logs."
 msgstr ""
 
-#: boot.php:1404
+#: boot.php:1494
 msgid "Create a New Account"
 msgstr ""
 
-#: boot.php:1429 include/nav.php:72
+#: boot.php:1519 include/nav.php:72
 msgid "Logout"
 msgstr ""
 
-#: boot.php:1432
+#: boot.php:1522
 msgid "Nickname or Email address: "
 msgstr ""
 
-#: boot.php:1433
+#: boot.php:1523
 msgid "Password: "
 msgstr ""
 
-#: boot.php:1434
+#: boot.php:1524
 msgid "Remember me"
 msgstr ""
 
-#: boot.php:1437
+#: boot.php:1527
 msgid "Or login using OpenID: "
 msgstr ""
 
-#: boot.php:1443
+#: boot.php:1533
 msgid "Forgot your password?"
 msgstr ""
 
-#: boot.php:1446
+#: boot.php:1536
 msgid "Website Terms of Service"
 msgstr ""
 
-#: boot.php:1447
+#: boot.php:1537
 msgid "terms of service"
 msgstr ""
 
-#: boot.php:1449
+#: boot.php:1539
 msgid "Website Privacy Policy"
 msgstr ""
 
-#: boot.php:1450
+#: boot.php:1540
 msgid "privacy policy"
 msgstr ""
 
@@ -6224,6 +6280,16 @@ msgstr ""
 msgid "via"
 msgstr ""
 
+#: include/dfrn.php:1092
+#, php-format
+msgid "%s\\'s birthday"
+msgstr ""
+
+#: include/dfrn.php:1093 include/datetime.php:565
+#, php-format
+msgid "Happy Birthday %s"
+msgstr ""
+
 #: include/dbstructure.php:26
 #, php-format
 msgid ""
@@ -6296,7 +6362,7 @@ msgid "Examples: Robert Morgenstein, Fishing"
 msgstr ""
 
 #: include/contact_widgets.php:36 view/theme/diabook/theme.php:526
-#: view/theme/vier/theme.php:206
+#: view/theme/vier/theme.php:202
 msgid "Similar Interests"
 msgstr ""
 
@@ -6305,7 +6371,7 @@ msgid "Random Profile"
 msgstr ""
 
 #: include/contact_widgets.php:38 view/theme/diabook/theme.php:528
-#: view/theme/vier/theme.php:208
+#: view/theme/vier/theme.php:204
 msgid "Invite Friends"
 msgstr ""
 
@@ -6527,58 +6593,58 @@ msgstr ""
 msgid "Show visitors public community forums at the Advanced Profile Page"
 msgstr ""
 
-#: include/follow.php:77
+#: include/follow.php:81
 msgid "Connect URL missing."
 msgstr ""
 
-#: include/follow.php:104
+#: include/follow.php:108
 msgid ""
 "This site is not configured to allow communications with other networks."
 msgstr ""
 
-#: include/follow.php:105 include/follow.php:125
+#: include/follow.php:109 include/follow.php:129
 msgid "No compatible communication protocols or feeds were discovered."
 msgstr ""
 
-#: include/follow.php:123
+#: include/follow.php:127
 msgid "The profile address specified does not provide adequate information."
 msgstr ""
 
-#: include/follow.php:127
+#: include/follow.php:131
 msgid "An author or name was not found."
 msgstr ""
 
-#: include/follow.php:129
+#: include/follow.php:133
 msgid "No browser URL could be matched to this address."
 msgstr ""
 
-#: include/follow.php:131
+#: include/follow.php:135
 msgid ""
 "Unable to match @-style Identity Address with a known protocol or email "
 "contact."
 msgstr ""
 
-#: include/follow.php:132
+#: include/follow.php:136
 msgid "Use mailto: in front of address to force email check."
 msgstr ""
 
-#: include/follow.php:138
+#: include/follow.php:142
 msgid ""
 "The profile address specified belongs to a network which has been disabled "
 "on this site."
 msgstr ""
 
-#: include/follow.php:148
+#: include/follow.php:152
 msgid ""
 "Limited profile. This person will be unable to receive direct/personal "
 "notifications from you."
 msgstr ""
 
-#: include/follow.php:249
+#: include/follow.php:253
 msgid "Unable to retrieve contact information."
 msgstr ""
 
-#: include/follow.php:302
+#: include/follow.php:288
 msgid "following"
 msgstr ""
 
@@ -6593,245 +6659,240 @@ msgstr ""
 msgid "Default privacy group for new contacts"
 msgstr ""
 
-#: include/group.php:239
+#: include/group.php:242
 msgid "Everybody"
 msgstr ""
 
-#: include/group.php:262
+#: include/group.php:265
 msgid "edit"
 msgstr ""
 
-#: include/group.php:285
+#: include/group.php:288
 msgid "Edit groups"
 msgstr ""
 
-#: include/group.php:287
+#: include/group.php:290
 msgid "Edit group"
 msgstr ""
 
-#: include/group.php:288
+#: include/group.php:291
 msgid "Create a new group"
 msgstr ""
 
-#: include/group.php:291
+#: include/group.php:294
 msgid "Contacts not in any group"
 msgstr ""
 
-#: include/datetime.php:43 include/datetime.php:45
+#: include/datetime.php:57 include/datetime.php:59
 msgid "Miscellaneous"
 msgstr ""
 
-#: include/datetime.php:141
+#: include/datetime.php:178
 msgid "YYYY-MM-DD or MM-DD"
 msgstr ""
 
-#: include/datetime.php:271
+#: include/datetime.php:327
 msgid "never"
 msgstr ""
 
-#: include/datetime.php:277
+#: include/datetime.php:333
 msgid "less than a second ago"
 msgstr ""
 
-#: include/datetime.php:287
+#: include/datetime.php:343
 msgid "year"
 msgstr ""
 
-#: include/datetime.php:287
+#: include/datetime.php:343
 msgid "years"
 msgstr ""
 
-#: include/datetime.php:288
+#: include/datetime.php:344
 msgid "months"
 msgstr ""
 
-#: include/datetime.php:289
+#: include/datetime.php:345
 msgid "weeks"
 msgstr ""
 
-#: include/datetime.php:290
+#: include/datetime.php:346
 msgid "days"
 msgstr ""
 
-#: include/datetime.php:291
+#: include/datetime.php:347
 msgid "hour"
 msgstr ""
 
-#: include/datetime.php:291
+#: include/datetime.php:347
 msgid "hours"
 msgstr ""
 
-#: include/datetime.php:292
+#: include/datetime.php:348
 msgid "minute"
 msgstr ""
 
-#: include/datetime.php:292
+#: include/datetime.php:348
 msgid "minutes"
 msgstr ""
 
-#: include/datetime.php:293
+#: include/datetime.php:349
 msgid "second"
 msgstr ""
 
-#: include/datetime.php:293
+#: include/datetime.php:349
 msgid "seconds"
 msgstr ""
 
-#: include/datetime.php:302
+#: include/datetime.php:358
 #, php-format
 msgid "%1$d %2$s ago"
 msgstr ""
 
-#: include/datetime.php:474 include/items.php:2500
+#: include/datetime.php:564
 #, php-format
 msgid "%s's birthday"
 msgstr ""
 
-#: include/datetime.php:475 include/items.php:2501
-#, php-format
-msgid "Happy Birthday %s"
-msgstr ""
-
 #: include/identity.php:42
 msgid "Requested account is not available."
 msgstr ""
 
-#: include/identity.php:95 include/identity.php:284 include/identity.php:662
+#: include/identity.php:95 include/identity.php:285 include/identity.php:663
 msgid "Edit profile"
 msgstr ""
 
-#: include/identity.php:244
+#: include/identity.php:245
 msgid "Atom feed"
 msgstr ""
 
-#: include/identity.php:249
+#: include/identity.php:250
 msgid "Message"
 msgstr ""
 
-#: include/identity.php:255 include/nav.php:185
+#: include/identity.php:256 include/nav.php:185
 msgid "Profiles"
 msgstr ""
 
-#: include/identity.php:255
+#: include/identity.php:256
 msgid "Manage/edit profiles"
 msgstr ""
 
-#: include/identity.php:425 include/identity.php:509
+#: include/identity.php:426 include/identity.php:510
 msgid "g A l F d"
 msgstr ""
 
-#: include/identity.php:426 include/identity.php:510
+#: include/identity.php:427 include/identity.php:511
 msgid "F d"
 msgstr ""
 
-#: include/identity.php:471 include/identity.php:556
+#: include/identity.php:472 include/identity.php:557
 msgid "[today]"
 msgstr ""
 
-#: include/identity.php:483
+#: include/identity.php:484
 msgid "Birthday Reminders"
 msgstr ""
 
-#: include/identity.php:484
+#: include/identity.php:485
 msgid "Birthdays this week:"
 msgstr ""
 
-#: include/identity.php:543
+#: include/identity.php:544
 msgid "[No description]"
 msgstr ""
 
-#: include/identity.php:567
+#: include/identity.php:568
 msgid "Event Reminders"
 msgstr ""
 
-#: include/identity.php:568
+#: include/identity.php:569
 msgid "Events this week:"
 msgstr ""
 
-#: include/identity.php:595
+#: include/identity.php:596
 msgid "j F, Y"
 msgstr ""
 
-#: include/identity.php:596
+#: include/identity.php:597
 msgid "j F"
 msgstr ""
 
-#: include/identity.php:603
+#: include/identity.php:604
 msgid "Birthday:"
 msgstr ""
 
-#: include/identity.php:607
+#: include/identity.php:608
 msgid "Age:"
 msgstr ""
 
-#: include/identity.php:616
+#: include/identity.php:617
 #, php-format
 msgid "for %1$d %2$s"
 msgstr ""
 
-#: include/identity.php:629
+#: include/identity.php:630
 msgid "Religion:"
 msgstr ""
 
-#: include/identity.php:633
+#: include/identity.php:634
 msgid "Hobbies/Interests:"
 msgstr ""
 
-#: include/identity.php:640
+#: include/identity.php:641
 msgid "Contact information and Social Networks:"
 msgstr ""
 
-#: include/identity.php:642
+#: include/identity.php:643
 msgid "Musical interests:"
 msgstr ""
 
-#: include/identity.php:644
+#: include/identity.php:645
 msgid "Books, literature:"
 msgstr ""
 
-#: include/identity.php:646
+#: include/identity.php:647
 msgid "Television:"
 msgstr ""
 
-#: include/identity.php:648
+#: include/identity.php:649
 msgid "Film/dance/culture/entertainment:"
 msgstr ""
 
-#: include/identity.php:650
+#: include/identity.php:651
 msgid "Love/Romance:"
 msgstr ""
 
-#: include/identity.php:652
+#: include/identity.php:653
 msgid "Work/employment:"
 msgstr ""
 
-#: include/identity.php:654
+#: include/identity.php:655
 msgid "School/education:"
 msgstr ""
 
-#: include/identity.php:658
+#: include/identity.php:659
 msgid "Forums:"
 msgstr ""
 
-#: include/identity.php:710 include/identity.php:713 include/nav.php:78
+#: include/identity.php:711 include/identity.php:714 include/nav.php:78
 msgid "Videos"
 msgstr ""
 
-#: include/identity.php:725 include/nav.php:140
+#: include/identity.php:726 include/nav.php:140
 msgid "Events and Calendar"
 msgstr ""
 
-#: include/identity.php:733
+#: include/identity.php:734
 msgid "Only You Can See This"
 msgstr ""
 
 #: include/like.php:167 include/conversation.php:122
-#: include/conversation.php:258 include/text.php:1998
+#: include/conversation.php:258 include/text.php:1921
 #: view/theme/diabook/theme.php:463
 msgid "event"
 msgstr ""
 
-#: include/like.php:184 include/conversation.php:141 include/diaspora.php:2185
+#: include/like.php:184 include/conversation.php:141 include/diaspora.php:2133
 #: view/theme/diabook/theme.php:480
 #, php-format
 msgid "%1$s likes %2$s's %3$s"
@@ -6892,31 +6953,31 @@ msgstr ""
 msgid "stopped following"
 msgstr ""
 
-#: include/Contact.php:337 include/conversation.php:911
+#: include/Contact.php:339 include/conversation.php:911
 msgid "View Status"
 msgstr ""
 
-#: include/Contact.php:339 include/conversation.php:913
+#: include/Contact.php:341 include/conversation.php:913
 msgid "View Photos"
 msgstr ""
 
-#: include/Contact.php:340 include/conversation.php:914
+#: include/Contact.php:342 include/conversation.php:914
 msgid "Network Posts"
 msgstr ""
 
-#: include/Contact.php:341 include/conversation.php:915
+#: include/Contact.php:343 include/conversation.php:915
 msgid "Edit Contact"
 msgstr ""
 
-#: include/Contact.php:342
+#: include/Contact.php:344
 msgid "Drop Contact"
 msgstr ""
 
-#: include/Contact.php:343 include/conversation.php:916
+#: include/Contact.php:345 include/conversation.php:916
 msgid "Send PM"
 msgstr ""
 
-#: include/Contact.php:344 include/conversation.php:920
+#: include/Contact.php:346 include/conversation.php:920
 msgid "Poke"
 msgstr ""
 
@@ -7131,16 +7192,7 @@ msgid_plural "Undecided"
 msgstr[0] ""
 msgstr[1] ""
 
-#: include/forums.php:105 include/text.php:1015 include/nav.php:126
-#: view/theme/vier/theme.php:259
-msgid "Forums"
-msgstr ""
-
-#: include/forums.php:107 view/theme/vier/theme.php:261
-msgid "External link to forum"
-msgstr ""
-
-#: include/network.php:967
+#: include/network.php:975
 msgid "view full size"
 msgstr ""
 
@@ -7176,186 +7228,191 @@ msgstr ""
 msgid "The end"
 msgstr ""
 
-#: include/text.php:894
+#: include/text.php:865
 msgid "No contacts"
 msgstr ""
 
-#: include/text.php:909
+#: include/text.php:880
 #, php-format
 msgid "%d Contact"
 msgid_plural "%d Contacts"
 msgstr[0] ""
 msgstr[1] ""
 
-#: include/text.php:921
+#: include/text.php:892
 msgid "View Contacts"
 msgstr ""
 
-#: include/text.php:1010 include/nav.php:121
+#: include/text.php:981 include/nav.php:121
 msgid "Full Text"
 msgstr ""
 
-#: include/text.php:1011 include/nav.php:122
+#: include/text.php:982 include/nav.php:122
 msgid "Tags"
 msgstr ""
 
-#: include/text.php:1066
+#: include/text.php:986 include/ForumManager.php:112 include/nav.php:126
+#: view/theme/vier/theme.php:255
+msgid "Forums"
+msgstr ""
+
+#: include/text.php:1037
 msgid "poke"
 msgstr ""
 
-#: include/text.php:1066
+#: include/text.php:1037
 msgid "poked"
 msgstr ""
 
-#: include/text.php:1067
+#: include/text.php:1038
 msgid "ping"
 msgstr ""
 
-#: include/text.php:1067
+#: include/text.php:1038
 msgid "pinged"
 msgstr ""
 
-#: include/text.php:1068
+#: include/text.php:1039
 msgid "prod"
 msgstr ""
 
-#: include/text.php:1068
+#: include/text.php:1039
 msgid "prodded"
 msgstr ""
 
-#: include/text.php:1069
+#: include/text.php:1040
 msgid "slap"
 msgstr ""
 
-#: include/text.php:1069
+#: include/text.php:1040
 msgid "slapped"
 msgstr ""
 
-#: include/text.php:1070
+#: include/text.php:1041
 msgid "finger"
 msgstr ""
 
-#: include/text.php:1070
+#: include/text.php:1041
 msgid "fingered"
 msgstr ""
 
-#: include/text.php:1071
+#: include/text.php:1042
 msgid "rebuff"
 msgstr ""
 
-#: include/text.php:1071
+#: include/text.php:1042
 msgid "rebuffed"
 msgstr ""
 
-#: include/text.php:1085
+#: include/text.php:1056
 msgid "happy"
 msgstr ""
 
-#: include/text.php:1086
+#: include/text.php:1057
 msgid "sad"
 msgstr ""
 
-#: include/text.php:1087
+#: include/text.php:1058
 msgid "mellow"
 msgstr ""
 
-#: include/text.php:1088
+#: include/text.php:1059
 msgid "tired"
 msgstr ""
 
-#: include/text.php:1089
+#: include/text.php:1060
 msgid "perky"
 msgstr ""
 
-#: include/text.php:1090
+#: include/text.php:1061
 msgid "angry"
 msgstr ""
 
-#: include/text.php:1091
+#: include/text.php:1062
 msgid "stupified"
 msgstr ""
 
-#: include/text.php:1092
+#: include/text.php:1063
 msgid "puzzled"
 msgstr ""
 
-#: include/text.php:1093
+#: include/text.php:1064
 msgid "interested"
 msgstr ""
 
-#: include/text.php:1094
+#: include/text.php:1065
 msgid "bitter"
 msgstr ""
 
-#: include/text.php:1095
+#: include/text.php:1066
 msgid "cheerful"
 msgstr ""
 
-#: include/text.php:1096
+#: include/text.php:1067
 msgid "alive"
 msgstr ""
 
-#: include/text.php:1097
+#: include/text.php:1068
 msgid "annoyed"
 msgstr ""
 
-#: include/text.php:1098
+#: include/text.php:1069
 msgid "anxious"
 msgstr ""
 
-#: include/text.php:1099
+#: include/text.php:1070
 msgid "cranky"
 msgstr ""
 
-#: include/text.php:1100
+#: include/text.php:1071
 msgid "disturbed"
 msgstr ""
 
-#: include/text.php:1101
+#: include/text.php:1072
 msgid "frustrated"
 msgstr ""
 
-#: include/text.php:1102
+#: include/text.php:1073
 msgid "motivated"
 msgstr ""
 
-#: include/text.php:1103
+#: include/text.php:1074
 msgid "relaxed"
 msgstr ""
 
-#: include/text.php:1104
+#: include/text.php:1075
 msgid "surprised"
 msgstr ""
 
-#: include/text.php:1504
+#: include/text.php:1475
 msgid "bytes"
 msgstr ""
 
-#: include/text.php:1536 include/text.php:1548
+#: include/text.php:1507 include/text.php:1519
 msgid "Click to open/close"
 msgstr ""
 
-#: include/text.php:1722
+#: include/text.php:1645
 msgid "View on separate page"
 msgstr ""
 
-#: include/text.php:1723
+#: include/text.php:1646
 msgid "view on separate page"
 msgstr ""
 
-#: include/text.php:2002
+#: include/text.php:1925
 msgid "activity"
 msgstr ""
 
-#: include/text.php:2005
+#: include/text.php:1928
 msgid "post"
 msgstr ""
 
-#: include/text.php:2173
+#: include/text.php:2096
 msgid "Item filed"
 msgstr ""
 
-#: include/bbcode.php:482 include/bbcode.php:1157 include/bbcode.php:1158
+#: include/bbcode.php:482 include/bbcode.php:1159 include/bbcode.php:1160
 msgid "Image/photo"
 msgstr ""
 
@@ -7371,11 +7428,11 @@ msgid ""
 "\"%s\" target=\"_blank\">post</a>"
 msgstr ""
 
-#: include/bbcode.php:1117 include/bbcode.php:1137
+#: include/bbcode.php:1119 include/bbcode.php:1139
 msgid "$1 wrote:"
 msgstr ""
 
-#: include/bbcode.php:1166 include/bbcode.php:1167
+#: include/bbcode.php:1168 include/bbcode.php:1169
 msgid "Encrypted content"
 msgstr ""
 
@@ -7469,10 +7526,10 @@ msgid "App.net"
 msgstr ""
 
 #: include/contact_selectors.php:103
-msgid "Redmatrix"
+msgid "Hubzilla/Redmatrix"
 msgstr ""
 
-#: include/Scrape.php:624
+#: include/Scrape.php:623
 msgid " on Last.fm"
 msgstr ""
 
@@ -7496,6 +7553,10 @@ msgstr ""
 msgid "This action is not available under your subscription plan."
 msgstr ""
 
+#: include/ForumManager.php:114 view/theme/vier/theme.php:257
+msgid "External link to forum"
+msgstr ""
+
 #: include/nav.php:72
 msgid "End this session"
 msgstr ""
@@ -7648,17 +7709,17 @@ msgstr ""
 msgid "Site map"
 msgstr ""
 
-#: include/api.php:878
+#: include/api.php:906
 #, php-format
 msgid "Daily posting limit of %d posts reached. The post was rejected."
 msgstr ""
 
-#: include/api.php:897
+#: include/api.php:926
 #, php-format
 msgid "Weekly posting limit of %d posts reached. The post was rejected."
 msgstr ""
 
-#: include/api.php:916
+#: include/api.php:947
 #, php-format
 msgid "Monthly posting limit of %d posts reached. The post was rejected."
 msgstr ""
@@ -7781,27 +7842,27 @@ msgid ""
 "\t\tThank you and welcome to %2$s."
 msgstr ""
 
-#: include/diaspora.php:720
+#: include/diaspora.php:719
 msgid "Sharing notification from Diaspora network"
 msgstr ""
 
-#: include/diaspora.php:2625
+#: include/diaspora.php:2570
 msgid "Attachments:"
 msgstr ""
 
-#: include/delivery.php:533
+#: include/delivery.php:438
 msgid "(no subject)"
 msgstr ""
 
-#: include/delivery.php:544 include/enotify.php:37
+#: include/delivery.php:449 include/enotify.php:37
 msgid "noreply"
 msgstr ""
 
-#: include/items.php:4926
+#: include/items.php:1832
 msgid "Do you really want to delete this item?"
 msgstr ""
 
-#: include/items.php:5201
+#: include/items.php:2107
 msgid "Archives"
 msgstr ""
 
@@ -8361,7 +8422,7 @@ msgstr[1] ""
 msgid "Done. You can now login with your username and password"
 msgstr ""
 
-#: index.php:442
+#: index.php:434
 msgid "toggle mobile"
 msgstr ""
 
@@ -8443,7 +8504,7 @@ msgstr ""
 
 #: view/theme/diabook/config.php:160 view/theme/diabook/theme.php:391
 #: view/theme/diabook/theme.php:626 view/theme/vier/config.php:112
-#: view/theme/vier/theme.php:156
+#: view/theme/vier/theme.php:152
 msgid "Community Profiles"
 msgstr ""
 
@@ -8454,19 +8515,19 @@ msgstr ""
 
 #: view/theme/diabook/config.php:162 view/theme/diabook/theme.php:606
 #: view/theme/diabook/theme.php:628 view/theme/vier/config.php:114
-#: view/theme/vier/theme.php:377
+#: view/theme/vier/theme.php:373
 msgid "Connect Services"
 msgstr ""
 
 #: view/theme/diabook/config.php:163 view/theme/diabook/theme.php:523
 #: view/theme/diabook/theme.php:629 view/theme/vier/config.php:115
-#: view/theme/vier/theme.php:203
+#: view/theme/vier/theme.php:199
 msgid "Find Friends"
 msgstr ""
 
 #: view/theme/diabook/config.php:164 view/theme/diabook/theme.php:412
 #: view/theme/diabook/theme.php:630 view/theme/vier/config.php:116
-#: view/theme/vier/theme.php:185
+#: view/theme/vier/theme.php:181
 msgid "Last users"
 msgstr ""
 
@@ -8488,7 +8549,7 @@ msgstr ""
 msgid "Your personal photos"
 msgstr ""
 
-#: view/theme/diabook/theme.php:524 view/theme/vier/theme.php:204
+#: view/theme/diabook/theme.php:524 view/theme/vier/theme.php:200
 msgid "Local Directory"
 msgstr ""
 
@@ -8508,7 +8569,7 @@ msgstr ""
 msgid "Set style"
 msgstr ""
 
-#: view/theme/vier/theme.php:295
+#: view/theme/vier/theme.php:291
 msgid "Quick Start"
 msgstr ""
 
index 78ca97ac94c0c2a4cb050c09ab5094a3962b6c05..df9adbc39218f6c170934ead79ce9948b24d016e 100644 (file)
@@ -1,5 +1,5 @@
 <!DOCTYPE html >\r
-<html itemscope itemtype="http://schema.org/Blog" />\r
+<html itemscope itemtype="http://schema.org/Blog" lang="<?php echo $lang; ?>">\r
 <head>\r
   <title><?php if(x($page,'title')) echo $page['title'] ?></title>\r
   <script>var baseurl="<?php echo $a->get_baseurl() ?>";</script>\r
@@ -8,11 +8,12 @@
 <body>\r
        <?php if(x($page,'nav')) echo $page['nav']; ?>\r
        <aside><?php if(x($page,'aside')) echo $page['aside']; ?></aside>\r
-       <section><?php if(x($page,'content')) echo $page['content']; ?>\r
+       <section>\r
+               <?php if(x($page,'content')) echo $page['content']; ?>\r
+               <div id="pause"></div> <!-- The pause/resume Ajax indicator -->\r
                <div id="page-footer"></div>\r
        </section>\r
        <right_aside><?php if(x($page,'right_aside')) echo $page['right_aside']; ?></right_aside>\r
        <footer><?php if(x($page,'footer')) echo $page['footer']; ?></footer>\r
 </body>\r
 </html>\r
-\r
index 8646bf8e448c532ee66165f6f7e77a831c847faf..41af643ecc49ca37447b9eedee335985933840b1 100644 (file)
@@ -1,6 +1,32 @@
 /* General style rules .*/
 .pull-right { float: right }
 
+/* General designing elements */
+.btn {
+  outline: none;
+  -moz-box-shadow: inset 0px 1px 0px 0px #ffffff;
+  -webkit-box-shadow: inset 0px 1px 0px 0px #ffffff;
+  box-shadow: inset 0px 1px 0px 0px #ffffff;
+  background-color: #ededed;
+  text-indent: 0;
+  border: 1px solid #dcdcdc;
+  display: inline-block;
+  color: #777777;
+  padding: 5px 10px;
+  text-align: center;
+}
+a.btn, a.btn:hover {
+  text-decoration: none;
+  color: inherit;
+}
+
+.menu-popup .divider {
+  height: 1px;
+  margin: 3px 0;
+  overflow: hidden;
+  background-color: #2d2d2d;
+}
+
 /* List of social Networks */
 img.connector, img.connector-disabled {
   height: 40px;
@@ -277,20 +303,20 @@ a {
   margin: 10px 0 10px;
 }
 .version-match {
-    font-weight: bold;
-    color: #00a700;
+  font-weight: bold;
+  color: #00a700;
 }
 .federation-graph {
-    width: 400px; 
-    height: 400px; 
-    float: right; 
-    margin: 20px;
+  width: 400px; 
+  height: 400px; 
+  float: right; 
+  margin: 20px;
 }
 .federation-network-graph {
-    width: 240px; 
-    height: 240px; 
-    float: left; 
-    margin: 20px;
+  width: 240px; 
+  height: 240px; 
+  float: left; 
+  margin: 20px;
 }
 ul.federation-stats,
 ul.credits {
@@ -302,10 +328,10 @@ ul.credits li {
   width: 240px;
 }
 table#federation-stats {
-    width: 100%;
+  width: 100%;
 }
 td.federation-data {
-    border-bottom: 1px solid #000;
+  border-bottom: 1px solid #000;
 }
 
 .contact-entry-photo img {
@@ -329,25 +355,48 @@ td.federation-data {
 }
 
 .crepair-label {
-        margin-top: 10px;
-        float: left;
-        width: 250px;
+  margin-top: 10px;
+  float: left;
+  width: 250px;
 }
 
 .crepair-input {
-        margin-top: 10px;
-        float: left;
-        width: 200px;
+  margin-top: 10px;
+  float: left;
+  width: 200px;
 }
 
 .renderinfo {
-       clear: both;
+  clear: both;
 }
 
 .p-addr {
-       clear: both;    
+  clear: both; 
 }
 
 #live-community {
-       clear: both;
+  clear: both;
+}
+
+/* contact-edit */
+#contact-edit-status-wrapper {
+  border: 1px solid;
+  padding: 10px;
+}
+#contact-edit-actions {
+  float: right;
+  display: inline-block;
+  position: relative;
+}
+#contact-edit-actions > .menu-popup {
+  right: 0;
+  left: auto;
+}
+
+#contact-edit-settings-label:after {
+  content: ' »';
+}
+
+#contact-edit-settings {
+  display: none;
 }
index b2b88bc72f69be6092e2d4aa327a93b0f59f154e..a9df298ca974f78d3c25bb4fbd532a0a32b31012 100644 (file)
@@ -16,7 +16,7 @@ msgstr ""
 "Project-Id-Version: friendica\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2016-01-24 06:49+0100\n"
-"PO-Revision-Date: 2016-01-30 08:43+0000\n"
+"PO-Revision-Date: 2016-02-16 10:29+0000\n"
 "Last-Translator: Sandro Santilli <strk@keybit.net>\n"
 "Language-Team: Italian (http://www.transifex.com/Friendica/friendica/language/it/)\n"
 "MIME-Version: 1.0\n"
@@ -41,8 +41,8 @@ msgstr "Forum"
 #, php-format
 msgid "%d contact edited."
 msgid_plural "%d contacts edited."
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d contatto modificato."
+msgstr[1] "%d contatti modificati"
 
 #: mod/contacts.php:159 mod/contacts.php:383
 msgid "Could not access contact record."
@@ -887,7 +887,7 @@ msgstr "Rimuovi"
 
 #: mod/ostatus_subscribe.php:14
 msgid "Subscribing to OStatus contacts"
-msgstr ""
+msgstr "Iscrizione a contatti OStatus"
 
 #: mod/ostatus_subscribe.php:25
 msgid "No contact provided."
@@ -1943,7 +1943,7 @@ msgstr "Ispeziona Coda di invio"
 
 #: mod/admin.php:163 mod/admin.php:354
 msgid "Federation Statistics"
-msgstr ""
+msgstr "Statistiche sulla Federazione"
 
 #: mod/admin.php:177 mod/admin.php:188 mod/admin.php:1792
 msgid "Logs"
@@ -1951,7 +1951,7 @@ msgstr "Log"
 
 #: mod/admin.php:178 mod/admin.php:1859
 msgid "View Logs"
-msgstr ""
+msgstr "Vedi i log"
 
 #: mod/admin.php:179
 msgid "probe address"
@@ -1982,7 +1982,7 @@ msgid ""
 "This page offers you some numbers to the known part of the federated social "
 "network your Friendica node is part of. These numbers are not complete but "
 "only reflect the part of the network your node is aware of."
-msgstr ""
+msgstr "Questa pagina offre alcuni numeri riguardo la porzione del social network federato di cui il tuo nodo Friendica fa parte. Questi numeri non sono completi ma riflettono esclusivamente la porzione di rete di cui il tuo nodo e' a conoscenza."
 
 #: mod/admin.php:348
 msgid ""
index 606ae1e16ca2e64f08bab8491ee11cbc73aecb7c..131e03080b68b96b9dfea55be2d3c9498a069572 100644 (file)
@@ -8,8 +8,8 @@ function string_plural_select_it($n){
 $a->strings["Network:"] = "Rete:";
 $a->strings["Forum"] = "Forum";
 $a->strings["%d contact edited."] = array(
-       0 => "",
-       1 => "",
+       0 => "%d contatto modificato.",
+       1 => "%d contatti modificati",
 );
 $a->strings["Could not access contact record."] = "Non è possibile accedere al contatto.";
 $a->strings["Could not locate selected profile."] = "Non riesco a trovare il profilo selezionato.";
@@ -186,7 +186,7 @@ $a->strings["Tag removed"] = "Tag rimosso";
 $a->strings["Remove Item Tag"] = "Rimuovi il tag";
 $a->strings["Select a tag to remove: "] = "Seleziona un tag da rimuovere: ";
 $a->strings["Remove"] = "Rimuovi";
-$a->strings["Subscribing to OStatus contacts"] = "";
+$a->strings["Subscribing to OStatus contacts"] = "Iscrizione a contatti OStatus";
 $a->strings["No contact provided."] = "Nessun contatto disponibile.";
 $a->strings["Couldn't fetch information for contact."] = "Non è stato possibile recuperare le informazioni del contatto.";
 $a->strings["Couldn't fetch friends for contact."] = "Non è stato possibile recuperare gli amici del contatto.";
@@ -419,16 +419,16 @@ $a->strings["Themes"] = "Temi";
 $a->strings["Additional features"] = "Funzionalità aggiuntive";
 $a->strings["DB updates"] = "Aggiornamenti Database";
 $a->strings["Inspect Queue"] = "Ispeziona Coda di invio";
-$a->strings["Federation Statistics"] = "";
+$a->strings["Federation Statistics"] = "Statistiche sulla Federazione";
 $a->strings["Logs"] = "Log";
-$a->strings["View Logs"] = "";
+$a->strings["View Logs"] = "Vedi i log";
 $a->strings["probe address"] = "controlla indirizzo";
 $a->strings["check webfinger"] = "verifica webfinger";
 $a->strings["Admin"] = "Amministrazione";
 $a->strings["Plugin Features"] = "Impostazioni Plugins";
 $a->strings["diagnostics"] = "diagnostiche";
 $a->strings["User registrations waiting for confirmation"] = "Utenti registrati in attesa di conferma";
-$a->strings["This page offers you some numbers to the known part of the federated social network your Friendica node is part of. These numbers are not complete but only reflect the part of the network your node is aware of."] = "";
+$a->strings["This page offers you some numbers to the known part of the federated social network your Friendica node is part of. These numbers are not complete but only reflect the part of the network your node is aware of."] = "Questa pagina offre alcuni numeri riguardo la porzione del social network federato di cui il tuo nodo Friendica fa parte. Questi numeri non sono completi ma riflettono esclusivamente la porzione di rete di cui il tuo nodo e' a conoscenza.";
 $a->strings["The <em>Auto Discovered Contact Directory</em> feature is not enabled, it will improve the data displayed here."] = "";
 $a->strings["Administration"] = "Amministrazione";
 $a->strings["Currently this node is aware of %d nodes from the following platforms:"] = "";
index 4b0e372dd62827528d77cc0a2823f0ccc61e645a..7f65e8f1becc580b4e30cec7b5f26093cedece92 100644 (file)
@@ -1,8 +1,9 @@
 # FRIENDICA Distributed Social Network
 # Copyright (C) 2010, 2011, 2012, 2013 the Friendica Project
 # This file is distributed under the same license as the Friendica package.
-# 
+#
 # Translators:
+# Aliaksei Sakalou <nullbsd@gmail.com>, 2016
 # Alex <info@pixelbits.de>, 2013
 # vislav <bizadmin@list.ru>, 2014
 # Alex <info@pixelbits.de>, 2013
@@ -15,1151 +16,1317 @@ msgid ""
 msgstr ""
 "Project-Id-Version: friendica\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-09 08:57+0100\n"
-"PO-Revision-Date: 2015-02-09 09:46+0000\n"
-"Last-Translator: fabrixxm <fabrix.xm@gmail.com>\n"
-"Language-Team: Russian (http://www.transifex.com/projects/p/friendica/language/ru/)\n"
+"POT-Creation-Date: 2016-01-24 06:49+0100\n"
+"PO-Revision-Date: 2016-02-16 18:28+0300\n"
+"Last-Translator: Aliaksei Sakalou <nullbsd@gmail.com>\n"
+"Language-Team: Russian (http://www.transifex.com/Friendica/friendica/"
+"language/ru/)\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Language: ru\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
+"%100>=11 && n%100<=14)? 2 : 3);\n"
+"X-Generator: Poedit 1.5.4\n"
 
-#: ../../mod/contacts.php:108
+#: mod/contacts.php:50 include/identity.php:395
+msgid "Network:"
+msgstr "Сеть:"
+
+#: mod/contacts.php:51 mod/contacts.php:961 mod/videos.php:37
+#: mod/viewcontacts.php:105 mod/dirfind.php:214 mod/network.php:598
+#: mod/allfriends.php:77 mod/match.php:82 mod/directory.php:172
+#: mod/common.php:123 mod/suggest.php:95 mod/photos.php:41
+#: include/identity.php:298
+msgid "Forum"
+msgstr "Форум"
+
+#: mod/contacts.php:128
 #, php-format
 msgid "%d contact edited."
-msgid_plural "%d contacts edited"
-msgstr[0] "%d контакт изменён."
-msgstr[1] "%d контакты изменены"
-msgstr[2] "%d контакты изменены"
+msgid_plural "%d contacts edited."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
 
-#: ../../mod/contacts.php:139 ../../mod/contacts.php:272
+#: mod/contacts.php:159 mod/contacts.php:383
 msgid "Could not access contact record."
 msgstr "Не удалось получить доступ к записи контакта."
 
-#: ../../mod/contacts.php:153
+#: mod/contacts.php:173
 msgid "Could not locate selected profile."
 msgstr "Не удалось найти выбранный профиль."
 
-#: ../../mod/contacts.php:186
+#: mod/contacts.php:206
 msgid "Contact updated."
 msgstr "Контакт обновлен."
 
-#: ../../mod/contacts.php:188 ../../mod/dfrn_request.php:576
+#: mod/contacts.php:208 mod/dfrn_request.php:575
 msgid "Failed to update contact record."
 msgstr "Не удалось обновить запись контакта."
 
-#: ../../mod/contacts.php:254 ../../mod/manage.php:96
-#: ../../mod/display.php:499 ../../mod/profile_photo.php:19
-#: ../../mod/profile_photo.php:169 ../../mod/profile_photo.php:180
-#: ../../mod/profile_photo.php:193 ../../mod/follow.php:9
-#: ../../mod/item.php:168 ../../mod/item.php:184 ../../mod/group.php:19
-#: ../../mod/dfrn_confirm.php:55 ../../mod/fsuggest.php:78
-#: ../../mod/wall_upload.php:66 ../../mod/viewcontacts.php:24
-#: ../../mod/notifications.php:66 ../../mod/message.php:38
-#: ../../mod/message.php:174 ../../mod/crepair.php:119
-#: ../../mod/nogroup.php:25 ../../mod/network.php:4 ../../mod/allfriends.php:9
-#: ../../mod/events.php:140 ../../mod/install.php:151
-#: ../../mod/wallmessage.php:9 ../../mod/wallmessage.php:33
-#: ../../mod/wallmessage.php:79 ../../mod/wallmessage.php:103
-#: ../../mod/wall_attach.php:55 ../../mod/settings.php:102
-#: ../../mod/settings.php:596 ../../mod/settings.php:601
-#: ../../mod/register.php:42 ../../mod/delegate.php:12 ../../mod/mood.php:114
-#: ../../mod/suggest.php:58 ../../mod/profiles.php:165
-#: ../../mod/profiles.php:618 ../../mod/editpost.php:10 ../../mod/api.php:26
-#: ../../mod/api.php:31 ../../mod/notes.php:20 ../../mod/poke.php:135
-#: ../../mod/invite.php:15 ../../mod/invite.php:101 ../../mod/photos.php:134
-#: ../../mod/photos.php:1050 ../../mod/regmod.php:110 ../../mod/uimport.php:23
-#: ../../mod/attach.php:33 ../../include/items.php:4712 ../../index.php:369
+#: mod/contacts.php:365 mod/manage.php:96 mod/display.php:509
+#: mod/profile_photo.php:19 mod/profile_photo.php:175
+#: mod/profile_photo.php:186 mod/profile_photo.php:199
+#: mod/ostatus_subscribe.php:9 mod/follow.php:11 mod/follow.php:73
+#: mod/follow.php:155 mod/item.php:180 mod/item.php:192 mod/group.php:19
+#: mod/dfrn_confirm.php:55 mod/fsuggest.php:78 mod/wall_upload.php:77
+#: mod/wall_upload.php:80 mod/viewcontacts.php:40 mod/notifications.php:69
+#: mod/message.php:45 mod/message.php:181 mod/crepair.php:117
+#: mod/dirfind.php:11 mod/nogroup.php:25 mod/network.php:4
+#: mod/allfriends.php:12 mod/events.php:165 mod/wallmessage.php:9
+#: mod/wallmessage.php:33 mod/wallmessage.php:79 mod/wallmessage.php:103
+#: mod/wall_attach.php:67 mod/wall_attach.php:70 mod/settings.php:20
+#: mod/settings.php:126 mod/settings.php:646 mod/register.php:42
+#: mod/delegate.php:12 mod/common.php:18 mod/mood.php:114 mod/suggest.php:58
+#: mod/profiles.php:165 mod/profiles.php:615 mod/editpost.php:10
+#: mod/api.php:26 mod/api.php:31 mod/notes.php:22 mod/poke.php:149
+#: mod/repair_ostatus.php:9 mod/invite.php:15 mod/invite.php:101
+#: mod/photos.php:171 mod/photos.php:1105 mod/regmod.php:110
+#: mod/uimport.php:23 mod/attach.php:33 include/items.php:5096 index.php:383
 msgid "Permission denied."
 msgstr "Нет разрешения."
 
-#: ../../mod/contacts.php:287
+#: mod/contacts.php:404
 msgid "Contact has been blocked"
 msgstr "Контакт заблокирован"
 
-#: ../../mod/contacts.php:287
+#: mod/contacts.php:404
 msgid "Contact has been unblocked"
 msgstr "Контакт разблокирован"
 
-#: ../../mod/contacts.php:298
+#: mod/contacts.php:415
 msgid "Contact has been ignored"
 msgstr "Контакт проигнорирован"
 
-#: ../../mod/contacts.php:298
+#: mod/contacts.php:415
 msgid "Contact has been unignored"
 msgstr "У контакта отменено игнорирование"
 
-#: ../../mod/contacts.php:310
+#: mod/contacts.php:427
 msgid "Contact has been archived"
 msgstr "Контакт заархивирован"
 
-#: ../../mod/contacts.php:310
+#: mod/contacts.php:427
 msgid "Contact has been unarchived"
 msgstr "Контакт разархивирован"
 
-#: ../../mod/contacts.php:335 ../../mod/contacts.php:711
+#: mod/contacts.php:454 mod/contacts.php:802
 msgid "Do you really want to delete this contact?"
 msgstr "Вы действительно хотите удалить этот контакт?"
 
-#: ../../mod/contacts.php:337 ../../mod/message.php:209
-#: ../../mod/settings.php:1010 ../../mod/settings.php:1016
-#: ../../mod/settings.php:1024 ../../mod/settings.php:1028
-#: ../../mod/settings.php:1033 ../../mod/settings.php:1039
-#: ../../mod/settings.php:1045 ../../mod/settings.php:1051
-#: ../../mod/settings.php:1081 ../../mod/settings.php:1082
-#: ../../mod/settings.php:1083 ../../mod/settings.php:1084
-#: ../../mod/settings.php:1085 ../../mod/dfrn_request.php:830
-#: ../../mod/register.php:233 ../../mod/suggest.php:29
-#: ../../mod/profiles.php:661 ../../mod/profiles.php:664 ../../mod/api.php:105
-#: ../../include/items.php:4557
+#: mod/contacts.php:456 mod/follow.php:110 mod/message.php:216
+#: mod/settings.php:1103 mod/settings.php:1109 mod/settings.php:1117
+#: mod/settings.php:1121 mod/settings.php:1126 mod/settings.php:1132
+#: mod/settings.php:1138 mod/settings.php:1144 mod/settings.php:1170
+#: mod/settings.php:1171 mod/settings.php:1172 mod/settings.php:1173
+#: mod/settings.php:1174 mod/dfrn_request.php:857 mod/register.php:238
+#: mod/suggest.php:29 mod/profiles.php:658 mod/profiles.php:661
+#: mod/profiles.php:687 mod/api.php:105 include/items.php:4928
 msgid "Yes"
 msgstr "Да"
 
-#: ../../mod/contacts.php:340 ../../mod/tagrm.php:11 ../../mod/tagrm.php:94
-#: ../../mod/message.php:212 ../../mod/fbrowser.php:81
-#: ../../mod/fbrowser.php:116 ../../mod/settings.php:615
-#: ../../mod/settings.php:641 ../../mod/dfrn_request.php:844
-#: ../../mod/suggest.php:32 ../../mod/editpost.php:148
-#: ../../mod/photos.php:203 ../../mod/photos.php:292
-#: ../../include/conversation.php:1129 ../../include/items.php:4560
+#: mod/contacts.php:459 mod/tagrm.php:11 mod/tagrm.php:94 mod/follow.php:121
+#: mod/videos.php:131 mod/message.php:219 mod/fbrowser.php:93
+#: mod/fbrowser.php:128 mod/settings.php:660 mod/settings.php:686
+#: mod/dfrn_request.php:871 mod/suggest.php:32 mod/editpost.php:148
+#: mod/photos.php:247 mod/photos.php:336 include/conversation.php:1220
+#: include/items.php:4931
 msgid "Cancel"
 msgstr "Отмена"
 
-#: ../../mod/contacts.php:352
+#: mod/contacts.php:471
 msgid "Contact has been removed."
 msgstr "Контакт удален."
 
-#: ../../mod/contacts.php:390
+#: mod/contacts.php:512
 #, php-format
 msgid "You are mutual friends with %s"
 msgstr "У Вас взаимная дружба с %s"
 
-#: ../../mod/contacts.php:394
+#: mod/contacts.php:516
 #, php-format
 msgid "You are sharing with %s"
 msgstr "Вы делитесь с %s"
 
-#: ../../mod/contacts.php:399
+#: mod/contacts.php:521
 #, php-format
 msgid "%s is sharing with you"
 msgstr "%s делитса с Вами"
 
-#: ../../mod/contacts.php:416
+#: mod/contacts.php:541
 msgid "Private communications are not available for this contact."
 msgstr "Личные коммуникации недоступны для этого контакта."
 
-#: ../../mod/contacts.php:419 ../../mod/admin.php:569
+#: mod/contacts.php:544 mod/admin.php:822
 msgid "Never"
 msgstr "Никогда"
 
-#: ../../mod/contacts.php:423
+#: mod/contacts.php:548
 msgid "(Update was successful)"
 msgstr "(Обновление было успешно)"
 
-#: ../../mod/contacts.php:423
+#: mod/contacts.php:548
 msgid "(Update was not successful)"
 msgstr "(Обновление не удалось)"
 
-#: ../../mod/contacts.php:425
+#: mod/contacts.php:550
 msgid "Suggest friends"
 msgstr "Предложить друзей"
 
-#: ../../mod/contacts.php:429
+#: mod/contacts.php:554
 #, php-format
 msgid "Network type: %s"
 msgstr "Сеть: %s"
 
-#: ../../mod/contacts.php:432 ../../include/contact_widgets.php:200
-#, php-format
-msgid "%d contact in common"
-msgid_plural "%d contacts in common"
-msgstr[0] "%d Контакт"
-msgstr[1] "%d Контактов"
-msgstr[2] "%d Контактов"
-
-#: ../../mod/contacts.php:437
-msgid "View all contacts"
-msgstr "Показать все контакты"
-
-#: ../../mod/contacts.php:442 ../../mod/contacts.php:501
-#: ../../mod/contacts.php:714 ../../mod/admin.php:1009
-msgid "Unblock"
-msgstr "Разблокировать"
-
-#: ../../mod/contacts.php:442 ../../mod/contacts.php:501
-#: ../../mod/contacts.php:714 ../../mod/admin.php:1008
-msgid "Block"
-msgstr "Заблокировать"
-
-#: ../../mod/contacts.php:445
-msgid "Toggle Blocked status"
-msgstr "Изменить статус блокированности (заблокировать/разблокировать)"
-
-#: ../../mod/contacts.php:448 ../../mod/contacts.php:502
-#: ../../mod/contacts.php:715
-msgid "Unignore"
-msgstr "Не игнорировать"
-
-#: ../../mod/contacts.php:448 ../../mod/contacts.php:502
-#: ../../mod/contacts.php:715 ../../mod/notifications.php:51
-#: ../../mod/notifications.php:164 ../../mod/notifications.php:210
-msgid "Ignore"
-msgstr "Игнорировать"
-
-#: ../../mod/contacts.php:451
-msgid "Toggle Ignored status"
-msgstr "Изменить статус игнорирования"
-
-#: ../../mod/contacts.php:455 ../../mod/contacts.php:716
-msgid "Unarchive"
-msgstr "Разархивировать"
-
-#: ../../mod/contacts.php:455 ../../mod/contacts.php:716
-msgid "Archive"
-msgstr "Архивировать"
+#: mod/contacts.php:567
+msgid "Communications lost with this contact!"
+msgstr "Связь с контактом утеряна!"
 
-#: ../../mod/contacts.php:458
-msgid "Toggle Archive status"
-msgstr "Сменить статус архивации (архивирова/не архивировать)"
+#: mod/contacts.php:570
+msgid "Fetch further information for feeds"
+msgstr ""
 
-#: ../../mod/contacts.php:461
-msgid "Repair"
-msgstr "Ð\92оÑ\81Ñ\81Ñ\82ановиÑ\82Ñ\8c"
+#: mod/contacts.php:571 mod/admin.php:831
+msgid "Disabled"
+msgstr "Ð\9eÑ\82клÑ\8eÑ\87еннÑ\8bй"
 
-#: ../../mod/contacts.php:464
-msgid "Advanced Contact Settings"
-msgstr "Дополнительные Настройки Контакта"
+#: mod/contacts.php:571
+msgid "Fetch information"
+msgstr ""
 
-#: ../../mod/contacts.php:470
-msgid "Communications lost with this contact!"
-msgstr "Связь с контактом утеряна!"
+#: mod/contacts.php:571
+msgid "Fetch information and keywords"
+msgstr ""
 
-#: ../../mod/contacts.php:473
-msgid "Contact Editor"
-msgstr "Редактор контакта"
-
-#: ../../mod/contacts.php:475 ../../mod/manage.php:110
-#: ../../mod/fsuggest.php:107 ../../mod/message.php:335
-#: ../../mod/message.php:564 ../../mod/crepair.php:186
-#: ../../mod/events.php:478 ../../mod/content.php:710
-#: ../../mod/install.php:248 ../../mod/install.php:286 ../../mod/mood.php:137
-#: ../../mod/profiles.php:686 ../../mod/localtime.php:45
-#: ../../mod/poke.php:199 ../../mod/invite.php:140 ../../mod/photos.php:1084
-#: ../../mod/photos.php:1203 ../../mod/photos.php:1514
-#: ../../mod/photos.php:1565 ../../mod/photos.php:1609
-#: ../../mod/photos.php:1697 ../../object/Item.php:678
-#: ../../view/theme/cleanzero/config.php:80
-#: ../../view/theme/dispy/config.php:70 ../../view/theme/quattro/config.php:64
-#: ../../view/theme/diabook/config.php:148
-#: ../../view/theme/diabook/theme.php:633 ../../view/theme/vier/config.php:53
-#: ../../view/theme/duepuntozero/config.php:59
+#: mod/contacts.php:587 mod/manage.php:143 mod/fsuggest.php:107
+#: mod/message.php:342 mod/message.php:525 mod/crepair.php:196
+#: mod/events.php:574 mod/content.php:712 mod/install.php:261
+#: mod/install.php:299 mod/mood.php:137 mod/profiles.php:696
+#: mod/localtime.php:45 mod/poke.php:198 mod/invite.php:140
+#: mod/photos.php:1137 mod/photos.php:1261 mod/photos.php:1579
+#: mod/photos.php:1630 mod/photos.php:1678 mod/photos.php:1766
+#: object/Item.php:710 view/theme/cleanzero/config.php:80
+#: view/theme/dispy/config.php:70 view/theme/quattro/config.php:64
+#: view/theme/diabook/config.php:148 view/theme/diabook/theme.php:633
+#: view/theme/vier/config.php:107 view/theme/duepuntozero/config.php:59
 msgid "Submit"
-msgstr "Ð\9fодÑ\82веÑ\80дить"
+msgstr "Ð\94обавить"
 
-#: ../../mod/contacts.php:476
+#: mod/contacts.php:588
 msgid "Profile Visibility"
 msgstr "Видимость профиля"
 
-#: ../../mod/contacts.php:477
+#: mod/contacts.php:589
 #, php-format
 msgid ""
 "Please choose the profile you would like to display to %s when viewing your "
 "profile securely."
-msgstr "Пожалуйста, выберите профиль, который вы хотите отображать %s, когда просмотр вашего профиля безопасен."
+msgstr ""
+"Пожалуйста, выберите профиль, который вы хотите отображать %s, когда "
+"просмотр вашего профиля безопасен."
 
-#: ../../mod/contacts.php:478
+#: mod/contacts.php:590
 msgid "Contact Information / Notes"
 msgstr "Информация о контакте / Заметки"
 
-#: ../../mod/contacts.php:479
+#: mod/contacts.php:591
 msgid "Edit contact notes"
 msgstr "Редактировать заметки контакта"
 
-#: ../../mod/contacts.php:484 ../../mod/contacts.php:679
-#: ../../mod/viewcontacts.php:64 ../../mod/nogroup.php:40
+#: mod/contacts.php:596 mod/contacts.php:952 mod/viewcontacts.php:97
+#: mod/nogroup.php:41
 #, php-format
 msgid "Visit %s's profile [%s]"
 msgstr "Посетить профиль %s [%s]"
 
-#: ../../mod/contacts.php:485
+#: mod/contacts.php:597
 msgid "Block/Unblock contact"
 msgstr "Блокировать / Разблокировать контакт"
 
-#: ../../mod/contacts.php:486
+#: mod/contacts.php:598
 msgid "Ignore contact"
 msgstr "Игнорировать контакт"
 
-#: ../../mod/contacts.php:487
+#: mod/contacts.php:599
 msgid "Repair URL settings"
 msgstr "Восстановить настройки URL"
 
-#: ../../mod/contacts.php:488
+#: mod/contacts.php:600
 msgid "View conversations"
 msgstr "Просмотр бесед"
 
-#: ../../mod/contacts.php:490
+#: mod/contacts.php:602
 msgid "Delete contact"
 msgstr "Удалить контакт"
 
-#: ../../mod/contacts.php:494
+#: mod/contacts.php:606
 msgid "Last update:"
 msgstr "Последнее обновление: "
 
-#: ../../mod/contacts.php:496
+#: mod/contacts.php:608
 msgid "Update public posts"
 msgstr "Обновить публичные сообщения"
 
-#: ../../mod/contacts.php:498 ../../mod/admin.php:1503
+#: mod/contacts.php:610
 msgid "Update now"
 msgstr "Обновить сейчас"
 
-#: ../../mod/contacts.php:505
+#: mod/contacts.php:612 mod/follow.php:103 mod/dirfind.php:196
+#: mod/allfriends.php:65 mod/match.php:71 mod/suggest.php:82
+#: include/contact_widgets.php:32 include/Contact.php:297
+#: include/conversation.php:924
+msgid "Connect/Follow"
+msgstr "Подключиться/Следовать"
+
+#: mod/contacts.php:615 mod/contacts.php:806 mod/contacts.php:865
+#: mod/admin.php:1312
+msgid "Unblock"
+msgstr "Разблокировать"
+
+#: mod/contacts.php:615 mod/contacts.php:806 mod/contacts.php:865
+#: mod/admin.php:1311
+msgid "Block"
+msgstr "Заблокировать"
+
+#: mod/contacts.php:616 mod/contacts.php:807 mod/contacts.php:872
+msgid "Unignore"
+msgstr "Не игнорировать"
+
+#: mod/contacts.php:616 mod/contacts.php:807 mod/contacts.php:872
+#: mod/notifications.php:54 mod/notifications.php:179
+#: mod/notifications.php:259
+msgid "Ignore"
+msgstr "Игнорировать"
+
+#: mod/contacts.php:619
 msgid "Currently blocked"
 msgstr "В настоящее время заблокирован"
 
-#: ../../mod/contacts.php:506
+#: mod/contacts.php:620
 msgid "Currently ignored"
 msgstr "В настоящее время игнорируется"
 
-#: ../../mod/contacts.php:507
+#: mod/contacts.php:621
 msgid "Currently archived"
 msgstr "В данный момент архивирован"
 
-#: ../../mod/contacts.php:508 ../../mod/notifications.php:157
-#: ../../mod/notifications.php:204
+#: mod/contacts.php:622 mod/notifications.php:172 mod/notifications.php:251
 msgid "Hide this contact from others"
 msgstr "Скрыть этот контакт от других"
 
-#: ../../mod/contacts.php:508
+#: mod/contacts.php:622
 msgid ""
 "Replies/likes to your public posts <strong>may</strong> still be visible"
 msgstr "Ответы/лайки ваших публичных сообщений <strong>будут</strong> видимы."
 
-#: ../../mod/contacts.php:509
+#: mod/contacts.php:623
 msgid "Notification for new posts"
 msgstr ""
 
-#: ../../mod/contacts.php:509
+#: mod/contacts.php:623
 msgid "Send a notification of every new post of this contact"
 msgstr ""
 
-#: ../../mod/contacts.php:510
-msgid "Fetch further information for feeds"
-msgstr ""
-
-#: ../../mod/contacts.php:511
-msgid "Disabled"
-msgstr ""
-
-#: ../../mod/contacts.php:511
-msgid "Fetch information"
-msgstr ""
-
-#: ../../mod/contacts.php:511
-msgid "Fetch information and keywords"
-msgstr ""
-
-#: ../../mod/contacts.php:513
+#: mod/contacts.php:626
 msgid "Blacklisted keywords"
 msgstr ""
 
-#: ../../mod/contacts.php:513
+#: mod/contacts.php:626
 msgid ""
 "Comma separated list of keywords that should not be converted to hashtags, "
 "when \"Fetch information and keywords\" is selected"
 msgstr ""
 
-#: ../../mod/contacts.php:564
+#: mod/contacts.php:633 mod/follow.php:126 mod/notifications.php:255
+msgid "Profile URL"
+msgstr "URL профиля"
+
+#: mod/contacts.php:636 mod/notifications.php:244 mod/events.php:566
+#: mod/directory.php:145 include/identity.php:308 include/bb2diaspora.php:170
+#: include/event.php:36 include/event.php:60
+msgid "Location:"
+msgstr "Откуда:"
+
+#: mod/contacts.php:638 mod/notifications.php:246 mod/directory.php:153
+#: include/identity.php:317 include/identity.php:631
+msgid "About:"
+msgstr "О себе:"
+
+#: mod/contacts.php:640 mod/follow.php:134 mod/notifications.php:248
+#: include/identity.php:625
+msgid "Tags:"
+msgstr "Ключевые слова: "
+
+#: mod/contacts.php:685
 msgid "Suggestions"
 msgstr "Предложения"
 
-#: ../../mod/contacts.php:567
+#: mod/contacts.php:688
 msgid "Suggest potential friends"
 msgstr "Предложить потенциального знакомого"
 
-#: ../../mod/contacts.php:570 ../../mod/group.php:194
+#: mod/contacts.php:693 mod/group.php:192
 msgid "All Contacts"
 msgstr "Все контакты"
 
-#: ../../mod/contacts.php:573
+#: mod/contacts.php:696
 msgid "Show all contacts"
 msgstr "Показать все контакты"
 
-#: ../../mod/contacts.php:576
+#: mod/contacts.php:701
 msgid "Unblocked"
 msgstr "Не блокирован"
 
-#: ../../mod/contacts.php:579
+#: mod/contacts.php:704
 msgid "Only show unblocked contacts"
 msgstr "Показать только не блокированные контакты"
 
-#: ../../mod/contacts.php:583
+#: mod/contacts.php:710
 msgid "Blocked"
 msgstr "Заблокирован"
 
-#: ../../mod/contacts.php:586
+#: mod/contacts.php:713
 msgid "Only show blocked contacts"
 msgstr "Показать только блокированные контакты"
 
-#: ../../mod/contacts.php:590
+#: mod/contacts.php:719
 msgid "Ignored"
 msgstr "Игнорирован"
 
-#: ../../mod/contacts.php:593
+#: mod/contacts.php:722
 msgid "Only show ignored contacts"
 msgstr "Показать только игнорируемые контакты"
 
-#: ../../mod/contacts.php:597
+#: mod/contacts.php:728
 msgid "Archived"
 msgstr "Архивированные"
 
-#: ../../mod/contacts.php:600
+#: mod/contacts.php:731
 msgid "Only show archived contacts"
 msgstr "Показывать только архивные контакты"
 
-#: ../../mod/contacts.php:604
+#: mod/contacts.php:737
 msgid "Hidden"
 msgstr "Скрытые"
 
-#: ../../mod/contacts.php:607
+#: mod/contacts.php:740
 msgid "Only show hidden contacts"
 msgstr "Показывать только скрытые контакты"
 
-#: ../../mod/contacts.php:655
-msgid "Mutual Friendship"
-msgstr "Взаимная дружба"
-
-#: ../../mod/contacts.php:659
-msgid "is a fan of yours"
-msgstr "является вашим поклонником"
-
-#: ../../mod/contacts.php:663
-msgid "you are a fan of"
-msgstr "Вы - поклонник"
-
-#: ../../mod/contacts.php:680 ../../mod/nogroup.php:41
-msgid "Edit contact"
-msgstr "Редактировать контакт"
-
-#: ../../mod/contacts.php:702 ../../include/nav.php:177
-#: ../../view/theme/diabook/theme.php:125
+#: mod/contacts.php:793 mod/contacts.php:841 mod/viewcontacts.php:116
+#: include/identity.php:741 include/identity.php:744 include/text.php:1012
+#: include/nav.php:123 include/nav.php:187 view/theme/diabook/theme.php:125
 msgid "Contacts"
 msgstr "Контакты"
 
-#: ../../mod/contacts.php:706
+#: mod/contacts.php:797
 msgid "Search your contacts"
 msgstr "Поиск ваших контактов"
 
-#: ../../mod/contacts.php:707 ../../mod/directory.php:61
+#: mod/contacts.php:798
 msgid "Finding: "
 msgstr "Результат поиска: "
 
-#: ../../mod/contacts.php:708 ../../mod/directory.php:63
-#: ../../include/contact_widgets.php:34
+#: mod/contacts.php:799 mod/directory.php:210 include/contact_widgets.php:34
 msgid "Find"
 msgstr "Найти"
 
-#: ../../mod/contacts.php:713 ../../mod/settings.php:132
-#: ../../mod/settings.php:640
+#: mod/contacts.php:805 mod/settings.php:156 mod/settings.php:685
 msgid "Update"
 msgstr "Обновление"
 
-#: ../../mod/contacts.php:717 ../../mod/group.php:171 ../../mod/admin.php:1007
-#: ../../mod/content.php:438 ../../mod/content.php:741
-#: ../../mod/settings.php:677 ../../mod/photos.php:1654
-#: ../../object/Item.php:130 ../../include/conversation.php:614
+#: mod/contacts.php:808 mod/contacts.php:879
+msgid "Archive"
+msgstr "Архивировать"
+
+#: mod/contacts.php:808 mod/contacts.php:879
+msgid "Unarchive"
+msgstr "Разархивировать"
+
+#: mod/contacts.php:809 mod/group.php:171 mod/admin.php:1310
+#: mod/content.php:440 mod/content.php:743 mod/settings.php:722
+#: mod/photos.php:1723 object/Item.php:134 include/conversation.php:635
 msgid "Delete"
 msgstr "Удалить"
 
-#: ../../mod/hcard.php:10
+#: mod/contacts.php:822 include/identity.php:686 include/nav.php:75
+msgid "Status"
+msgstr "Посты"
+
+#: mod/contacts.php:825 mod/follow.php:143 include/identity.php:689
+msgid "Status Messages and Posts"
+msgstr "Ваши посты"
+
+#: mod/contacts.php:830 mod/profperm.php:104 mod/newmember.php:32
+#: include/identity.php:579 include/identity.php:665 include/identity.php:694
+#: include/nav.php:76 view/theme/diabook/theme.php:124
+msgid "Profile"
+msgstr "Информация"
+
+#: mod/contacts.php:833 include/identity.php:697
+msgid "Profile Details"
+msgstr "Информация о вас"
+
+#: mod/contacts.php:844
+msgid "View all contacts"
+msgstr "Показать все контакты"
+
+#: mod/contacts.php:850 mod/common.php:134
+msgid "Common Friends"
+msgstr "Общие друзья"
+
+#: mod/contacts.php:853
+msgid "View all common friends"
+msgstr ""
+
+#: mod/contacts.php:857
+msgid "Repair"
+msgstr "Восстановить"
+
+#: mod/contacts.php:860
+msgid "Advanced Contact Settings"
+msgstr "Дополнительные Настройки Контакта"
+
+#: mod/contacts.php:868
+msgid "Toggle Blocked status"
+msgstr "Изменить статус блокированности (заблокировать/разблокировать)"
+
+#: mod/contacts.php:875
+msgid "Toggle Ignored status"
+msgstr "Изменить статус игнорирования"
+
+#: mod/contacts.php:882
+msgid "Toggle Archive status"
+msgstr "Сменить статус архивации (архивирова/не архивировать)"
+
+#: mod/contacts.php:924
+msgid "Mutual Friendship"
+msgstr "Взаимная дружба"
+
+#: mod/contacts.php:928
+msgid "is a fan of yours"
+msgstr "является вашим поклонником"
+
+#: mod/contacts.php:932
+msgid "you are a fan of"
+msgstr "Вы - поклонник"
+
+#: mod/contacts.php:953 mod/nogroup.php:42
+msgid "Edit contact"
+msgstr "Редактировать контакт"
+
+#: mod/hcard.php:10
 msgid "No profile"
 msgstr "Нет профиля"
 
-#: ../../mod/manage.php:106
+#: mod/manage.php:139
 msgid "Manage Identities and/or Pages"
 msgstr "Управление идентификацией и / или страницами"
 
-#: ../../mod/manage.php:107
+#: mod/manage.php:140
 msgid ""
 "Toggle between different identities or community/group pages which share "
 "your account details or which you have been granted \"manage\" permissions"
 msgstr ""
 
-#: ../../mod/manage.php:108
+#: mod/manage.php:141
 msgid "Select an identity to manage: "
 msgstr "Выберите идентификацию для управления: "
 
-#: ../../mod/oexchange.php:25
+#: mod/oexchange.php:25
 msgid "Post successful."
 msgstr "Успешно добавлено."
 
-#: ../../mod/profperm.php:19 ../../mod/group.php:72 ../../index.php:368
+#: mod/profperm.php:19 mod/group.php:72 index.php:382
 msgid "Permission denied"
 msgstr "Доступ запрещен"
 
-#: ../../mod/profperm.php:25 ../../mod/profperm.php:55
+#: mod/profperm.php:25 mod/profperm.php:56
 msgid "Invalid profile identifier."
 msgstr "Недопустимый идентификатор профиля."
 
-#: ../../mod/profperm.php:101
+#: mod/profperm.php:102
 msgid "Profile Visibility Editor"
 msgstr "Редактор видимости профиля"
 
-#: ../../mod/profperm.php:103 ../../mod/newmember.php:32 ../../boot.php:2119
-#: ../../include/profile_advanced.php:7 ../../include/profile_advanced.php:87
-#: ../../include/nav.php:77 ../../view/theme/diabook/theme.php:124
-msgid "Profile"
-msgstr "Профиль"
-
-#: ../../mod/profperm.php:105 ../../mod/group.php:224
+#: mod/profperm.php:106 mod/group.php:223
 msgid "Click on a contact to add or remove."
 msgstr "Нажмите на контакт, чтобы добавить или удалить."
 
-#: ../../mod/profperm.php:114
+#: mod/profperm.php:115
 msgid "Visible To"
 msgstr "Видимый для"
 
-#: ../../mod/profperm.php:130
+#: mod/profperm.php:131
 msgid "All Contacts (with secure profile access)"
 msgstr "Все контакты (с безопасным доступом к профилю)"
 
-#: ../../mod/display.php:82 ../../mod/display.php:284
-#: ../../mod/display.php:503 ../../mod/viewsrc.php:15 ../../mod/admin.php:169
-#: ../../mod/admin.php:1052 ../../mod/admin.php:1265 ../../mod/notice.php:15
-#: ../../include/items.php:4516
+#: mod/display.php:82 mod/display.php:291 mod/display.php:513
+#: mod/viewsrc.php:15 mod/admin.php:234 mod/admin.php:1365 mod/admin.php:1599
+#: mod/notice.php:15 include/items.php:4887
 msgid "Item not found."
 msgstr "Пункт не найден."
 
-#: ../../mod/display.php:212 ../../mod/videos.php:115
-#: ../../mod/viewcontacts.php:19 ../../mod/community.php:18
-#: ../../mod/dfrn_request.php:762 ../../mod/search.php:89
-#: ../../mod/directory.php:33 ../../mod/photos.php:920
+#: mod/display.php:220 mod/videos.php:197 mod/viewcontacts.php:35
+#: mod/community.php:22 mod/dfrn_request.php:786 mod/search.php:93
+#: mod/search.php:99 mod/directory.php:37 mod/photos.php:976
 msgid "Public access denied."
 msgstr "Свободный доступ закрыт."
 
-#: ../../mod/display.php:332 ../../mod/profile.php:155
+#: mod/display.php:339 mod/profile.php:155
 msgid "Access to this profile has been restricted."
 msgstr "Доступ к этому профилю ограничен."
 
-#: ../../mod/display.php:496
+#: mod/display.php:506
 msgid "Item has been removed."
 msgstr "Пункт был удален."
 
-#: ../../mod/newmember.php:6
+#: mod/newmember.php:6
 msgid "Welcome to Friendica"
 msgstr "Добро пожаловать в Friendica"
 
-#: ../../mod/newmember.php:8
+#: mod/newmember.php:8
 msgid "New Member Checklist"
 msgstr "Новый контрольный список участников"
 
-#: ../../mod/newmember.php:12
+#: mod/newmember.php:12
 msgid ""
 "We would like to offer some tips and links to help make your experience "
 "enjoyable. Click any item to visit the relevant page. A link to this page "
 "will be visible from your home page for two weeks after your initial "
 "registration and then will quietly disappear."
-msgstr "Мы хотели бы предложить некоторые советы и ссылки, помогающие сделать вашу работу приятнее. Нажмите на любой элемент, чтобы посетить соответствующую страницу. Ссылка на эту страницу будет видна на  вашей домашней странице в течение двух недель после первоначальной регистрации, а затем она исчезнет."
+msgstr ""
+"Мы хотели бы предложить некоторые советы и ссылки, помогающие сделать вашу "
+"работу приятнее. Нажмите на любой элемент, чтобы посетить соответствующую "
+"страницу. Ссылка на эту страницу будет видна на  вашей домашней странице в "
+"течение двух недель после первоначальной регистрации, а затем она исчезнет."
 
-#: ../../mod/newmember.php:14
+#: mod/newmember.php:14
 msgid "Getting Started"
 msgstr "Начало работы"
 
-#: ../../mod/newmember.php:18
+#: mod/newmember.php:18
 msgid "Friendica Walk-Through"
 msgstr "Friendica тур"
 
-#: ../../mod/newmember.php:18
+#: mod/newmember.php:18
 msgid ""
 "On your <em>Quick Start</em> page - find a brief introduction to your "
-"profile and network tabs, make some new connections, and find some groups to"
-" join."
-msgstr "На вашей странице <em>Быстрый старт</em> - можно найти краткое введение в ваш профиль и сетевые закладки, создать новые связи, и найти группы, чтобы присоединиться к ним."
-
-#: ../../mod/newmember.php:22 ../../mod/admin.php:1104
-#: ../../mod/admin.php:1325 ../../mod/settings.php:85
-#: ../../include/nav.php:172 ../../view/theme/diabook/theme.php:544
-#: ../../view/theme/diabook/theme.php:648
+"profile and network tabs, make some new connections, and find some groups to "
+"join."
+msgstr ""
+"На вашей странице <em>Быстрый старт</em> - можно найти краткое введение в "
+"ваш профиль и сетевые закладки, создать новые связи, и найти группы, чтобы "
+"присоединиться к ним."
+
+#: mod/newmember.php:22 mod/admin.php:1418 mod/admin.php:1676
+#: mod/settings.php:109 include/nav.php:182 view/theme/diabook/theme.php:544
+#: view/theme/diabook/theme.php:648
 msgid "Settings"
 msgstr "Настройки"
 
-#: ../../mod/newmember.php:26
+#: mod/newmember.php:26
 msgid "Go to Your Settings"
 msgstr "Перейти к вашим настройкам"
 
-#: ../../mod/newmember.php:26
+#: mod/newmember.php:26
 msgid ""
 "On your <em>Settings</em> page -  change your initial password. Also make a "
 "note of your Identity Address. This looks just like an email address - and "
 "will be useful in making friends on the free social web."
-msgstr "На вашей странице <em>Настройки</em> - вы можете изменить свой первоначальный пароль. Также обратите внимание на ваш личный адрес. Он выглядит так же, как адрес электронной почты - и будет полезен для поиска друзей в свободной социальной сети."
+msgstr ""
+"На вашей странице <em>Настройки</em> - вы можете изменить свой "
+"первоначальный пароль. Также обратите внимание на ваш личный адрес. Он "
+"выглядит так же, как адрес электронной почты - и будет полезен для поиска "
+"друзей в свободной социальной сети."
 
-#: ../../mod/newmember.php:28
+#: mod/newmember.php:28
 msgid ""
-"Review the other settings, particularly the privacy settings. An unpublished"
-" directory listing is like having an unlisted phone number. In general, you "
+"Review the other settings, particularly the privacy settings. An unpublished "
+"directory listing is like having an unlisted phone number. In general, you "
 "should probably publish your listing - unless all of your friends and "
 "potential friends know exactly how to find you."
-msgstr "Просмотрите другие установки, в частности, параметры конфиденциальности. Неопубликованные пункты каталога с частными номерами телефона. В общем, вам, вероятно, следует опубликовать свою информацию - если все ваши друзья и потенциальные друзья точно знают, как вас найти."
+msgstr ""
+"Просмотрите другие установки, в частности, параметры конфиденциальности. "
+"Неопубликованные пункты каталога с частными номерами телефона. В общем, вам, "
+"вероятно, следует опубликовать свою информацию - если все ваши друзья и "
+"потенциальные друзья точно знают, как вас найти."
 
-#: ../../mod/newmember.php:36 ../../mod/profile_photo.php:244
-#: ../../mod/profiles.php:699
+#: mod/newmember.php:36 mod/profile_photo.php:250 mod/profiles.php:709
 msgid "Upload Profile Photo"
 msgstr "Загрузить фото профиля"
 
-#: ../../mod/newmember.php:36
+#: mod/newmember.php:36
 msgid ""
 "Upload a profile photo if you have not done so already. Studies have shown "
-"that people with real photos of themselves are ten times more likely to make"
-" friends than people who do not."
-msgstr "Загрузите фотографию профиля, если вы еще не сделали это. Исследования показали, что люди с реальными фотографиями имеют в десять раз больше шансов подружиться, чем люди, которые этого не делают."
+"that people with real photos of themselves are ten times more likely to make "
+"friends than people who do not."
+msgstr ""
+"Загрузите фотографию профиля, если вы еще не сделали это. Исследования "
+"показали, что люди с реальными фотографиями имеют в десять раз больше шансов "
+"подружиться, чем люди, которые этого не делают."
 
-#: ../../mod/newmember.php:38
+#: mod/newmember.php:38
 msgid "Edit Your Profile"
 msgstr "Редактировать профиль"
 
-#: ../../mod/newmember.php:38
+#: mod/newmember.php:38
 msgid ""
 "Edit your <strong>default</strong> profile to your liking. Review the "
-"settings for hiding your list of friends and hiding the profile from unknown"
-" visitors."
-msgstr "Отредактируйте профиль <strong>по умолчанию</strong> на свой ​​вкус. Просмотрите установки для сокрытия вашего списка друзей и сокрытия профиля от неизвестных посетителей."
+"settings for hiding your list of friends and hiding the profile from unknown "
+"visitors."
+msgstr ""
+"Отредактируйте профиль <strong>по умолчанию</strong> на свой ​​вкус. "
+"Просмотрите установки для сокрытия вашего списка друзей и сокрытия профиля "
+"от неизвестных посетителей."
 
-#: ../../mod/newmember.php:40
+#: mod/newmember.php:40
 msgid "Profile Keywords"
 msgstr "Ключевые слова профиля"
 
-#: ../../mod/newmember.php:40
+#: mod/newmember.php:40
 msgid ""
 "Set some public keywords for your default profile which describe your "
 "interests. We may be able to find other people with similar interests and "
 "suggest friendships."
-msgstr "Установите некоторые публичные ключевые слова для вашего профиля по умолчанию, которые описывают ваши интересы. Мы можем быть в состоянии найти других людей со схожими интересами и предложить дружбу."
+msgstr ""
+"Установите некоторые публичные ключевые слова для вашего профиля по "
+"умолчанию, которые описывают ваши интересы. Мы можем быть в состоянии найти "
+"других людей со схожими интересами и предложить дружбу."
 
-#: ../../mod/newmember.php:44
+#: mod/newmember.php:44
 msgid "Connecting"
 msgstr "Подключение"
 
-#: ../../mod/newmember.php:49 ../../mod/newmember.php:51
-#: ../../include/contact_selectors.php:81
-msgid "Facebook"
-msgstr "Facebook"
-
-#: ../../mod/newmember.php:49
-msgid ""
-"Authorise the Facebook Connector if you currently have a Facebook account "
-"and we will (optionally) import all your Facebook friends and conversations."
-msgstr "Авторизуйте Facebook Connector , если у вас уже есть аккаунт на Facebook, и мы (по желанию) импортируем всех ваших друзей и беседы с Facebook."
-
-#: ../../mod/newmember.php:51
-msgid ""
-"<em>If</em> this is your own personal server, installing the Facebook addon "
-"may ease your transition to the free social web."
-msgstr "<em>Если</em> это ваш личный сервер, установите дополнение Facebook, это может облегчить ваш переход на свободную социальную сеть."
-
-#: ../../mod/newmember.php:56
+#: mod/newmember.php:51
 msgid "Importing Emails"
 msgstr "Импортирование Email-ов"
 
-#: ../../mod/newmember.php:56
+#: mod/newmember.php:51
 msgid ""
 "Enter your email access information on your Connector Settings page if you "
 "wish to import and interact with friends or mailing lists from your email "
 "INBOX"
-msgstr "Введите информацию о доступе к вашему email на странице настроек вашего коннектора, если вы хотите импортировать, и общаться с друзьями или получать рассылки на ваш ящик электронной почты"
+msgstr ""
+"Введите информацию о доступе к вашему email на странице настроек вашего "
+"коннектора, если вы хотите импортировать, и общаться с друзьями или получать "
+"рассылки на ваш ящик электронной почты"
 
-#: ../../mod/newmember.php:58
+#: mod/newmember.php:53
 msgid "Go to Your Contacts Page"
 msgstr "Перейти на страницу ваших контактов"
 
-#: ../../mod/newmember.php:58
+#: mod/newmember.php:53
 msgid ""
 "Your Contacts page is your gateway to managing friendships and connecting "
 "with friends on other networks. Typically you enter their address or site "
 "URL in the <em>Add New Contact</em> dialog."
-msgstr "Ваша страница контактов - это ваш шлюз к управлению дружбой и общением с друзьями в других сетях. Обычно вы вводите свой ​​адрес или адрес сайта в диалог <em>Добавить новый контакт</em>."
+msgstr ""
+"Ваша страница контактов - это ваш шлюз к управлению дружбой и общением с "
+"друзьями в других сетях. Обычно вы вводите свой ​​адрес или адрес сайта в "
+"диалог <em>Добавить новый контакт</em>."
 
-#: ../../mod/newmember.php:60
+#: mod/newmember.php:55
 msgid "Go to Your Site's Directory"
 msgstr "Перейти в каталог вашего сайта"
 
-#: ../../mod/newmember.php:60
+#: mod/newmember.php:55
 msgid ""
 "The Directory page lets you find other people in this network or other "
 "federated sites. Look for a <em>Connect</em> or <em>Follow</em> link on "
 "their profile page. Provide your own Identity Address if requested."
-msgstr "На странице каталога вы можете найти других людей в этой сети или на других похожих сайтах. Ищите ссылки <em>Подключить</em> или <em>Следовать</em> на страницах их профилей. Укажите свой собственный адрес идентификации, если требуется."
+msgstr ""
+"На странице каталога вы можете найти других людей в этой сети или на других "
+"похожих сайтах. Ищите ссылки <em>Подключить</em> или <em>Следовать</em> на "
+"страницах их профилей. Укажите свой собственный адрес идентификации, если "
+"требуется."
 
-#: ../../mod/newmember.php:62
+#: mod/newmember.php:57
 msgid "Finding New People"
 msgstr "Поиск людей"
 
-#: ../../mod/newmember.php:62
+#: mod/newmember.php:57
 msgid ""
 "On the side panel of the Contacts page are several tools to find new "
 "friends. We can match people by interest, look up people by name or "
-"interest, and provide suggestions based on network relationships. On a brand"
-" new site, friend suggestions will usually begin to be populated within 24 "
+"interest, and provide suggestions based on network relationships. On a brand "
+"new site, friend suggestions will usually begin to be populated within 24 "
 "hours."
-msgstr "На боковой панели страницы Контакты есть несколько инструментов, чтобы найти новых друзей. Мы можем  искать по соответствию интересам, посмотреть людей по имени или интересам, и внести предложения на основе сетевых отношений. На новом сайте, предложения дружбы, как правило, начинают заполняться в течение 24 часов."
+msgstr ""
+"На боковой панели страницы Контакты есть несколько инструментов, чтобы найти "
+"новых друзей. Мы можем  искать по соответствию интересам, посмотреть людей "
+"по имени или интересам, и внести предложения на основе сетевых отношений. На "
+"новом сайте, предложения дружбы, как правило, начинают заполняться в течение "
+"24 часов."
 
-#: ../../mod/newmember.php:66 ../../include/group.php:270
+#: mod/newmember.php:61 include/group.php:283
 msgid "Groups"
 msgstr "Группы"
 
-#: ../../mod/newmember.php:70
+#: mod/newmember.php:65
 msgid "Group Your Contacts"
 msgstr "Группа \"ваши контакты\""
 
-#: ../../mod/newmember.php:70
+#: mod/newmember.php:65
 msgid ""
 "Once you have made some friends, organize them into private conversation "
-"groups from the sidebar of your Contacts page and then you can interact with"
-" each group privately on your Network page."
-msgstr "После того, как вы найдете несколько друзей, организуйте их в группы частных бесед в боковой панели на странице Контакты, а затем вы можете взаимодействовать с каждой группой приватно или на вашей странице Сеть."
+"groups from the sidebar of your Contacts page and then you can interact with "
+"each group privately on your Network page."
+msgstr ""
+"После того, как вы найдете несколько друзей, организуйте их в группы частных "
+"бесед в боковой панели на странице Контакты, а затем вы можете "
+"взаимодействовать с каждой группой приватно или на вашей странице Сеть."
 
-#: ../../mod/newmember.php:73
+#: mod/newmember.php:68
 msgid "Why Aren't My Posts Public?"
 msgstr "Почему мои посты не публичные?"
 
-#: ../../mod/newmember.php:73
+#: mod/newmember.php:68
 msgid ""
-"Friendica respects your privacy. By default, your posts will only show up to"
-" people you've added as friends. For more information, see the help section "
+"Friendica respects your privacy. By default, your posts will only show up to "
+"people you've added as friends. For more information, see the help section "
 "from the link above."
-msgstr "Friendica уважает вашу приватность. По умолчанию, ваши сообщения будут показываться только для людей, которых вы добавили в список друзей. Для получения дополнительной информации см. раздел справки по ссылке выше."
+msgstr ""
+"Friendica уважает вашу приватность. По умолчанию, ваши сообщения будут "
+"показываться только для людей, которых вы добавили в список друзей. Для "
+"получения дополнительной информации см. раздел справки по ссылке выше."
 
-#: ../../mod/newmember.php:78
+#: mod/newmember.php:73
 msgid "Getting Help"
 msgstr "Получить помощь"
 
-#: ../../mod/newmember.php:82
+#: mod/newmember.php:77
 msgid "Go to the Help Section"
 msgstr "Перейти в раздел справки"
 
-#: ../../mod/newmember.php:82
+#: mod/newmember.php:77
 msgid ""
-"Our <strong>help</strong> pages may be consulted for detail on other program"
-" features and resources."
-msgstr "Наши страницы <strong>помощи</strong> могут проконсультировать о подробностях и возможностях программы и ресурса."
+"Our <strong>help</strong> pages may be consulted for detail on other program "
+"features and resources."
+msgstr ""
+"Наши страницы <strong>помощи</strong> могут проконсультировать о "
+"подробностях и возможностях программы и ресурса."
 
-#: ../../mod/openid.php:24
+#: mod/openid.php:24
 msgid "OpenID protocol error. No ID returned."
 msgstr "Ошибка протокола OpenID. Не возвращён ID."
 
-#: ../../mod/openid.php:53
+#: mod/openid.php:53
 msgid ""
 "Account not found and OpenID registration is not permitted on this site."
 msgstr "Аккаунт не найден и OpenID регистрация не допускается на этом сайте."
 
-#: ../../mod/openid.php:93 ../../include/auth.php:112
-#: ../../include/auth.php:175
+#: mod/openid.php:93 include/auth.php:118 include/auth.php:181
 msgid "Login failed."
 msgstr "Войти не удалось."
 
-#: ../../mod/profile_photo.php:44
+#: mod/profile_photo.php:44
 msgid "Image uploaded but image cropping failed."
 msgstr "Изображение загружено, но обрезка изображения не удалась."
 
-#: ../../mod/profile_photo.php:74 ../../mod/profile_photo.php:81
-#: ../../mod/profile_photo.php:88 ../../mod/profile_photo.php:204
-#: ../../mod/profile_photo.php:296 ../../mod/profile_photo.php:305
-#: ../../mod/photos.php:155 ../../mod/photos.php:731 ../../mod/photos.php:1187
-#: ../../mod/photos.php:1210 ../../include/user.php:335
-#: ../../include/user.php:342 ../../include/user.php:349
-#: ../../view/theme/diabook/theme.php:500
+#: mod/profile_photo.php:74 mod/profile_photo.php:81 mod/profile_photo.php:88
+#: mod/profile_photo.php:210 mod/profile_photo.php:302
+#: mod/profile_photo.php:311 mod/photos.php:78 mod/photos.php:192
+#: mod/photos.php:775 mod/photos.php:1245 mod/photos.php:1268
+#: mod/photos.php:1862 include/user.php:345 include/user.php:352
+#: include/user.php:359 view/theme/diabook/theme.php:500
 msgid "Profile Photos"
 msgstr "Фотографии профиля"
 
-#: ../../mod/profile_photo.php:77 ../../mod/profile_photo.php:84
-#: ../../mod/profile_photo.php:91 ../../mod/profile_photo.php:308
+#: mod/profile_photo.php:77 mod/profile_photo.php:84 mod/profile_photo.php:91
+#: mod/profile_photo.php:314
 #, php-format
 msgid "Image size reduction [%s] failed."
 msgstr "Уменьшение размера изображения [%s] не удалось."
 
-#: ../../mod/profile_photo.php:118
+#: mod/profile_photo.php:124
 msgid ""
 "Shift-reload the page or clear browser cache if the new photo does not "
 "display immediately."
-msgstr "Перезагрузите страницу с зажатой клавишей \"Shift\" для того, чтобы увидеть свое новое фото немедленно."
+msgstr ""
+"Перезагрузите страницу с зажатой клавишей \"Shift\" для того, чтобы увидеть "
+"свое новое фото немедленно."
 
-#: ../../mod/profile_photo.php:128
+#: mod/profile_photo.php:134
 msgid "Unable to process image"
 msgstr "Не удается обработать изображение"
 
-#: ../../mod/profile_photo.php:144 ../../mod/wall_upload.php:122
+#: mod/profile_photo.php:150 mod/wall_upload.php:151 mod/photos.php:811
 #, php-format
-msgid "Image exceeds size limit of %d"
-msgstr "Изображение превышает предельный размер %d"
+msgid "Image exceeds size limit of %s"
+msgstr ""
 
-#: ../../mod/profile_photo.php:153 ../../mod/wall_upload.php:144
-#: ../../mod/photos.php:807
+#: mod/profile_photo.php:159 mod/wall_upload.php:183 mod/photos.php:851
 msgid "Unable to process image."
 msgstr "Невозможно обработать фото."
 
-#: ../../mod/profile_photo.php:242
+#: mod/profile_photo.php:248
 msgid "Upload File:"
 msgstr "Загрузить файл:"
 
-#: ../../mod/profile_photo.php:243
+#: mod/profile_photo.php:249
 msgid "Select a profile:"
 msgstr "Выбрать этот профиль:"
 
-#: ../../mod/profile_photo.php:245
+#: mod/profile_photo.php:251
 msgid "Upload"
 msgstr "Загрузить"
 
-#: ../../mod/profile_photo.php:248 ../../mod/settings.php:1062
+#: mod/profile_photo.php:254
 msgid "or"
 msgstr "или"
 
-#: ../../mod/profile_photo.php:248
+#: mod/profile_photo.php:254
 msgid "skip this step"
 msgstr "пропустить этот шаг"
 
-#: ../../mod/profile_photo.php:248
+#: mod/profile_photo.php:254
 msgid "select a photo from your photo albums"
 msgstr "выберите фото из ваших фотоальбомов"
 
-#: ../../mod/profile_photo.php:262
+#: mod/profile_photo.php:268
 msgid "Crop Image"
 msgstr "Обрезать изображение"
 
-#: ../../mod/profile_photo.php:263
+#: mod/profile_photo.php:269
 msgid "Please adjust the image cropping for optimum viewing."
 msgstr "Пожалуйста, настройте обрезку изображения для оптимального просмотра."
 
-#: ../../mod/profile_photo.php:265
+#: mod/profile_photo.php:271
 msgid "Done Editing"
 msgstr "Редактирование выполнено"
 
-#: ../../mod/profile_photo.php:299
+#: mod/profile_photo.php:305
 msgid "Image uploaded successfully."
 msgstr "Изображение загружено успешно."
 
-#: ../../mod/profile_photo.php:301 ../../mod/wall_upload.php:172
-#: ../../mod/photos.php:834
+#: mod/profile_photo.php:307 mod/wall_upload.php:216 mod/photos.php:878
 msgid "Image upload failed."
 msgstr "Загрузка фото неудачная."
 
-#: ../../mod/subthread.php:87 ../../mod/tagger.php:62 ../../mod/like.php:149
-#: ../../include/conversation.php:126 ../../include/conversation.php:254
-#: ../../include/text.php:1968 ../../include/diaspora.php:2087
-#: ../../view/theme/diabook/theme.php:471
+#: mod/subthread.php:87 mod/tagger.php:62 include/like.php:165
+#: include/conversation.php:130 include/conversation.php:266
+#: include/text.php:2000 include/diaspora.php:2169
+#: view/theme/diabook/theme.php:471
 msgid "photo"
 msgstr "фото"
 
-#: ../../mod/subthread.php:87 ../../mod/tagger.php:62 ../../mod/like.php:149
-#: ../../mod/like.php:319 ../../include/conversation.php:121
-#: ../../include/conversation.php:130 ../../include/conversation.php:249
-#: ../../include/conversation.php:258 ../../include/diaspora.php:2087
-#: ../../view/theme/diabook/theme.php:466
-#: ../../view/theme/diabook/theme.php:475
+#: mod/subthread.php:87 mod/tagger.php:62 include/like.php:165
+#: include/like.php:334 include/conversation.php:125
+#: include/conversation.php:134 include/conversation.php:261
+#: include/conversation.php:270 include/diaspora.php:2169
+#: view/theme/diabook/theme.php:466 view/theme/diabook/theme.php:475
 msgid "status"
 msgstr "статус"
 
-#: ../../mod/subthread.php:103
+#: mod/subthread.php:103
 #, php-format
 msgid "%1$s is following %2$s's %3$s"
 msgstr ""
 
-#: ../../mod/tagrm.php:41
+#: mod/tagrm.php:41
 msgid "Tag removed"
 msgstr "Ключевое слово удалено"
 
-#: ../../mod/tagrm.php:79
+#: mod/tagrm.php:79
 msgid "Remove Item Tag"
 msgstr "Удалить ключевое слово"
 
-#: ../../mod/tagrm.php:81
+#: mod/tagrm.php:81
 msgid "Select a tag to remove: "
 msgstr "Выберите ключевое слово для удаления: "
 
-#: ../../mod/tagrm.php:93 ../../mod/delegate.php:139
+#: mod/tagrm.php:93 mod/delegate.php:139
 msgid "Remove"
 msgstr "Удалить"
 
-#: ../../mod/filer.php:30 ../../include/conversation.php:1006
-#: ../../include/conversation.php:1024
+#: mod/ostatus_subscribe.php:14
+msgid "Subscribing to OStatus contacts"
+msgstr ""
+
+#: mod/ostatus_subscribe.php:25
+msgid "No contact provided."
+msgstr ""
+
+#: mod/ostatus_subscribe.php:30
+msgid "Couldn't fetch information for contact."
+msgstr ""
+
+#: mod/ostatus_subscribe.php:38
+msgid "Couldn't fetch friends for contact."
+msgstr ""
+
+#: mod/ostatus_subscribe.php:51 mod/repair_ostatus.php:44
+msgid "Done"
+msgstr "Готово"
+
+#: mod/ostatus_subscribe.php:65
+msgid "success"
+msgstr "удачно"
+
+#: mod/ostatus_subscribe.php:67
+msgid "failed"
+msgstr "неудача"
+
+#: mod/ostatus_subscribe.php:69 object/Item.php:235
+msgid "ignored"
+msgstr ""
+
+#: mod/ostatus_subscribe.php:73 mod/repair_ostatus.php:50
+msgid "Keep this window open until done."
+msgstr ""
+
+#: mod/filer.php:30 include/conversation.php:1132
+#: include/conversation.php:1150
 msgid "Save to Folder:"
 msgstr "Сохранить в папку:"
 
-#: ../../mod/filer.php:30
+#: mod/filer.php:30
 msgid "- select -"
 msgstr "- выбрать -"
 
-#: ../../mod/filer.php:31 ../../mod/editpost.php:109 ../../mod/notes.php:63
-#: ../../include/text.php:956
+#: mod/filer.php:31 mod/editpost.php:109 mod/notes.php:61
+#: include/text.php:1004
 msgid "Save"
 msgstr "Сохранить"
 
-#: ../../mod/follow.php:27
+#: mod/follow.php:19 mod/dfrn_request.php:870
+msgid "Submit Request"
+msgstr "Отправить запрос"
+
+#: mod/follow.php:30
+msgid "You already added this contact."
+msgstr ""
+
+#: mod/follow.php:39
+msgid "Diaspora support isn't enabled. Contact can't be added."
+msgstr ""
+
+#: mod/follow.php:46
+msgid "OStatus support is disabled. Contact can't be added."
+msgstr ""
+
+#: mod/follow.php:53
+msgid "The network type couldn't be detected. Contact can't be added."
+msgstr ""
+
+#: mod/follow.php:109 mod/dfrn_request.php:856
+msgid "Please answer the following:"
+msgstr "Пожалуйста, ответьте следующее:"
+
+#: mod/follow.php:110 mod/dfrn_request.php:857
+#, php-format
+msgid "Does %s know you?"
+msgstr "%s знает вас?"
+
+#: mod/follow.php:110 mod/settings.php:1103 mod/settings.php:1109
+#: mod/settings.php:1117 mod/settings.php:1121 mod/settings.php:1126
+#: mod/settings.php:1132 mod/settings.php:1138 mod/settings.php:1144
+#: mod/settings.php:1170 mod/settings.php:1171 mod/settings.php:1172
+#: mod/settings.php:1173 mod/settings.php:1174 mod/dfrn_request.php:857
+#: mod/register.php:239 mod/profiles.php:658 mod/profiles.php:662
+#: mod/profiles.php:687 mod/api.php:106
+msgid "No"
+msgstr "Нет"
+
+#: mod/follow.php:111 mod/dfrn_request.php:861
+msgid "Add a personal note:"
+msgstr "Добавить личную заметку:"
+
+#: mod/follow.php:117 mod/dfrn_request.php:867
+msgid "Your Identity Address:"
+msgstr "Ваш идентификационный адрес:"
+
+#: mod/follow.php:180
 msgid "Contact added"
 msgstr "Контакт добавлен"
 
-#: ../../mod/item.php:113
+#: mod/item.php:114
 msgid "Unable to locate original post."
 msgstr "Не удалось найти оригинальный пост."
 
-#: ../../mod/item.php:345
+#: mod/item.php:329
 msgid "Empty post discarded."
 msgstr "Пустое сообщение отбрасывается."
 
-#: ../../mod/item.php:484 ../../mod/wall_upload.php:169
-#: ../../mod/wall_upload.php:178 ../../mod/wall_upload.php:185
-#: ../../include/Photo.php:916 ../../include/Photo.php:931
-#: ../../include/Photo.php:938 ../../include/Photo.php:960
-#: ../../include/message.php:144
+#: mod/item.php:467 mod/wall_upload.php:213 mod/wall_upload.php:227
+#: mod/wall_upload.php:234 include/Photo.php:958 include/Photo.php:973
+#: include/Photo.php:980 include/Photo.php:1002 include/message.php:145
 msgid "Wall Photos"
 msgstr "Фото стены"
 
-#: ../../mod/item.php:938
+#: mod/item.php:842
 msgid "System error. Post not saved."
 msgstr "Системная ошибка. Сообщение не сохранено."
 
-#: ../../mod/item.php:964
+#: mod/item.php:971
 #, php-format
 msgid ""
-"This message was sent to you by %s, a member of the Friendica social "
-"network."
-msgstr "Это сообщение было отправлено вам %s, участником социальной сети Friendica."
+"This message was sent to you by %s, a member of the Friendica social network."
+msgstr ""
+"Это сообщение было отправлено вам %s, участником социальной сети Friendica."
 
-#: ../../mod/item.php:966
+#: mod/item.php:973
 #, php-format
 msgid "You may visit them online at %s"
 msgstr "Вы можете посетить их в онлайне на %s"
 
-#: ../../mod/item.php:967
+#: mod/item.php:974
 msgid ""
 "Please contact the sender by replying to this post if you do not wish to "
 "receive these messages."
-msgstr "Пожалуйста, свяжитесь с отправителем, ответив на это сообщение, если вы не хотите получать эти сообщения."
+msgstr ""
+"Пожалуйста, свяжитесь с отправителем, ответив на это сообщение, если вы не "
+"хотите получать эти сообщения."
 
-#: ../../mod/item.php:971
+#: mod/item.php:978
 #, php-format
 msgid "%s posted an update."
 msgstr "%s отправил/а/ обновление."
 
-#: ../../mod/group.php:29
+#: mod/group.php:29
 msgid "Group created."
 msgstr "Группа создана."
 
-#: ../../mod/group.php:35
+#: mod/group.php:35
 msgid "Could not create group."
 msgstr "Не удалось создать группу."
 
-#: ../../mod/group.php:47 ../../mod/group.php:140
+#: mod/group.php:47 mod/group.php:140
 msgid "Group not found."
 msgstr "Группа не найдена."
 
-#: ../../mod/group.php:60
+#: mod/group.php:60
 msgid "Group name changed."
 msgstr "Название группы изменено."
 
-#: ../../mod/group.php:87
+#: mod/group.php:87
 msgid "Save Group"
 msgstr "Сохранить группу"
 
-#: ../../mod/group.php:93
+#: mod/group.php:93
 msgid "Create a group of contacts/friends."
 msgstr "Создать группу контактов / друзей."
 
-#: ../../mod/group.php:94 ../../mod/group.php:180
+#: mod/group.php:94 mod/group.php:178 include/group.php:289
 msgid "Group Name: "
 msgstr "Название группы: "
 
-#: ../../mod/group.php:113
+#: mod/group.php:113
 msgid "Group removed."
 msgstr "Группа удалена."
 
-#: ../../mod/group.php:115
+#: mod/group.php:115
 msgid "Unable to remove group."
 msgstr "Не удается удалить группу."
 
-#: ../../mod/group.php:179
+#: mod/group.php:177
 msgid "Group Editor"
 msgstr "Редактор групп"
 
-#: ../../mod/group.php:192
+#: mod/group.php:190
 msgid "Members"
 msgstr "Участники"
 
-#: ../../mod/apps.php:7 ../../index.php:212
+#: mod/group.php:193 mod/network.php:576 mod/content.php:130
+msgid "Group is empty"
+msgstr "Группа пуста"
+
+#: mod/apps.php:7 index.php:226
 msgid "You must be logged in to use addons. "
 msgstr "Вы должны войти в систему, чтобы использовать аддоны."
 
-#: ../../mod/apps.php:11
+#: mod/apps.php:11
 msgid "Applications"
 msgstr "Приложения"
 
-#: ../../mod/apps.php:14
+#: mod/apps.php:14
 msgid "No installed applications."
 msgstr "Нет установленных приложений."
 
-#: ../../mod/dfrn_confirm.php:64 ../../mod/profiles.php:18
-#: ../../mod/profiles.php:133 ../../mod/profiles.php:179
-#: ../../mod/profiles.php:630
+#: mod/dfrn_confirm.php:64 mod/profiles.php:18 mod/profiles.php:133
+#: mod/profiles.php:179 mod/profiles.php:627
 msgid "Profile not found."
 msgstr "Профиль не найден."
 
-#: ../../mod/dfrn_confirm.php:120 ../../mod/fsuggest.php:20
-#: ../../mod/fsuggest.php:92 ../../mod/crepair.php:133
+#: mod/dfrn_confirm.php:120 mod/fsuggest.php:20 mod/fsuggest.php:92
+#: mod/crepair.php:131
 msgid "Contact not found."
 msgstr "Контакт не найден."
 
-#: ../../mod/dfrn_confirm.php:121
+#: mod/dfrn_confirm.php:121
 msgid ""
-"This may occasionally happen if contact was requested by both persons and it"
-" has already been approved."
-msgstr "Это может иногда происходить, если контакт запрашивали двое людей, и он был уже одобрен."
+"This may occasionally happen if contact was requested by both persons and it "
+"has already been approved."
+msgstr ""
+"Это может иногда происходить, если контакт запрашивали двое людей, и он был "
+"уже одобрен."
 
-#: ../../mod/dfrn_confirm.php:240
+#: mod/dfrn_confirm.php:240
 msgid "Response from remote site was not understood."
 msgstr "Ответ от удаленного сайта не был понят."
 
-#: ../../mod/dfrn_confirm.php:249 ../../mod/dfrn_confirm.php:254
+#: mod/dfrn_confirm.php:249 mod/dfrn_confirm.php:254
 msgid "Unexpected response from remote site: "
 msgstr "Неожиданный ответ от удаленного сайта: "
 
-#: ../../mod/dfrn_confirm.php:263
+#: mod/dfrn_confirm.php:263
 msgid "Confirmation completed successfully."
 msgstr "Подтверждение успешно завершено."
 
-#: ../../mod/dfrn_confirm.php:265 ../../mod/dfrn_confirm.php:279
-#: ../../mod/dfrn_confirm.php:286
+#: mod/dfrn_confirm.php:265 mod/dfrn_confirm.php:279 mod/dfrn_confirm.php:286
 msgid "Remote site reported: "
 msgstr "Удаленный сайт сообщил: "
 
-#: ../../mod/dfrn_confirm.php:277
+#: mod/dfrn_confirm.php:277
 msgid "Temporary failure. Please wait and try again."
 msgstr "Временные неудачи. Подождите и попробуйте еще раз."
 
-#: ../../mod/dfrn_confirm.php:284
+#: mod/dfrn_confirm.php:284
 msgid "Introduction failed or was revoked."
 msgstr "Запрос ошибочен или был отозван."
 
-#: ../../mod/dfrn_confirm.php:429
+#: mod/dfrn_confirm.php:430
 msgid "Unable to set contact photo."
 msgstr "Не удается установить фото контакта."
 
-#: ../../mod/dfrn_confirm.php:486 ../../include/conversation.php:172
-#: ../../include/diaspora.php:620
+#: mod/dfrn_confirm.php:487 include/conversation.php:185
+#: include/diaspora.php:637
 #, php-format
 msgid "%1$s is now friends with %2$s"
 msgstr "%1$s и %2$s теперь друзья"
 
-#: ../../mod/dfrn_confirm.php:571
+#: mod/dfrn_confirm.php:572
 #, php-format
 msgid "No user record found for '%s' "
 msgstr "Не найдено записи пользователя для '%s' "
 
-#: ../../mod/dfrn_confirm.php:581
+#: mod/dfrn_confirm.php:582
 msgid "Our site encryption key is apparently messed up."
 msgstr "Наш ключ шифрования сайта, по-видимому, перепутался."
 
-#: ../../mod/dfrn_confirm.php:592
+#: mod/dfrn_confirm.php:593
 msgid "Empty site URL was provided or URL could not be decrypted by us."
-msgstr "Был предоставлен пустой URL сайта ​​или URL не может быть расшифрован нами."
+msgstr ""
+"Был предоставлен пустой URL сайта ​​или URL не может быть расшифрован нами."
 
-#: ../../mod/dfrn_confirm.php:613
+#: mod/dfrn_confirm.php:614
 msgid "Contact record was not found for you on our site."
 msgstr "Запись контакта не найдена для вас на нашем сайте."
 
-#: ../../mod/dfrn_confirm.php:627
+#: mod/dfrn_confirm.php:628
 #, php-format
 msgid "Site public key not available in contact record for URL %s."
 msgstr "Публичный ключ недоступен в записи о контакте по ссылке %s"
 
-#: ../../mod/dfrn_confirm.php:647
+#: mod/dfrn_confirm.php:648
 msgid ""
 "The ID provided by your system is a duplicate on our system. It should work "
 "if you try again."
-msgstr "ID, предложенный вашей системой, является дубликатом в нашей системе. Он должен работать, если вы повторите попытку."
+msgstr ""
+"ID, предложенный вашей системой, является дубликатом в нашей системе. Он "
+"должен работать, если вы повторите попытку."
 
-#: ../../mod/dfrn_confirm.php:658
+#: mod/dfrn_confirm.php:659
 msgid "Unable to set your contact credentials on our system."
 msgstr "Не удалось установить ваши учетные данные контакта в нашей системе."
 
-#: ../../mod/dfrn_confirm.php:725
+#: mod/dfrn_confirm.php:726
 msgid "Unable to update your contact profile details on our system"
 msgstr "Не удается обновить ваши контактные детали профиля в нашей системе"
 
-#: ../../mod/dfrn_confirm.php:752 ../../mod/dfrn_request.php:717
-#: ../../include/items.php:4008
+#: mod/dfrn_confirm.php:753 mod/dfrn_request.php:741 include/items.php:4299
 msgid "[Name Withheld]"
 msgstr "[Имя не разглашается]"
 
-#: ../../mod/dfrn_confirm.php:797
+#: mod/dfrn_confirm.php:798
 #, php-format
 msgid "%1$s has joined %2$s"
 msgstr "%1$s присоединился %2$s"
 
-#: ../../mod/profile.php:21 ../../boot.php:1458
+#: mod/profile.php:21 include/identity.php:51
 msgid "Requested profile is not available."
 msgstr "Запрашиваемый профиль недоступен."
 
-#: ../../mod/profile.php:180
+#: mod/profile.php:179
 msgid "Tips for New Members"
 msgstr "Советы для новых участников"
 
-#: ../../mod/videos.php:125
+#: mod/videos.php:123
+msgid "Do you really want to delete this video?"
+msgstr ""
+
+#: mod/videos.php:128
+msgid "Delete Video"
+msgstr "Удалить видео"
+
+#: mod/videos.php:207
 msgid "No videos selected"
 msgstr "Видео не выбрано"
 
-#: ../../mod/videos.php:226 ../../mod/photos.php:1031
+#: mod/videos.php:308 mod/photos.php:1087
 msgid "Access to this item is restricted."
 msgstr "Доступ к этому пункту ограничен."
 
-#: ../../mod/videos.php:301 ../../include/text.php:1405
+#: mod/videos.php:383 include/text.php:1472
 msgid "View Video"
 msgstr "Просмотреть видео"
 
-#: ../../mod/videos.php:308 ../../mod/photos.php:1808
+#: mod/videos.php:390 mod/photos.php:1890
 msgid "View Album"
 msgstr "Просмотреть альбом"
 
-#: ../../mod/videos.php:317
+#: mod/videos.php:399
 msgid "Recent Videos"
 msgstr "Последние видео"
 
-#: ../../mod/videos.php:319
+#: mod/videos.php:401
 msgid "Upload New Videos"
 msgstr "Загрузить новые видео"
 
-#: ../../mod/tagger.php:95 ../../include/conversation.php:266
+#: mod/tagger.php:95 include/conversation.php:278
 #, php-format
 msgid "%1$s tagged %2$s's %3$s with %4$s"
 msgstr "%1$s tagged %2$s's %3$s в %4$s"
 
-#: ../../mod/fsuggest.php:63
+#: mod/fsuggest.php:63
 msgid "Friend suggestion sent."
 msgstr "Приглашение в друзья отправлено."
 
-#: ../../mod/fsuggest.php:97
+#: mod/fsuggest.php:97
 msgid "Suggest Friends"
 msgstr "Предложить друзей"
 
-#: ../../mod/fsuggest.php:99
+#: mod/fsuggest.php:99
 #, php-format
 msgid "Suggest a friend for %s"
 msgstr "Предложить друга для %s."
 
-#: ../../mod/lostpass.php:19
+#: mod/wall_upload.php:20 mod/wall_upload.php:33 mod/wall_upload.php:86
+#: mod/wall_upload.php:122 mod/wall_upload.php:125 mod/wall_attach.php:17
+#: mod/wall_attach.php:25 mod/wall_attach.php:76 include/api.php:1781
+msgid "Invalid request."
+msgstr "Неверный запрос."
+
+#: mod/lostpass.php:19
 msgid "No valid account found."
 msgstr "Не найдено действительного аккаунта."
 
-#: ../../mod/lostpass.php:35
+#: mod/lostpass.php:35
 msgid "Password reset request issued. Check your email."
 msgstr "Запрос на сброс пароля принят. Проверьте вашу электронную почту."
 
-#: ../../mod/lostpass.php:42
+#: mod/lostpass.php:42
 #, php-format
 msgid ""
 "\n"
 "\t\tDear %1$s,\n"
 "\t\t\tA request was recently received at \"%2$s\" to reset your account\n"
-"\t\tpassword. In order to confirm this request, please select the verification link\n"
+"\t\tpassword. In order to confirm this request, please select the "
+"verification link\n"
 "\t\tbelow or paste it into your web browser address bar.\n"
 "\n"
 "\t\tIf you did NOT request this change, please DO NOT follow the link\n"
@@ -1169,7 +1336,7 @@ msgid ""
 "\t\tissued this request."
 msgstr ""
 
-#: ../../mod/lostpass.php:53
+#: mod/lostpass.php:53
 #, php-format
 msgid ""
 "\n"
@@ -1178,7 +1345,8 @@ msgid ""
 "\t\t%1$s\n"
 "\n"
 "\t\tYou will then receive a follow-up message containing the new password.\n"
-"\t\tYou may change that password from your account settings page after logging in.\n"
+"\t\tYou may change that password from your account settings page after "
+"logging in.\n"
 "\n"
 "\t\tThe login details are as follows:\n"
 "\n"
@@ -1186,55 +1354,60 @@ msgid ""
 "\t\tLogin Name:\t%3$s"
 msgstr ""
 
-#: ../../mod/lostpass.php:72
+#: mod/lostpass.php:72
 #, php-format
 msgid "Password reset requested at %s"
 msgstr "Запрос на сброс пароля получен %s"
 
-#: ../../mod/lostpass.php:92
+#: mod/lostpass.php:92
 msgid ""
 "Request could not be verified. (You may have previously submitted it.) "
 "Password reset failed."
-msgstr "Запрос не может быть проверен. (Вы, возможно, ранее представляли его.) Попытка сброса пароля неудачная."
+msgstr ""
+"Запрос не может быть проверен. (Вы, возможно, ранее представляли его.) "
+"Попытка сброса пароля неудачная."
 
-#: ../../mod/lostpass.php:109 ../../boot.php:1280
+#: mod/lostpass.php:109 boot.php:1444
 msgid "Password Reset"
 msgstr "Сброс пароля"
 
-#: ../../mod/lostpass.php:110
+#: mod/lostpass.php:110
 msgid "Your password has been reset as requested."
 msgstr "Ваш пароль был сброшен по требованию."
 
-#: ../../mod/lostpass.php:111
+#: mod/lostpass.php:111
 msgid "Your new password is"
 msgstr "Ваш новый пароль"
 
-#: ../../mod/lostpass.php:112
+#: mod/lostpass.php:112
 msgid "Save or copy your new password - and then"
 msgstr "Сохраните или скопируйте новый пароль - и затем"
 
-#: ../../mod/lostpass.php:113
+#: mod/lostpass.php:113
 msgid "click here to login"
 msgstr "нажмите здесь для входа"
 
-#: ../../mod/lostpass.php:114
+#: mod/lostpass.php:114
 msgid ""
 "Your password may be changed from the <em>Settings</em> page after "
 "successful login."
-msgstr "Ваш пароль может быть изменен на странице <em>Настройки</em> после успешного входа."
+msgstr ""
+"Ваш пароль может быть изменен на странице <em>Настройки</em> после успешного "
+"входа."
 
-#: ../../mod/lostpass.php:125
+#: mod/lostpass.php:125
 #, php-format
 msgid ""
 "\n"
 "\t\t\t\tDear %1$s,\n"
 "\t\t\t\t\tYour password has been changed as requested. Please retain this\n"
-"\t\t\t\tinformation for your records (or change your password immediately to\n"
+"\t\t\t\tinformation for your records (or change your password immediately "
+"to\n"
 "\t\t\t\tsomething that you will remember).\n"
 "\t\t\t"
 msgstr ""
 
-#: ../../mod/lostpass.php:131
+#: mod/lostpass.php:131
 #, php-format
 msgid ""
 "\n"
@@ -1244,1379 +1417,1693 @@ msgid ""
 "\t\t\t\tLogin Name:\t%2$s\n"
 "\t\t\t\tPassword:\t%3$s\n"
 "\n"
-"\t\t\t\tYou may change that password from your account settings page after logging in.\n"
+"\t\t\t\tYou may change that password from your account settings page after "
+"logging in.\n"
 "\t\t\t"
 msgstr ""
 
-#: ../../mod/lostpass.php:147
+#: mod/lostpass.php:147
 #, php-format
 msgid "Your password has been changed at %s"
 msgstr "Ваш пароль был изменен %s"
 
-#: ../../mod/lostpass.php:159
+#: mod/lostpass.php:159
 msgid "Forgot your Password?"
 msgstr "Забыли пароль?"
 
-#: ../../mod/lostpass.php:160
+#: mod/lostpass.php:160
 msgid ""
 "Enter your email address and submit to have your password reset. Then check "
 "your email for further instructions."
-msgstr "Введите адрес электронной почты и подтвердите, что вы хотите сбросить ваш пароль. Затем проверьте свою электронную почту для получения дальнейших инструкций."
+msgstr ""
+"Введите адрес электронной почты и подтвердите, что вы хотите сбросить ваш "
+"пароль. Затем проверьте свою электронную почту для получения дальнейших "
+"инструкций."
 
-#: ../../mod/lostpass.php:161
+#: mod/lostpass.php:161
 msgid "Nickname or Email: "
 msgstr "Ник или E-mail: "
 
-#: ../../mod/lostpass.php:162
+#: mod/lostpass.php:162
 msgid "Reset"
 msgstr "Сброс"
 
-#: ../../mod/like.php:166 ../../include/conversation.php:137
-#: ../../include/diaspora.php:2103 ../../view/theme/diabook/theme.php:480
-#, php-format
-msgid "%1$s likes %2$s's %3$s"
-msgstr "%1$s нравится %3$s от %2$s "
-
-#: ../../mod/like.php:168 ../../include/conversation.php:140
-#, php-format
-msgid "%1$s doesn't like %2$s's %3$s"
-msgstr "%1$s не нравится %3$s от %2$s "
-
-#: ../../mod/ping.php:240
+#: mod/ping.php:265
 msgid "{0} wants to be your friend"
 msgstr "{0} хочет стать Вашим другом"
 
-#: ../../mod/ping.php:245
+#: mod/ping.php:280
 msgid "{0} sent you a message"
 msgstr "{0} отправил Вам сообщение"
 
-#: ../../mod/ping.php:250
+#: mod/ping.php:295
 msgid "{0} requested registration"
 msgstr "{0} требуемая регистрация"
 
-#: ../../mod/ping.php:256
-#, php-format
-msgid "{0} commented %s's post"
-msgstr "{0} прокомментировал сообщение от %s"
+#: mod/viewcontacts.php:72
+msgid "No contacts."
+msgstr "Нет контактов."
 
-#: ../../mod/ping.php:261
-#, php-format
-msgid "{0} liked %s's post"
-msgstr "{0} нравится сообщение от %s"
+#: mod/notifications.php:29
+msgid "Invalid request identifier."
+msgstr "Неверный идентификатор запроса."
 
-#: ../../mod/ping.php:266
-#, php-format
-msgid "{0} disliked %s's post"
-msgstr "{0} не нравится сообщение от %s"
-
-#: ../../mod/ping.php:271
-#, php-format
-msgid "{0} is now friends with %s"
-msgstr "{0} теперь друзья с %s"
-
-#: ../../mod/ping.php:276
-msgid "{0} posted"
-msgstr "{0} опубликовано"
-
-#: ../../mod/ping.php:281
-#, php-format
-msgid "{0} tagged %s's post with #%s"
-msgstr "{0} пометил сообщение %s с #%s"
-
-#: ../../mod/ping.php:287
-msgid "{0} mentioned you in a post"
-msgstr "{0} упоменул Вас в сообщение"
-
-#: ../../mod/viewcontacts.php:41
-msgid "No contacts."
-msgstr "Нет контактов."
-
-#: ../../mod/viewcontacts.php:78 ../../include/text.php:876
-msgid "View Contacts"
-msgstr "Просмотр контактов"
-
-#: ../../mod/notifications.php:26
-msgid "Invalid request identifier."
-msgstr "Неверный идентификатор запроса."
-
-#: ../../mod/notifications.php:35 ../../mod/notifications.php:165
-#: ../../mod/notifications.php:211
+#: mod/notifications.php:38 mod/notifications.php:180
+#: mod/notifications.php:260
 msgid "Discard"
 msgstr "Отказаться"
 
-#: ../../mod/notifications.php:78
+#: mod/notifications.php:81
 msgid "System"
 msgstr "Система"
 
-#: ../../mod/notifications.php:83 ../../include/nav.php:145
+#: mod/notifications.php:87 mod/admin.php:390 include/nav.php:154
 msgid "Network"
-msgstr "СеÑ\82Ñ\8c"
+msgstr "Ð\9dовоÑ\81Ñ\82и"
 
-#: ../../mod/notifications.php:88 ../../mod/network.php:371
+#: mod/notifications.php:93 mod/network.php:384
 msgid "Personal"
 msgstr "Персонал"
 
-#: ../../mod/notifications.php:93 ../../include/nav.php:105
-#: ../../include/nav.php:148 ../../view/theme/diabook/theme.php:123
+#: mod/notifications.php:99 include/nav.php:104 include/nav.php:157
+#: view/theme/diabook/theme.php:123
 msgid "Home"
-msgstr "Ð\93лавнаÑ\8f"
+msgstr "Ð\9cой Ð¿Ñ\80оÑ\84илÑ\8c"
 
-#: ../../mod/notifications.php:98 ../../include/nav.php:154
+#: mod/notifications.php:105 include/nav.php:162
 msgid "Introductions"
 msgstr "Запросы"
 
-#: ../../mod/notifications.php:122
+#: mod/notifications.php:130
 msgid "Show Ignored Requests"
 msgstr "Показать проигнорированные запросы"
 
-#: ../../mod/notifications.php:122
+#: mod/notifications.php:130
 msgid "Hide Ignored Requests"
 msgstr "Скрыть проигнорированные запросы"
 
-#: ../../mod/notifications.php:149 ../../mod/notifications.php:195
+#: mod/notifications.php:164 mod/notifications.php:234
 msgid "Notification type: "
 msgstr "Тип уведомления: "
 
-#: ../../mod/notifications.php:150
+#: mod/notifications.php:165
 msgid "Friend Suggestion"
 msgstr "Предложение в друзья"
 
-#: ../../mod/notifications.php:152
+#: mod/notifications.php:167
 #, php-format
 msgid "suggested by %s"
 msgstr "предложено юзером %s"
 
-#: ../../mod/notifications.php:158 ../../mod/notifications.php:205
+#: mod/notifications.php:173 mod/notifications.php:252
 msgid "Post a new friend activity"
 msgstr "Настроение"
 
-#: ../../mod/notifications.php:158 ../../mod/notifications.php:205
+#: mod/notifications.php:173 mod/notifications.php:252
 msgid "if applicable"
 msgstr "если требуется"
 
-#: ../../mod/notifications.php:161 ../../mod/notifications.php:208
-#: ../../mod/admin.php:1005
+#: mod/notifications.php:176 mod/notifications.php:257 mod/admin.php:1308
 msgid "Approve"
 msgstr "Одобрить"
 
-#: ../../mod/notifications.php:181
+#: mod/notifications.php:196
 msgid "Claims to be known to you: "
 msgstr "Утверждения, о которых должно быть вам известно: "
 
-#: ../../mod/notifications.php:181
+#: mod/notifications.php:196
 msgid "yes"
 msgstr "да"
 
-#: ../../mod/notifications.php:181
+#: mod/notifications.php:196
 msgid "no"
 msgstr "нет"
 
-#: ../../mod/notifications.php:188
-msgid "Approve as: "
-msgstr "Утвердить как: "
+#: mod/notifications.php:197
+msgid ""
+"Shall your connection be bidirectional or not? \"Friend\" implies that you "
+"allow to read and you subscribe to their posts. \"Fan/Admirer\" means that "
+"you allow to read but you do not want to read theirs. Approve as: "
+msgstr ""
+
+#: mod/notifications.php:200
+msgid ""
+"Shall your connection be bidirectional or not? \"Friend\" implies that you "
+"allow to read and you subscribe to their posts. \"Sharer\" means that you "
+"allow to read but you do not want to read theirs. Approve as: "
+msgstr ""
 
-#: ../../mod/notifications.php:189
+#: mod/notifications.php:208
 msgid "Friend"
 msgstr "Друг"
 
-#: ../../mod/notifications.php:190
+#: mod/notifications.php:209
 msgid "Sharer"
 msgstr "Участник"
 
-#: ../../mod/notifications.php:190
+#: mod/notifications.php:209
 msgid "Fan/Admirer"
 msgstr "Фанат / Поклонник"
 
-#: ../../mod/notifications.php:196
+#: mod/notifications.php:235
 msgid "Friend/Connect Request"
 msgstr "Запрос в друзья / на подключение"
 
-#: ../../mod/notifications.php:196
+#: mod/notifications.php:235
 msgid "New Follower"
 msgstr "Новый фолловер"
 
-#: ../../mod/notifications.php:217
+#: mod/notifications.php:250 mod/directory.php:147 include/identity.php:310
+#: include/identity.php:590
+msgid "Gender:"
+msgstr "Пол:"
+
+#: mod/notifications.php:266
 msgid "No introductions."
 msgstr "Запросов нет."
 
-#: ../../mod/notifications.php:220 ../../include/nav.php:155
+#: mod/notifications.php:269 include/nav.php:165
 msgid "Notifications"
 msgstr "Уведомления"
 
-#: ../../mod/notifications.php:258 ../../mod/notifications.php:387
-#: ../../mod/notifications.php:478
+#: mod/notifications.php:307 mod/notifications.php:436
+#: mod/notifications.php:527
 #, php-format
 msgid "%s liked %s's post"
 msgstr "%s нравится %s сообшение"
 
-#: ../../mod/notifications.php:268 ../../mod/notifications.php:397
-#: ../../mod/notifications.php:488
+#: mod/notifications.php:317 mod/notifications.php:446
+#: mod/notifications.php:537
 #, php-format
 msgid "%s disliked %s's post"
 msgstr "%s не нравится %s сообшение"
 
-#: ../../mod/notifications.php:283 ../../mod/notifications.php:412
-#: ../../mod/notifications.php:503
+#: mod/notifications.php:332 mod/notifications.php:461
+#: mod/notifications.php:552
 #, php-format
 msgid "%s is now friends with %s"
 msgstr "%s теперь друзья с %s"
 
-#: ../../mod/notifications.php:290 ../../mod/notifications.php:419
+#: mod/notifications.php:339 mod/notifications.php:468
 #, php-format
 msgid "%s created a new post"
 msgstr "%s написал новое сообщение"
 
-#: ../../mod/notifications.php:291 ../../mod/notifications.php:420
-#: ../../mod/notifications.php:513
+#: mod/notifications.php:340 mod/notifications.php:469
+#: mod/notifications.php:562
 #, php-format
 msgid "%s commented on %s's post"
 msgstr "%s прокомментировал %s сообщение"
 
-#: ../../mod/notifications.php:306
+#: mod/notifications.php:355
 msgid "No more network notifications."
 msgstr "Уведомлений из сети больше нет."
 
-#: ../../mod/notifications.php:310
+#: mod/notifications.php:359
 msgid "Network Notifications"
 msgstr "Уведомления сети"
 
-#: ../../mod/notifications.php:336 ../../mod/notify.php:75
+#: mod/notifications.php:385 mod/notify.php:72
 msgid "No more system notifications."
 msgstr "Системных уведомлений больше нет."
 
-#: ../../mod/notifications.php:340 ../../mod/notify.php:79
+#: mod/notifications.php:389 mod/notify.php:76
 msgid "System Notifications"
 msgstr "Уведомления системы"
 
-#: ../../mod/notifications.php:435
+#: mod/notifications.php:484
 msgid "No more personal notifications."
 msgstr "Персональных уведомлений больше нет."
 
-#: ../../mod/notifications.php:439
+#: mod/notifications.php:488
 msgid "Personal Notifications"
 msgstr "Личные уведомления"
 
-#: ../../mod/notifications.php:520
+#: mod/notifications.php:569
 msgid "No more home notifications."
 msgstr "Уведомлений больше нет."
 
-#: ../../mod/notifications.php:524
+#: mod/notifications.php:573
 msgid "Home Notifications"
 msgstr "Уведомления"
 
-#: ../../mod/babel.php:17
+#: mod/babel.php:17
 msgid "Source (bbcode) text:"
 msgstr "Код (bbcode):"
 
-#: ../../mod/babel.php:23
+#: mod/babel.php:23
 msgid "Source (Diaspora) text to convert to BBcode:"
 msgstr "Код (Diaspora) для конвертации в BBcode:"
 
-#: ../../mod/babel.php:31
+#: mod/babel.php:31
 msgid "Source input: "
 msgstr "Ввести код:"
 
-#: ../../mod/babel.php:35
+#: mod/babel.php:35
 msgid "bb2html (raw HTML): "
 msgstr "bb2html (raw HTML): "
 
-#: ../../mod/babel.php:39
+#: mod/babel.php:39
 msgid "bb2html: "
 msgstr "bb2html: "
 
-#: ../../mod/babel.php:43
+#: mod/babel.php:43
 msgid "bb2html2bb: "
 msgstr "bb2html2bb: "
 
-#: ../../mod/babel.php:47
+#: mod/babel.php:47
 msgid "bb2md: "
 msgstr "bb2md: "
 
-#: ../../mod/babel.php:51
+#: mod/babel.php:51
 msgid "bb2md2html: "
 msgstr "bb2md2html: "
 
-#: ../../mod/babel.php:55
+#: mod/babel.php:55
 msgid "bb2dia2bb: "
 msgstr "bb2dia2bb: "
 
-#: ../../mod/babel.php:59
+#: mod/babel.php:59
 msgid "bb2md2html2bb: "
 msgstr "bb2md2html2bb: "
 
-#: ../../mod/babel.php:69
+#: mod/babel.php:69
 msgid "Source input (Diaspora format): "
 msgstr "Ввод кода (формат Diaspora):"
 
-#: ../../mod/babel.php:74
+#: mod/babel.php:74
 msgid "diaspora2bb: "
 msgstr "diaspora2bb: "
 
-#: ../../mod/navigation.php:20 ../../include/nav.php:34
+#: mod/navigation.php:19 include/nav.php:33
 msgid "Nothing new here"
 msgstr "Ничего нового здесь"
 
-#: ../../mod/navigation.php:24 ../../include/nav.php:38
+#: mod/navigation.php:23 include/nav.php:37
 msgid "Clear notifications"
 msgstr "Стереть уведомления"
 
-#: ../../mod/message.php:9 ../../include/nav.php:164
+#: mod/message.php:15 include/nav.php:174
 msgid "New Message"
 msgstr "Новое сообщение"
 
-#: ../../mod/message.php:63 ../../mod/wallmessage.php:56
+#: mod/message.php:70 mod/wallmessage.php:56
 msgid "No recipient selected."
 msgstr "Не выбран получатель."
 
-#: ../../mod/message.php:67
+#: mod/message.php:74
 msgid "Unable to locate contact information."
 msgstr "Не удалось найти контактную информацию."
 
-#: ../../mod/message.php:70 ../../mod/wallmessage.php:62
+#: mod/message.php:77 mod/wallmessage.php:62
 msgid "Message could not be sent."
 msgstr "Сообщение не может быть отправлено."
 
-#: ../../mod/message.php:73 ../../mod/wallmessage.php:65
+#: mod/message.php:80 mod/wallmessage.php:65
 msgid "Message collection failure."
 msgstr "Неудача коллекции сообщения."
 
-#: ../../mod/message.php:76 ../../mod/wallmessage.php:68
+#: mod/message.php:83 mod/wallmessage.php:68
 msgid "Message sent."
 msgstr "Сообщение отправлено."
 
-#: ../../mod/message.php:182 ../../include/nav.php:161
+#: mod/message.php:189 include/nav.php:171
 msgid "Messages"
 msgstr "Сообщения"
 
-#: ../../mod/message.php:207
+#: mod/message.php:214
 msgid "Do you really want to delete this message?"
 msgstr "Вы действительно хотите удалить это сообщение?"
 
-#: ../../mod/message.php:227
+#: mod/message.php:234
 msgid "Message deleted."
 msgstr "Сообщение удалено."
 
-#: ../../mod/message.php:258
+#: mod/message.php:265
 msgid "Conversation removed."
 msgstr "Беседа удалена."
 
-#: ../../mod/message.php:283 ../../mod/message.php:291
-#: ../../mod/message.php:466 ../../mod/message.php:474
-#: ../../mod/wallmessage.php:127 ../../mod/wallmessage.php:135
-#: ../../include/conversation.php:1002 ../../include/conversation.php:1020
+#: mod/message.php:290 mod/message.php:298 mod/message.php:427
+#: mod/message.php:435 mod/wallmessage.php:127 mod/wallmessage.php:135
+#: include/conversation.php:1128 include/conversation.php:1146
 msgid "Please enter a link URL:"
 msgstr "Пожалуйста, введите URL ссылки:"
 
-#: ../../mod/message.php:319 ../../mod/wallmessage.php:142
+#: mod/message.php:326 mod/wallmessage.php:142
 msgid "Send Private Message"
 msgstr "Отправить личное сообщение"
 
-#: ../../mod/message.php:320 ../../mod/message.php:553
-#: ../../mod/wallmessage.php:144
+#: mod/message.php:327 mod/message.php:514 mod/wallmessage.php:144
 msgid "To:"
 msgstr "Кому:"
 
-#: ../../mod/message.php:325 ../../mod/message.php:555
-#: ../../mod/wallmessage.php:145
+#: mod/message.php:332 mod/message.php:516 mod/wallmessage.php:145
 msgid "Subject:"
 msgstr "Тема:"
 
-#: ../../mod/message.php:329 ../../mod/message.php:558
-#: ../../mod/wallmessage.php:151 ../../mod/invite.php:134
+#: mod/message.php:336 mod/message.php:519 mod/wallmessage.php:151
+#: mod/invite.php:134
 msgid "Your message:"
 msgstr "Ваше сообщение:"
 
-#: ../../mod/message.php:332 ../../mod/message.php:562
-#: ../../mod/wallmessage.php:154 ../../mod/editpost.php:110
-#: ../../include/conversation.php:1091
+#: mod/message.php:339 mod/message.php:523 mod/wallmessage.php:154
+#: mod/editpost.php:110 include/conversation.php:1183
 msgid "Upload photo"
 msgstr "Загрузить фото"
 
-#: ../../mod/message.php:333 ../../mod/message.php:563
-#: ../../mod/wallmessage.php:155 ../../mod/editpost.php:114
-#: ../../include/conversation.php:1095
+#: mod/message.php:340 mod/message.php:524 mod/wallmessage.php:155
+#: mod/editpost.php:114 include/conversation.php:1187
 msgid "Insert web link"
 msgstr "Вставить веб-ссылку"
 
-#: ../../mod/message.php:334 ../../mod/message.php:565
-#: ../../mod/content.php:499 ../../mod/content.php:883
-#: ../../mod/wallmessage.php:156 ../../mod/editpost.php:124
-#: ../../mod/photos.php:1545 ../../object/Item.php:364
-#: ../../include/conversation.php:692 ../../include/conversation.php:1109
+#: mod/message.php:341 mod/message.php:526 mod/content.php:501
+#: mod/content.php:885 mod/wallmessage.php:156 mod/editpost.php:124
+#: mod/photos.php:1610 object/Item.php:396 include/conversation.php:713
+#: include/conversation.php:1201
 msgid "Please wait"
 msgstr "Пожалуйста, подождите"
 
-#: ../../mod/message.php:371
+#: mod/message.php:368
 msgid "No messages."
 msgstr "Нет сообщений."
 
-#: ../../mod/message.php:378
+#: mod/message.php:411
+msgid "Message not available."
+msgstr "Сообщение не доступно."
+
+#: mod/message.php:481
+msgid "Delete message"
+msgstr "Удалить сообщение"
+
+#: mod/message.php:507 mod/message.php:584
+msgid "Delete conversation"
+msgstr "Удалить историю общения"
+
+#: mod/message.php:509
+msgid ""
+"No secure communications available. You <strong>may</strong> be able to "
+"respond from the sender's profile page."
+msgstr ""
+"Невозможно защищённое соединение. Вы <strong>имеете</strong> возможность "
+"ответить со страницы профиля отправителя."
+
+#: mod/message.php:513
+msgid "Send Reply"
+msgstr "Отправить ответ"
+
+#: mod/message.php:557
 #, php-format
 msgid "Unknown sender - %s"
 msgstr "Неизвестный отправитель - %s"
 
-#: ../../mod/message.php:381
+#: mod/message.php:560
 #, php-format
 msgid "You and %s"
 msgstr "Вы и %s"
 
-#: ../../mod/message.php:384
+#: mod/message.php:563
 #, php-format
 msgid "%s and You"
 msgstr "%s и Вы"
 
-#: ../../mod/message.php:405 ../../mod/message.php:546
-msgid "Delete conversation"
-msgstr "Удалить историю общения"
-
-#: ../../mod/message.php:408
+#: mod/message.php:587
 msgid "D, d M Y - g:i A"
 msgstr "D, d M Y - g:i A"
 
-#: ../../mod/message.php:411
+#: mod/message.php:590
 #, php-format
 msgid "%d message"
 msgid_plural "%d messages"
 msgstr[0] "%d сообщение"
 msgstr[1] "%d сообщений"
 msgstr[2] "%d сообщений"
+msgstr[3] "%d сообщений"
 
-#: ../../mod/message.php:450
-msgid "Message not available."
-msgstr "Сообщение не доступно."
-
-#: ../../mod/message.php:520
-msgid "Delete message"
-msgstr "Удалить сообщение"
-
-#: ../../mod/message.php:548
-msgid ""
-"No secure communications available. You <strong>may</strong> be able to "
-"respond from the sender's profile page."
-msgstr "Невозможно защищённое соединение. Вы <strong>имеете</strong> возможность ответить со страницы профиля отправителя."
-
-#: ../../mod/message.php:552
-msgid "Send Reply"
-msgstr "Отправить ответ"
-
-#: ../../mod/update_display.php:22 ../../mod/update_community.php:18
-#: ../../mod/update_notes.php:37 ../../mod/update_profile.php:41
-#: ../../mod/update_network.php:25
+#: mod/update_display.php:22 mod/update_community.php:18
+#: mod/update_notes.php:37 mod/update_profile.php:41 mod/update_network.php:25
 msgid "[Embedded content - reload page to view]"
 msgstr "[Встроенное содержание - перезагрузите страницу для просмотра]"
 
-#: ../../mod/crepair.php:106
+#: mod/crepair.php:104
 msgid "Contact settings applied."
 msgstr "Установки контакта приняты."
 
-#: ../../mod/crepair.php:108
+#: mod/crepair.php:106
 msgid "Contact update failed."
 msgstr "Обновление контакта неудачное."
 
-#: ../../mod/crepair.php:139
-msgid "Repair Contact Settings"
-msgstr "Восстановить установки контакта"
-
-#: ../../mod/crepair.php:141
+#: mod/crepair.php:137
 msgid ""
-"<strong>WARNING: This is highly advanced</strong> and if you enter incorrect"
-" information your communications with this contact may stop working."
-msgstr "<strong>ВНИМАНИЕ: Это крайне важно!</strong> Если вы введете неверную информацию, ваша связь с этим контактом перестанет работать."
+"<strong>WARNING: This is highly advanced</strong> and if you enter incorrect "
+"information your communications with this contact may stop working."
+msgstr ""
+"<strong>ВНИМАНИЕ: Это крайне важно!</strong> Если вы введете неверную "
+"информацию, ваша связь с этим контактом перестанет работать."
 
-#: ../../mod/crepair.php:142
+#: mod/crepair.php:138
 msgid ""
 "Please use your browser 'Back' button <strong>now</strong> if you are "
 "uncertain what to do on this page."
-msgstr "Пожалуйста, нажмите клавишу вашего браузера 'Back' или 'Назад' <strong>сейчас</strong>, если вы не уверены, что делаете на этой странице."
-
-#: ../../mod/crepair.php:148
-msgid "Return to contact editor"
-msgstr "Возврат к редактору контакта"
+msgstr ""
+"Пожалуйста, нажмите клавишу вашего браузера 'Back' или 'Назад' "
+"<strong>сейчас</strong>, если вы не уверены, что делаете на этой странице."
 
-#: ../../mod/crepair.php:159 ../../mod/crepair.php:161
+#: mod/crepair.php:151 mod/crepair.php:153
 msgid "No mirroring"
 msgstr ""
 
-#: ../../mod/crepair.php:159
+#: mod/crepair.php:151
 msgid "Mirror as forwarded posting"
 msgstr ""
 
-#: ../../mod/crepair.php:159 ../../mod/crepair.php:161
+#: mod/crepair.php:151 mod/crepair.php:153
 msgid "Mirror as my own posting"
 msgstr ""
 
-#: ../../mod/crepair.php:165 ../../mod/admin.php:1003 ../../mod/admin.php:1015
-#: ../../mod/admin.php:1016 ../../mod/admin.php:1029
-#: ../../mod/settings.php:616 ../../mod/settings.php:642
+#: mod/crepair.php:167
+msgid "Return to contact editor"
+msgstr "Возврат к редактору контакта"
+
+#: mod/crepair.php:169
+msgid "Refetch contact data"
+msgstr ""
+
+#: mod/crepair.php:170 mod/admin.php:1306 mod/admin.php:1318
+#: mod/admin.php:1319 mod/admin.php:1332 mod/settings.php:661
+#: mod/settings.php:687
 msgid "Name"
 msgstr "Имя"
 
-#: ../../mod/crepair.php:166
+#: mod/crepair.php:171
 msgid "Account Nickname"
 msgstr "Ник аккаунта"
 
-#: ../../mod/crepair.php:167
+#: mod/crepair.php:172
 msgid "@Tagname - overrides Name/Nickname"
 msgstr ""
 
-#: ../../mod/crepair.php:168
+#: mod/crepair.php:173
 msgid "Account URL"
 msgstr "URL аккаунта"
 
-#: ../../mod/crepair.php:169
+#: mod/crepair.php:174
 msgid "Friend Request URL"
 msgstr "URL запроса в друзья"
 
-#: ../../mod/crepair.php:170
+#: mod/crepair.php:175
 msgid "Friend Confirm URL"
 msgstr "URL подтверждения друга"
 
-#: ../../mod/crepair.php:171
+#: mod/crepair.php:176
 msgid "Notification Endpoint URL"
 msgstr "URL эндпоинта уведомления"
 
-#: ../../mod/crepair.php:172
+#: mod/crepair.php:177
 msgid "Poll/Feed URL"
 msgstr "URL опроса/ленты"
 
-#: ../../mod/crepair.php:173
+#: mod/crepair.php:178
 msgid "New photo from this URL"
 msgstr "Новое фото из этой URL"
 
-#: ../../mod/crepair.php:174
+#: mod/crepair.php:179
 msgid "Remote Self"
 msgstr ""
 
-#: ../../mod/crepair.php:176
+#: mod/crepair.php:182
 msgid "Mirror postings from this contact"
 msgstr ""
 
-#: ../../mod/crepair.php:176
+#: mod/crepair.php:184
 msgid ""
 "Mark this contact as remote_self, this will cause friendica to repost new "
 "entries from this contact."
 msgstr ""
 
-#: ../../mod/bookmarklet.php:12 ../../boot.php:1266 ../../include/nav.php:92
+#: mod/bookmarklet.php:12 boot.php:1430 include/nav.php:91
 msgid "Login"
 msgstr "Вход"
 
-#: ../../mod/bookmarklet.php:41
+#: mod/bookmarklet.php:41
 msgid "The post was created"
 msgstr ""
 
-#: ../../mod/viewsrc.php:7
+#: mod/viewsrc.php:7
 msgid "Access denied."
 msgstr "Доступ запрещен."
 
-#: ../../mod/dirfind.php:26
-msgid "People Search"
-msgstr "Поиск людей"
+#: mod/dirfind.php:194 mod/allfriends.php:80 mod/match.php:85
+#: mod/suggest.php:98 include/contact_widgets.php:10 include/identity.php:212
+msgid "Connect"
+msgstr "Подключить"
+
+#: mod/dirfind.php:195 mod/allfriends.php:64 mod/match.php:70
+#: mod/directory.php:162 mod/suggest.php:81 include/Contact.php:283
+#: include/Contact.php:296 include/Contact.php:338
+#: include/conversation.php:912 include/conversation.php:926
+msgid "View Profile"
+msgstr "Просмотреть профиль"
+
+#: mod/dirfind.php:224
+#, php-format
+msgid "People Search - %s"
+msgstr ""
 
-#: ../../mod/dirfind.php:60 ../../mod/match.php:65
+#: mod/dirfind.php:231 mod/match.php:105
 msgid "No matches"
 msgstr "Нет соответствий"
 
-#: ../../mod/fbrowser.php:25 ../../boot.php:2126 ../../include/nav.php:78
-#: ../../view/theme/diabook/theme.php:126
+#: mod/fbrowser.php:32 include/identity.php:702 include/nav.php:77
+#: view/theme/diabook/theme.php:126
 msgid "Photos"
 msgstr "Фото"
 
-#: ../../mod/fbrowser.php:113
+#: mod/fbrowser.php:41 mod/fbrowser.php:62 mod/photos.php:62
+#: mod/photos.php:192 mod/photos.php:1119 mod/photos.php:1245
+#: mod/photos.php:1268 mod/photos.php:1838 mod/photos.php:1850
+#: view/theme/diabook/theme.php:499
+msgid "Contact Photos"
+msgstr "Фотографии контакта"
+
+#: mod/fbrowser.php:125
 msgid "Files"
 msgstr "Файлы"
 
-#: ../../mod/nogroup.php:59
+#: mod/nogroup.php:63
 msgid "Contacts who are not members of a group"
 msgstr "Контакты, которые не являются членами группы"
 
-#: ../../mod/admin.php:57
+#: mod/admin.php:92
 msgid "Theme settings updated."
 msgstr "Настройки темы обновлены."
 
-#: ../../mod/admin.php:104 ../../mod/admin.php:619
+#: mod/admin.php:156 mod/admin.php:888
 msgid "Site"
 msgstr "Сайт"
 
-#: ../../mod/admin.php:105 ../../mod/admin.php:998 ../../mod/admin.php:1013
+#: mod/admin.php:157 mod/admin.php:832 mod/admin.php:1301 mod/admin.php:1316
 msgid "Users"
 msgstr "Пользователи"
 
-#: ../../mod/admin.php:106 ../../mod/admin.php:1102 ../../mod/admin.php:1155
-#: ../../mod/settings.php:57
+#: mod/admin.php:158 mod/admin.php:1416 mod/admin.php:1476 mod/settings.php:72
 msgid "Plugins"
 msgstr "Плагины"
 
-#: ../../mod/admin.php:107 ../../mod/admin.php:1323 ../../mod/admin.php:1357
+#: mod/admin.php:159 mod/admin.php:1674 mod/admin.php:1724
 msgid "Themes"
 msgstr "Темы"
 
-#: ../../mod/admin.php:108
+#: mod/admin.php:160 mod/settings.php:50
+msgid "Additional features"
+msgstr "Дополнительные возможности"
+
+#: mod/admin.php:161
 msgid "DB updates"
 msgstr "Обновление БД"
 
-#: ../../mod/admin.php:123 ../../mod/admin.php:132 ../../mod/admin.php:1444
+#: mod/admin.php:162 mod/admin.php:385
+msgid "Inspect Queue"
+msgstr ""
+
+#: mod/admin.php:163 mod/admin.php:354
+msgid "Federation Statistics"
+msgstr ""
+
+#: mod/admin.php:177 mod/admin.php:188 mod/admin.php:1792
 msgid "Logs"
 msgstr "Журналы"
 
-#: ../../mod/admin.php:124
+#: mod/admin.php:178 mod/admin.php:1859
+msgid "View Logs"
+msgstr "Просмотр логов"
+
+#: mod/admin.php:179
 msgid "probe address"
 msgstr ""
 
-#: ../../mod/admin.php:125
+#: mod/admin.php:180
 msgid "check webfinger"
 msgstr ""
 
-#: ../../mod/admin.php:130 ../../include/nav.php:184
+#: mod/admin.php:186 include/nav.php:194
 msgid "Admin"
 msgstr "Администратор"
 
-#: ../../mod/admin.php:131
+#: mod/admin.php:187
 msgid "Plugin Features"
 msgstr "Возможности плагина"
 
-#: ../../mod/admin.php:133
+#: mod/admin.php:189
 msgid "diagnostics"
-msgstr ""
+msgstr "Диагностика"
 
-#: ../../mod/admin.php:134
+#: mod/admin.php:190
 msgid "User registrations waiting for confirmation"
 msgstr "Регистрации пользователей, ожидающие подтверждения"
 
-#: ../../mod/admin.php:193 ../../mod/admin.php:952
+#: mod/admin.php:347
+msgid ""
+"This page offers you some numbers to the known part of the federated social "
+"network your Friendica node is part of. These numbers are not complete but "
+"only reflect the part of the network your node is aware of."
+msgstr ""
+
+#: mod/admin.php:348
+msgid ""
+"The <em>Auto Discovered Contact Directory</em> feature is not enabled, it "
+"will improve the data displayed here."
+msgstr ""
+
+#: mod/admin.php:353 mod/admin.php:384 mod/admin.php:441 mod/admin.php:887
+#: mod/admin.php:1300 mod/admin.php:1415 mod/admin.php:1475 mod/admin.php:1673
+#: mod/admin.php:1723 mod/admin.php:1791 mod/admin.php:1858
+msgid "Administration"
+msgstr "Администрация"
+
+#: mod/admin.php:360
+#, php-format
+msgid "Currently this node is aware of %d nodes from the following platforms:"
+msgstr ""
+
+#: mod/admin.php:387
+msgid "ID"
+msgstr ""
+
+#: mod/admin.php:388
+msgid "Recipient Name"
+msgstr ""
+
+#: mod/admin.php:389
+msgid "Recipient Profile"
+msgstr ""
+
+#: mod/admin.php:391
+msgid "Created"
+msgstr ""
+
+#: mod/admin.php:392
+msgid "Last Tried"
+msgstr ""
+
+#: mod/admin.php:393
+msgid ""
+"This page lists the content of the queue for outgoing postings. These are "
+"postings the initial delivery failed for. They will be resend later and "
+"eventually deleted if the delivery fails permanently."
+msgstr ""
+
+#: mod/admin.php:412 mod/admin.php:1254
 msgid "Normal Account"
 msgstr "Обычный аккаунт"
 
-#: ../../mod/admin.php:194 ../../mod/admin.php:953
+#: mod/admin.php:413 mod/admin.php:1255
 msgid "Soapbox Account"
 msgstr "Аккаунт Витрина"
 
-#: ../../mod/admin.php:195 ../../mod/admin.php:954
+#: mod/admin.php:414 mod/admin.php:1256
 msgid "Community/Celebrity Account"
 msgstr "Аккаунт Сообщество / Знаменитость"
 
-#: ../../mod/admin.php:196 ../../mod/admin.php:955
+#: mod/admin.php:415 mod/admin.php:1257
 msgid "Automatic Friend Account"
 msgstr "\"Автоматический друг\" Аккаунт"
 
-#: ../../mod/admin.php:197
+#: mod/admin.php:416
 msgid "Blog Account"
 msgstr "Аккаунт блога"
 
-#: ../../mod/admin.php:198
+#: mod/admin.php:417
 msgid "Private Forum"
 msgstr "Личный форум"
 
-#: ../../mod/admin.php:217
+#: mod/admin.php:436
 msgid "Message queues"
 msgstr "Очереди сообщений"
 
-#: ../../mod/admin.php:222 ../../mod/admin.php:618 ../../mod/admin.php:997
-#: ../../mod/admin.php:1101 ../../mod/admin.php:1154 ../../mod/admin.php:1322
-#: ../../mod/admin.php:1356 ../../mod/admin.php:1443
-msgid "Administration"
-msgstr "Администрация"
-
-#: ../../mod/admin.php:223
+#: mod/admin.php:442
 msgid "Summary"
 msgstr "Резюме"
 
-#: ../../mod/admin.php:225
+#: mod/admin.php:444
 msgid "Registered users"
 msgstr "Зарегистрированные пользователи"
 
-#: ../../mod/admin.php:227
+#: mod/admin.php:446
 msgid "Pending registrations"
 msgstr "Ожидающие регистрации"
 
-#: ../../mod/admin.php:228
+#: mod/admin.php:447
 msgid "Version"
 msgstr "Версия"
 
-#: ../../mod/admin.php:232
+#: mod/admin.php:452
 msgid "Active plugins"
 msgstr "Активные плагины"
 
-#: ../../mod/admin.php:255
+#: mod/admin.php:475
 msgid "Can not parse base url. Must have at least <scheme>://<domain>"
-msgstr "Невозможно определить базовый URL. Он должен иметь следующий вид - <scheme>://<domain>"
+msgstr ""
+"Невозможно определить базовый URL. Он должен иметь следующий вид - "
+"<scheme>://<domain>"
 
-#: ../../mod/admin.php:516
+#: mod/admin.php:760
+msgid "RINO2 needs mcrypt php extension to work."
+msgstr "Для функционирования RINO2 необходим пакет php5-mcrypt"
+
+#: mod/admin.php:768
 msgid "Site settings updated."
 msgstr "Установки сайта обновлены."
 
-#: ../../mod/admin.php:545 ../../mod/settings.php:828
+#: mod/admin.php:796 mod/settings.php:912
 msgid "No special theme for mobile devices"
 msgstr "Нет специальной темы для мобильных устройств"
 
-#: ../../mod/admin.php:562
+#: mod/admin.php:815
 msgid "No community page"
 msgstr ""
 
-#: ../../mod/admin.php:563
+#: mod/admin.php:816
 msgid "Public postings from users of this site"
 msgstr ""
 
-#: ../../mod/admin.php:564
+#: mod/admin.php:817
 msgid "Global community page"
 msgstr ""
 
-#: ../../mod/admin.php:570
+#: mod/admin.php:823
 msgid "At post arrival"
 msgstr ""
 
-#: ../../mod/admin.php:571 ../../include/contact_selectors.php:56
+#: mod/admin.php:824 include/contact_selectors.php:56
 msgid "Frequently"
 msgstr "Часто"
 
-#: ../../mod/admin.php:572 ../../include/contact_selectors.php:57
+#: mod/admin.php:825 include/contact_selectors.php:57
 msgid "Hourly"
 msgstr "Раз в час"
 
-#: ../../mod/admin.php:573 ../../include/contact_selectors.php:58
+#: mod/admin.php:826 include/contact_selectors.php:58
 msgid "Twice daily"
 msgstr "Два раза в день"
 
-#: ../../mod/admin.php:574 ../../include/contact_selectors.php:59
+#: mod/admin.php:827 include/contact_selectors.php:59
 msgid "Daily"
 msgstr "Ежедневно"
 
-#: ../../mod/admin.php:579
+#: mod/admin.php:833
+msgid "Users, Global Contacts"
+msgstr ""
+
+#: mod/admin.php:834
+msgid "Users, Global Contacts/fallback"
+msgstr ""
+
+#: mod/admin.php:838
+msgid "One month"
+msgstr "Один месяц"
+
+#: mod/admin.php:839
+msgid "Three months"
+msgstr "Три месяца"
+
+#: mod/admin.php:840
+msgid "Half a year"
+msgstr "Пол года"
+
+#: mod/admin.php:841
+msgid "One year"
+msgstr "Один год"
+
+#: mod/admin.php:846
 msgid "Multi user instance"
 msgstr "Многопользовательский вид"
 
-#: ../../mod/admin.php:602
+#: mod/admin.php:869
 msgid "Closed"
 msgstr "Закрыто"
 
-#: ../../mod/admin.php:603
+#: mod/admin.php:870
 msgid "Requires approval"
 msgstr "Требуется подтверждение"
 
-#: ../../mod/admin.php:604
+#: mod/admin.php:871
 msgid "Open"
 msgstr "Открыто"
 
-#: ../../mod/admin.php:608
+#: mod/admin.php:875
 msgid "No SSL policy, links will track page SSL state"
 msgstr "Нет режима SSL, состояние SSL не будет отслеживаться"
 
-#: ../../mod/admin.php:609
+#: mod/admin.php:876
 msgid "Force all links to use SSL"
 msgstr "Заставить все ссылки использовать SSL"
 
-#: ../../mod/admin.php:610
+#: mod/admin.php:877
 msgid "Self-signed certificate, use SSL for local links only (discouraged)"
-msgstr "Само-подписанный сертификат, использовать SSL только локально (не рекомендуется)"
+msgstr ""
+"Само-подписанный сертификат, использовать SSL только локально (не "
+"рекомендуется)"
 
-#: ../../mod/admin.php:620 ../../mod/admin.php:1156 ../../mod/admin.php:1358
-#: ../../mod/admin.php:1445 ../../mod/settings.php:614
-#: ../../mod/settings.php:724 ../../mod/settings.php:798
-#: ../../mod/settings.php:880 ../../mod/settings.php:1113
+#: mod/admin.php:889 mod/admin.php:1477 mod/admin.php:1725 mod/admin.php:1793
+#: mod/admin.php:1942 mod/settings.php:659 mod/settings.php:769
+#: mod/settings.php:813 mod/settings.php:882 mod/settings.php:969
+#: mod/settings.php:1204
 msgid "Save Settings"
 msgstr "Сохранить настройки"
 
-#: ../../mod/admin.php:621 ../../mod/register.php:255
+#: mod/admin.php:890 mod/register.php:263
 msgid "Registration"
 msgstr "Регистрация"
 
-#: ../../mod/admin.php:622
+#: mod/admin.php:891
 msgid "File upload"
 msgstr "Загрузка файлов"
 
-#: ../../mod/admin.php:623
+#: mod/admin.php:892
 msgid "Policies"
 msgstr "Политики"
 
-#: ../../mod/admin.php:624
+#: mod/admin.php:893
 msgid "Advanced"
 msgstr "Расширенный"
 
-#: ../../mod/admin.php:625
+#: mod/admin.php:894
+msgid "Auto Discovered Contact Directory"
+msgstr ""
+
+#: mod/admin.php:895
 msgid "Performance"
 msgstr "Производительность"
 
-#: ../../mod/admin.php:626
+#: mod/admin.php:896
 msgid ""
 "Relocate - WARNING: advanced function. Could make this server unreachable."
-msgstr "Переместить - ПРЕДУПРЕЖДЕНИЕ: расширеная функция. Может сделать этот сервер недоступным."
+msgstr ""
+"Переместить - ПРЕДУПРЕЖДЕНИЕ: расширеная функция. Может сделать этот сервер "
+"недоступным."
 
-#: ../../mod/admin.php:629
+#: mod/admin.php:899
 msgid "Site name"
 msgstr "Название сайта"
 
-#: ../../mod/admin.php:630
+#: mod/admin.php:900
 msgid "Host name"
-msgstr ""
+msgstr "Имя хоста"
 
-#: ../../mod/admin.php:631
+#: mod/admin.php:901
 msgid "Sender Email"
-msgstr ""
+msgstr "Системный Email"
+
+#: mod/admin.php:901
+msgid ""
+"The email address your server shall use to send notification emails from."
+msgstr "Адрес с которого будут приходить письма пользователям."
 
-#: ../../mod/admin.php:632
+#: mod/admin.php:902
 msgid "Banner/Logo"
 msgstr "Баннер/Логотип"
 
-#: ../../mod/admin.php:633
+#: mod/admin.php:903
 msgid "Shortcut icon"
 msgstr ""
 
-#: ../../mod/admin.php:634
+#: mod/admin.php:903
+msgid "Link to an icon that will be used for browsers."
+msgstr ""
+
+#: mod/admin.php:904
 msgid "Touch icon"
 msgstr ""
 
-#: ../../mod/admin.php:635
+#: mod/admin.php:904
+msgid "Link to an icon that will be used for tablets and mobiles."
+msgstr ""
+
+#: mod/admin.php:905
 msgid "Additional Info"
 msgstr "Дополнительная информация"
 
-#: ../../mod/admin.php:635
+#: mod/admin.php:905
+#, php-format
 msgid ""
 "For public servers: you can add additional information here that will be "
-"listed at dir.friendica.com/siteinfo."
-msgstr "Для публичных серверов: вы можете добавить дополнительную информацию, которая будет перечислена в dir.friendica.com/siteinfo."
+"listed at %s/siteinfo."
+msgstr ""
 
-#: ../../mod/admin.php:636
+#: mod/admin.php:906
 msgid "System language"
 msgstr "Системный язык"
 
-#: ../../mod/admin.php:637
+#: mod/admin.php:907
 msgid "System theme"
 msgstr "Системная тема"
 
-#: ../../mod/admin.php:637
+#: mod/admin.php:907
 msgid ""
 "Default system theme - may be over-ridden by user profiles - <a href='#' "
 "id='cnftheme'>change theme settings</a>"
-msgstr "Тема системы по умолчанию - может быть переопределена пользователем - <a href='#' id='cnftheme'>изменить настройки темы</a>"
+msgstr ""
+"Тема системы по умолчанию - может быть переопределена пользователем - <a "
+"href='#' id='cnftheme'>изменить настройки темы</a>"
 
-#: ../../mod/admin.php:638
+#: mod/admin.php:908
 msgid "Mobile system theme"
 msgstr "Мобильная тема системы"
 
-#: ../../mod/admin.php:638
+#: mod/admin.php:908
 msgid "Theme for mobile devices"
 msgstr "Тема для мобильных устройств"
 
-#: ../../mod/admin.php:639
+#: mod/admin.php:909
 msgid "SSL link policy"
 msgstr "Политика SSL"
 
-#: ../../mod/admin.php:639
+#: mod/admin.php:909
 msgid "Determines whether generated links should be forced to use SSL"
 msgstr "Ссылки должны быть вынуждены использовать SSL"
 
-#: ../../mod/admin.php:640
+#: mod/admin.php:910
 msgid "Force SSL"
-msgstr ""
+msgstr "SSL принудительно"
 
-#: ../../mod/admin.php:640
+#: mod/admin.php:910
 msgid ""
-"Force all Non-SSL requests to SSL - Attention: on some systems it could lead"
-" to endless loops."
+"Force all Non-SSL requests to SSL - Attention: on some systems it could lead "
+"to endless loops."
 msgstr ""
 
-#: ../../mod/admin.php:641
+#: mod/admin.php:911
 msgid "Old style 'Share'"
 msgstr "Старый стиль 'Share'"
 
-#: ../../mod/admin.php:641
+#: mod/admin.php:911
 msgid "Deactivates the bbcode element 'share' for repeating items."
 msgstr "Отключение BBCode элемента 'share' для повторяющихся элементов."
 
-#: ../../mod/admin.php:642
+#: mod/admin.php:912
 msgid "Hide help entry from navigation menu"
 msgstr "Скрыть пункт \"помощь\" в меню навигации"
 
-#: ../../mod/admin.php:642
+#: mod/admin.php:912
 msgid ""
 "Hides the menu entry for the Help pages from the navigation menu. You can "
 "still access it calling /help directly."
-msgstr "Скрывает элемент меню для страницы справки из меню навигации. Вы все еще можете получить доступ к нему через вызов/помощь напрямую."
+msgstr ""
+"Скрывает элемент меню для страницы справки из меню навигации. Вы все еще "
+"можете получить доступ к нему через вызов/помощь напрямую."
 
-#: ../../mod/admin.php:643
+#: mod/admin.php:913
 msgid "Single user instance"
 msgstr "Однопользовательский режим"
 
-#: ../../mod/admin.php:643
+#: mod/admin.php:913
 msgid "Make this instance multi-user or single-user for the named user"
-msgstr "Сделать этот экземпляр многопользовательским, или однопользовательским для названного пользователя"
+msgstr ""
+"Сделать этот экземпляр многопользовательским, или однопользовательским для "
+"названного пользователя"
 
-#: ../../mod/admin.php:644
+#: mod/admin.php:914
 msgid "Maximum image size"
 msgstr "Максимальный размер изображения"
 
-#: ../../mod/admin.php:644
+#: mod/admin.php:914
 msgid ""
 "Maximum size in bytes of uploaded images. Default is 0, which means no "
 "limits."
-msgstr "Максимальный размер в байтах для загружаемых изображений. По умолчанию 0, что означает отсутствие ограничений."
+msgstr ""
+"Максимальный размер в байтах для загружаемых изображений. По умолчанию 0, "
+"что означает отсутствие ограничений."
 
-#: ../../mod/admin.php:645
+#: mod/admin.php:915
 msgid "Maximum image length"
 msgstr "Максимальная длина картинки"
 
-#: ../../mod/admin.php:645
+#: mod/admin.php:915
 msgid ""
 "Maximum length in pixels of the longest side of uploaded images. Default is "
 "-1, which means no limits."
-msgstr "Максимальная длина в пикселях для длинной стороны загруженных изображений. По умолчанию равно -1, что означает отсутствие ограничений."
+msgstr ""
+"Максимальная длина в пикселях для длинной стороны загруженных изображений. "
+"По умолчанию равно -1, что означает отсутствие ограничений."
 
-#: ../../mod/admin.php:646
+#: mod/admin.php:916
 msgid "JPEG image quality"
 msgstr "Качество JPEG изображения"
 
-#: ../../mod/admin.php:646
+#: mod/admin.php:916
 msgid ""
 "Uploaded JPEGS will be saved at this quality setting [0-100]. Default is "
 "100, which is full quality."
-msgstr "Загруженные изображения JPEG будут сохранены в этом качестве [0-100]. По умолчанию 100, что означает полное качество."
+msgstr ""
+"Загруженные изображения JPEG будут сохранены в этом качестве [0-100]. По "
+"умолчанию 100, что означает полное качество."
 
-#: ../../mod/admin.php:648
+#: mod/admin.php:918
 msgid "Register policy"
 msgstr "Политика регистрация"
 
-#: ../../mod/admin.php:649
+#: mod/admin.php:919
 msgid "Maximum Daily Registrations"
 msgstr "Максимальное число регистраций в день"
 
-#: ../../mod/admin.php:649
+#: mod/admin.php:919
 msgid ""
-"If registration is permitted above, this sets the maximum number of new user"
-" registrations to accept per day.  If register is set to closed, this "
-"setting has no effect."
-msgstr "Если регистрация разрешена, этот параметр устанавливает максимальное количество новых регистраций пользователей в день. Если регистрация закрыта, эта опция не имеет никакого эффекта."
+"If registration is permitted above, this sets the maximum number of new user "
+"registrations to accept per day.  If register is set to closed, this setting "
+"has no effect."
+msgstr ""
+"Если регистрация разрешена, этот параметр устанавливает максимальное "
+"количество новых регистраций пользователей в день. Если регистрация закрыта, "
+"эта опция не имеет никакого эффекта."
 
-#: ../../mod/admin.php:650
+#: mod/admin.php:920
 msgid "Register text"
 msgstr "Текст регистрации"
 
-#: ../../mod/admin.php:650
+#: mod/admin.php:920
 msgid "Will be displayed prominently on the registration page."
 msgstr "Будет находиться на видном месте на странице регистрации."
 
-#: ../../mod/admin.php:651
+#: mod/admin.php:921
 msgid "Accounts abandoned after x days"
 msgstr "Аккаунт считается после x дней не воспользованным"
 
-#: ../../mod/admin.php:651
+#: mod/admin.php:921
 msgid ""
 "Will not waste system resources polling external sites for abandonded "
 "accounts. Enter 0 for no time limit."
-msgstr "Не будет тратить ресурсы для опроса сайтов для бесхозных контактов. Введите 0 для отключения лимита времени."
+msgstr ""
+"Не будет тратить ресурсы для опроса сайтов для бесхозных контактов. Введите "
+"0 для отключения лимита времени."
 
-#: ../../mod/admin.php:652
+#: mod/admin.php:922
 msgid "Allowed friend domains"
 msgstr "Разрешенные домены друзей"
 
-#: ../../mod/admin.php:652
+#: mod/admin.php:922
 msgid ""
 "Comma separated list of domains which are allowed to establish friendships "
 "with this site. Wildcards are accepted. Empty to allow any domains"
-msgstr "Разделенный запятыми список доменов, которые разрешены для установления связей. Групповые символы принимаются. Оставьте пустым для разрешения связи со всеми доменами."
+msgstr ""
+"Разделенный запятыми список доменов, которые разрешены для установления "
+"связей. Групповые символы принимаются. Оставьте пустым для разрешения связи "
+"со всеми доменами."
 
-#: ../../mod/admin.php:653
+#: mod/admin.php:923
 msgid "Allowed email domains"
 msgstr "Разрешенные почтовые домены"
 
-#: ../../mod/admin.php:653
+#: mod/admin.php:923
 msgid ""
 "Comma separated list of domains which are allowed in email addresses for "
 "registrations to this site. Wildcards are accepted. Empty to allow any "
 "domains"
-msgstr "Разделенный запятыми список доменов, которые разрешены для установления связей. Групповые символы принимаются. Оставьте пустым для разрешения связи со всеми доменами."
+msgstr ""
+"Разделенный запятыми список доменов, которые разрешены для установления "
+"связей. Групповые символы принимаются. Оставьте пустым для разрешения связи "
+"со всеми доменами."
 
-#: ../../mod/admin.php:654
+#: mod/admin.php:924
 msgid "Block public"
 msgstr "Блокировать общественный доступ"
 
-#: ../../mod/admin.php:654
+#: mod/admin.php:924
 msgid ""
 "Check to block public access to all otherwise public personal pages on this "
 "site unless you are currently logged in."
-msgstr "Отметьте, чтобы заблокировать публичный доступ ко всем иным публичным персональным страницам на этом сайте, если вы не вошли на сайт."
+msgstr ""
+"Отметьте, чтобы заблокировать публичный доступ ко всем иным публичным "
+"персональным страницам на этом сайте, если вы не вошли на сайт."
 
-#: ../../mod/admin.php:655
+#: mod/admin.php:925
 msgid "Force publish"
 msgstr "Принудительная публикация"
 
-#: ../../mod/admin.php:655
+#: mod/admin.php:925
 msgid ""
 "Check to force all profiles on this site to be listed in the site directory."
-msgstr "Отметьте, чтобы принудительно заставить все профили на этом сайте, быть перечислеными в каталоге сайта."
+msgstr ""
+"Отметьте, чтобы принудительно заставить все профили на этом сайте, быть "
+"перечислеными в каталоге сайта."
 
-#: ../../mod/admin.php:656
-msgid "Global directory update URL"
-msgstr "URL обновления глобального каталога"
+#: mod/admin.php:926
+msgid "Global directory URL"
+msgstr ""
 
-#: ../../mod/admin.php:656
+#: mod/admin.php:926
 msgid ""
-"URL to update the global directory. If this is not set, the global directory"
-" is completely unavailable to the application."
-msgstr "URL для обновления глобального каталога. Если он не установлен, глобальный каталог полностью недоступен для приложения."
+"URL to the global directory. If this is not set, the global directory is "
+"completely unavailable to the application."
+msgstr ""
 
-#: ../../mod/admin.php:657
+#: mod/admin.php:927
 msgid "Allow threaded items"
 msgstr "Разрешить темы в обсуждении"
 
-#: ../../mod/admin.php:657
+#: mod/admin.php:927
 msgid "Allow infinite level threading for items on this site."
 msgstr "Разрешить бесконечный уровень для тем на этом сайте."
 
-#: ../../mod/admin.php:658
+#: mod/admin.php:928
 msgid "Private posts by default for new users"
 msgstr "Частные сообщения по умолчанию для новых пользователей"
 
-#: ../../mod/admin.php:658
+#: mod/admin.php:928
 msgid ""
 "Set default post permissions for all new members to the default privacy "
 "group rather than public."
-msgstr "Установить права на создание постов по умолчанию для всех участников в дефолтной приватной группе, а не для публичных участников."
+msgstr ""
+"Установить права на создание постов по умолчанию для всех участников в "
+"дефолтной приватной группе, а не для публичных участников."
 
-#: ../../mod/admin.php:659
+#: mod/admin.php:929
 msgid "Don't include post content in email notifications"
 msgstr "Не включать текст сообщения в email-оповещение."
 
-#: ../../mod/admin.php:659
+#: mod/admin.php:929
 msgid ""
 "Don't include the content of a post/comment/private message/etc. in the "
 "email notifications that are sent out from this site, as a privacy measure."
-msgstr "Не включать содержание сообщения/комментария/личного сообщения  и т.д.. в уведомления электронной почты, отправленных с сайта, в качестве меры конфиденциальности."
+msgstr ""
+"Не включать содержание сообщения/комментария/личного сообщения  и т.д.. в "
+"уведомления электронной почты, отправленных с сайта, в качестве меры "
+"конфиденциальности."
 
-#: ../../mod/admin.php:660
+#: mod/admin.php:930
 msgid "Disallow public access to addons listed in the apps menu."
 msgstr "Запретить публичный доступ к аддонам, перечисленным в меню приложений."
 
-#: ../../mod/admin.php:660
+#: mod/admin.php:930
 msgid ""
 "Checking this box will restrict addons listed in the apps menu to members "
 "only."
-msgstr "При установке этого флажка, будут ограничены аддоны, перечисленные в меню приложений, только для участников."
+msgstr ""
+"При установке этого флажка, будут ограничены аддоны, перечисленные в меню "
+"приложений, только для участников."
 
-#: ../../mod/admin.php:661
+#: mod/admin.php:931
 msgid "Don't embed private images in posts"
 msgstr "Не вставлять личные картинки в постах"
 
-#: ../../mod/admin.php:661
+#: mod/admin.php:931
 msgid ""
 "Don't replace locally-hosted private photos in posts with an embedded copy "
 "of the image. This means that contacts who receive posts containing private "
-"photos will have to authenticate and load each image, which may take a "
-"while."
-msgstr "Не заменяйте локально расположенные фотографии в постах на внедрённые копии изображений. Это означает, что контакты, которые получают сообщения, содержащие личные фотографии, будут вынуждены идентефицироваться и грузить каждое изображение, что может занять некоторое время."
+"photos will have to authenticate and load each image, which may take a while."
+msgstr ""
+"Не заменяйте локально расположенные фотографии в постах на внедрённые копии "
+"изображений. Это означает, что контакты, которые получают сообщения, "
+"содержащие личные фотографии, будут вынуждены идентефицироваться и грузить "
+"каждое изображение, что может занять некоторое время."
 
-#: ../../mod/admin.php:662
+#: mod/admin.php:932
 msgid "Allow Users to set remote_self"
 msgstr "Разрешить пользователям установить remote_self"
 
-#: ../../mod/admin.php:662
+#: mod/admin.php:932
 msgid ""
 "With checking this, every user is allowed to mark every contact as a "
 "remote_self in the repair contact dialog. Setting this flag on a contact "
 "causes mirroring every posting of that contact in the users stream."
 msgstr ""
 
-#: ../../mod/admin.php:663
+#: mod/admin.php:933
 msgid "Block multiple registrations"
 msgstr "Блокировать множественные регистрации"
 
-#: ../../mod/admin.php:663
+#: mod/admin.php:933
 msgid "Disallow users to register additional accounts for use as pages."
-msgstr "Запретить пользователям регистрировать дополнительные аккаунты для использования в качестве страниц."
+msgstr ""
+"Запретить пользователям регистрировать дополнительные аккаунты для "
+"использования в качестве страниц."
 
-#: ../../mod/admin.php:664
+#: mod/admin.php:934
 msgid "OpenID support"
 msgstr "Поддержка OpenID"
 
-#: ../../mod/admin.php:664
+#: mod/admin.php:934
 msgid "OpenID support for registration and logins."
 msgstr "OpenID поддержка для регистрации и входа в систему."
 
-#: ../../mod/admin.php:665
+#: mod/admin.php:935
 msgid "Fullname check"
 msgstr "Проверка полного имени"
 
-#: ../../mod/admin.php:665
+#: mod/admin.php:935
 msgid ""
 "Force users to register with a space between firstname and lastname in Full "
 "name, as an antispam measure"
-msgstr "Принудить пользователей регистрироваться с пробелом между именем и фамилией в строке \"полное имя\". Антиспам мера."
+msgstr ""
+"Принудить пользователей регистрироваться с пробелом между именем и фамилией "
+"в строке \"полное имя\". Антиспам мера."
 
-#: ../../mod/admin.php:666
+#: mod/admin.php:936
 msgid "UTF-8 Regular expressions"
 msgstr "UTF-8 регулярные выражения"
 
-#: ../../mod/admin.php:666
+#: mod/admin.php:936
 msgid "Use PHP UTF8 regular expressions"
 msgstr "Используйте PHP UTF-8 для регулярных выражений"
 
-#: ../../mod/admin.php:667
+#: mod/admin.php:937
 msgid "Community Page Style"
 msgstr ""
 
-#: ../../mod/admin.php:667
+#: mod/admin.php:937
 msgid ""
 "Type of community page to show. 'Global community' shows every public "
 "posting from an open distributed network that arrived on this server."
 msgstr ""
 
-#: ../../mod/admin.php:668
+#: mod/admin.php:938
 msgid "Posts per user on community page"
 msgstr ""
 
-#: ../../mod/admin.php:668
+#: mod/admin.php:938
 msgid ""
 "The maximum number of posts per user on the community page. (Not valid for "
 "'Global Community')"
 msgstr ""
 
-#: ../../mod/admin.php:669
+#: mod/admin.php:939
 msgid "Enable OStatus support"
 msgstr "Включить поддержку OStatus"
 
-#: ../../mod/admin.php:669
+#: mod/admin.php:939
 msgid ""
 "Provide built-in OStatus (StatusNet, GNU Social etc.) compatibility. All "
 "communications in OStatus are public, so privacy warnings will be "
 "occasionally displayed."
 msgstr ""
 
-#: ../../mod/admin.php:670
+#: mod/admin.php:940
 msgid "OStatus conversation completion interval"
 msgstr ""
 
-#: ../../mod/admin.php:670
+#: mod/admin.php:940
 msgid ""
 "How often shall the poller check for new entries in OStatus conversations? "
 "This can be a very ressource task."
-msgstr "Как часто процессы должны проверять наличие новых записей в OStatus разговорах? Это может быть очень ресурсоёмкой задачей."
+msgstr ""
+"Как часто процессы должны проверять наличие новых записей в OStatus "
+"разговорах? Это может быть очень ресурсоёмкой задачей."
+
+#: mod/admin.php:941
+msgid "OStatus support can only be enabled if threading is enabled."
+msgstr ""
+
+#: mod/admin.php:943
+msgid ""
+"Diaspora support can't be enabled because Friendica was installed into a sub "
+"directory."
+msgstr ""
 
-#: ../../mod/admin.php:671
+#: mod/admin.php:944
 msgid "Enable Diaspora support"
 msgstr "Включить поддержку Diaspora"
 
-#: ../../mod/admin.php:671
+#: mod/admin.php:944
 msgid "Provide built-in Diaspora network compatibility."
 msgstr "Обеспечить встроенную поддержку сети Diaspora."
 
-#: ../../mod/admin.php:672
+#: mod/admin.php:945
 msgid "Only allow Friendica contacts"
 msgstr "Позвольть только  Friendica контакты"
 
-#: ../../mod/admin.php:672
+#: mod/admin.php:945
 msgid ""
 "All contacts must use Friendica protocols. All other built-in communication "
 "protocols disabled."
-msgstr "Все контакты должны использовать только Friendica протоколы. Все другие встроенные коммуникационные протоколы отключены."
+msgstr ""
+"Все контакты должны использовать только Friendica протоколы. Все другие "
+"встроенные коммуникационные протоколы отключены."
 
-#: ../../mod/admin.php:673
+#: mod/admin.php:946
 msgid "Verify SSL"
 msgstr "Проверка SSL"
 
-#: ../../mod/admin.php:673
+#: mod/admin.php:946
 msgid ""
-"If you wish, you can turn on strict certificate checking. This will mean you"
-" cannot connect (at all) to self-signed SSL sites."
-msgstr "Если хотите, вы можете включить строгую проверку сертификатов. Это будет означать, что вы не сможете соединиться (вообще) с сайтами, имеющими само-подписанный SSL сертификат."
+"If you wish, you can turn on strict certificate checking. This will mean you "
+"cannot connect (at all) to self-signed SSL sites."
+msgstr ""
+"Если хотите, вы можете включить строгую проверку сертификатов. Это будет "
+"означать, что вы не сможете соединиться (вообще) с сайтами, имеющими само-"
+"подписанный SSL сертификат."
 
-#: ../../mod/admin.php:674
+#: mod/admin.php:947
 msgid "Proxy user"
 msgstr "Прокси пользователь"
 
-#: ../../mod/admin.php:675
+#: mod/admin.php:948
 msgid "Proxy URL"
 msgstr "Прокси URL"
 
-#: ../../mod/admin.php:676
+#: mod/admin.php:949
 msgid "Network timeout"
 msgstr "Тайм-аут сети"
 
-#: ../../mod/admin.php:676
+#: mod/admin.php:949
 msgid "Value is in seconds. Set to 0 for unlimited (not recommended)."
-msgstr "Значение указывается в секундах. Установите 0 для снятия ограничений (не рекомендуется)."
+msgstr ""
+"Значение указывается в секундах. Установите 0 для снятия ограничений (не "
+"рекомендуется)."
 
-#: ../../mod/admin.php:677
+#: mod/admin.php:950
 msgid "Delivery interval"
 msgstr "Интервал поставки"
 
-#: ../../mod/admin.php:677
+#: mod/admin.php:950
 msgid ""
 "Delay background delivery processes by this many seconds to reduce system "
 "load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 "
 "for large dedicated servers."
-msgstr "Установите задержку выполнения фоновых процессов доставки до указанного количества секунд, чтобы уменьшить нагрузку на систему. Рекомендация: 4-5 для обычного shared хостинга, 2-3 для виртуальных частных серверов. 0-1 для мощных выделенных серверов."
+msgstr ""
+"Установите задержку выполнения фоновых процессов доставки до указанного "
+"количества секунд, чтобы уменьшить нагрузку на систему. Рекомендация: 4-5 "
+"для обычного shared хостинга, 2-3 для виртуальных частных серверов. 0-1 для "
+"мощных выделенных серверов."
 
-#: ../../mod/admin.php:678
+#: mod/admin.php:951
 msgid "Poll interval"
 msgstr "Интервал опроса"
 
-#: ../../mod/admin.php:678
+#: mod/admin.php:951
 msgid ""
 "Delay background polling processes by this many seconds to reduce system "
 "load. If 0, use delivery interval."
-msgstr "Установить задержку фоновых процессов опросов путем ограничения количества секунд, чтобы уменьшить нагрузку на систему. Если 0, используется интервал доставки."
+msgstr ""
+"Установить задержку фоновых процессов опросов путем ограничения количества "
+"секунд, чтобы уменьшить нагрузку на систему. Если 0, используется интервал "
+"доставки."
 
-#: ../../mod/admin.php:679
+#: mod/admin.php:952
 msgid "Maximum Load Average"
 msgstr "Средняя максимальная нагрузка"
 
-#: ../../mod/admin.php:679
+#: mod/admin.php:952
 msgid ""
 "Maximum system load before delivery and poll processes are deferred - "
 "default 50."
-msgstr "Максимальная нагрузка на систему перед приостановкой процессов доставки и опросов - по умолчанию 50."
+msgstr ""
+"Максимальная нагрузка на систему перед приостановкой процессов доставки и "
+"опросов - по умолчанию 50."
+
+#: mod/admin.php:953
+msgid "Maximum Load Average (Frontend)"
+msgstr ""
+
+#: mod/admin.php:953
+msgid "Maximum system load before the frontend quits service - default 50."
+msgstr ""
+
+#: mod/admin.php:954
+msgid "Maximum table size for optimization"
+msgstr ""
+
+#: mod/admin.php:954
+msgid ""
+"Maximum table size (in MB) for the automatic optimization - default 100 MB. "
+"Enter -1 to disable it."
+msgstr ""
+
+#: mod/admin.php:955
+msgid "Minimum level of fragmentation"
+msgstr ""
+
+#: mod/admin.php:955
+msgid ""
+"Minimum fragmenation level to start the automatic optimization - default "
+"value is 30%."
+msgstr ""
+
+#: mod/admin.php:957
+msgid "Periodical check of global contacts"
+msgstr ""
+
+#: mod/admin.php:957
+msgid ""
+"If enabled, the global contacts are checked periodically for missing or "
+"outdated data and the vitality of the contacts and servers."
+msgstr ""
+
+#: mod/admin.php:958
+msgid "Days between requery"
+msgstr ""
+
+#: mod/admin.php:958
+msgid "Number of days after which a server is requeried for his contacts."
+msgstr ""
+
+#: mod/admin.php:959
+msgid "Discover contacts from other servers"
+msgstr ""
+
+#: mod/admin.php:959
+msgid ""
+"Periodically query other servers for contacts. You can choose between "
+"'users': the users on the remote system, 'Global Contacts': active contacts "
+"that are known on the system. The fallback is meant for Redmatrix servers "
+"and older friendica servers, where global contacts weren't available. The "
+"fallback increases the server load, so the recommened setting is 'Users, "
+"Global Contacts'."
+msgstr ""
+
+#: mod/admin.php:960
+msgid "Timeframe for fetching global contacts"
+msgstr ""
+
+#: mod/admin.php:960
+msgid ""
+"When the discovery is activated, this value defines the timeframe for the "
+"activity of the global contacts that are fetched from other servers."
+msgstr ""
 
-#: ../../mod/admin.php:681
+#: mod/admin.php:961
+msgid "Search the local directory"
+msgstr ""
+
+#: mod/admin.php:961
+msgid ""
+"Search the local directory instead of the global directory. When searching "
+"locally, every search will be executed on the global directory in the "
+"background. This improves the search results when the search is repeated."
+msgstr ""
+
+#: mod/admin.php:963
+msgid "Publish server information"
+msgstr ""
+
+#: mod/admin.php:963
+msgid ""
+"If enabled, general server and usage data will be published. The data "
+"contains the name and version of the server, number of users with public "
+"profiles, number of posts and the activated protocols and connectors. See <a "
+"href='http://the-federation.info/'>the-federation.info</a> for details."
+msgstr ""
+
+#: mod/admin.php:965
 msgid "Use MySQL full text engine"
 msgstr "Использовать систему полнотексного поиска MySQL"
 
-#: ../../mod/admin.php:681
+#: mod/admin.php:965
 msgid ""
 "Activates the full text engine. Speeds up search - but can only search for "
 "four and more characters."
-msgstr "Активизирует систему полнотексного поиска. Ускоряет поиск - но может искать только при указании четырех и более символов."
+msgstr ""
+"Активизирует систему полнотексного поиска. Ускоряет поиск - но может искать "
+"только при указании четырех и более символов."
 
-#: ../../mod/admin.php:682
+#: mod/admin.php:966
 msgid "Suppress Language"
 msgstr ""
 
-#: ../../mod/admin.php:682
+#: mod/admin.php:966
 msgid "Suppress language information in meta information about a posting."
 msgstr ""
 
-#: ../../mod/admin.php:683
+#: mod/admin.php:967
 msgid "Suppress Tags"
 msgstr ""
 
-#: ../../mod/admin.php:683
+#: mod/admin.php:967
 msgid "Suppress showing a list of hashtags at the end of the posting."
 msgstr ""
 
-#: ../../mod/admin.php:684
+#: mod/admin.php:968
 msgid "Path to item cache"
 msgstr "Путь к элементам кэша"
 
-#: ../../mod/admin.php:685
+#: mod/admin.php:968
+msgid "The item caches buffers generated bbcode and external images."
+msgstr ""
+
+#: mod/admin.php:969
 msgid "Cache duration in seconds"
 msgstr "Время жизни кэша в секундах"
 
-#: ../../mod/admin.php:685
+#: mod/admin.php:969
 msgid ""
-"How long should the cache files be hold? Default value is 86400 seconds (One"
-" day). To disable the item cache, set the value to -1."
+"How long should the cache files be hold? Default value is 86400 seconds (One "
+"day). To disable the item cache, set the value to -1."
 msgstr ""
 
-#: ../../mod/admin.php:686
+#: mod/admin.php:970
 msgid "Maximum numbers of comments per post"
 msgstr ""
 
-#: ../../mod/admin.php:686
+#: mod/admin.php:970
 msgid "How much comments should be shown for each post? Default value is 100."
 msgstr ""
 
-#: ../../mod/admin.php:687
+#: mod/admin.php:971
 msgid "Path for lock file"
 msgstr "Путь к файлу блокировки"
 
-#: ../../mod/admin.php:688
+#: mod/admin.php:971
+msgid ""
+"The lock file is used to avoid multiple pollers at one time. Only define a "
+"folder here."
+msgstr ""
+
+#: mod/admin.php:972
 msgid "Temp path"
 msgstr "Временная папка"
 
-#: ../../mod/admin.php:689
+#: mod/admin.php:972
+msgid ""
+"If you have a restricted system where the webserver can't access the system "
+"temp path, enter another path here."
+msgstr ""
+
+#: mod/admin.php:973
 msgid "Base path to installation"
 msgstr "Путь для установки"
 
-#: ../../mod/admin.php:690
+#: mod/admin.php:973
+msgid ""
+"If the system cannot detect the correct path to your installation, enter the "
+"correct path here. This setting should only be set if you are using a "
+"restricted system and symbolic links to your webroot."
+msgstr ""
+
+#: mod/admin.php:974
 msgid "Disable picture proxy"
 msgstr ""
 
-#: ../../mod/admin.php:690
+#: mod/admin.php:974
 msgid ""
-"The picture proxy increases performance and privacy. It shouldn't be used on"
-" systems with very low bandwith."
+"The picture proxy increases performance and privacy. It shouldn't be used on "
+"systems with very low bandwith."
 msgstr ""
 
-#: ../../mod/admin.php:691
+#: mod/admin.php:975
 msgid "Enable old style pager"
 msgstr ""
 
-#: ../../mod/admin.php:691
+#: mod/admin.php:975
 msgid ""
-"The old style pager has page numbers but slows down massively the page "
-"speed."
+"The old style pager has page numbers but slows down massively the page speed."
 msgstr ""
 
-#: ../../mod/admin.php:692
+#: mod/admin.php:976
 msgid "Only search in tags"
 msgstr ""
 
-#: ../../mod/admin.php:692
+#: mod/admin.php:976
 msgid "On large systems the text search can slow down the system extremely."
 msgstr ""
 
-#: ../../mod/admin.php:694
+#: mod/admin.php:978
 msgid "New base url"
 msgstr "Новый базовый url"
 
-#: ../../mod/admin.php:711
+#: mod/admin.php:978
+msgid ""
+"Change base url for this server. Sends relocate message to all DFRN contacts "
+"of all users."
+msgstr ""
+
+#: mod/admin.php:980
+msgid "RINO Encryption"
+msgstr "RINO шифрование"
+
+#: mod/admin.php:980
+msgid "Encryption layer between nodes."
+msgstr "Слой шифрования между узлами."
+
+#: mod/admin.php:981
+msgid "Embedly API key"
+msgstr ""
+
+#: mod/admin.php:981
+msgid ""
+"<a href='http://embed.ly'>Embedly</a> is used to fetch additional data for "
+"web pages. This is an optional parameter."
+msgstr ""
+
+#: mod/admin.php:1010
 msgid "Update has been marked successful"
 msgstr "Обновление было успешно отмечено"
 
-#: ../../mod/admin.php:719
+#: mod/admin.php:1018
 #, php-format
 msgid "Database structure update %s was successfully applied."
 msgstr ""
 
-#: ../../mod/admin.php:722
+#: mod/admin.php:1021
 #, php-format
 msgid "Executing of database structure update %s failed with error: %s"
 msgstr ""
 
-#: ../../mod/admin.php:734
+#: mod/admin.php:1033
 #, php-format
 msgid "Executing %s failed with error: %s"
 msgstr ""
 
-#: ../../mod/admin.php:737
+#: mod/admin.php:1036
 #, php-format
 msgid "Update %s was successfully applied."
 msgstr "Обновление %s успешно применено."
 
-#: ../../mod/admin.php:741
+#: mod/admin.php:1040
 #, php-format
 msgid "Update %s did not return a status. Unknown if it succeeded."
-msgstr "Процесс обновления %s не вернул статус. Не известно, выполнено, или нет."
+msgstr ""
+"Процесс обновления %s не вернул статус. Не известно, выполнено, или нет."
 
-#: ../../mod/admin.php:743
+#: mod/admin.php:1042
 #, php-format
 msgid "There was no additional update function %s that needed to be called."
 msgstr ""
 
-#: ../../mod/admin.php:762
+#: mod/admin.php:1061
 msgid "No failed updates."
 msgstr "Неудавшихся обновлений нет."
 
-#: ../../mod/admin.php:763
+#: mod/admin.php:1062
 msgid "Check database structure"
 msgstr ""
 
-#: ../../mod/admin.php:768
+#: mod/admin.php:1067
 msgid "Failed Updates"
 msgstr "Неудавшиеся обновления"
 
-#: ../../mod/admin.php:769
+#: mod/admin.php:1068
 msgid ""
 "This does not include updates prior to 1139, which did not return a status."
-msgstr "Эта цифра не включает обновления до 1139, которое не возвращает статус."
+msgstr ""
+"Эта цифра не включает обновления до 1139, которое не возвращает статус."
 
-#: ../../mod/admin.php:770
+#: mod/admin.php:1069
 msgid "Mark success (if update was manually applied)"
 msgstr "Отмечено успешно (если обновление было применено вручную)"
 
-#: ../../mod/admin.php:771
+#: mod/admin.php:1070
 msgid "Attempt to execute this update step automatically"
 msgstr "Попытаться выполнить этот шаг обновления автоматически"
 
-#: ../../mod/admin.php:803
+#: mod/admin.php:1102
 #, php-format
 msgid ""
 "\n"
@@ -2624,7 +3111,7 @@ msgid ""
 "\t\t\t\tthe administrator of %2$s has set up an account for you."
 msgstr ""
 
-#: ../../mod/admin.php:806
+#: mod/admin.php:1105
 #, php-format
 msgid ""
 "\n"
@@ -2634,310 +3121,354 @@ msgid ""
 "\t\t\tLogin Name:\t\t%2$s\n"
 "\t\t\tPassword:\t\t%3$s\n"
 "\n"
-"\t\t\tYou may change your password from your account \"Settings\" page after logging\n"
+"\t\t\tYou may change your password from your account \"Settings\" page after "
+"logging\n"
 "\t\t\tin.\n"
 "\n"
-"\t\t\tPlease take a few moments to review the other account settings on that page.\n"
+"\t\t\tPlease take a few moments to review the other account settings on that "
+"page.\n"
 "\n"
-"\t\t\tYou may also wish to add some basic information to your default profile\n"
+"\t\t\tYou may also wish to add some basic information to your default "
+"profile\n"
 "\t\t\t(on the \"Profiles\" page) so that other people can easily find you.\n"
 "\n"
 "\t\t\tWe recommend setting your full name, adding a profile photo,\n"
-"\t\t\tadding some profile \"keywords\" (very useful in making new friends) - and\n"
-"\t\t\tperhaps what country you live in; if you do not wish to be more specific\n"
+"\t\t\tadding some profile \"keywords\" (very useful in making new friends) - "
+"and\n"
+"\t\t\tperhaps what country you live in; if you do not wish to be more "
+"specific\n"
 "\t\t\tthan that.\n"
 "\n"
-"\t\t\tWe fully respect your right to privacy, and none of these items are necessary.\n"
+"\t\t\tWe fully respect your right to privacy, and none of these items are "
+"necessary.\n"
 "\t\t\tIf you are new and do not know anybody here, they may help\n"
 "\t\t\tyou to make some new and interesting friends.\n"
 "\n"
 "\t\t\tThank you and welcome to %4$s."
 msgstr ""
 
-#: ../../mod/admin.php:838 ../../include/user.php:413
+#: mod/admin.php:1137 include/user.php:423
 #, php-format
 msgid "Registration details for %s"
 msgstr "Подробности регистрации для %s"
 
-#: ../../mod/admin.php:850
+#: mod/admin.php:1149
 #, php-format
 msgid "%s user blocked/unblocked"
 msgid_plural "%s users blocked/unblocked"
 msgstr[0] "%s пользователь заблокирован/разблокирован"
 msgstr[1] "%s пользователей заблокировано/разблокировано"
 msgstr[2] "%s пользователей заблокировано/разблокировано"
+msgstr[3] "%s пользователей заблокировано/разблокировано"
 
-#: ../../mod/admin.php:857
+#: mod/admin.php:1156
 #, php-format
 msgid "%s user deleted"
 msgid_plural "%s users deleted"
 msgstr[0] "%s человек удален"
 msgstr[1] "%s чел. удалено"
 msgstr[2] "%s чел. удалено"
+msgstr[3] "%s чел. удалено"
 
-#: ../../mod/admin.php:896
+#: mod/admin.php:1203
 #, php-format
 msgid "User '%s' deleted"
 msgstr "Пользователь '%s' удален"
 
-#: ../../mod/admin.php:904
+#: mod/admin.php:1211
 #, php-format
 msgid "User '%s' unblocked"
 msgstr "Пользователь '%s' разблокирован"
 
-#: ../../mod/admin.php:904
+#: mod/admin.php:1211
 #, php-format
 msgid "User '%s' blocked"
 msgstr "Пользователь '%s' блокирован"
 
-#: ../../mod/admin.php:999
+#: mod/admin.php:1302
 msgid "Add User"
 msgstr "Добавить пользователя"
 
-#: ../../mod/admin.php:1000
+#: mod/admin.php:1303
 msgid "select all"
 msgstr "выбрать все"
 
-#: ../../mod/admin.php:1001
+#: mod/admin.php:1304
 msgid "User registrations waiting for confirm"
 msgstr "Регистрации пользователей, ожидающие подтверждения"
 
-#: ../../mod/admin.php:1002
+#: mod/admin.php:1305
 msgid "User waiting for permanent deletion"
 msgstr "Пользователь ожидает окончательного удаления"
 
-#: ../../mod/admin.php:1003
+#: mod/admin.php:1306
 msgid "Request date"
 msgstr "Запрос даты"
 
-#: ../../mod/admin.php:1003 ../../mod/admin.php:1015 ../../mod/admin.php:1016
-#: ../../mod/admin.php:1031 ../../include/contact_selectors.php:79
-#: ../../include/contact_selectors.php:86
+#: mod/admin.php:1306 mod/admin.php:1318 mod/admin.php:1319 mod/admin.php:1334
+#: include/contact_selectors.php:79 include/contact_selectors.php:86
 msgid "Email"
 msgstr "Эл. почта"
 
-#: ../../mod/admin.php:1004
+#: mod/admin.php:1307
 msgid "No registrations."
 msgstr "Нет регистраций."
 
-#: ../../mod/admin.php:1006
+#: mod/admin.php:1309
 msgid "Deny"
 msgstr "Отклонить"
 
-#: ../../mod/admin.php:1010
+#: mod/admin.php:1313
 msgid "Site admin"
 msgstr "Админ сайта"
 
-#: ../../mod/admin.php:1011
+#: mod/admin.php:1314
 msgid "Account expired"
 msgstr "Аккаунт просрочен"
 
-#: ../../mod/admin.php:1014
+#: mod/admin.php:1317
 msgid "New User"
 msgstr "Новый пользователь"
 
-#: ../../mod/admin.php:1015 ../../mod/admin.php:1016
+#: mod/admin.php:1318 mod/admin.php:1319
 msgid "Register date"
 msgstr "Дата регистрации"
 
-#: ../../mod/admin.php:1015 ../../mod/admin.php:1016
+#: mod/admin.php:1318 mod/admin.php:1319
 msgid "Last login"
 msgstr "Последний вход"
 
-#: ../../mod/admin.php:1015 ../../mod/admin.php:1016
+#: mod/admin.php:1318 mod/admin.php:1319
 msgid "Last item"
 msgstr "Последний пункт"
 
-#: ../../mod/admin.php:1015
+#: mod/admin.php:1318
 msgid "Deleted since"
 msgstr "Удалён с"
 
-#: ../../mod/admin.php:1016 ../../mod/settings.php:36
+#: mod/admin.php:1319 mod/settings.php:41
 msgid "Account"
 msgstr "Аккаунт"
 
-#: ../../mod/admin.php:1018
+#: mod/admin.php:1321
 msgid ""
 "Selected users will be deleted!\\n\\nEverything these users had posted on "
 "this site will be permanently deleted!\\n\\nAre you sure?"
-msgstr "Выбранные пользователи будут удалены!\\n\\nВсе, что эти пользователи написали на этом сайте, будет удалено!\\n\\nВы уверены в вашем действии?"
+msgstr ""
+"Выбранные пользователи будут удалены!\\n\\nВсе, что эти пользователи "
+"написали на этом сайте, будет удалено!\\n\\nВы уверены в вашем действии?"
 
-#: ../../mod/admin.php:1019
+#: mod/admin.php:1322
 msgid ""
 "The user {0} will be deleted!\\n\\nEverything this user has posted on this "
 "site will be permanently deleted!\\n\\nAre you sure?"
-msgstr "Пользователь {0} будет удален!\\n\\nВсе, что этот пользователь написал на этом сайте, будет удалено!\\n\\nВы уверены в вашем действии?"
+msgstr ""
+"Пользователь {0} будет удален!\\n\\nВсе, что этот пользователь написал на "
+"этом сайте, будет удалено!\\n\\nВы уверены в вашем действии?"
 
-#: ../../mod/admin.php:1029
+#: mod/admin.php:1332
 msgid "Name of the new user."
 msgstr "Имя нового пользователя."
 
-#: ../../mod/admin.php:1030
+#: mod/admin.php:1333
 msgid "Nickname"
 msgstr "Ник"
 
-#: ../../mod/admin.php:1030
+#: mod/admin.php:1333
 msgid "Nickname of the new user."
 msgstr "Ник нового пользователя."
 
-#: ../../mod/admin.php:1031
+#: mod/admin.php:1334
 msgid "Email address of the new user."
 msgstr "Email адрес нового пользователя."
 
-#: ../../mod/admin.php:1064
+#: mod/admin.php:1377
 #, php-format
 msgid "Plugin %s disabled."
 msgstr "Плагин %s отключен."
 
-#: ../../mod/admin.php:1068
+#: mod/admin.php:1381
 #, php-format
 msgid "Plugin %s enabled."
 msgstr "Плагин %s включен."
 
-#: ../../mod/admin.php:1078 ../../mod/admin.php:1294
+#: mod/admin.php:1392 mod/admin.php:1628
 msgid "Disable"
 msgstr "Отключить"
 
-#: ../../mod/admin.php:1080 ../../mod/admin.php:1296
+#: mod/admin.php:1394 mod/admin.php:1630
 msgid "Enable"
 msgstr "Включить"
 
-#: ../../mod/admin.php:1103 ../../mod/admin.php:1324
+#: mod/admin.php:1417 mod/admin.php:1675
 msgid "Toggle"
 msgstr "Переключить"
 
-#: ../../mod/admin.php:1111 ../../mod/admin.php:1334
+#: mod/admin.php:1425 mod/admin.php:1684
 msgid "Author: "
 msgstr "Автор:"
 
-#: ../../mod/admin.php:1112 ../../mod/admin.php:1335
+#: mod/admin.php:1426 mod/admin.php:1685
 msgid "Maintainer: "
 msgstr "Программа обслуживания: "
 
-#: ../../mod/admin.php:1254
+#: mod/admin.php:1478
+msgid "Reload active plugins"
+msgstr ""
+
+#: mod/admin.php:1483
+#, php-format
+msgid ""
+"There are currently no plugins available on your node. You can find the "
+"official plugin repository at %1$s and might find other interesting plugins "
+"in the open plugin registry at %2$s"
+msgstr ""
+
+#: mod/admin.php:1588
 msgid "No themes found."
 msgstr "Темы не найдены."
 
-#: ../../mod/admin.php:1316
+#: mod/admin.php:1666
 msgid "Screenshot"
 msgstr "Скриншот"
 
-#: ../../mod/admin.php:1362
+#: mod/admin.php:1726
+msgid "Reload active themes"
+msgstr ""
+
+#: mod/admin.php:1731
+#, php-format
+msgid "No themes found on the system. They should be paced in %1$s"
+msgstr ""
+
+#: mod/admin.php:1732
 msgid "[Experimental]"
 msgstr "[экспериментально]"
 
-#: ../../mod/admin.php:1363
+#: mod/admin.php:1733
 msgid "[Unsupported]"
 msgstr "[Неподдерживаемое]"
 
-#: ../../mod/admin.php:1390
+#: mod/admin.php:1757
 msgid "Log settings updated."
 msgstr "Настройки журнала обновлены."
 
-#: ../../mod/admin.php:1446
+#: mod/admin.php:1794
 msgid "Clear"
 msgstr "Очистить"
 
-#: ../../mod/admin.php:1452
+#: mod/admin.php:1799
 msgid "Enable Debugging"
 msgstr "Включить отладку"
 
-#: ../../mod/admin.php:1453
+#: mod/admin.php:1800
 msgid "Log file"
 msgstr "Лог-файл"
 
-#: ../../mod/admin.php:1453
+#: mod/admin.php:1800
 msgid ""
 "Must be writable by web server. Relative to your Friendica top-level "
 "directory."
-msgstr "Должно быть доступно для записи в веб-сервере. Относительно вашего Friendica каталога верхнего уровня."
+msgstr ""
+"Должно быть доступно для записи в веб-сервере. Относительно вашего Friendica "
+"каталога верхнего уровня."
 
-#: ../../mod/admin.php:1454
+#: mod/admin.php:1801
 msgid "Log level"
 msgstr "Уровень лога"
 
-#: ../../mod/admin.php:1504
-msgid "Close"
-msgstr "Закрыть"
+#: mod/admin.php:1804
+msgid "PHP logging"
+msgstr "PHP логирование"
+
+#: mod/admin.php:1805
+msgid ""
+"To enable logging of PHP errors and warnings you can add the following to "
+"the .htconfig.php file of your installation. The filename set in the "
+"'error_log' line is relative to the friendica top-level directory and must "
+"be writeable by the web server. The option '1' for 'log_errors' and "
+"'display_errors' is to enable these options, set to '0' to disable them."
+msgstr ""
 
-#: ../../mod/admin.php:1510
-msgid "FTP Host"
-msgstr "FTP хост"
+#: mod/admin.php:1931 mod/admin.php:1932 mod/settings.php:759
+msgid "Off"
+msgstr "Выкл."
 
-#: ../../mod/admin.php:1511
-msgid "FTP Path"
-msgstr "Ð\9fÑ\83Ñ\82Ñ\8c FTP"
+#: mod/admin.php:1931 mod/admin.php:1932 mod/settings.php:759
+msgid "On"
+msgstr "Ð\92кл."
 
-#: ../../mod/admin.php:1512
-msgid "FTP User"
-msgstr "FTP пользователь"
+#: mod/admin.php:1932
+#, php-format
+msgid "Lock feature %s"
+msgstr ""
 
-#: ../../mod/admin.php:1513
-msgid "FTP Password"
-msgstr "FTP пароль"
+#: mod/admin.php:1940
+msgid "Manage Additional Features"
+msgstr ""
 
-#: ../../mod/network.php:142
-msgid "Search Results For:"
-msgstr "Результаты поиска для:"
+#: mod/network.php:146
+#, php-format
+msgid "Search Results For: %s"
+msgstr ""
 
-#: ../../mod/network.php:185 ../../mod/search.php:21
+#: mod/network.php:191 mod/search.php:25
 msgid "Remove term"
 msgstr "Удалить элемент"
 
-#: ../../mod/network.php:194 ../../mod/search.php:30
-#: ../../include/features.php:42
+#: mod/network.php:200 mod/search.php:34 include/features.php:84
 msgid "Saved Searches"
 msgstr "запомненные поиски"
 
-#: ../../mod/network.php:195 ../../include/group.php:275
+#: mod/network.php:201 include/group.php:293
 msgid "add"
 msgstr "добавить"
 
-#: ../../mod/network.php:356
+#: mod/network.php:365
 msgid "Commented Order"
-msgstr "Прокомментированный запрос"
+msgstr "Последние комментарии"
 
-#: ../../mod/network.php:359
+#: mod/network.php:368
 msgid "Sort by Comment Date"
 msgstr "Сортировать по дате комментария"
 
-#: ../../mod/network.php:362
+#: mod/network.php:373
 msgid "Posted Order"
-msgstr "Ð\9eÑ\82пÑ\80авленнÑ\8bй Ð·Ð°Ð¿Ñ\80оÑ\81"
+msgstr "Ð\9bенÑ\82а Ð·Ð°Ð¿Ð¸Ñ\81ей"
 
-#: ../../mod/network.php:365
+#: mod/network.php:376
 msgid "Sort by Post Date"
 msgstr "Сортировать по дате отправки"
 
-#: ../../mod/network.php:374
+#: mod/network.php:387
 msgid "Posts that mention or involve you"
 msgstr ""
 
-#: ../../mod/network.php:380
+#: mod/network.php:395
 msgid "New"
 msgstr "Новый"
 
-#: ../../mod/network.php:383
+#: mod/network.php:398
 msgid "Activity Stream - by date"
 msgstr "Лента активности - по дате"
 
-#: ../../mod/network.php:389
+#: mod/network.php:406
 msgid "Shared Links"
 msgstr "Ссылки, которыми поделились"
 
-#: ../../mod/network.php:392
+#: mod/network.php:409
 msgid "Interesting Links"
 msgstr "Интересные ссылки"
 
-#: ../../mod/network.php:398
+#: mod/network.php:417
 msgid "Starred"
 msgstr "Помеченный"
 
-#: ../../mod/network.php:401
+#: mod/network.php:420
 msgid "Favourite Posts"
 msgstr "Избранные посты"
 
-#: ../../mod/network.php:463
+#: mod/network.php:479
 #, php-format
 msgid "Warning: This group contains %s member from an insecure network."
 msgid_plural ""
@@ -2945,4152 +3476,4632 @@ msgid_plural ""
 msgstr[0] "Внимание: Эта группа содержит %s участника с незащищенной сети."
 msgstr[1] "Внимание: Эта группа содержит %s участников с незащищенной сети."
 msgstr[2] "Внимание: Эта группа содержит %s участников с незащищенной сети."
+msgstr[3] "Внимание: Эта группа содержит %s участников с незащищенной сети."
 
-#: ../../mod/network.php:466
+#: mod/network.php:482
 msgid "Private messages to this group are at risk of public disclosure."
 msgstr "Личные сообщения к этой группе находятся под угрозой обнародования."
 
-#: ../../mod/network.php:520 ../../mod/content.php:119
+#: mod/network.php:549 mod/content.php:119
 msgid "No such group"
 msgstr "Нет такой группы"
 
-#: ../../mod/network.php:537 ../../mod/content.php:130
-msgid "Group is empty"
-msgstr "Группа пуста"
-
-#: ../../mod/network.php:544 ../../mod/content.php:134
-msgid "Group: "
-msgstr "Группа: "
-
-#: ../../mod/network.php:554
-msgid "Contact: "
-msgstr "Контакт: "
+#: mod/network.php:580 mod/content.php:135
+#, php-format
+msgid "Group: %s"
+msgstr "Группа: %s"
 
-#: ../../mod/network.php:556
+#: mod/network.php:608
 msgid "Private messages to this person are at risk of public disclosure."
 msgstr "Личные сообщения этому человеку находятся под угрозой обнародования."
 
-#: ../../mod/network.php:561
+#: mod/network.php:613
 msgid "Invalid contact."
 msgstr "Недопустимый контакт."
 
-#: ../../mod/allfriends.php:34
-#, php-format
-msgid "Friends of %s"
-msgstr "%s Друзья"
-
-#: ../../mod/allfriends.php:40
+#: mod/allfriends.php:43
 msgid "No friends to display."
 msgstr "Нет друзей."
 
-#: ../../mod/events.php:66
+#: mod/events.php:71 mod/events.php:73
+msgid "Event can not end before it has started."
+msgstr ""
+
+#: mod/events.php:80 mod/events.php:82
 msgid "Event title and start time are required."
 msgstr "Название мероприятия и время начала обязательны для заполнения."
 
-#: ../../mod/events.php:291
+#: mod/events.php:201
+msgid "Sun"
+msgstr "Вс"
+
+#: mod/events.php:202
+msgid "Mon"
+msgstr "Пн"
+
+#: mod/events.php:203
+msgid "Tue"
+msgstr "Вт"
+
+#: mod/events.php:204
+msgid "Wed"
+msgstr "Ср"
+
+#: mod/events.php:205
+msgid "Thu"
+msgstr "Чт"
+
+#: mod/events.php:206
+msgid "Fri"
+msgstr "Пт"
+
+#: mod/events.php:207
+msgid "Sat"
+msgstr "Сб"
+
+#: mod/events.php:208 mod/settings.php:948 include/text.php:1274
+msgid "Sunday"
+msgstr "Воскресенье"
+
+#: mod/events.php:209 mod/settings.php:948 include/text.php:1274
+msgid "Monday"
+msgstr "Понедельник"
+
+#: mod/events.php:210 include/text.php:1274
+msgid "Tuesday"
+msgstr "Вторник"
+
+#: mod/events.php:211 include/text.php:1274
+msgid "Wednesday"
+msgstr "Среда"
+
+#: mod/events.php:212 include/text.php:1274
+msgid "Thursday"
+msgstr "Четверг"
+
+#: mod/events.php:213 include/text.php:1274
+msgid "Friday"
+msgstr "Пятница"
+
+#: mod/events.php:214 include/text.php:1274
+msgid "Saturday"
+msgstr "Суббота"
+
+#: mod/events.php:215
+msgid "Jan"
+msgstr "Янв"
+
+#: mod/events.php:216
+msgid "Feb"
+msgstr "Фев"
+
+#: mod/events.php:217
+msgid "Mar"
+msgstr "Мрт"
+
+#: mod/events.php:218
+msgid "Apr"
+msgstr "Апр"
+
+#: mod/events.php:219 mod/events.php:231 include/text.php:1278
+msgid "May"
+msgstr "Май"
+
+#: mod/events.php:220
+msgid "Jun"
+msgstr "Июн"
+
+#: mod/events.php:221
+msgid "Jul"
+msgstr "Июл"
+
+#: mod/events.php:222
+msgid "Aug"
+msgstr "Авг"
+
+#: mod/events.php:223
+msgid "Sept"
+msgstr "Сен"
+
+#: mod/events.php:224
+msgid "Oct"
+msgstr "Окт"
+
+#: mod/events.php:225
+msgid "Nov"
+msgstr "Нбр"
+
+#: mod/events.php:226
+msgid "Dec"
+msgstr "Дек"
+
+#: mod/events.php:227 include/text.php:1278
+msgid "January"
+msgstr "Январь"
+
+#: mod/events.php:228 include/text.php:1278
+msgid "February"
+msgstr "Февраль"
+
+#: mod/events.php:229 include/text.php:1278
+msgid "March"
+msgstr "Март"
+
+#: mod/events.php:230 include/text.php:1278
+msgid "April"
+msgstr "Апрель"
+
+#: mod/events.php:232 include/text.php:1278
+msgid "June"
+msgstr "Июнь"
+
+#: mod/events.php:233 include/text.php:1278
+msgid "July"
+msgstr "Июль"
+
+#: mod/events.php:234 include/text.php:1278
+msgid "August"
+msgstr "Август"
+
+#: mod/events.php:235 include/text.php:1278
+msgid "September"
+msgstr "Сентябрь"
+
+#: mod/events.php:236 include/text.php:1278
+msgid "October"
+msgstr "Октябрь"
+
+#: mod/events.php:237 include/text.php:1278
+msgid "November"
+msgstr "Ноябрь"
+
+#: mod/events.php:238 include/text.php:1278
+msgid "December"
+msgstr "Декабрь"
+
+#: mod/events.php:239
+msgid "today"
+msgstr "сегодня"
+
+#: mod/events.php:240 include/datetime.php:288
+msgid "month"
+msgstr "мес."
+
+#: mod/events.php:241 include/datetime.php:289
+msgid "week"
+msgstr "неделя"
+
+#: mod/events.php:242 include/datetime.php:290
+msgid "day"
+msgstr "день"
+
+#: mod/events.php:377
 msgid "l, F j"
 msgstr "l, j F"
 
-#: ../../mod/events.php:313
+#: mod/events.php:399
 msgid "Edit event"
 msgstr "Редактировать мероприятие"
 
-#: ../../mod/events.php:335 ../../include/text.php:1647
-#: ../../include/text.php:1657
+#: mod/events.php:421 include/text.php:1728 include/text.php:1735
 msgid "link to source"
-msgstr "ссылка на источник"
+msgstr "ссылка на сообщение"
 
-#: ../../mod/events.php:370 ../../boot.php:2143 ../../include/nav.php:80
-#: ../../view/theme/diabook/theme.php:127
+#: mod/events.php:456 include/identity.php:722 include/nav.php:79
+#: include/nav.php:140 view/theme/diabook/theme.php:127
 msgid "Events"
 msgstr "Мероприятия"
 
-#: ../../mod/events.php:371
+#: mod/events.php:457
 msgid "Create New Event"
 msgstr "Создать новое мероприятие"
 
-#: ../../mod/events.php:372
+#: mod/events.php:458
 msgid "Previous"
 msgstr "Назад"
 
-#: ../../mod/events.php:373 ../../mod/install.php:207
+#: mod/events.php:459 mod/install.php:220
 msgid "Next"
 msgstr "Далее"
 
-#: ../../mod/events.php:446
-msgid "hour:minute"
-msgstr "час:минута"
-
-#: ../../mod/events.php:456
+#: mod/events.php:554
 msgid "Event details"
 msgstr "Сведения о мероприятии"
 
-#: ../../mod/events.php:457
-#, php-format
-msgid "Format is %s %s. Starting date and Title are required."
-msgstr "Формат %s %s. Необхлдима дата старта и заголовок."
+#: mod/events.php:555
+msgid "Starting date and Title are required."
+msgstr ""
 
-#: ../../mod/events.php:459
+#: mod/events.php:556
 msgid "Event Starts:"
 msgstr "Начало мероприятия:"
 
-#: ../../mod/events.php:459 ../../mod/events.php:473
+#: mod/events.php:556 mod/events.php:568
 msgid "Required"
 msgstr "Требуется"
 
-#: ../../mod/events.php:462
+#: mod/events.php:558
 msgid "Finish date/time is not known or not relevant"
 msgstr "Дата/время окончания не известны, или не указаны"
 
-#: ../../mod/events.php:464
+#: mod/events.php:560
 msgid "Event Finishes:"
 msgstr "Окончание мероприятия:"
 
-#: ../../mod/events.php:467
+#: mod/events.php:562
 msgid "Adjust for viewer timezone"
 msgstr "Настройка часового пояса"
 
-#: ../../mod/events.php:469
+#: mod/events.php:564
 msgid "Description:"
 msgstr "Описание:"
 
-#: ../../mod/events.php:471 ../../mod/directory.php:136 ../../boot.php:1648
-#: ../../include/bb2diaspora.php:170 ../../include/event.php:40
-msgid "Location:"
-msgstr "Откуда:"
-
-#: ../../mod/events.php:473
+#: mod/events.php:568
 msgid "Title:"
 msgstr "Титул:"
 
-#: ../../mod/events.php:475
+#: mod/events.php:570
 msgid "Share this event"
 msgstr "Поделитесь этим мероприятием"
 
-#: ../../mod/content.php:437 ../../mod/content.php:740
-#: ../../mod/photos.php:1653 ../../object/Item.php:129
-#: ../../include/conversation.php:613
+#: mod/events.php:572 mod/content.php:721 mod/editpost.php:145
+#: mod/photos.php:1631 mod/photos.php:1679 mod/photos.php:1767
+#: object/Item.php:719 include/conversation.php:1216
+msgid "Preview"
+msgstr "Предварительный просмотр"
+
+#: mod/credits.php:16
+msgid "Credits"
+msgstr ""
+
+#: mod/credits.php:17
+msgid ""
+"Friendica is a community project, that would not be possible without the "
+"help of many people. Here is a list of those who have contributed to the "
+"code or the translation of Friendica. Thank you all!"
+msgstr ""
+
+#: mod/content.php:439 mod/content.php:742 mod/photos.php:1722
+#: object/Item.php:133 include/conversation.php:634
 msgid "Select"
 msgstr "Выберите"
 
-#: ../../mod/content.php:471 ../../mod/content.php:852
-#: ../../mod/content.php:853 ../../object/Item.php:326
-#: ../../object/Item.php:327 ../../include/conversation.php:654
+#: mod/content.php:473 mod/content.php:854 mod/content.php:855
+#: object/Item.php:357 object/Item.php:358 include/conversation.php:675
 #, php-format
 msgid "View %s's profile @ %s"
 msgstr "Просмотреть профиль %s [@ %s]"
 
-#: ../../mod/content.php:481 ../../mod/content.php:864
-#: ../../object/Item.php:340 ../../include/conversation.php:674
+#: mod/content.php:483 mod/content.php:866 object/Item.php:371
+#: include/conversation.php:695
 #, php-format
 msgid "%s from %s"
 msgstr "%s с %s"
 
-#: ../../mod/content.php:497 ../../include/conversation.php:690
+#: mod/content.php:499 include/conversation.php:711
 msgid "View in context"
 msgstr "Смотреть в контексте"
 
-#: ../../mod/content.php:603 ../../object/Item.php:387
+#: mod/content.php:605 object/Item.php:419
 #, php-format
 msgid "%d comment"
 msgid_plural "%d comments"
 msgstr[0] "%d комментарий"
 msgstr[1] "%d комментариев"
 msgstr[2] "%d комментариев"
+msgstr[3] "%d комментариев"
 
-#: ../../mod/content.php:605 ../../object/Item.php:389
-#: ../../object/Item.php:402 ../../include/text.php:1972
+#: mod/content.php:607 object/Item.php:421 object/Item.php:434
+#: include/text.php:2004
 msgid "comment"
 msgid_plural "comments"
 msgstr[0] ""
 msgstr[1] ""
 msgstr[2] "комментарий"
+msgstr[3] "комментарий"
 
-#: ../../mod/content.php:606 ../../boot.php:751 ../../object/Item.php:390
-#: ../../include/contact_widgets.php:205
+#: mod/content.php:608 boot.php:870 object/Item.php:422
+#: include/contact_widgets.php:242 include/forums.php:110
+#: include/items.php:5207 view/theme/vier/theme.php:264
 msgid "show more"
 msgstr "показать больше"
 
-#: ../../mod/content.php:620 ../../mod/photos.php:1359
-#: ../../object/Item.php:116
+#: mod/content.php:622 mod/photos.php:1418 object/Item.php:117
 msgid "Private Message"
 msgstr "Личное сообщение"
 
-#: ../../mod/content.php:684 ../../mod/photos.php:1542
-#: ../../object/Item.php:231
+#: mod/content.php:686 mod/photos.php:1607 object/Item.php:253
 msgid "I like this (toggle)"
 msgstr "Нравится"
 
-#: ../../mod/content.php:684 ../../object/Item.php:231
+#: mod/content.php:686 object/Item.php:253
 msgid "like"
 msgstr "нравится"
 
-#: ../../mod/content.php:685 ../../mod/photos.php:1543
-#: ../../object/Item.php:232
+#: mod/content.php:687 mod/photos.php:1608 object/Item.php:254
 msgid "I don't like this (toggle)"
 msgstr "Не нравится"
 
-#: ../../mod/content.php:685 ../../object/Item.php:232
+#: mod/content.php:687 object/Item.php:254
 msgid "dislike"
 msgstr "не нравитса"
 
-#: ../../mod/content.php:687 ../../object/Item.php:234
+#: mod/content.php:689 object/Item.php:256
 msgid "Share this"
 msgstr "Поделитесь этим"
 
-#: ../../mod/content.php:687 ../../object/Item.php:234
+#: mod/content.php:689 object/Item.php:256
 msgid "share"
 msgstr "делиться"
 
-#: ../../mod/content.php:707 ../../mod/photos.php:1562
-#: ../../mod/photos.php:1606 ../../mod/photos.php:1694
-#: ../../object/Item.php:675
+#: mod/content.php:709 mod/photos.php:1627 mod/photos.php:1675
+#: mod/photos.php:1763 object/Item.php:707
 msgid "This is you"
 msgstr "Это вы"
 
-#: ../../mod/content.php:709 ../../mod/photos.php:1564
-#: ../../mod/photos.php:1608 ../../mod/photos.php:1696 ../../boot.php:750
-#: ../../object/Item.php:361 ../../object/Item.php:677
+#: mod/content.php:711 mod/photos.php:1629 mod/photos.php:1677
+#: mod/photos.php:1765 boot.php:869 object/Item.php:393 object/Item.php:709
 msgid "Comment"
-msgstr "Ð\9aомментарий"
+msgstr "Ð\9eÑ\81Ñ\82авиÑ\82Ñ\8c Ðºомментарий"
 
-#: ../../mod/content.php:711 ../../object/Item.php:679
+#: mod/content.php:713 object/Item.php:711
 msgid "Bold"
 msgstr "Жирный"
 
-#: ../../mod/content.php:712 ../../object/Item.php:680
+#: mod/content.php:714 object/Item.php:712
 msgid "Italic"
 msgstr "Kурсивный"
 
-#: ../../mod/content.php:713 ../../object/Item.php:681
+#: mod/content.php:715 object/Item.php:713
 msgid "Underline"
 msgstr "Подчеркнутый"
 
-#: ../../mod/content.php:714 ../../object/Item.php:682
+#: mod/content.php:716 object/Item.php:714
 msgid "Quote"
 msgstr "Цитата"
 
-#: ../../mod/content.php:715 ../../object/Item.php:683
+#: mod/content.php:717 object/Item.php:715
 msgid "Code"
 msgstr "Код"
 
-#: ../../mod/content.php:716 ../../object/Item.php:684
+#: mod/content.php:718 object/Item.php:716
 msgid "Image"
 msgstr "Изображение / Фото"
 
-#: ../../mod/content.php:717 ../../object/Item.php:685
+#: mod/content.php:719 object/Item.php:717
 msgid "Link"
 msgstr "Ссылка"
 
-#: ../../mod/content.php:718 ../../object/Item.php:686
+#: mod/content.php:720 object/Item.php:718
 msgid "Video"
 msgstr "Видео"
 
-#: ../../mod/content.php:719 ../../mod/editpost.php:145
-#: ../../mod/photos.php:1566 ../../mod/photos.php:1610
-#: ../../mod/photos.php:1698 ../../object/Item.php:687
-#: ../../include/conversation.php:1126
-msgid "Preview"
-msgstr "предварительный просмотр"
-
-#: ../../mod/content.php:728 ../../mod/settings.php:676
-#: ../../object/Item.php:120
+#: mod/content.php:730 mod/settings.php:721 object/Item.php:122
+#: object/Item.php:124
 msgid "Edit"
 msgstr "Редактировать"
 
-#: ../../mod/content.php:753 ../../object/Item.php:195
+#: mod/content.php:755 object/Item.php:217
 msgid "add star"
 msgstr "пометить"
 
-#: ../../mod/content.php:754 ../../object/Item.php:196
+#: mod/content.php:756 object/Item.php:218
 msgid "remove star"
 msgstr "убрать метку"
 
-#: ../../mod/content.php:755 ../../object/Item.php:197
+#: mod/content.php:757 object/Item.php:219
 msgid "toggle star status"
 msgstr "переключить статус"
 
-#: ../../mod/content.php:758 ../../object/Item.php:200
+#: mod/content.php:760 object/Item.php:222
 msgid "starred"
 msgstr "помечено"
 
-#: ../../mod/content.php:759 ../../object/Item.php:220
+#: mod/content.php:761 object/Item.php:242
 msgid "add tag"
 msgstr "добавить ключевое слово (таг)"
 
-#: ../../mod/content.php:763 ../../object/Item.php:133
+#: mod/content.php:765 object/Item.php:137
 msgid "save to folder"
 msgstr "сохранить в папке"
 
-#: ../../mod/content.php:854 ../../object/Item.php:328
+#: mod/content.php:856 object/Item.php:359
 msgid "to"
 msgstr "к"
 
-#: ../../mod/content.php:855 ../../object/Item.php:330
+#: mod/content.php:857 object/Item.php:361
 msgid "Wall-to-Wall"
 msgstr "Стена-на-Стену"
 
-#: ../../mod/content.php:856 ../../object/Item.php:331
+#: mod/content.php:858 object/Item.php:362
 msgid "via Wall-To-Wall:"
 msgstr "через Стена-на-Стену:"
 
-#: ../../mod/removeme.php:46 ../../mod/removeme.php:49
+#: mod/removeme.php:46 mod/removeme.php:49
 msgid "Remove My Account"
 msgstr "Удалить мой аккаунт"
 
-#: ../../mod/removeme.php:47
+#: mod/removeme.php:47
 msgid ""
 "This will completely remove your account. Once this has been done it is not "
 "recoverable."
-msgstr "Это позволит полностью удалить ваш аккаунт. Как только это будет сделано, аккаунт восстановлению не подлежит."
+msgstr ""
+"Это позволит полностью удалить ваш аккаунт. Как только это будет сделано, "
+"аккаунт восстановлению не подлежит."
 
-#: ../../mod/removeme.php:48
+#: mod/removeme.php:48
 msgid "Please enter your password for verification:"
 msgstr "Пожалуйста, введите свой пароль для проверки:"
 
-#: ../../mod/install.php:117
+#: mod/install.php:128
 msgid "Friendica Communications Server - Setup"
 msgstr "Коммуникационный сервер Friendica - Доступ"
 
-#: ../../mod/install.php:123
+#: mod/install.php:134
 msgid "Could not connect to database."
 msgstr "Не удалось подключиться к базе данных."
 
-#: ../../mod/install.php:127
+#: mod/install.php:138
 msgid "Could not create table."
 msgstr "Не удалось создать таблицу."
 
-#: ../../mod/install.php:133
+#: mod/install.php:144
 msgid "Your Friendica site database has been installed."
 msgstr "База данных сайта установлена."
 
-#: ../../mod/install.php:138
+#: mod/install.php:149
 msgid ""
 "You may need to import the file \"database.sql\" manually using phpmyadmin "
 "or mysql."
-msgstr "Вам может понадобиться импортировать файл \"database.sql\" вручную с помощью PhpMyAdmin или MySQL."
+msgstr ""
+"Вам может понадобиться импортировать файл \"database.sql\" вручную с помощью "
+"PhpMyAdmin или MySQL."
 
-#: ../../mod/install.php:139 ../../mod/install.php:206
-#: ../../mod/install.php:525
+#: mod/install.php:150 mod/install.php:219 mod/install.php:577
 msgid "Please see the file \"INSTALL.txt\"."
 msgstr "Пожалуйста, смотрите файл \"INSTALL.txt\"."
 
-#: ../../mod/install.php:203
+#: mod/install.php:162
+msgid "Database already in use."
+msgstr ""
+
+#: mod/install.php:216
 msgid "System check"
 msgstr "Проверить систему"
 
-#: ../../mod/install.php:208
+#: mod/install.php:221
 msgid "Check again"
 msgstr "Проверить еще раз"
 
-#: ../../mod/install.php:227
+#: mod/install.php:240
 msgid "Database connection"
 msgstr "Подключение к базе данных"
 
-#: ../../mod/install.php:228
+#: mod/install.php:241
 msgid ""
 "In order to install Friendica we need to know how to connect to your "
 "database."
-msgstr "Для того, чтобы установить Friendica, мы должны знать, как подключиться к базе данных."
+msgstr ""
+"Для того, чтобы установить Friendica, мы должны знать, как подключиться к "
+"базе данных."
 
-#: ../../mod/install.php:229
+#: mod/install.php:242
 msgid ""
 "Please contact your hosting provider or site administrator if you have "
 "questions about these settings."
-msgstr "Пожалуйста, свяжитесь с вашим хостинг-провайдером или администратором сайта, если у вас есть вопросы об этих параметрах."
+msgstr ""
+"Пожалуйста, свяжитесь с вашим хостинг-провайдером или администратором сайта, "
+"если у вас есть вопросы об этих параметрах."
 
-#: ../../mod/install.php:230
+#: mod/install.php:243
 msgid ""
 "The database you specify below should already exist. If it does not, please "
 "create it before continuing."
-msgstr "Базы данных, указанная ниже, должна уже существовать. Если этого нет, пожалуйста, создайте ее перед продолжением."
+msgstr ""
+"Базы данных, указанная ниже, должна уже существовать. Если этого нет, "
+"пожалуйста, создайте ее перед продолжением."
 
-#: ../../mod/install.php:234
+#: mod/install.php:247
 msgid "Database Server Name"
 msgstr "Имя сервера базы данных"
 
-#: ../../mod/install.php:235
+#: mod/install.php:248
 msgid "Database Login Name"
 msgstr "Логин базы данных"
 
-#: ../../mod/install.php:236
+#: mod/install.php:249
 msgid "Database Login Password"
 msgstr "Пароль базы данных"
 
-#: ../../mod/install.php:237
+#: mod/install.php:250
 msgid "Database Name"
 msgstr "Имя базы данных"
 
-#: ../../mod/install.php:238 ../../mod/install.php:277
+#: mod/install.php:251 mod/install.php:290
 msgid "Site administrator email address"
 msgstr "Адрес электронной почты администратора сайта"
 
-#: ../../mod/install.php:238 ../../mod/install.php:277
+#: mod/install.php:251 mod/install.php:290
 msgid ""
 "Your account email address must match this in order to use the web admin "
 "panel."
-msgstr "Ваш адрес электронной почты аккаунта должен соответствовать этому, чтобы использовать веб-панель администратора."
+msgstr ""
+"Ваш адрес электронной почты аккаунта должен соответствовать этому, чтобы "
+"использовать веб-панель администратора."
 
-#: ../../mod/install.php:242 ../../mod/install.php:280
+#: mod/install.php:255 mod/install.php:293
 msgid "Please select a default timezone for your website"
 msgstr "Пожалуйста, выберите часовой пояс по умолчанию для вашего сайта"
 
-#: ../../mod/install.php:267
+#: mod/install.php:280
 msgid "Site settings"
 msgstr "Настройки сайта"
 
-#: ../../mod/install.php:321
+#: mod/install.php:334
 msgid "Could not find a command line version of PHP in the web server PATH."
 msgstr "Не удалось найти PATH веб-сервера в установках PHP."
 
-#: ../../mod/install.php:322
+#: mod/install.php:335
 msgid ""
 "If you don't have a command line version of PHP installed on server, you "
-"will not be able to run background polling via cron. See <a "
-"href='http://friendica.com/node/27'>'Activating scheduled tasks'</a>"
-msgstr "Если на вашем сервере не установлена версия командной строки PHP, вы не будете иметь возможность запускать фоновые опросы через крон. См. <a href='http://friendica.com/node/27'> 'Активация запланированных задачах' </a>"
+"will not be able to run background polling via cron. See <a href='https://"
+"github.com/friendica/friendica/blob/master/doc/Install.md#set-up-the-"
+"poller'>'Setup the poller'</a>"
+msgstr ""
 
-#: ../../mod/install.php:326
+#: mod/install.php:339
 msgid "PHP executable path"
 msgstr "PHP executable path"
 
-#: ../../mod/install.php:326
+#: mod/install.php:339
 msgid ""
 "Enter full path to php executable. You can leave this blank to continue the "
 "installation."
-msgstr "Введите полный путь к исполняемому файлу PHP. Вы можете оставить это поле пустым, чтобы продолжить установку."
+msgstr ""
+"Введите полный путь к исполняемому файлу PHP. Вы можете оставить это поле "
+"пустым, чтобы продолжить установку."
 
-#: ../../mod/install.php:331
+#: mod/install.php:344
 msgid "Command line PHP"
 msgstr "Command line PHP"
 
-#: ../../mod/install.php:340
+#: mod/install.php:353
 msgid "PHP executable is not the php cli binary (could be cgi-fgci version)"
 msgstr ""
 
-#: ../../mod/install.php:341
+#: mod/install.php:354
 msgid "Found PHP version: "
 msgstr "Найденная PHP версия: "
 
-#: ../../mod/install.php:343
+#: mod/install.php:356
 msgid "PHP cli binary"
 msgstr "PHP cli binary"
 
-#: ../../mod/install.php:354
+#: mod/install.php:367
 msgid ""
 "The command line version of PHP on your system does not have "
 "\"register_argc_argv\" enabled."
 msgstr "Не включено \"register_argc_argv\" в установках PHP."
 
-#: ../../mod/install.php:355
+#: mod/install.php:368
 msgid "This is required for message delivery to work."
 msgstr "Это необходимо для работы доставки сообщений."
 
-#: ../../mod/install.php:357
+#: mod/install.php:370
 msgid "PHP register_argc_argv"
 msgstr "PHP register_argc_argv"
 
-#: ../../mod/install.php:378
+#: mod/install.php:391
 msgid ""
 "Error: the \"openssl_pkey_new\" function on this system is not able to "
 "generate encryption keys"
-msgstr "Ошибка: функция \"openssl_pkey_new\" в этой системе не в состоянии генерировать ключи шифрования"
+msgstr ""
+"Ошибка: функция \"openssl_pkey_new\" в этой системе не в состоянии "
+"генерировать ключи шифрования"
 
-#: ../../mod/install.php:379
+#: mod/install.php:392
 msgid ""
-"If running under Windows, please see "
-"\"http://www.php.net/manual/en/openssl.installation.php\"."
-msgstr "Если вы работаете под Windows, см. \"http://www.php.net/manual/en/openssl.installation.php\"."
+"If running under Windows, please see \"http://www.php.net/manual/en/openssl."
+"installation.php\"."
+msgstr ""
+"Если вы работаете под Windows, см. \"http://www.php.net/manual/en/openssl."
+"installation.php\"."
 
-#: ../../mod/install.php:381
+#: mod/install.php:394
 msgid "Generate encryption keys"
 msgstr "Генерация шифрованых ключей"
 
-#: ../../mod/install.php:388
+#: mod/install.php:401
 msgid "libCurl PHP module"
 msgstr "libCurl PHP модуль"
 
-#: ../../mod/install.php:389
+#: mod/install.php:402
 msgid "GD graphics PHP module"
 msgstr "GD graphics PHP модуль"
 
-#: ../../mod/install.php:390
+#: mod/install.php:403
 msgid "OpenSSL PHP module"
 msgstr "OpenSSL PHP модуль"
 
-#: ../../mod/install.php:391
+#: mod/install.php:404
 msgid "mysqli PHP module"
 msgstr "mysqli PHP модуль"
 
-#: ../../mod/install.php:392
+#: mod/install.php:405
 msgid "mb_string PHP module"
 msgstr "mb_string PHP модуль"
 
-#: ../../mod/install.php:397 ../../mod/install.php:399
+#: mod/install.php:406
+msgid "mcrypt PHP module"
+msgstr ""
+
+#: mod/install.php:411 mod/install.php:413
 msgid "Apache mod_rewrite module"
 msgstr "Apache mod_rewrite module"
 
-#: ../../mod/install.php:397
+#: mod/install.php:411
 msgid ""
 "Error: Apache webserver mod-rewrite module is required but not installed."
-msgstr "Ошибка: необходим модуль веб-сервера Apache mod-rewrite, но он не установлен."
+msgstr ""
+"Ошибка: необходим модуль веб-сервера Apache mod-rewrite, но он не установлен."
 
-#: ../../mod/install.php:405
+#: mod/install.php:419
 msgid "Error: libCURL PHP module required but not installed."
 msgstr "Ошибка: необходим libCURL PHP модуль, но он не установлен."
 
-#: ../../mod/install.php:409
+#: mod/install.php:423
 msgid ""
 "Error: GD graphics PHP module with JPEG support required but not installed."
-msgstr "Ошибка: необходим PHP модуль GD графики с поддержкой JPEG, но он не установлен."
+msgstr ""
+"Ошибка: необходим PHP модуль GD графики с поддержкой JPEG, но он не "
+"установлен."
 
-#: ../../mod/install.php:413
+#: mod/install.php:427
 msgid "Error: openssl PHP module required but not installed."
 msgstr "Ошибка: необходим PHP модуль OpenSSL, но он не установлен."
 
-#: ../../mod/install.php:417
+#: mod/install.php:431
 msgid "Error: mysqli PHP module required but not installed."
 msgstr "Ошибка: необходим PHP модуль MySQLi, но он не установлен."
 
-#: ../../mod/install.php:421
+#: mod/install.php:435
 msgid "Error: mb_string PHP module required but not installed."
 msgstr "Ошибка: необходим PHP модуль mb_string, но он не установлен."
 
-#: ../../mod/install.php:438
+#: mod/install.php:439
+msgid "Error: mcrypt PHP module required but not installed."
+msgstr ""
+
+#: mod/install.php:451
+msgid ""
+"Function mcrypt_create_iv() is not defined. This is needed to enable RINO2 "
+"encryption layer."
+msgstr ""
+
+#: mod/install.php:453
+msgid "mcrypt_create_iv() function"
+msgstr ""
+
+#: mod/install.php:469
 msgid ""
-"The web installer needs to be able to create a file called \".htconfig.php\""
-" in the top folder of your web server and it is unable to do so."
-msgstr "Веб-инсталлятору требуется создать файл с именем \". htconfig.php\" в верхней папке веб-сервера, но он не в состоянии это сделать."
+"The web installer needs to be able to create a file called \".htconfig.php\" "
+"in the top folder of your web server and it is unable to do so."
+msgstr ""
+"Веб-инсталлятору требуется создать файл с именем \". htconfig.php\" в "
+"верхней папке веб-сервера, но он не в состоянии это сделать."
 
-#: ../../mod/install.php:439
+#: mod/install.php:470
 msgid ""
 "This is most often a permission setting, as the web server may not be able "
 "to write files in your folder - even if you can."
-msgstr "Это наиболее частые параметры разрешений, когда веб-сервер не может записать файлы в папке - даже если вы можете."
+msgstr ""
+"Это наиболее частые параметры разрешений, когда веб-сервер не может записать "
+"файлы в папке - даже если вы можете."
 
-#: ../../mod/install.php:440
+#: mod/install.php:471
 msgid ""
 "At the end of this procedure, we will give you a text to save in a file "
 "named .htconfig.php in your Friendica top folder."
-msgstr "В конце этой процедуры, мы дадим вам текст, для сохранения в файле с именем .htconfig.php в корневой папке, где установлена Friendica."
+msgstr ""
+"В конце этой процедуры, мы дадим вам текст, для сохранения в файле с именем ."
+"htconfig.php в корневой папке, где установлена Friendica."
 
-#: ../../mod/install.php:441
+#: mod/install.php:472
 msgid ""
-"You can alternatively skip this procedure and perform a manual installation."
-" Please see the file \"INSTALL.txt\" for instructions."
-msgstr "В качестве альтернативы вы можете пропустить эту процедуру и выполнить установку вручную. Пожалуйста, обратитесь к файлу \"INSTALL.txt\" для получения инструкций."
+"You can alternatively skip this procedure and perform a manual installation. "
+"Please see the file \"INSTALL.txt\" for instructions."
+msgstr ""
+"В качестве альтернативы вы можете пропустить эту процедуру и выполнить "
+"установку вручную. Пожалуйста, обратитесь к файлу \"INSTALL.txt\" для "
+"получения инструкций."
 
-#: ../../mod/install.php:444
+#: mod/install.php:475
 msgid ".htconfig.php is writable"
 msgstr ".htconfig.php is writable"
 
-#: ../../mod/install.php:454
+#: mod/install.php:485
 msgid ""
 "Friendica uses the Smarty3 template engine to render its web views. Smarty3 "
 "compiles templates to PHP to speed up rendering."
-msgstr "Friendica использует механизм шаблонов Smarty3 для генерации веб-страниц. Smarty3 компилирует шаблоны в PHP для увеличения скорости загрузки."
+msgstr ""
+"Friendica использует механизм шаблонов Smarty3 для генерации веб-страниц. "
+"Smarty3 компилирует шаблоны в PHP для увеличения скорости загрузки."
 
-#: ../../mod/install.php:455
+#: mod/install.php:486
 msgid ""
 "In order to store these compiled templates, the web server needs to have "
 "write access to the directory view/smarty3/ under the Friendica top level "
 "folder."
-msgstr "Для того чтобы хранить эти скомпилированные шаблоны, веб-сервер должен иметь доступ на запись для папки view/smarty3 в директории, где установлена Friendica."
+msgstr ""
+"Для того чтобы хранить эти скомпилированные шаблоны, веб-сервер должен иметь "
+"доступ на запись для папки view/smarty3 в директории, где установлена "
+"Friendica."
 
-#: ../../mod/install.php:456
+#: mod/install.php:487
 msgid ""
-"Please ensure that the user that your web server runs as (e.g. www-data) has"
-" write access to this folder."
-msgstr "Пожалуйста, убедитесь, что пользователь, под которым работает ваш веб-сервер (например www-data), имеет доступ на запись в этой папке."
+"Please ensure that the user that your web server runs as (e.g. www-data) has "
+"write access to this folder."
+msgstr ""
+"Пожалуйста, убедитесь, что пользователь, под которым работает ваш веб-сервер "
+"(например www-data), имеет доступ на запись в этой папке."
 
-#: ../../mod/install.php:457
+#: mod/install.php:488
 msgid ""
 "Note: as a security measure, you should give the web server write access to "
 "view/smarty3/ only--not the template files (.tpl) that it contains."
-msgstr "Примечание: в качестве меры безопасности, вы должны дать вебсерверу доступ на запись только в view/smarty3 - но не на сами файлы шаблонов (.tpl)., Которые содержатся в этой папке."
+msgstr ""
+"Примечание: в качестве меры безопасности, вы должны дать вебсерверу доступ "
+"на запись только в view/smarty3 - но не на сами файлы шаблонов (.tpl)., "
+"Которые содержатся в этой папке."
 
-#: ../../mod/install.php:460
+#: mod/install.php:491
 msgid "view/smarty3 is writable"
 msgstr "view/smarty3 доступен для записи"
 
-#: ../../mod/install.php:472
+#: mod/install.php:507
 msgid ""
 "Url rewrite in .htaccess is not working. Check your server configuration."
-msgstr "Url rewrite в .htaccess не работает. Проверьте конфигурацию вашего сервера.."
+msgstr ""
+"Url rewrite в .htaccess не работает. Проверьте конфигурацию вашего сервера.."
 
-#: ../../mod/install.php:474
+#: mod/install.php:509
 msgid "Url rewrite is working"
 msgstr "Url rewrite работает"
 
-#: ../../mod/install.php:484
+#: mod/install.php:526
+msgid "ImageMagick PHP extension is installed"
+msgstr ""
+
+#: mod/install.php:528
+msgid "ImageMagick supports GIF"
+msgstr ""
+
+#: mod/install.php:536
 msgid ""
 "The database configuration file \".htconfig.php\" could not be written. "
 "Please use the enclosed text to create a configuration file in your web "
 "server root."
-msgstr "Файл конфигурации базы данных \".htconfig.php\" не могла быть записан. Пожалуйста, используйте приложенный текст, чтобы создать конфигурационный файл в корневом каталоге веб-сервера."
+msgstr ""
+"Файл конфигурации базы данных \".htconfig.php\" не могла быть записан. "
+"Пожалуйста, используйте приложенный текст, чтобы создать конфигурационный "
+"файл в корневом каталоге веб-сервера."
 
-#: ../../mod/install.php:523
+#: mod/install.php:575
 msgid "<h1>What next</h1>"
 msgstr "<h1>Что далее</h1>"
 
-#: ../../mod/install.php:524
+#: mod/install.php:576
 msgid ""
-"IMPORTANT: You will need to [manually] setup a scheduled task for the "
-"poller."
-msgstr "ВАЖНО: Вам нужно будет [вручную] установить запланированное задание для регистратора."
+"IMPORTANT: You will need to [manually] setup a scheduled task for the poller."
+msgstr ""
+"ВАЖНО: Вам нужно будет [вручную] установить запланированное задание для "
+"регистратора."
 
-#: ../../mod/wallmessage.php:42 ../../mod/wallmessage.php:112
+#: mod/wallmessage.php:42 mod/wallmessage.php:112
 #, php-format
 msgid "Number of daily wall messages for %s exceeded. Message failed."
-msgstr "Количество ежедневных сообщений на стене %s превышено. Сообщение отменено.."
+msgstr ""
+"Количество ежедневных сообщений на стене %s превышено. Сообщение отменено.."
 
-#: ../../mod/wallmessage.php:59
+#: mod/wallmessage.php:59
 msgid "Unable to check your home location."
 msgstr "Невозможно проверить местоположение."
 
-#: ../../mod/wallmessage.php:86 ../../mod/wallmessage.php:95
+#: mod/wallmessage.php:86 mod/wallmessage.php:95
 msgid "No recipient."
 msgstr "Без адресата."
 
-#: ../../mod/wallmessage.php:143
+#: mod/wallmessage.php:143
 #, php-format
 msgid ""
 "If you wish for %s to respond, please check that the privacy settings on "
 "your site allow private mail from unknown senders."
-msgstr "Если Вы хотите ответить %s, пожалуйста, проверьте, позволяют ли настройки конфиденциальности на Вашем сайте принимать персональную почту от неизвестных отправителей."
+msgstr ""
+"Если Вы хотите ответить %s, пожалуйста, проверьте, позволяют ли настройки "
+"конфиденциальности на Вашем сайте принимать персональную почту от "
+"неизвестных отправителей."
 
-#: ../../mod/help.php:79
+#: mod/help.php:41
 msgid "Help:"
 msgstr "Помощь:"
 
-#: ../../mod/help.php:84 ../../include/nav.php:114
+#: mod/help.php:47 include/nav.php:113 view/theme/vier/theme.php:302
 msgid "Help"
 msgstr "Помощь"
 
-#: ../../mod/help.php:90 ../../index.php:256
+#: mod/help.php:53 mod/p.php:16 mod/p.php:25 index.php:270
 msgid "Not Found"
 msgstr "Не найдено"
 
-#: ../../mod/help.php:93 ../../index.php:259
+#: mod/help.php:56 index.php:273
 msgid "Page not found."
 msgstr "Страница не найдена."
 
-#: ../../mod/dfrn_poll.php:103 ../../mod/dfrn_poll.php:536
+#: mod/dfrn_poll.php:103 mod/dfrn_poll.php:536
 #, php-format
 msgid "%1$s welcomes %2$s"
 msgstr "%1$s добро пожаловать %2$s"
 
-#: ../../mod/home.php:35
+#: mod/home.php:35
 #, php-format
 msgid "Welcome to %s"
 msgstr "Добро пожаловать на %s!"
 
-#: ../../mod/wall_attach.php:75
+#: mod/wall_attach.php:94
 msgid "Sorry, maybe your upload is bigger than the PHP configuration allows"
 msgstr ""
 
-#: ../../mod/wall_attach.php:75
+#: mod/wall_attach.php:94
 msgid "Or - did you try to upload an empty file?"
 msgstr ""
 
-#: ../../mod/wall_attach.php:81
+#: mod/wall_attach.php:105
 #, php-format
-msgid "File exceeds size limit of %d"
-msgstr "Файл превышает предельный размер %d"
+msgid "File exceeds size limit of %s"
+msgstr ""
 
-#: ../../mod/wall_attach.php:122 ../../mod/wall_attach.php:133
+#: mod/wall_attach.php:156 mod/wall_attach.php:172
 msgid "File upload failed."
 msgstr "Загрузка файла не удалась."
 
-#: ../../mod/match.php:12
-msgid "Profile Match"
-msgstr "Похожие профили"
-
-#: ../../mod/match.php:20
+#: mod/match.php:33
 msgid "No keywords to match. Please add keywords to your default profile."
-msgstr "Нет соответствующих ключевых слов. Пожалуйста, добавьте ключевые слова для вашего профиля по умолчанию."
+msgstr ""
+"Нет соответствующих ключевых слов. Пожалуйста, добавьте ключевые слова для "
+"вашего профиля по умолчанию."
 
-#: ../../mod/match.php:57
+#: mod/match.php:84
 msgid "is interested in:"
 msgstr "интересуется:"
 
-#: ../../mod/match.php:58 ../../mod/suggest.php:90 ../../boot.php:1568
-#: ../../include/contact_widgets.php:10
-msgid "Connect"
-msgstr "Подключить"
+#: mod/match.php:98
+msgid "Profile Match"
+msgstr "Похожие профили"
 
-#: ../../mod/share.php:44
+#: mod/share.php:38
 msgid "link"
 msgstr "ссылка"
 
-#: ../../mod/community.php:23
+#: mod/community.php:27
 msgid "Not available."
 msgstr "Недоступно."
 
-#: ../../mod/community.php:32 ../../include/nav.php:129
-#: ../../include/nav.php:131 ../../view/theme/diabook/theme.php:129
+#: mod/community.php:36 include/nav.php:136 include/nav.php:138
+#: view/theme/diabook/theme.php:129
 msgid "Community"
 msgstr "Сообщество"
 
-#: ../../mod/community.php:62 ../../mod/community.php:71
-#: ../../mod/search.php:168 ../../mod/search.php:192
+#: mod/community.php:66 mod/community.php:75 mod/search.php:228
 msgid "No results."
 msgstr "Нет результатов."
 
-#: ../../mod/settings.php:29 ../../mod/photos.php:80
+#: mod/settings.php:34 mod/photos.php:117
 msgid "everybody"
 msgstr "каждый"
 
-#: ../../mod/settings.php:41
-msgid "Additional features"
-msgstr "Дополнительные возможности"
-
-#: ../../mod/settings.php:46
+#: mod/settings.php:58
 msgid "Display"
-msgstr ""
+msgstr "Внешний вид"
 
-#: ../../mod/settings.php:52 ../../mod/settings.php:780
+#: mod/settings.php:65 mod/settings.php:864
 msgid "Social Networks"
-msgstr ""
+msgstr "Социальные сети"
 
-#: ../../mod/settings.php:62 ../../include/nav.php:170
+#: mod/settings.php:79 include/nav.php:180
 msgid "Delegations"
-msgstr ""
+msgstr "Делегирование"
 
-#: ../../mod/settings.php:67
+#: mod/settings.php:86
 msgid "Connected apps"
 msgstr "Подключенные приложения"
 
-#: ../../mod/settings.php:72 ../../mod/uexport.php:85
+#: mod/settings.php:93 mod/uexport.php:85
 msgid "Export personal data"
 msgstr "Экспорт личных данных"
 
-#: ../../mod/settings.php:77
+#: mod/settings.php:100
 msgid "Remove account"
 msgstr "Удалить аккаунт"
 
-#: ../../mod/settings.php:129
+#: mod/settings.php:153
 msgid "Missing some important data!"
 msgstr "Не хватает важных данных!"
 
-#: ../../mod/settings.php:238
+#: mod/settings.php:266
 msgid "Failed to connect with email account using the settings provided."
-msgstr "Не удалось подключиться к аккаунту e-mail, используя указанные настройки."
+msgstr ""
+"Не удалось подключиться к аккаунту e-mail, используя указанные настройки."
 
-#: ../../mod/settings.php:243
+#: mod/settings.php:271
 msgid "Email settings updated."
 msgstr "Настройки эл. почты обновлены."
 
-#: ../../mod/settings.php:258
+#: mod/settings.php:286
 msgid "Features updated"
 msgstr "Настройки обновлены"
 
-#: ../../mod/settings.php:321
+#: mod/settings.php:353
 msgid "Relocate message has been send to your contacts"
 msgstr "Перемещённое сообщение было отправлено списку контактов"
 
-#: ../../mod/settings.php:335
+#: mod/settings.php:367 include/user.php:39
 msgid "Passwords do not match. Password unchanged."
 msgstr "Пароли не совпадают. Пароль не изменен."
 
-#: ../../mod/settings.php:340
+#: mod/settings.php:372
 msgid "Empty passwords are not allowed. Password unchanged."
 msgstr "Пустые пароли не допускаются. Пароль не изменен."
 
-#: ../../mod/settings.php:348
+#: mod/settings.php:380
 msgid "Wrong password."
 msgstr "Неверный пароль."
 
-#: ../../mod/settings.php:359
+#: mod/settings.php:391
 msgid "Password changed."
 msgstr "Пароль изменен."
 
-#: ../../mod/settings.php:361
+#: mod/settings.php:393
 msgid "Password update failed. Please try again."
 msgstr "Обновление пароля не удалось. Пожалуйста, попробуйте еще раз."
 
-#: ../../mod/settings.php:428
+#: mod/settings.php:462
 msgid " Please use a shorter name."
 msgstr " Пожалуйста, используйте более короткое имя."
 
-#: ../../mod/settings.php:430
+#: mod/settings.php:464
 msgid " Name too short."
 msgstr " Имя слишком короткое."
 
-#: ../../mod/settings.php:439
+#: mod/settings.php:473
 msgid "Wrong Password"
 msgstr "Неверный пароль."
 
-#: ../../mod/settings.php:444
+#: mod/settings.php:478
 msgid " Not valid email."
 msgstr " Неверный e-mail."
 
-#: ../../mod/settings.php:450
+#: mod/settings.php:484
 msgid " Cannot change to that email."
 msgstr " Невозможно изменить на этот e-mail."
 
-#: ../../mod/settings.php:506
+#: mod/settings.php:540
 msgid "Private forum has no privacy permissions. Using default privacy group."
-msgstr "Частный форум не имеет настроек приватности. Используется группа конфиденциальности по умолчанию."
+msgstr ""
+"Частный форум не имеет настроек приватности. Используется группа "
+"конфиденциальности по умолчанию."
 
-#: ../../mod/settings.php:510
+#: mod/settings.php:544
 msgid "Private forum has no privacy permissions and no default privacy group."
-msgstr "Частный форум не имеет настроек приватности и не имеет групп приватности по умолчанию."
+msgstr ""
+"Частный форум не имеет настроек приватности и не имеет групп приватности по "
+"умолчанию."
 
-#: ../../mod/settings.php:540
+#: mod/settings.php:583
 msgid "Settings updated."
 msgstr "Настройки обновлены."
 
-#: ../../mod/settings.php:613 ../../mod/settings.php:639
-#: ../../mod/settings.php:675
+#: mod/settings.php:658 mod/settings.php:684 mod/settings.php:720
 msgid "Add application"
 msgstr "Добавить приложения"
 
-#: ../../mod/settings.php:617 ../../mod/settings.php:643
+#: mod/settings.php:662 mod/settings.php:688
 msgid "Consumer Key"
 msgstr "Consumer Key"
 
-#: ../../mod/settings.php:618 ../../mod/settings.php:644
+#: mod/settings.php:663 mod/settings.php:689
 msgid "Consumer Secret"
 msgstr "Consumer Secret"
 
-#: ../../mod/settings.php:619 ../../mod/settings.php:645
+#: mod/settings.php:664 mod/settings.php:690
 msgid "Redirect"
 msgstr "Перенаправление"
 
-#: ../../mod/settings.php:620 ../../mod/settings.php:646
+#: mod/settings.php:665 mod/settings.php:691
 msgid "Icon url"
 msgstr "URL символа"
 
-#: ../../mod/settings.php:631
+#: mod/settings.php:676
 msgid "You can't edit this application."
 msgstr "Вы не можете изменить это приложение."
 
-#: ../../mod/settings.php:674
+#: mod/settings.php:719
 msgid "Connected Apps"
 msgstr "Подключенные приложения"
 
-#: ../../mod/settings.php:678
+#: mod/settings.php:723
 msgid "Client key starts with"
 msgstr "Ключ клиента начинается с"
 
-#: ../../mod/settings.php:679
+#: mod/settings.php:724
 msgid "No name"
 msgstr "Нет имени"
 
-#: ../../mod/settings.php:680
+#: mod/settings.php:725
 msgid "Remove authorization"
 msgstr "Удалить авторизацию"
 
-#: ../../mod/settings.php:692
+#: mod/settings.php:737
 msgid "No Plugin settings configured"
 msgstr "Нет сконфигурированных настроек плагина"
 
-#: ../../mod/settings.php:700
+#: mod/settings.php:745
 msgid "Plugin Settings"
 msgstr "Настройки плагина"
 
-#: ../../mod/settings.php:714
-msgid "Off"
+#: mod/settings.php:767
+msgid "Additional Features"
+msgstr "Дополнительные возможности"
+
+#: mod/settings.php:777 mod/settings.php:781
+msgid "General Social Media Settings"
 msgstr ""
 
-#: ../../mod/settings.php:714
-msgid "On"
+#: mod/settings.php:787
+msgid "Disable intelligent shortening"
 msgstr ""
 
-#: ../../mod/settings.php:722
-msgid "Additional Features"
-msgstr "Дополнительные возможности"
+#: mod/settings.php:789
+msgid ""
+"Normally the system tries to find the best link to add to shortened posts. "
+"If this option is enabled then every shortened post will always point to the "
+"original friendica post."
+msgstr ""
+
+#: mod/settings.php:795
+msgid "Automatically follow any GNU Social (OStatus) followers/mentioners"
+msgstr ""
+
+#: mod/settings.php:797
+msgid ""
+"If you receive a message from an unknown OStatus user, this option decides "
+"what to do. If it is checked, a new contact will be created for every "
+"unknown user."
+msgstr ""
+
+#: mod/settings.php:806
+msgid "Your legacy GNU Social account"
+msgstr ""
+
+#: mod/settings.php:808
+msgid ""
+"If you enter your old GNU Social/Statusnet account name here (in the format "
+"user@domain.tld), your contacts will be added automatically. The field will "
+"be emptied when done."
+msgstr ""
+
+#: mod/settings.php:811
+msgid "Repair OStatus subscriptions"
+msgstr ""
 
-#: ../../mod/settings.php:736 ../../mod/settings.php:737
+#: mod/settings.php:820 mod/settings.php:821
 #, php-format
 msgid "Built-in support for %s connectivity is %s"
 msgstr "Встроенная  поддержка для %s подключение %s"
 
-#: ../../mod/settings.php:736 ../../mod/dfrn_request.php:838
-#: ../../include/contact_selectors.php:80
+#: mod/settings.php:820 mod/dfrn_request.php:865
+#: include/contact_selectors.php:80
 msgid "Diaspora"
 msgstr "Diaspora"
 
-#: ../../mod/settings.php:736 ../../mod/settings.php:737
+#: mod/settings.php:820 mod/settings.php:821
 msgid "enabled"
 msgstr "подключено"
 
-#: ../../mod/settings.php:736 ../../mod/settings.php:737
+#: mod/settings.php:820 mod/settings.php:821
 msgid "disabled"
 msgstr "отключено"
 
-#: ../../mod/settings.php:737
-msgid "StatusNet"
-msgstr "StatusNet"
+#: mod/settings.php:821
+msgid "GNU Social (OStatus)"
+msgstr ""
 
-#: ../../mod/settings.php:773
+#: mod/settings.php:857
 msgid "Email access is disabled on this site."
 msgstr "Доступ эл. почты отключен на этом сайте."
 
-#: ../../mod/settings.php:785
+#: mod/settings.php:869
 msgid "Email/Mailbox Setup"
 msgstr "Настройка эл. почты / почтового ящика"
 
-#: ../../mod/settings.php:786
+#: mod/settings.php:870
 msgid ""
 "If you wish to communicate with email contacts using this service "
 "(optional), please specify how to connect to your mailbox."
-msgstr "Если вы хотите общаться с Email контактами, используя этот сервис (по желанию), пожалуйста, уточните, как подключиться к вашему почтовому ящику."
+msgstr ""
+"Если вы хотите общаться с Email контактами, используя этот сервис (по "
+"желанию), пожалуйста, уточните, как подключиться к вашему почтовому ящику."
 
-#: ../../mod/settings.php:787
+#: mod/settings.php:871
 msgid "Last successful email check:"
 msgstr "Последняя успешная проверка электронной почты:"
 
-#: ../../mod/settings.php:789
+#: mod/settings.php:873
 msgid "IMAP server name:"
 msgstr "Имя IMAP сервера:"
 
-#: ../../mod/settings.php:790
+#: mod/settings.php:874
 msgid "IMAP port:"
 msgstr "Порт IMAP:"
 
-#: ../../mod/settings.php:791
+#: mod/settings.php:875
 msgid "Security:"
 msgstr "Безопасность:"
 
-#: ../../mod/settings.php:791 ../../mod/settings.php:796
+#: mod/settings.php:875 mod/settings.php:880
 msgid "None"
 msgstr "Ничего"
 
-#: ../../mod/settings.php:792
+#: mod/settings.php:876
 msgid "Email login name:"
 msgstr "Логин эл. почты:"
 
-#: ../../mod/settings.php:793
+#: mod/settings.php:877
 msgid "Email password:"
 msgstr "Пароль эл. почты:"
 
-#: ../../mod/settings.php:794
+#: mod/settings.php:878
 msgid "Reply-to address:"
 msgstr "Адрес для ответа:"
 
-#: ../../mod/settings.php:795
+#: mod/settings.php:879
 msgid "Send public posts to all email contacts:"
 msgstr "Отправлять открытые сообщения на все контакты электронной почты:"
 
-#: ../../mod/settings.php:796
+#: mod/settings.php:880
 msgid "Action after import:"
 msgstr "Действие после импорта:"
 
-#: ../../mod/settings.php:796
+#: mod/settings.php:880
 msgid "Mark as seen"
 msgstr "Отметить, как прочитанное"
 
-#: ../../mod/settings.php:796
+#: mod/settings.php:880
 msgid "Move to folder"
 msgstr "Переместить в папку"
 
-#: ../../mod/settings.php:797
+#: mod/settings.php:881
 msgid "Move to folder:"
 msgstr "Переместить в папку:"
 
-#: ../../mod/settings.php:878
+#: mod/settings.php:967
 msgid "Display Settings"
 msgstr "Параметры дисплея"
 
-#: ../../mod/settings.php:884 ../../mod/settings.php:899
+#: mod/settings.php:973 mod/settings.php:991
 msgid "Display Theme:"
 msgstr "Показать тему:"
 
-#: ../../mod/settings.php:885
+#: mod/settings.php:974
 msgid "Mobile Theme:"
 msgstr "Мобильная тема:"
 
-#: ../../mod/settings.php:886
+#: mod/settings.php:975
 msgid "Update browser every xx seconds"
 msgstr "Обновление браузера каждые хх секунд"
 
-#: ../../mod/settings.php:886
-msgid "Minimum of 10 seconds, no maximum"
-msgstr "Минимум 10 секунд, максимума нет"
+#: mod/settings.php:975
+msgid "Minimum of 10 seconds. Enter -1 to disable it."
+msgstr ""
 
-#: ../../mod/settings.php:887
+#: mod/settings.php:976
 msgid "Number of items to display per page:"
 msgstr "Количество элементов, отображаемых на одной странице:"
 
-#: ../../mod/settings.php:887 ../../mod/settings.php:888
+#: mod/settings.php:976 mod/settings.php:977
 msgid "Maximum of 100 items"
 msgstr "Максимум 100 элементов"
 
-#: ../../mod/settings.php:888
+#: mod/settings.php:977
 msgid "Number of items to display per page when viewed from mobile device:"
-msgstr "Количество элементов на странице, когда просмотр осуществляется с мобильных устройств:"
+msgstr ""
+"Количество элементов на странице, когда просмотр осуществляется с мобильных "
+"устройств:"
 
-#: ../../mod/settings.php:889
+#: mod/settings.php:978
 msgid "Don't show emoticons"
 msgstr "не показывать emoticons"
 
-#: ../../mod/settings.php:890
+#: mod/settings.php:979
+msgid "Calendar"
+msgstr ""
+
+#: mod/settings.php:980
+msgid "Beginning of week:"
+msgstr ""
+
+#: mod/settings.php:981
 msgid "Don't show notices"
 msgstr ""
 
-#: ../../mod/settings.php:891
+#: mod/settings.php:982
 msgid "Infinite scroll"
 msgstr "Бесконечная прокрутка"
 
-#: ../../mod/settings.php:892
+#: mod/settings.php:983
 msgid "Automatic updates only at the top of the network page"
 msgstr ""
 
-#: ../../mod/settings.php:969
+#: mod/settings.php:985 view/theme/cleanzero/config.php:82
+#: view/theme/dispy/config.php:72 view/theme/quattro/config.php:66
+#: view/theme/diabook/config.php:150 view/theme/vier/config.php:109
+#: view/theme/duepuntozero/config.php:61
+msgid "Theme settings"
+msgstr "Настройки темы"
+
+#: mod/settings.php:1062
 msgid "User Types"
 msgstr ""
 
-#: ../../mod/settings.php:970
+#: mod/settings.php:1063
 msgid "Community Types"
 msgstr ""
 
-#: ../../mod/settings.php:971
+#: mod/settings.php:1064
 msgid "Normal Account Page"
 msgstr "Стандартная страница аккаунта"
 
-#: ../../mod/settings.php:972
+#: mod/settings.php:1065
 msgid "This account is a normal personal profile"
 msgstr "Этот аккаунт является обычным персональным профилем"
 
-#: ../../mod/settings.php:975
+#: mod/settings.php:1068
 msgid "Soapbox Page"
 msgstr ""
 
-#: ../../mod/settings.php:976
+#: mod/settings.php:1069
 msgid "Automatically approve all connection/friend requests as read-only fans"
-msgstr "Автоматически одобряются все подключения / запросы в друзья, \"только для чтения\" поклонниками"
+msgstr ""
+"Автоматически одобряются все подключения / запросы в друзья, \"только для "
+"чтения\" поклонниками"
 
-#: ../../mod/settings.php:979
+#: mod/settings.php:1072
 msgid "Community Forum/Celebrity Account"
 msgstr "Аккаунт сообщества Форум/Знаменитость"
 
-#: ../../mod/settings.php:980
-msgid ""
-"Automatically approve all connection/friend requests as read-write fans"
-msgstr "Автоматически одобряются все подключения / запросы в друзья, \"для чтения и записей\" поклонников"
+#: mod/settings.php:1073
+msgid "Automatically approve all connection/friend requests as read-write fans"
+msgstr ""
+"Автоматически одобряются все подключения / запросы в друзья, \"для чтения и "
+"записей\" поклонников"
 
-#: ../../mod/settings.php:983
+#: mod/settings.php:1076
 msgid "Automatic Friend Page"
 msgstr "\"Автоматический друг\" страница"
 
-#: ../../mod/settings.php:984
+#: mod/settings.php:1077
 msgid "Automatically approve all connection/friend requests as friends"
-msgstr "Автоматически одобряются все подключения / запросы в друзья, расширяется список друзей"
+msgstr ""
+"Автоматически одобряются все подключения / запросы в друзья, расширяется "
+"список друзей"
 
-#: ../../mod/settings.php:987
+#: mod/settings.php:1080
 msgid "Private Forum [Experimental]"
 msgstr "Личный форум [экспериментально]"
 
-#: ../../mod/settings.php:988
+#: mod/settings.php:1081
 msgid "Private forum - approved members only"
 msgstr "Приватный форум - разрешено только участникам"
 
-#: ../../mod/settings.php:1000
+#: mod/settings.php:1093
 msgid "OpenID:"
 msgstr "OpenID:"
 
-#: ../../mod/settings.php:1000
+#: mod/settings.php:1093
 msgid "(Optional) Allow this OpenID to login to this account."
 msgstr "(Необязательно) Разрешить этому OpenID входить в этот аккаунт"
 
-#: ../../mod/settings.php:1010
+#: mod/settings.php:1103
 msgid "Publish your default profile in your local site directory?"
-msgstr "Публиковать ваш профиль по умолчанию в вашем локальном каталоге на сайте?"
-
-#: ../../mod/settings.php:1010 ../../mod/settings.php:1016
-#: ../../mod/settings.php:1024 ../../mod/settings.php:1028
-#: ../../mod/settings.php:1033 ../../mod/settings.php:1039
-#: ../../mod/settings.php:1045 ../../mod/settings.php:1051
-#: ../../mod/settings.php:1081 ../../mod/settings.php:1082
-#: ../../mod/settings.php:1083 ../../mod/settings.php:1084
-#: ../../mod/settings.php:1085 ../../mod/dfrn_request.php:830
-#: ../../mod/register.php:234 ../../mod/profiles.php:661
-#: ../../mod/profiles.php:665 ../../mod/api.php:106
-msgid "No"
-msgstr "Нет"
+msgstr ""
+"Публиковать ваш профиль по умолчанию в вашем локальном каталоге на сайте?"
 
-#: ../../mod/settings.php:1016
+#: mod/settings.php:1109
 msgid "Publish your default profile in the global social directory?"
 msgstr "Публиковать ваш профиль по умолчанию в глобальном социальном каталоге?"
 
-#: ../../mod/settings.php:1024
+#: mod/settings.php:1117
 msgid "Hide your contact/friend list from viewers of your default profile?"
-msgstr "Скрывать ваш список контактов/друзей от посетителей вашего профиля по умолчанию?"
+msgstr ""
+"Скрывать ваш список контактов/друзей от посетителей вашего профиля по "
+"умолчанию?"
 
-#: ../../mod/settings.php:1028 ../../include/conversation.php:1057
+#: mod/settings.php:1121 include/acl_selectors.php:331
 msgid "Hide your profile details from unknown viewers?"
 msgstr "Скрыть данные профиля из неизвестных зрителей?"
 
-#: ../../mod/settings.php:1028
+#: mod/settings.php:1121
 msgid ""
 "If enabled, posting public messages to Diaspora and other networks isn't "
 "possible."
 msgstr ""
 
-#: ../../mod/settings.php:1033
+#: mod/settings.php:1126
 msgid "Allow friends to post to your profile page?"
 msgstr "Разрешить друзьям оставлять сообщения на страницу вашего профиля?"
 
-#: ../../mod/settings.php:1039
+#: mod/settings.php:1132
 msgid "Allow friends to tag your posts?"
 msgstr "Разрешить друзьям отмечять ваши сообщения?"
 
-#: ../../mod/settings.php:1045
+#: mod/settings.php:1138
 msgid "Allow us to suggest you as a potential friend to new members?"
 msgstr "Позвольть предлогать Вам потенциальных друзей?"
 
-#: ../../mod/settings.php:1051
+#: mod/settings.php:1144
 msgid "Permit unknown people to send you private mail?"
 msgstr "Разрешить незнакомым людям отправлять вам личные сообщения?"
 
-#: ../../mod/settings.php:1059
+#: mod/settings.php:1152
 msgid "Profile is <strong>not published</strong>."
 msgstr "Профиль <strong>не публикуется</strong>."
 
-#: ../../mod/settings.php:1067
-msgid "Your Identity Address is"
-msgstr "Ваш идентификационный адрес"
+#: mod/settings.php:1160
+#, php-format
+msgid "Your Identity Address is <strong>'%s'</strong> or '%s'."
+msgstr ""
 
-#: ../../mod/settings.php:1078
+#: mod/settings.php:1167
 msgid "Automatically expire posts after this many days:"
 msgstr "Автоматическое истекание срока действия сообщения после стольких дней:"
 
-#: ../../mod/settings.php:1078
+#: mod/settings.php:1167
 msgid "If empty, posts will not expire. Expired posts will be deleted"
-msgstr "Если пусто, срок действия сообщений не будет ограничен. Сообщения с истекшим сроком действия будут удалены"
+msgstr ""
+"Если пусто, срок действия сообщений не будет ограничен. Сообщения с истекшим "
+"сроком действия будут удалены"
 
-#: ../../mod/settings.php:1079
+#: mod/settings.php:1168
 msgid "Advanced expiration settings"
 msgstr "Настройки расширенного окончания срока действия"
 
-#: ../../mod/settings.php:1080
+#: mod/settings.php:1169
 msgid "Advanced Expiration"
 msgstr "Расширенное окончание срока действия"
 
-#: ../../mod/settings.php:1081
+#: mod/settings.php:1170
 msgid "Expire posts:"
 msgstr "Срок хранения сообщений:"
 
-#: ../../mod/settings.php:1082
+#: mod/settings.php:1171
 msgid "Expire personal notes:"
 msgstr "Срок хранения личных заметок:"
 
-#: ../../mod/settings.php:1083
+#: mod/settings.php:1172
 msgid "Expire starred posts:"
 msgstr "Срок хранения усеянных сообщений:"
 
-#: ../../mod/settings.php:1084
+#: mod/settings.php:1173
 msgid "Expire photos:"
 msgstr "Срок хранения фотографий:"
 
-#: ../../mod/settings.php:1085
+#: mod/settings.php:1174
 msgid "Only expire posts by others:"
 msgstr "Только устаревшие посты других:"
 
-#: ../../mod/settings.php:1111
+#: mod/settings.php:1202
 msgid "Account Settings"
 msgstr "Настройки аккаунта"
 
-#: ../../mod/settings.php:1119
+#: mod/settings.php:1210
 msgid "Password Settings"
-msgstr "Ð\9dаÑ\81Ñ\82Ñ\80ойка пароля"
+msgstr "Смена пароля"
 
-#: ../../mod/settings.php:1120
+#: mod/settings.php:1211 mod/register.php:274
 msgid "New Password:"
 msgstr "Новый пароль:"
 
-#: ../../mod/settings.php:1121
+#: mod/settings.php:1212 mod/register.php:275
 msgid "Confirm:"
 msgstr "Подтвердите:"
 
-#: ../../mod/settings.php:1121
+#: mod/settings.php:1212
 msgid "Leave password fields blank unless changing"
 msgstr "Оставьте поля пароля пустыми, если он не изменяется"
 
-#: ../../mod/settings.php:1122
+#: mod/settings.php:1213
 msgid "Current Password:"
 msgstr "Текущий пароль:"
 
-#: ../../mod/settings.php:1122 ../../mod/settings.php:1123
+#: mod/settings.php:1213 mod/settings.php:1214
 msgid "Your current password to confirm the changes"
 msgstr "Ваш текущий пароль, для подтверждения изменений"
 
-#: ../../mod/settings.php:1123
+#: mod/settings.php:1214
 msgid "Password:"
 msgstr "Пароль:"
 
-#: ../../mod/settings.php:1127
+#: mod/settings.php:1218
 msgid "Basic Settings"
 msgstr "Основные параметры"
 
-#: ../../mod/settings.php:1128 ../../include/profile_advanced.php:15
+#: mod/settings.php:1219 include/identity.php:588
 msgid "Full Name:"
 msgstr "Полное имя:"
 
-#: ../../mod/settings.php:1129
+#: mod/settings.php:1220
 msgid "Email Address:"
 msgstr "Адрес электронной почты:"
 
-#: ../../mod/settings.php:1130
+#: mod/settings.php:1221
 msgid "Your Timezone:"
 msgstr "Ваш часовой пояс:"
 
-#: ../../mod/settings.php:1131
+#: mod/settings.php:1222
+msgid "Your Language:"
+msgstr ""
+
+#: mod/settings.php:1222
+msgid ""
+"Set the language we use to show you friendica interface and to send you "
+"emails"
+msgstr ""
+
+#: mod/settings.php:1223
 msgid "Default Post Location:"
 msgstr "Местонахождение по умолчанию:"
 
-#: ../../mod/settings.php:1132
+#: mod/settings.php:1224
 msgid "Use Browser Location:"
 msgstr "Использовать определение местоположения браузером:"
 
-#: ../../mod/settings.php:1135
+#: mod/settings.php:1227
 msgid "Security and Privacy Settings"
 msgstr "Параметры безопасности и конфиденциальности"
 
-#: ../../mod/settings.php:1137
+#: mod/settings.php:1229
 msgid "Maximum Friend Requests/Day:"
 msgstr "Максимум запросов в друзья в день:"
 
-#: ../../mod/settings.php:1137 ../../mod/settings.php:1167
+#: mod/settings.php:1229 mod/settings.php:1259
 msgid "(to prevent spam abuse)"
 msgstr "(для предотвращения спама)"
 
-#: ../../mod/settings.php:1138
+#: mod/settings.php:1230
 msgid "Default Post Permissions"
 msgstr "Разрешение на сообщения по умолчанию"
 
-#: ../../mod/settings.php:1139
+#: mod/settings.php:1231
 msgid "(click to open/close)"
 msgstr "(нажмите, чтобы открыть / закрыть)"
 
-#: ../../mod/settings.php:1148 ../../mod/photos.php:1146
-#: ../../mod/photos.php:1519
+#: mod/settings.php:1240 mod/photos.php:1199 mod/photos.php:1584
 msgid "Show to Groups"
 msgstr "Показать в группах"
 
-#: ../../mod/settings.php:1149 ../../mod/photos.php:1147
-#: ../../mod/photos.php:1520
+#: mod/settings.php:1241 mod/photos.php:1200 mod/photos.php:1585
 msgid "Show to Contacts"
 msgstr "Показывать контактам"
 
-#: ../../mod/settings.php:1150
+#: mod/settings.php:1242
 msgid "Default Private Post"
 msgstr "Личное сообщение по умолчанию"
 
-#: ../../mod/settings.php:1151
+#: mod/settings.php:1243
 msgid "Default Public Post"
 msgstr "Публичное сообщение по умолчанию"
 
-#: ../../mod/settings.php:1155
+#: mod/settings.php:1247
 msgid "Default Permissions for New Posts"
 msgstr "Права для новых записей по умолчанию"
 
-#: ../../mod/settings.php:1167
+#: mod/settings.php:1259
 msgid "Maximum private messages per day from unknown people:"
 msgstr "Максимальное количество личных сообщений от незнакомых людей в день:"
 
-#: ../../mod/settings.php:1170
+#: mod/settings.php:1262
 msgid "Notification Settings"
 msgstr "Настройка уведомлений"
 
-#: ../../mod/settings.php:1171
+#: mod/settings.php:1263
 msgid "By default post a status message when:"
 msgstr "Отправить состояние о статусе по умолчанию, если:"
 
-#: ../../mod/settings.php:1172
+#: mod/settings.php:1264
 msgid "accepting a friend request"
 msgstr "принятие запроса на добавление в друзья"
 
-#: ../../mod/settings.php:1173
+#: mod/settings.php:1265
 msgid "joining a forum/community"
 msgstr "вступление в сообщество/форум"
 
-#: ../../mod/settings.php:1174
+#: mod/settings.php:1266
 msgid "making an <em>interesting</em> profile change"
 msgstr "сделать изменения в <em>настройках интересов</em> профиля"
 
-#: ../../mod/settings.php:1175
+#: mod/settings.php:1267
 msgid "Send a notification email when:"
 msgstr "Отправлять уведомление по электронной почте, когда:"
 
-#: ../../mod/settings.php:1176
+#: mod/settings.php:1268
 msgid "You receive an introduction"
 msgstr "Вы получили запрос"
 
-#: ../../mod/settings.php:1177
+#: mod/settings.php:1269
 msgid "Your introductions are confirmed"
 msgstr "Ваши запросы подтверждены"
 
-#: ../../mod/settings.php:1178
+#: mod/settings.php:1270
 msgid "Someone writes on your profile wall"
 msgstr "Кто-то пишет на стене вашего профиля"
 
-#: ../../mod/settings.php:1179
+#: mod/settings.php:1271
 msgid "Someone writes a followup comment"
 msgstr "Кто-то пишет последующий комментарий"
 
-#: ../../mod/settings.php:1180
+#: mod/settings.php:1272
 msgid "You receive a private message"
 msgstr "Вы получаете личное сообщение"
 
-#: ../../mod/settings.php:1181
+#: mod/settings.php:1273
 msgid "You receive a friend suggestion"
 msgstr "Вы полулили предложение о добавлении в друзья"
 
-#: ../../mod/settings.php:1182
+#: mod/settings.php:1274
 msgid "You are tagged in a post"
 msgstr "Вы отмечены в посте"
 
-#: ../../mod/settings.php:1183
+#: mod/settings.php:1275
 msgid "You are poked/prodded/etc. in a post"
 msgstr ""
 
-#: ../../mod/settings.php:1185
+#: mod/settings.php:1277
+msgid "Activate desktop notifications"
+msgstr ""
+
+#: mod/settings.php:1277
+msgid "Show desktop popup on new notifications"
+msgstr ""
+
+#: mod/settings.php:1279
 msgid "Text-only notification emails"
 msgstr ""
 
-#: ../../mod/settings.php:1187
+#: mod/settings.php:1281
 msgid "Send text only notification emails, without the html part"
 msgstr ""
 
-#: ../../mod/settings.php:1189
+#: mod/settings.php:1283
 msgid "Advanced Account/Page Type Settings"
-msgstr "РаÑ\81Ñ\88иÑ\80еннÑ\8bе Ð½Ð°Ñ\81Ñ\82Ñ\80ойки Ñ\82ипа Ð°ÐºÐºÐ°Ñ\83нÑ\82а/Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b"
+msgstr "РаÑ\81Ñ\88иÑ\80еннÑ\8bе Ð½Ð°Ñ\81Ñ\82Ñ\80ойки Ñ\83Ñ\87Ñ\91Ñ\82ной Ð·Ð°Ð¿Ð¸Ñ\81и"
 
-#: ../../mod/settings.php:1190
+#: mod/settings.php:1284
 msgid "Change the behaviour of this account for special situations"
 msgstr "Измените поведение этого аккаунта в специальных ситуациях"
 
-#: ../../mod/settings.php:1193
+#: mod/settings.php:1287
 msgid "Relocate"
-msgstr "Ð\9fеÑ\80емеÑ\81Ñ\82иÑ\82Ñ\8c"
+msgstr "Ð\9fеÑ\80емеÑ\89ение"
 
-#: ../../mod/settings.php:1194
+#: mod/settings.php:1288
 msgid ""
 "If you have moved this profile from another server, and some of your "
 "contacts don't receive your updates, try pushing this button."
-msgstr "Если вы переместили эту анкету с другого сервера, и некоторые из ваших контактов не получили ваши обновления, попробуйте нажать эту кнопку."
+msgstr ""
+"Если вы переместили эту анкету с другого сервера, и некоторые из ваших "
+"контактов не получили ваши обновления, попробуйте нажать эту кнопку."
 
-#: ../../mod/settings.php:1195
+#: mod/settings.php:1289
 msgid "Resend relocate message to contacts"
 msgstr "Отправить перемещённые сообщения контактам"
 
-#: ../../mod/dfrn_request.php:95
+#: mod/dfrn_request.php:96
 msgid "This introduction has already been accepted."
 msgstr "Этот запрос был уже принят."
 
-#: ../../mod/dfrn_request.php:120 ../../mod/dfrn_request.php:518
+#: mod/dfrn_request.php:119 mod/dfrn_request.php:516
 msgid "Profile location is not valid or does not contain profile information."
-msgstr "Местоположение профиля является недопустимым или не содержит информацию о профиле."
+msgstr ""
+"Местоположение профиля является недопустимым или не содержит информацию о "
+"профиле."
 
-#: ../../mod/dfrn_request.php:125 ../../mod/dfrn_request.php:523
+#: mod/dfrn_request.php:124 mod/dfrn_request.php:521
 msgid "Warning: profile location has no identifiable owner name."
-msgstr "Внимание: местоположение профиля не имеет идентифицируемого имени владельца."
+msgstr ""
+"Внимание: местоположение профиля не имеет идентифицируемого имени владельца."
 
-#: ../../mod/dfrn_request.php:127 ../../mod/dfrn_request.php:525
+#: mod/dfrn_request.php:126 mod/dfrn_request.php:523
 msgid "Warning: profile location has no profile photo."
 msgstr "Внимание: местоположение профиля не имеет еще фотографии профиля."
 
-#: ../../mod/dfrn_request.php:130 ../../mod/dfrn_request.php:528
+#: mod/dfrn_request.php:129 mod/dfrn_request.php:526
 #, php-format
 msgid "%d required parameter was not found at the given location"
 msgid_plural "%d required parameters were not found at the given location"
 msgstr[0] "%d требуемый параметр не был найден в заданном месте"
 msgstr[1] "%d требуемых параметров не были найдены в заданном месте"
 msgstr[2] "%d требуемых параметров не были найдены в заданном месте"
+msgstr[3] "%d требуемых параметров не были найдены в заданном месте"
 
-#: ../../mod/dfrn_request.php:172
+#: mod/dfrn_request.php:172
 msgid "Introduction complete."
 msgstr "Запрос создан."
 
-#: ../../mod/dfrn_request.php:214
+#: mod/dfrn_request.php:214
 msgid "Unrecoverable protocol error."
 msgstr "Неисправимая ошибка протокола."
 
-#: ../../mod/dfrn_request.php:242
+#: mod/dfrn_request.php:242
 msgid "Profile unavailable."
 msgstr "Профиль недоступен."
 
-#: ../../mod/dfrn_request.php:267
+#: mod/dfrn_request.php:267
 #, php-format
 msgid "%s has received too many connection requests today."
 msgstr "К %s пришло сегодня слишком много запросов на подключение."
 
-#: ../../mod/dfrn_request.php:268
+#: mod/dfrn_request.php:268
 msgid "Spam protection measures have been invoked."
 msgstr "Были применены меры защиты от спама."
 
-#: ../../mod/dfrn_request.php:269
+#: mod/dfrn_request.php:269
 msgid "Friends are advised to please try again in 24 hours."
 msgstr "Друзья советуют попробовать еще раз в ближайшие 24 часа."
 
-#: ../../mod/dfrn_request.php:331
+#: mod/dfrn_request.php:331
 msgid "Invalid locator"
 msgstr "Недопустимый локатор"
 
-#: ../../mod/dfrn_request.php:340
+#: mod/dfrn_request.php:340
 msgid "Invalid email address."
 msgstr "Неверный адрес электронной почты."
 
-#: ../../mod/dfrn_request.php:367
+#: mod/dfrn_request.php:367
 msgid "This account has not been configured for email. Request failed."
 msgstr "Этот аккаунт не настроен для электронной почты. Запрос не удался."
 
-#: ../../mod/dfrn_request.php:463
-msgid "Unable to resolve your name at the provided location."
-msgstr "Не удается установить ваше имя на предложенном местоположении."
-
-#: ../../mod/dfrn_request.php:476
+#: mod/dfrn_request.php:474
 msgid "You have already introduced yourself here."
 msgstr "Вы уже ввели информацию о себе здесь."
 
-#: ../../mod/dfrn_request.php:480
+#: mod/dfrn_request.php:478
 #, php-format
 msgid "Apparently you are already friends with %s."
 msgstr "Похоже, что вы уже друзья с %s."
 
-#: ../../mod/dfrn_request.php:501
+#: mod/dfrn_request.php:499
 msgid "Invalid profile URL."
 msgstr "Неверный URL профиля."
 
-#: ../../mod/dfrn_request.php:507 ../../include/follow.php:27
+#: mod/dfrn_request.php:505 include/follow.php:72
 msgid "Disallowed profile URL."
 msgstr "Запрещенный URL профиля."
 
-#: ../../mod/dfrn_request.php:597
+#: mod/dfrn_request.php:596
 msgid "Your introduction has been sent."
 msgstr "Ваш запрос отправлен."
 
-#: ../../mod/dfrn_request.php:650
+#: mod/dfrn_request.php:636
+msgid ""
+"Remote subscription can't be done for your network. Please subscribe "
+"directly on your system."
+msgstr ""
+
+#: mod/dfrn_request.php:659
 msgid "Please login to confirm introduction."
 msgstr "Для подтверждения запроса войдите пожалуйста с паролем."
 
-#: ../../mod/dfrn_request.php:660
+#: mod/dfrn_request.php:669
 msgid ""
-"Incorrect identity currently logged in. Please login to "
-"<strong>this</strong> profile."
-msgstr "Неверно идентифицирован вход. Пожалуйста, войдите в <strong>этот</strong> профиль."
+"Incorrect identity currently logged in. Please login to <strong>this</"
+"strong> profile."
+msgstr ""
+"Неверно идентифицирован вход. Пожалуйста, войдите в <strong>этот</strong> "
+"профиль."
+
+#: mod/dfrn_request.php:683 mod/dfrn_request.php:700
+msgid "Confirm"
+msgstr "Подтвердить"
 
-#: ../../mod/dfrn_request.php:671
+#: mod/dfrn_request.php:695
 msgid "Hide this contact"
 msgstr "Скрыть этот контакт"
 
-#: ../../mod/dfrn_request.php:674
+#: mod/dfrn_request.php:698
 #, php-format
 msgid "Welcome home %s."
 msgstr "Добро пожаловать домой, %s!"
 
-#: ../../mod/dfrn_request.php:675
+#: mod/dfrn_request.php:699
 #, php-format
 msgid "Please confirm your introduction/connection request to %s."
-msgstr "Пожалуйста, подтвердите краткую информацию / запрос на подключение к %s."
-
-#: ../../mod/dfrn_request.php:676
-msgid "Confirm"
-msgstr "Подтвердить"
+msgstr ""
+"Пожалуйста, подтвердите краткую информацию / запрос на подключение к %s."
 
-#: ../../mod/dfrn_request.php:804
+#: mod/dfrn_request.php:828
 msgid ""
 "Please enter your 'Identity Address' from one of the following supported "
 "communications networks:"
-msgstr "Пожалуйста, введите ваш 'идентификационный адрес' одной из следующих поддерживаемых социальных сетей:"
+msgstr ""
+"Пожалуйста, введите ваш 'идентификационный адрес' одной из следующих "
+"поддерживаемых социальных сетей:"
 
-#: ../../mod/dfrn_request.php:824
+#: mod/dfrn_request.php:849
+#, php-format
 msgid ""
-"If you are not yet a member of the free social web, <a "
-"href=\"http://dir.friendica.com/siteinfo\">follow this link to find a public"
-" Friendica site and join us today</a>."
-msgstr "Если вы еще не являетесь членом свободной социальной сети, перейдите по <a href=\"http://dir.friendica.com/siteinfo\"> этой ссылке, чтобы найти публичный сервер Friendica и присоединиться к нам сейчас </a>."
+"If you are not yet a member of the free social web, <a href=\"%s/siteinfo"
+"\">follow this link to find a public Friendica site and join us today</a>."
+msgstr ""
 
-#: ../../mod/dfrn_request.php:827
+#: mod/dfrn_request.php:854
 msgid "Friend/Connection Request"
 msgstr "Запрос в друзья / на подключение"
 
-#: ../../mod/dfrn_request.php:828
+#: mod/dfrn_request.php:855
 msgid ""
 "Examples: jojo@demo.friendica.com, http://demo.friendica.com/profile/jojo, "
 "testuser@identi.ca"
-msgstr "Примеры: jojo@demo.friendica.com, http://demo.friendica.com/profile/jojo, testuser@identi.ca"
+msgstr ""
+"Примеры: jojo@demo.friendica.com, http://demo.friendica.com/profile/jojo, "
+"testuser@identi.ca"
 
-#: ../../mod/dfrn_request.php:829
-msgid "Please answer the following:"
-msgstr "Пожалуйста, ответьте следующее:"
+#: mod/dfrn_request.php:863 include/contact_selectors.php:76
+msgid "Friendica"
+msgstr "Friendica"
 
-#: ../../mod/dfrn_request.php:830
-#, php-format
-msgid "Does %s know you?"
-msgstr "%s знает вас?"
-
-#: ../../mod/dfrn_request.php:834
-msgid "Add a personal note:"
-msgstr "Добавить личную заметку:"
-
-#: ../../mod/dfrn_request.php:836 ../../include/contact_selectors.php:76
-msgid "Friendica"
-msgstr "Friendica"
-
-#: ../../mod/dfrn_request.php:837
+#: mod/dfrn_request.php:864
 msgid "StatusNet/Federated Social Web"
 msgstr "StatusNet / Federated Social Web"
 
-#: ../../mod/dfrn_request.php:839
+#: mod/dfrn_request.php:866
 #, php-format
 msgid ""
-" - please do not use this form.  Instead, enter %s into your Diaspora search"
-" bar."
-msgstr "Участники сети Diaspora: пожалуйста, не пользуйтесь этой формой. Вместо этого введите  %s в строке поиска Diaspora"
-
-#: ../../mod/dfrn_request.php:840
-msgid "Your Identity Address:"
-msgstr "Ваш идентификационный адрес:"
-
-#: ../../mod/dfrn_request.php:843
-msgid "Submit Request"
-msgstr "Отправить запрос"
+" - please do not use this form.  Instead, enter %s into your Diaspora search "
+"bar."
+msgstr ""
+"Участники сети Diaspora: пожалуйста, не пользуйтесь этой формой. Вместо "
+"этого введите  %s в строке поиска Diaspora"
 
-#: ../../mod/register.php:90
+#: mod/register.php:92
 msgid ""
 "Registration successful. Please check your email for further instructions."
-msgstr "Регистрация успешна. Пожалуйста, проверьте свою электронную почту для получения дальнейших инструкций."
+msgstr ""
+"Регистрация успешна. Пожалуйста, проверьте свою электронную почту для "
+"получения дальнейших инструкций."
 
-#: ../../mod/register.php:96
+#: mod/register.php:97
 #, php-format
 msgid ""
 "Failed to send email message. Here your accout details:<br> login: %s<br> "
 "password: %s<br><br>You can change your password after login."
 msgstr ""
 
-#: ../../mod/register.php:105
+#: mod/register.php:104
+msgid "Registration successful."
+msgstr ""
+
+#: mod/register.php:110
 msgid "Your registration can not be processed."
 msgstr "Ваша регистрация не может быть обработана."
 
-#: ../../mod/register.php:148
+#: mod/register.php:153
 msgid "Your registration is pending approval by the site owner."
 msgstr "Ваша регистрация в ожидании одобрения владельцем сайта."
 
-#: ../../mod/register.php:186 ../../mod/uimport.php:50
+#: mod/register.php:191 mod/uimport.php:50
 msgid ""
 "This site has exceeded the number of allowed daily account registrations. "
 "Please try again tomorrow."
-msgstr "Этот сайт превысил допустимое количество ежедневных регистраций. Пожалуйста, повторите попытку завтра."
+msgstr ""
+"Этот сайт превысил допустимое количество ежедневных регистраций. Пожалуйста, "
+"повторите попытку завтра."
 
-#: ../../mod/register.php:214
+#: mod/register.php:219
 msgid ""
 "You may (optionally) fill in this form via OpenID by supplying your OpenID "
 "and clicking 'Register'."
-msgstr "Вы можете (по желанию), заполнить эту форму с помощью OpenID, поддерживая ваш OpenID и нажав клавишу \"Регистрация\"."
+msgstr ""
+"Вы можете (по желанию), заполнить эту форму с помощью OpenID, поддерживая "
+"ваш OpenID и нажав клавишу \"Регистрация\"."
 
-#: ../../mod/register.php:215
+#: mod/register.php:220
 msgid ""
 "If you are not familiar with OpenID, please leave that field blank and fill "
 "in the rest of the items."
-msgstr "Если вы не знакомы с OpenID, пожалуйста, оставьте это поле пустым и заполните остальные элементы."
+msgstr ""
+"Если вы не знакомы с OpenID, пожалуйста, оставьте это поле пустым и "
+"заполните остальные элементы."
 
-#: ../../mod/register.php:216
+#: mod/register.php:221
 msgid "Your OpenID (optional): "
 msgstr "Ваш OpenID (необязательно):"
 
-#: ../../mod/register.php:230
+#: mod/register.php:235
 msgid "Include your profile in member directory?"
 msgstr "Включить ваш профиль в каталог участников?"
 
-#: ../../mod/register.php:251
+#: mod/register.php:259
 msgid "Membership on this site is by invitation only."
 msgstr "Членство на сайте только по приглашению."
 
-#: ../../mod/register.php:252
+#: mod/register.php:260
 msgid "Your invitation ID: "
 msgstr "ID вашего приглашения:"
 
-#: ../../mod/register.php:263
-msgid "Your Full Name (e.g. Joe Smith): "
-msgstr "Ваше полное имя (например, Joe Smith): "
+#: mod/register.php:271
+msgid "Your Full Name (e.g. Joe Smith, real or real-looking): "
+msgstr ""
 
-#: ../../mod/register.php:264
+#: mod/register.php:272
 msgid "Your Email Address: "
 msgstr "Ваш адрес электронной почты: "
 
-#: ../../mod/register.php:265
+#: mod/register.php:274
+msgid "Leave empty for an auto generated password."
+msgstr ""
+
+#: mod/register.php:276
 msgid ""
 "Choose a profile nickname. This must begin with a text character. Your "
-"profile address on this site will then be "
-"'<strong>nickname@$sitename</strong>'."
-msgstr "Выбор псевдонима профиля. Он должен начинаться с буквы. Адрес вашего профиля на данном сайте будет в этом случае '<strong>nickname@$sitename</strong>'."
+"profile address on this site will then be '<strong>nickname@$sitename</"
+"strong>'."
+msgstr ""
+"Выбор псевдонима профиля. Он должен начинаться с буквы. Адрес вашего профиля "
+"на данном сайте будет в этом случае '<strong>nickname@$sitename</strong>'."
 
-#: ../../mod/register.php:266
+#: mod/register.php:277
 msgid "Choose a nickname: "
 msgstr "Выберите псевдоним: "
 
-#: ../../mod/register.php:269 ../../boot.php:1241 ../../include/nav.php:109
+#: mod/register.php:280 boot.php:1405 include/nav.php:108
 msgid "Register"
 msgstr "Регистрация"
 
-#: ../../mod/register.php:275 ../../mod/uimport.php:64
+#: mod/register.php:286 mod/uimport.php:64
 msgid "Import"
 msgstr "Импорт"
 
-#: ../../mod/register.php:276
+#: mod/register.php:287
 msgid "Import your profile to this friendica instance"
 msgstr "Импорт своего профиля в этот экземпляр friendica"
 
-#: ../../mod/maintenance.php:5
+#: mod/maintenance.php:5
 msgid "System down for maintenance"
 msgstr "Система закрыта на техническое обслуживание"
 
-#: ../../mod/search.php:99 ../../include/text.php:953
-#: ../../include/text.php:954 ../../include/nav.php:119
-msgid "Search"
-msgstr "Поиск"
-
-#: ../../mod/directory.php:51 ../../view/theme/diabook/theme.php:525
-msgid "Global Directory"
-msgstr "Глобальный каталог"
+#: mod/search.php:100
+msgid "Only logged in users are permitted to perform a search."
+msgstr ""
 
-#: ../../mod/directory.php:59
-msgid "Find on this site"
-msgstr "Найти на этом сайте"
+#: mod/search.php:124
+msgid "Too Many Requests"
+msgstr ""
 
-#: ../../mod/directory.php:62
-msgid "Site Directory"
-msgstr "Каталог сайта"
+#: mod/search.php:125
+msgid "Only one search per minute is permitted for not logged in users."
+msgstr ""
 
-#: ../../mod/directory.php:113 ../../mod/profiles.php:750
-msgid "Age: "
-msgstr "Ð\92озÑ\80аÑ\81Ñ\82"
+#: mod/search.php:136 include/text.php:1003 include/nav.php:118
+msgid "Search"
+msgstr "Ð\9fоиÑ\81к"
 
-#: ../../mod/directory.php:116
-msgid "Gender: "
-msgstr "Пол: "
+#: mod/search.php:234
+#, php-format
+msgid "Items tagged with: %s"
+msgstr ""
 
-#: ../../mod/directory.php:138 ../../boot.php:1650
-#: ../../include/profile_advanced.php:17
-msgid "Gender:"
-msgstr "Пол:"
+#: mod/search.php:236
+#, php-format
+msgid "Search results for: %s"
+msgstr ""
 
-#: ../../mod/directory.php:140 ../../boot.php:1653
-#: ../../include/profile_advanced.php:37
+#: mod/directory.php:149 include/identity.php:313 include/identity.php:610
 msgid "Status:"
 msgstr "Статус:"
 
-#: ../../mod/directory.php:142 ../../boot.php:1655
-#: ../../include/profile_advanced.php:48
+#: mod/directory.php:151 include/identity.php:315 include/identity.php:621
 msgid "Homepage:"
 msgstr "Домашняя страничка:"
 
-#: ../../mod/directory.php:144 ../../boot.php:1657
-#: ../../include/profile_advanced.php:58
-msgid "About:"
-msgstr "О себе:"
+#: mod/directory.php:203 view/theme/diabook/theme.php:525
+#: view/theme/vier/theme.php:205
+msgid "Global Directory"
+msgstr "Глобальный каталог"
+
+#: mod/directory.php:205
+msgid "Find on this site"
+msgstr "Найти на этом сайте"
+
+#: mod/directory.php:207
+msgid "Finding:"
+msgstr ""
+
+#: mod/directory.php:209
+msgid "Site Directory"
+msgstr "Каталог сайта"
 
-#: ../../mod/directory.php:189
+#: mod/directory.php:216
 msgid "No entries (some entries may be hidden)."
 msgstr "Нет записей (некоторые записи могут быть скрыты)."
 
-#: ../../mod/delegate.php:101
+#: mod/delegate.php:101
 msgid "No potential page delegates located."
 msgstr ""
 
-#: ../../mod/delegate.php:130 ../../include/nav.php:170
+#: mod/delegate.php:130 include/nav.php:180
 msgid "Delegate Page Management"
 msgstr "Делегировать управление страницей"
 
-#: ../../mod/delegate.php:132
+#: mod/delegate.php:132
 msgid ""
 "Delegates are able to manage all aspects of this account/page except for "
 "basic account settings. Please do not delegate your personal account to "
 "anybody that you do not trust completely."
-msgstr "Доверенные лица могут управлять всеми аспектами этого аккаунта/страницы, за исключением основных настроек аккаунта. Пожалуйста, не предоставляйте доступ в личный кабинет тому, кому вы не полностью доверяете."
+msgstr ""
+"Доверенные лица могут управлять всеми аспектами этого аккаунта/страницы, за "
+"исключением основных настроек аккаунта. Пожалуйста, не предоставляйте доступ "
+"в личный кабинет тому, кому вы не полностью доверяете."
 
-#: ../../mod/delegate.php:133
+#: mod/delegate.php:133
 msgid "Existing Page Managers"
 msgstr "Существующие менеджеры страницы"
 
-#: ../../mod/delegate.php:135
+#: mod/delegate.php:135
 msgid "Existing Page Delegates"
 msgstr "Существующие уполномоченные страницы"
 
-#: ../../mod/delegate.php:137
+#: mod/delegate.php:137
 msgid "Potential Delegates"
 msgstr "Возможные доверенные лица"
 
-#: ../../mod/delegate.php:140
+#: mod/delegate.php:140
 msgid "Add"
 msgstr "Добавить"
 
-#: ../../mod/delegate.php:141
+#: mod/delegate.php:141
 msgid "No entries."
 msgstr "Нет записей."
 
-#: ../../mod/common.php:42
-msgid "Common Friends"
-msgstr "Общие друзья"
-
-#: ../../mod/common.php:78
+#: mod/common.php:86
 msgid "No contacts in common."
 msgstr "Нет общих контактов."
 
-#: ../../mod/uexport.php:77
+#: mod/uexport.php:77
 msgid "Export account"
 msgstr "Экспорт аккаунта"
 
-#: ../../mod/uexport.php:77
+#: mod/uexport.php:77
 msgid ""
 "Export your account info and contacts. Use this to make a backup of your "
 "account and/or to move it to another server."
-msgstr "Экспорт ваших регистрационных данные и контактов. Используйте, чтобы создать резервную копию вашего аккаунта и/или переместить его на другой сервер."
+msgstr ""
+"Экспорт ваших регистрационных данные и контактов. Используйте, чтобы создать "
+"резервную копию вашего аккаунта и/или переместить его на другой сервер."
 
-#: ../../mod/uexport.php:78
+#: mod/uexport.php:78
 msgid "Export all"
 msgstr "Экспорт всего"
 
-#: ../../mod/uexport.php:78
+#: mod/uexport.php:78
 msgid ""
 "Export your accout info, contacts and all your items as json. Could be a "
 "very big file, and could take a lot of time. Use this to make a full backup "
 "of your account (photos are not exported)"
-msgstr "Экспорт информации вашего аккаунта, контактов и всех ваших пунктов, как JSON. Может получиться очень большой файл и это может занять много времени. Используйте, чтобы создать полную резервную копию вашего аккаунта (фото не экспортируются)"
+msgstr ""
+"Экспорт информации вашего аккаунта, контактов и всех ваших пунктов, как "
+"JSON. Может получиться очень большой файл и это может занять много времени. "
+"Используйте, чтобы создать полную резервную копию вашего аккаунта (фото не "
+"экспортируются)"
 
-#: ../../mod/mood.php:62 ../../include/conversation.php:227
+#: mod/mood.php:62 include/conversation.php:239
 #, php-format
 msgid "%1$s is currently %2$s"
 msgstr ""
 
-#: ../../mod/mood.php:133
+#: mod/mood.php:133
 msgid "Mood"
 msgstr "Настроение"
 
-#: ../../mod/mood.php:134
+#: mod/mood.php:134
 msgid "Set your current mood and tell your friends"
 msgstr "Напишите о вашем настроении и расскажите своим друзьям"
 
-#: ../../mod/suggest.php:27
+#: mod/suggest.php:27
 msgid "Do you really want to delete this suggestion?"
 msgstr "Вы действительно хотите удалить это предложение?"
 
-#: ../../mod/suggest.php:68 ../../include/contact_widgets.php:35
-#: ../../view/theme/diabook/theme.php:527
-msgid "Friend Suggestions"
-msgstr "Предложения друзей"
-
-#: ../../mod/suggest.php:74
+#: mod/suggest.php:71
 msgid ""
 "No suggestions available. If this is a new site, please try again in 24 "
 "hours."
-msgstr "Нет предложений. Если это новый сайт, пожалуйста, попробуйте снова через 24 часа."
+msgstr ""
+"Нет предложений. Если это новый сайт, пожалуйста, попробуйте снова через 24 "
+"часа."
 
-#: ../../mod/suggest.php:92
+#: mod/suggest.php:83 mod/suggest.php:101
 msgid "Ignore/Hide"
 msgstr "Проигнорировать/Скрыть"
 
-#: ../../mod/profiles.php:37
+#: mod/suggest.php:111 include/contact_widgets.php:35
+#: view/theme/diabook/theme.php:527 view/theme/vier/theme.php:207
+msgid "Friend Suggestions"
+msgstr "Предложения друзей"
+
+#: mod/profiles.php:37
 msgid "Profile deleted."
 msgstr "Профиль удален."
 
-#: ../../mod/profiles.php:55 ../../mod/profiles.php:89
+#: mod/profiles.php:55 mod/profiles.php:89
 msgid "Profile-"
 msgstr "Профиль-"
 
-#: ../../mod/profiles.php:74 ../../mod/profiles.php:117
+#: mod/profiles.php:74 mod/profiles.php:117
 msgid "New profile created."
 msgstr "Новый профиль создан."
 
-#: ../../mod/profiles.php:95
+#: mod/profiles.php:95
 msgid "Profile unavailable to clone."
 msgstr "Профиль недоступен для клонирования."
 
-#: ../../mod/profiles.php:189
+#: mod/profiles.php:189
 msgid "Profile Name is required."
 msgstr "Необходимо имя профиля."
 
-#: ../../mod/profiles.php:340
+#: mod/profiles.php:336
 msgid "Marital Status"
 msgstr "Семейное положение"
 
-#: ../../mod/profiles.php:344
+#: mod/profiles.php:340
 msgid "Romantic Partner"
 msgstr "Любимый человек"
 
-#: ../../mod/profiles.php:348
+#: mod/profiles.php:344 mod/photos.php:1647 include/conversation.php:508
 msgid "Likes"
 msgstr "Лайки"
 
-#: ../../mod/profiles.php:352
+#: mod/profiles.php:348 mod/photos.php:1647 include/conversation.php:508
 msgid "Dislikes"
 msgstr "Дизлайк"
 
-#: ../../mod/profiles.php:356
+#: mod/profiles.php:352
 msgid "Work/Employment"
 msgstr "Работа/Занятость"
 
-#: ../../mod/profiles.php:359
+#: mod/profiles.php:355
 msgid "Religion"
 msgstr "Религия"
 
-#: ../../mod/profiles.php:363
+#: mod/profiles.php:359
 msgid "Political Views"
 msgstr "Политические взгляды"
 
-#: ../../mod/profiles.php:367
+#: mod/profiles.php:363
 msgid "Gender"
 msgstr "Пол"
 
-#: ../../mod/profiles.php:371
+#: mod/profiles.php:367
 msgid "Sexual Preference"
 msgstr "Сексуальные предпочтения"
 
-#: ../../mod/profiles.php:375
+#: mod/profiles.php:371
 msgid "Homepage"
 msgstr "Домашняя страница"
 
-#: ../../mod/profiles.php:379 ../../mod/profiles.php:698
+#: mod/profiles.php:375 mod/profiles.php:708
 msgid "Interests"
 msgstr "Хобби / Интересы"
 
-#: ../../mod/profiles.php:383
+#: mod/profiles.php:379
 msgid "Address"
 msgstr "Адрес"
 
-#: ../../mod/profiles.php:390 ../../mod/profiles.php:694
+#: mod/profiles.php:386 mod/profiles.php:704
 msgid "Location"
 msgstr "Местонахождение"
 
-#: ../../mod/profiles.php:473
+#: mod/profiles.php:469
 msgid "Profile updated."
 msgstr "Профиль обновлен."
 
-#: ../../mod/profiles.php:568
+#: mod/profiles.php:565
 msgid " and "
 msgstr "и"
 
-#: ../../mod/profiles.php:576
+#: mod/profiles.php:573
 msgid "public profile"
 msgstr "публичный профиль"
 
-#: ../../mod/profiles.php:579
+#: mod/profiles.php:576
 #, php-format
 msgid "%1$s changed %2$s to &ldquo;%3$s&rdquo;"
 msgstr "%1$s изменились с %2$s на &ldquo;%3$s&rdquo;"
 
-#: ../../mod/profiles.php:580
+#: mod/profiles.php:577
 #, php-format
 msgid " - Visit %1$s's %2$s"
 msgstr " - Посетить профиль %1$s [%2$s]"
 
-#: ../../mod/profiles.php:583
+#: mod/profiles.php:580
 #, php-format
 msgid "%1$s has an updated %2$s, changing %3$s."
 msgstr ""
 
-#: ../../mod/profiles.php:658
+#: mod/profiles.php:655
 msgid "Hide contacts and friends:"
 msgstr ""
 
-#: ../../mod/profiles.php:663
+#: mod/profiles.php:660
 msgid "Hide your contact/friend list from viewers of this profile?"
 msgstr "Скрывать ваш список контактов / друзей от посетителей этого профиля?"
 
-#: ../../mod/profiles.php:685
+#: mod/profiles.php:684
+msgid "Show more profile fields:"
+msgstr ""
+
+#: mod/profiles.php:695
 msgid "Edit Profile Details"
 msgstr "Редактировать детали профиля"
 
-#: ../../mod/profiles.php:687
+#: mod/profiles.php:697
 msgid "Change Profile Photo"
 msgstr "Изменить фото профиля"
 
-#: ../../mod/profiles.php:688
+#: mod/profiles.php:698
 msgid "View this profile"
 msgstr "Просмотреть этот профиль"
 
-#: ../../mod/profiles.php:689
+#: mod/profiles.php:699
 msgid "Create a new profile using these settings"
 msgstr "Создать новый профиль, используя эти настройки"
 
-#: ../../mod/profiles.php:690
+#: mod/profiles.php:700
 msgid "Clone this profile"
 msgstr "Клонировать этот профиль"
 
-#: ../../mod/profiles.php:691
+#: mod/profiles.php:701
 msgid "Delete this profile"
 msgstr "Удалить этот профиль"
 
-#: ../../mod/profiles.php:692
+#: mod/profiles.php:702
 msgid "Basic information"
 msgstr ""
 
-#: ../../mod/profiles.php:693
+#: mod/profiles.php:703
 msgid "Profile picture"
 msgstr ""
 
-#: ../../mod/profiles.php:695
+#: mod/profiles.php:705
 msgid "Preferences"
 msgstr ""
 
-#: ../../mod/profiles.php:696
+#: mod/profiles.php:706
 msgid "Status information"
 msgstr ""
 
-#: ../../mod/profiles.php:697
+#: mod/profiles.php:707
 msgid "Additional information"
 msgstr ""
 
-#: ../../mod/profiles.php:700
+#: mod/profiles.php:710
 msgid "Profile Name:"
 msgstr "Имя профиля:"
 
-#: ../../mod/profiles.php:701
+#: mod/profiles.php:711
 msgid "Your Full Name:"
 msgstr "Ваше полное имя:"
 
-#: ../../mod/profiles.php:702
+#: mod/profiles.php:712
 msgid "Title/Description:"
 msgstr "Заголовок / Описание:"
 
-#: ../../mod/profiles.php:703
+#: mod/profiles.php:713
 msgid "Your Gender:"
 msgstr "Ваш пол:"
 
-#: ../../mod/profiles.php:704
-#, php-format
-msgid "Birthday (%s):"
-msgstr "День рождения (%s):"
+#: mod/profiles.php:714
+msgid "Birthday :"
+msgstr ""
 
-#: ../../mod/profiles.php:705
+#: mod/profiles.php:715
 msgid "Street Address:"
 msgstr "Адрес:"
 
-#: ../../mod/profiles.php:706
+#: mod/profiles.php:716
 msgid "Locality/City:"
 msgstr "Город / Населенный пункт:"
 
-#: ../../mod/profiles.php:707
+#: mod/profiles.php:717
 msgid "Postal/Zip Code:"
 msgstr "Почтовый индекс:"
 
-#: ../../mod/profiles.php:708
+#: mod/profiles.php:718
 msgid "Country:"
 msgstr "Страна:"
 
-#: ../../mod/profiles.php:709
+#: mod/profiles.php:719
 msgid "Region/State:"
 msgstr "Район / Область:"
 
-#: ../../mod/profiles.php:710
+#: mod/profiles.php:720
 msgid "<span class=\"heart\">&hearts;</span> Marital Status:"
 msgstr "<span class=\"heart\">&hearts;</span> Семейное положение:"
 
-#: ../../mod/profiles.php:711
+#: mod/profiles.php:721
 msgid "Who: (if applicable)"
 msgstr "Кто: (если требуется)"
 
-#: ../../mod/profiles.php:712
+#: mod/profiles.php:722
 msgid "Examples: cathy123, Cathy Williams, cathy@example.com"
 msgstr "Примеры: cathy123, Кэти Уильямс, cathy@example.com"
 
-#: ../../mod/profiles.php:713
+#: mod/profiles.php:723
 msgid "Since [date]:"
 msgstr "С какого времени [дата]:"
 
-#: ../../mod/profiles.php:714 ../../include/profile_advanced.php:46
+#: mod/profiles.php:724 include/identity.php:619
 msgid "Sexual Preference:"
 msgstr "Сексуальные предпочтения:"
 
-#: ../../mod/profiles.php:715
+#: mod/profiles.php:725
 msgid "Homepage URL:"
 msgstr "Адрес домашней странички:"
 
-#: ../../mod/profiles.php:716 ../../include/profile_advanced.php:50
+#: mod/profiles.php:726 include/identity.php:623
 msgid "Hometown:"
 msgstr "Родной город:"
 
-#: ../../mod/profiles.php:717 ../../include/profile_advanced.php:54
+#: mod/profiles.php:727 include/identity.php:627
 msgid "Political Views:"
 msgstr "Политические взгляды:"
 
-#: ../../mod/profiles.php:718
+#: mod/profiles.php:728
 msgid "Religious Views:"
 msgstr "Религиозные взгляды:"
 
-#: ../../mod/profiles.php:719
+#: mod/profiles.php:729
 msgid "Public Keywords:"
 msgstr "Общественные ключевые слова:"
 
-#: ../../mod/profiles.php:720
+#: mod/profiles.php:730
 msgid "Private Keywords:"
 msgstr "Личные ключевые слова:"
 
-#: ../../mod/profiles.php:721 ../../include/profile_advanced.php:62
+#: mod/profiles.php:731 include/identity.php:635
 msgid "Likes:"
 msgstr "Нравится:"
 
-#: ../../mod/profiles.php:722 ../../include/profile_advanced.php:64
+#: mod/profiles.php:732 include/identity.php:637
 msgid "Dislikes:"
 msgstr "Не нравится:"
 
-#: ../../mod/profiles.php:723
+#: mod/profiles.php:733
 msgid "Example: fishing photography software"
 msgstr "Пример: рыбалка фотографии программное обеспечение"
 
-#: ../../mod/profiles.php:724
+#: mod/profiles.php:734
 msgid "(Used for suggesting potential friends, can be seen by others)"
-msgstr "(Используется для предложения потенциальным друзьям, могут увидеть другие)"
+msgstr ""
+"(Используется для предложения потенциальным друзьям, могут увидеть другие)"
 
-#: ../../mod/profiles.php:725
+#: mod/profiles.php:735
 msgid "(Used for searching profiles, never shown to others)"
 msgstr "(Используется для поиска профилей, никогда не показывается другим)"
 
-#: ../../mod/profiles.php:726
+#: mod/profiles.php:736
 msgid "Tell us about yourself..."
 msgstr "Расскажите нам о себе ..."
 
-#: ../../mod/profiles.php:727
+#: mod/profiles.php:737
 msgid "Hobbies/Interests"
 msgstr "Хобби / Интересы"
 
-#: ../../mod/profiles.php:728
+#: mod/profiles.php:738
 msgid "Contact information and Social Networks"
 msgstr "Контактная информация и социальные сети"
 
-#: ../../mod/profiles.php:729
+#: mod/profiles.php:739
 msgid "Musical interests"
 msgstr "Музыкальные интересы"
 
-#: ../../mod/profiles.php:730
+#: mod/profiles.php:740
 msgid "Books, literature"
 msgstr "Книги, литература"
 
-#: ../../mod/profiles.php:731
+#: mod/profiles.php:741
 msgid "Television"
 msgstr "Телевидение"
 
-#: ../../mod/profiles.php:732
+#: mod/profiles.php:742
 msgid "Film/dance/culture/entertainment"
 msgstr "Кино / танцы / культура / развлечения"
 
-#: ../../mod/profiles.php:733
+#: mod/profiles.php:743
 msgid "Love/romance"
 msgstr "Любовь / романтика"
 
-#: ../../mod/profiles.php:734
+#: mod/profiles.php:744
 msgid "Work/employment"
 msgstr "Работа / занятость"
 
-#: ../../mod/profiles.php:735
+#: mod/profiles.php:745
 msgid "School/education"
 msgstr "Школа / образование"
 
-#: ../../mod/profiles.php:740
+#: mod/profiles.php:750
 msgid ""
 "This is your <strong>public</strong> profile.<br />It <strong>may</strong> "
 "be visible to anybody using the internet."
-msgstr "Это ваш <strong>публичный</strong> профиль. <br /> Он <strong>может</strong> быть виден каждому через Интернет."
+msgstr ""
+"Это ваш <strong>публичный</strong> профиль. <br /> Он <strong>может</strong> "
+"быть виден каждому через Интернет."
+
+#: mod/profiles.php:760
+msgid "Age: "
+msgstr "Возраст: "
 
-#: ../../mod/profiles.php:803
+#: mod/profiles.php:813
 msgid "Edit/Manage Profiles"
 msgstr "Редактировать профиль"
 
-#: ../../mod/profiles.php:804 ../../boot.php:1611 ../../boot.php:1637
+#: mod/profiles.php:814 include/identity.php:260 include/identity.php:286
 msgid "Change profile photo"
 msgstr "Изменить фото профиля"
 
-#: ../../mod/profiles.php:805 ../../boot.php:1612
+#: mod/profiles.php:815 include/identity.php:261
 msgid "Create New Profile"
 msgstr "Создать новый профиль"
 
-#: ../../mod/profiles.php:816 ../../boot.php:1622
+#: mod/profiles.php:826 include/identity.php:271
 msgid "Profile Image"
 msgstr "Фото профиля"
 
-#: ../../mod/profiles.php:818 ../../boot.php:1625
+#: mod/profiles.php:828 include/identity.php:274
 msgid "visible to everybody"
 msgstr "видимый всем"
 
-#: ../../mod/profiles.php:819 ../../boot.php:1626
+#: mod/profiles.php:829 include/identity.php:275
 msgid "Edit visibility"
 msgstr "Редактировать видимость"
 
-#: ../../mod/editpost.php:17 ../../mod/editpost.php:27
+#: mod/editpost.php:17 mod/editpost.php:27
 msgid "Item not found"
 msgstr "Элемент не найден"
 
-#: ../../mod/editpost.php:39
+#: mod/editpost.php:40
 msgid "Edit post"
 msgstr "Редактировать сообщение"
 
-#: ../../mod/editpost.php:111 ../../include/conversation.php:1092
+#: mod/editpost.php:111 include/conversation.php:1184
 msgid "upload photo"
 msgstr "загрузить фото"
 
-#: ../../mod/editpost.php:112 ../../include/conversation.php:1093
+#: mod/editpost.php:112 include/conversation.php:1185
 msgid "Attach file"
-msgstr "Ð\9fÑ\80иложить файл"
+msgstr "Ð\9fÑ\80икÑ\80епить файл"
 
-#: ../../mod/editpost.php:113 ../../include/conversation.php:1094
+#: mod/editpost.php:113 include/conversation.php:1186
 msgid "attach file"
 msgstr "приложить файл"
 
-#: ../../mod/editpost.php:115 ../../include/conversation.php:1096
+#: mod/editpost.php:115 include/conversation.php:1188
 msgid "web link"
 msgstr "веб-ссылка"
 
-#: ../../mod/editpost.php:116 ../../include/conversation.php:1097
+#: mod/editpost.php:116 include/conversation.php:1189
 msgid "Insert video link"
 msgstr "Вставить ссылку видео"
 
-#: ../../mod/editpost.php:117 ../../include/conversation.php:1098
+#: mod/editpost.php:117 include/conversation.php:1190
 msgid "video link"
 msgstr "видео-ссылка"
 
-#: ../../mod/editpost.php:118 ../../include/conversation.php:1099
+#: mod/editpost.php:118 include/conversation.php:1191
 msgid "Insert audio link"
 msgstr "Вставить ссылку аудио"
 
-#: ../../mod/editpost.php:119 ../../include/conversation.php:1100
+#: mod/editpost.php:119 include/conversation.php:1192
 msgid "audio link"
 msgstr "аудио-ссылка"
 
-#: ../../mod/editpost.php:120 ../../include/conversation.php:1101
+#: mod/editpost.php:120 include/conversation.php:1193
 msgid "Set your location"
 msgstr "Задать ваше местоположение"
 
-#: ../../mod/editpost.php:121 ../../include/conversation.php:1102
+#: mod/editpost.php:121 include/conversation.php:1194
 msgid "set location"
 msgstr "установить местонахождение"
 
-#: ../../mod/editpost.php:122 ../../include/conversation.php:1103
+#: mod/editpost.php:122 include/conversation.php:1195
 msgid "Clear browser location"
 msgstr "Очистить местонахождение браузера"
 
-#: ../../mod/editpost.php:123 ../../include/conversation.php:1104
+#: mod/editpost.php:123 include/conversation.php:1196
 msgid "clear location"
 msgstr "убрать местонахождение"
 
-#: ../../mod/editpost.php:125 ../../include/conversation.php:1110
+#: mod/editpost.php:125 include/conversation.php:1202
 msgid "Permission settings"
 msgstr "Настройки разрешений"
 
-#: ../../mod/editpost.php:133 ../../include/conversation.php:1119
+#: mod/editpost.php:133 include/acl_selectors.php:344
 msgid "CC: email addresses"
 msgstr "Копии на email адреса"
 
-#: ../../mod/editpost.php:134 ../../include/conversation.php:1120
+#: mod/editpost.php:134 include/conversation.php:1211
 msgid "Public post"
 msgstr "Публичное сообщение"
 
-#: ../../mod/editpost.php:137 ../../include/conversation.php:1106
+#: mod/editpost.php:137 include/conversation.php:1198
 msgid "Set title"
 msgstr "Установить заголовок"
 
-#: ../../mod/editpost.php:139 ../../include/conversation.php:1108
+#: mod/editpost.php:139 include/conversation.php:1200
 msgid "Categories (comma-separated list)"
 msgstr "Категории (список через запятую)"
 
-#: ../../mod/editpost.php:140 ../../include/conversation.php:1122
+#: mod/editpost.php:140 include/acl_selectors.php:345
 msgid "Example: bob@example.com, mary@example.com"
 msgstr "Пример: bob@example.com, mary@example.com"
 
-#: ../../mod/friendica.php:59
+#: mod/friendica.php:70
 msgid "This is Friendica, version"
 msgstr "Это Friendica, версия"
 
-#: ../../mod/friendica.php:60
+#: mod/friendica.php:71
 msgid "running at web location"
 msgstr "работает на веб-узле"
 
-#: ../../mod/friendica.php:62
+#: mod/friendica.php:73
 msgid ""
 "Please visit <a href=\"http://friendica.com\">Friendica.com</a> to learn "
 "more about the Friendica project."
-msgstr "Пожалуйста, посетите сайт <a href=\"http://friendica.com\">Friendica.com</a>, чтобы узнать больше о проекте Friendica."
+msgstr ""
+"Пожалуйста, посетите сайт <a href=\"http://friendica.com\">Friendica.com</"
+"a>, чтобы узнать больше о проекте Friendica."
 
-#: ../../mod/friendica.php:64
+#: mod/friendica.php:75
 msgid "Bug reports and issues: please visit"
 msgstr "Отчет об ошибках и проблемах: пожалуйста, посетите"
 
-#: ../../mod/friendica.php:65
+#: mod/friendica.php:75
+msgid "the bugtracker at github"
+msgstr ""
+
+#: mod/friendica.php:76
 msgid ""
 "Suggestions, praise, donations, etc. - please email \"Info\" at Friendica - "
 "dot com"
-msgstr "Предложения, похвала, пожертвования? Пишите на \"info\" на Friendica - точка com"
+msgstr ""
+"Предложения, похвала, пожертвования? Пишите на \"info\" на Friendica - точка "
+"com"
 
-#: ../../mod/friendica.php:79
+#: mod/friendica.php:90
 msgid "Installed plugins/addons/apps:"
 msgstr "Установленные плагины / добавки / приложения:"
 
-#: ../../mod/friendica.php:92
+#: mod/friendica.php:103
 msgid "No installed plugins/addons/apps"
 msgstr "Нет установленных плагинов / добавок / приложений"
 
-#: ../../mod/api.php:76 ../../mod/api.php:102
+#: mod/api.php:76 mod/api.php:102
 msgid "Authorize application connection"
 msgstr "Разрешить связь с приложением"
 
-#: ../../mod/api.php:77
+#: mod/api.php:77
 msgid "Return to your app and insert this Securty Code:"
 msgstr "Вернитесь в ваше приложение и задайте этот код:"
 
-#: ../../mod/api.php:89
+#: mod/api.php:89
 msgid "Please login to continue."
 msgstr "Пожалуйста, войдите для продолжения."
 
-#: ../../mod/api.php:104
+#: mod/api.php:104
 msgid ""
-"Do you want to authorize this application to access your posts and contacts,"
-" and/or create new posts for you?"
-msgstr "Вы действительно хотите разрешить этому приложению доступ к своим постам и контактам, а также создавать новые записи от вашего имени?"
+"Do you want to authorize this application to access your posts and contacts, "
+"and/or create new posts for you?"
+msgstr ""
+"Вы действительно хотите разрешить этому приложению доступ к своим постам и "
+"контактам, а также создавать новые записи от вашего имени?"
 
-#: ../../mod/lockview.php:31 ../../mod/lockview.php:39
+#: mod/lockview.php:31 mod/lockview.php:39
 msgid "Remote privacy information not available."
 msgstr "Личная информация удаленно недоступна."
 
-#: ../../mod/lockview.php:48
+#: mod/lockview.php:48
 msgid "Visible to:"
 msgstr "Кто может видеть:"
 
-#: ../../mod/notes.php:44 ../../boot.php:2150
+#: mod/notes.php:46 include/identity.php:730
 msgid "Personal Notes"
 msgstr "Личные заметки"
 
-#: ../../mod/localtime.php:12 ../../include/bb2diaspora.php:148
-#: ../../include/event.php:11
+#: mod/localtime.php:12 include/bb2diaspora.php:148 include/event.php:13
 msgid "l F d, Y \\@ g:i A"
 msgstr "l F d, Y \\@ g:i A"
 
-#: ../../mod/localtime.php:24
+#: mod/localtime.php:24
 msgid "Time Conversion"
 msgstr "История общения"
 
-#: ../../mod/localtime.php:26
+#: mod/localtime.php:26
 msgid ""
 "Friendica provides this service for sharing events with other networks and "
 "friends in unknown timezones."
-msgstr "Friendica предоставляет этот сервис для обмена событиями с другими сетями и друзьями, находящимися в неопределённых часовых поясах."
+msgstr ""
+"Friendica предоставляет этот сервис для обмена событиями с другими сетями и "
+"друзьями, находящимися в неопределённых часовых поясах."
 
-#: ../../mod/localtime.php:30
+#: mod/localtime.php:30
 #, php-format
 msgid "UTC time: %s"
 msgstr "UTC время: %s"
 
-#: ../../mod/localtime.php:33
+#: mod/localtime.php:33
 #, php-format
 msgid "Current timezone: %s"
 msgstr "Ваш часовой пояс: %s"
 
-#: ../../mod/localtime.php:36
+#: mod/localtime.php:36
 #, php-format
 msgid "Converted localtime: %s"
 msgstr "Ваше изменённое время: %s"
 
-#: ../../mod/localtime.php:41
+#: mod/localtime.php:41
 msgid "Please select your timezone:"
 msgstr "Выберите пожалуйста ваш часовой пояс:"
 
-#: ../../mod/poke.php:192
+#: mod/poke.php:191
 msgid "Poke/Prod"
 msgstr ""
 
-#: ../../mod/poke.php:193
+#: mod/poke.php:192
 msgid "poke, prod or do other things to somebody"
 msgstr ""
 
-#: ../../mod/poke.php:194
+#: mod/poke.php:193
 msgid "Recipient"
 msgstr "Получатель"
 
-#: ../../mod/poke.php:195
+#: mod/poke.php:194
 msgid "Choose what you wish to do to recipient"
 msgstr "Выберите действия для получателя"
 
-#: ../../mod/poke.php:198
+#: mod/poke.php:197
 msgid "Make this post private"
 msgstr "Сделать эту запись личной"
 
-#: ../../mod/invite.php:27
+#: mod/repair_ostatus.php:14
+msgid "Resubscribing to OStatus contacts"
+msgstr ""
+
+#: mod/repair_ostatus.php:30
+msgid "Error"
+msgstr "Ошибка"
+
+#: mod/invite.php:27
 msgid "Total invitation limit exceeded."
 msgstr "Превышен общий лимит приглашений."
 
-#: ../../mod/invite.php:49
+#: mod/invite.php:49
 #, php-format
 msgid "%s : Not a valid email address."
 msgstr "%s: Неверный адрес электронной почты."
 
-#: ../../mod/invite.php:73
+#: mod/invite.php:73
 msgid "Please join us on Friendica"
 msgstr "Пожалуйста, присоединяйтесь к нам на Friendica"
 
-#: ../../mod/invite.php:84
+#: mod/invite.php:84
 msgid "Invitation limit exceeded. Please contact your site administrator."
-msgstr "Лимит приглашений превышен. Пожалуйста, свяжитесь с администратором сайта."
+msgstr ""
+"Лимит приглашений превышен. Пожалуйста, свяжитесь с администратором сайта."
 
-#: ../../mod/invite.php:89
+#: mod/invite.php:89
 #, php-format
 msgid "%s : Message delivery failed."
 msgstr "%s: Доставка сообщения не удалась."
 
-#: ../../mod/invite.php:93
+#: mod/invite.php:93
 #, php-format
 msgid "%d message sent."
 msgid_plural "%d messages sent."
 msgstr[0] "%d сообщение отправлено."
 msgstr[1] "%d сообщений отправлено."
 msgstr[2] "%d сообщений отправлено."
+msgstr[3] "%d сообщений отправлено."
 
-#: ../../mod/invite.php:112
+#: mod/invite.php:112
 msgid "You have no more invitations available"
 msgstr "У вас нет больше приглашений"
 
-#: ../../mod/invite.php:120
+#: mod/invite.php:120
 #, php-format
 msgid ""
 "Visit %s for a list of public sites that you can join. Friendica members on "
-"other sites can all connect with each other, as well as with members of many"
-" other social networks."
-msgstr "Посетите %s со списком общедоступных сайтов, к которым вы можете присоединиться. Все участники Friendica на других сайтах могут соединиться друг с другом, а также с участниками многих других социальных сетей."
+"other sites can all connect with each other, as well as with members of many "
+"other social networks."
+msgstr ""
+"Посетите %s со списком общедоступных сайтов, к которым вы можете "
+"присоединиться. Все участники Friendica на других сайтах могут соединиться "
+"друг с другом, а также с участниками многих других социальных сетей."
 
-#: ../../mod/invite.php:122
+#: mod/invite.php:122
 #, php-format
 msgid ""
 "To accept this invitation, please visit and register at %s or any other "
 "public Friendica website."
-msgstr "Для одобрения этого приглашения, пожалуйста, посетите и зарегистрируйтесь на %s ,или любом другом публичном сервере Friendica"
+msgstr ""
+"Для одобрения этого приглашения, пожалуйста, посетите и зарегистрируйтесь на "
+"%s ,или любом другом публичном сервере Friendica"
 
-#: ../../mod/invite.php:123
+#: mod/invite.php:123
 #, php-format
 msgid ""
 "Friendica sites all inter-connect to create a huge privacy-enhanced social "
 "web that is owned and controlled by its members. They can also connect with "
 "many traditional social networks. See %s for a list of alternate Friendica "
 "sites you can join."
-msgstr "Сайты Friendica, подключившись между собой, могут создать сеть с повышенной безопасностью, которая принадлежит и управляется её членами. Они также могут подключаться ко многим традиционным социальным сетям. См. %s  со списком альтернативных сайтов Friendica, к которым вы можете присоединиться."
+msgstr ""
+"Сайты Friendica, подключившись между собой, могут создать сеть с повышенной "
+"безопасностью, которая принадлежит и управляется её членами. Они также могут "
+"подключаться ко многим традиционным социальным сетям. См. %s  со списком "
+"альтернативных сайтов Friendica, к которым вы можете присоединиться."
 
-#: ../../mod/invite.php:126
+#: mod/invite.php:126
 msgid ""
-"Our apologies. This system is not currently configured to connect with other"
-" public sites or invite members."
-msgstr "Извините. Эта система в настоящее время не сконфигурирована для соединения с другими общественными сайтами и для приглашения участников."
+"Our apologies. This system is not currently configured to connect with other "
+"public sites or invite members."
+msgstr ""
+"Извините. Эта система в настоящее время не сконфигурирована для соединения с "
+"другими общественными сайтами и для приглашения участников."
 
-#: ../../mod/invite.php:132
+#: mod/invite.php:132
 msgid "Send invitations"
 msgstr "Отправить приглашения"
 
-#: ../../mod/invite.php:133
+#: mod/invite.php:133
 msgid "Enter email addresses, one per line:"
 msgstr "Введите адреса электронной почты, по одному в строке:"
 
-#: ../../mod/invite.php:135
+#: mod/invite.php:135
 msgid ""
 "You are cordially invited to join me and other close friends on Friendica - "
 "and help us to create a better social web."
-msgstr "Приглашаем Вас присоединиться ко мне и другим близким друзьям на Friendica - помочь нам создать лучшую социальную сеть."
+msgstr ""
+"Приглашаем Вас присоединиться ко мне и другим близким друзьям на Friendica - "
+"помочь нам создать лучшую социальную сеть."
 
-#: ../../mod/invite.php:137
+#: mod/invite.php:137
 msgid "You will need to supply this invitation code: $invite_code"
 msgstr "Вам нужно будет предоставить этот код приглашения: $invite_code"
 
-#: ../../mod/invite.php:137
+#: mod/invite.php:137
 msgid ""
 "Once you have registered, please connect with me via my profile page at:"
-msgstr "После того как вы зарегистрировались, пожалуйста, свяжитесь со мной через мою страницу профиля по адресу:"
+msgstr ""
+"После того как вы зарегистрировались, пожалуйста, свяжитесь со мной через "
+"мою страницу профиля по адресу:"
 
-#: ../../mod/invite.php:139
+#: mod/invite.php:139
 msgid ""
 "For more information about the Friendica project and why we feel it is "
 "important, please visit http://friendica.com"
-msgstr "Для получения более подробной информации о проекте Friendica, пожалуйста, посетите http://friendica.com"
+msgstr ""
+"Для получения более подробной информации о проекте Friendica, пожалуйста, "
+"посетите http://friendica.com"
 
-#: ../../mod/photos.php:52 ../../boot.php:2129
+#: mod/photos.php:99 include/identity.php:705
 msgid "Photo Albums"
 msgstr "Фотоальбомы"
 
-#: ../../mod/photos.php:60 ../../mod/photos.php:155 ../../mod/photos.php:1064
-#: ../../mod/photos.php:1187 ../../mod/photos.php:1210
-#: ../../mod/photos.php:1760 ../../mod/photos.php:1772
-#: ../../view/theme/diabook/theme.php:499
-msgid "Contact Photos"
-msgstr "Фотографии контакта"
+#: mod/photos.php:100 mod/photos.php:1899
+msgid "Recent Photos"
+msgstr "Последние фото"
 
-#: ../../mod/photos.php:67 ../../mod/photos.php:1262 ../../mod/photos.php:1819
+#: mod/photos.php:103 mod/photos.php:1320 mod/photos.php:1901
 msgid "Upload New Photos"
 msgstr "Загрузить новые фото"
 
-#: ../../mod/photos.php:144
+#: mod/photos.php:181
 msgid "Contact information unavailable"
 msgstr "Информация о контакте недоступна"
 
-#: ../../mod/photos.php:165
+#: mod/photos.php:202
 msgid "Album not found."
 msgstr "Альбом не найден."
 
-#: ../../mod/photos.php:188 ../../mod/photos.php:200 ../../mod/photos.php:1204
+#: mod/photos.php:232 mod/photos.php:244 mod/photos.php:1262
 msgid "Delete Album"
 msgstr "Удалить альбом"
 
-#: ../../mod/photos.php:198
+#: mod/photos.php:242
 msgid "Do you really want to delete this photo album and all its photos?"
 msgstr "Вы действительно хотите удалить этот альбом и все его фотографии?"
 
-#: ../../mod/photos.php:278 ../../mod/photos.php:289 ../../mod/photos.php:1515
+#: mod/photos.php:322 mod/photos.php:333 mod/photos.php:1580
 msgid "Delete Photo"
 msgstr "Удалить фото"
 
-#: ../../mod/photos.php:287
+#: mod/photos.php:331
 msgid "Do you really want to delete this photo?"
 msgstr "Вы действительно хотите удалить эту фотографию?"
 
-#: ../../mod/photos.php:662
+#: mod/photos.php:706
 #, php-format
 msgid "%1$s was tagged in %2$s by %3$s"
 msgstr "%1$s отмечен/а/ в %2$s by %3$s"
 
-#: ../../mod/photos.php:662
+#: mod/photos.php:706
 msgid "a photo"
 msgstr "фото"
 
-#: ../../mod/photos.php:767
-msgid "Image exceeds size limit of "
-msgstr "Размер фото превышает лимит "
-
-#: ../../mod/photos.php:775
+#: mod/photos.php:819
 msgid "Image file is empty."
 msgstr "Файл изображения пуст."
 
-#: ../../mod/photos.php:930
+#: mod/photos.php:986
 msgid "No photos selected"
 msgstr "Не выбрано фото."
 
-#: ../../mod/photos.php:1094
+#: mod/photos.php:1147
 #, php-format
 msgid "You have used %1$.2f Mbytes of %2$.2f Mbytes photo storage."
-msgstr "Вы использовали %1$.2f мегабайт из %2$.2f возможных для хранения фотографий."
+msgstr ""
+"Вы использовали %1$.2f мегабайт из %2$.2f возможных для хранения фотографий."
 
-#: ../../mod/photos.php:1129
+#: mod/photos.php:1182
 msgid "Upload Photos"
 msgstr "Загрузить фото"
 
-#: ../../mod/photos.php:1133 ../../mod/photos.php:1199
+#: mod/photos.php:1186 mod/photos.php:1257
 msgid "New album name: "
 msgstr "Название нового альбома: "
 
-#: ../../mod/photos.php:1134
+#: mod/photos.php:1187
 msgid "or existing album name: "
 msgstr "или название существующего альбома: "
 
-#: ../../mod/photos.php:1135
+#: mod/photos.php:1188
 msgid "Do not show a status post for this upload"
 msgstr "Не показывать статус-сообщение для этой закачки"
 
-#: ../../mod/photos.php:1137 ../../mod/photos.php:1510
+#: mod/photos.php:1190 mod/photos.php:1575 include/acl_selectors.php:347
 msgid "Permissions"
 msgstr "Разрешения"
 
-#: ../../mod/photos.php:1148
+#: mod/photos.php:1201
 msgid "Private Photo"
 msgstr "Личное фото"
 
-#: ../../mod/photos.php:1149
+#: mod/photos.php:1202
 msgid "Public Photo"
 msgstr "Публичное фото"
 
-#: ../../mod/photos.php:1212
+#: mod/photos.php:1270
 msgid "Edit Album"
 msgstr "Редактировать альбом"
 
-#: ../../mod/photos.php:1218
+#: mod/photos.php:1276
 msgid "Show Newest First"
 msgstr "Показать новые первыми"
 
-#: ../../mod/photos.php:1220
+#: mod/photos.php:1278
 msgid "Show Oldest First"
 msgstr "Показать старые первыми"
 
-#: ../../mod/photos.php:1248 ../../mod/photos.php:1802
+#: mod/photos.php:1306 mod/photos.php:1884
 msgid "View Photo"
 msgstr "Просмотр фото"
 
-#: ../../mod/photos.php:1294
+#: mod/photos.php:1353
 msgid "Permission denied. Access to this item may be restricted."
 msgstr "Нет разрешения. Доступ к этому элементу ограничен."
 
-#: ../../mod/photos.php:1296
+#: mod/photos.php:1355
 msgid "Photo not available"
 msgstr "Фото недоступно"
 
-#: ../../mod/photos.php:1352
+#: mod/photos.php:1411
 msgid "View photo"
 msgstr "Просмотр фото"
 
-#: ../../mod/photos.php:1352
+#: mod/photos.php:1411
 msgid "Edit photo"
 msgstr "Редактировать фото"
 
-#: ../../mod/photos.php:1353
+#: mod/photos.php:1412
 msgid "Use as profile photo"
 msgstr "Использовать как фото профиля"
 
-#: ../../mod/photos.php:1378
+#: mod/photos.php:1437
 msgid "View Full Size"
 msgstr "Просмотреть полный размер"
 
-#: ../../mod/photos.php:1457
+#: mod/photos.php:1523
 msgid "Tags: "
 msgstr "Ключевые слова: "
 
-#: ../../mod/photos.php:1460
+#: mod/photos.php:1526
 msgid "[Remove any tag]"
 msgstr "[Удалить любое ключевое слово]"
 
-#: ../../mod/photos.php:1500
-msgid "Rotate CW (right)"
-msgstr "Поворот по часовой стрелке (направо)"
-
-#: ../../mod/photos.php:1501
-msgid "Rotate CCW (left)"
-msgstr "Поворот против часовой стрелки (налево)"
-
-#: ../../mod/photos.php:1503
+#: mod/photos.php:1566
 msgid "New album name"
 msgstr "Название нового альбома"
 
-#: ../../mod/photos.php:1506
+#: mod/photos.php:1567
 msgid "Caption"
 msgstr "Подпись"
 
-#: ../../mod/photos.php:1508
+#: mod/photos.php:1568
 msgid "Add a Tag"
 msgstr "Добавить ключевое слово (таг)"
 
-#: ../../mod/photos.php:1512
-msgid ""
-"Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping"
+#: mod/photos.php:1568
+msgid "Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping"
 msgstr "Пример: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping"
 
-#: ../../mod/photos.php:1521
+#: mod/photos.php:1569
+msgid "Do not rotate"
+msgstr ""
+
+#: mod/photos.php:1570
+msgid "Rotate CW (right)"
+msgstr "Поворот по часовой стрелке (направо)"
+
+#: mod/photos.php:1571
+msgid "Rotate CCW (left)"
+msgstr "Поворот против часовой стрелки (налево)"
+
+#: mod/photos.php:1586
 msgid "Private photo"
 msgstr "Личное фото"
 
-#: ../../mod/photos.php:1522
+#: mod/photos.php:1587
 msgid "Public photo"
 msgstr "Публичное фото"
 
-#: ../../mod/photos.php:1544 ../../include/conversation.php:1090
+#: mod/photos.php:1609 include/conversation.php:1182
 msgid "Share"
 msgstr "Поделиться"
 
-#: ../../mod/photos.php:1817
-msgid "Recent Photos"
-msgstr "Последние фото"
+#: mod/photos.php:1648 include/conversation.php:509
+#: include/conversation.php:1413
+msgid "Attending"
+msgid_plural "Attending"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+#: mod/photos.php:1648 include/conversation.php:509
+msgid "Not attending"
+msgstr ""
+
+#: mod/photos.php:1648 include/conversation.php:509
+msgid "Might attend"
+msgstr ""
+
+#: mod/photos.php:1813
+msgid "Map"
+msgstr "Карта"
+
+#: mod/p.php:9
+msgid "Not Extended"
+msgstr ""
 
-#: ../../mod/regmod.php:55
+#: mod/regmod.php:55
 msgid "Account approved."
 msgstr "Аккаунт утвержден."
 
-#: ../../mod/regmod.php:92
+#: mod/regmod.php:92
 #, php-format
 msgid "Registration revoked for %s"
 msgstr "Регистрация отменена для %s"
 
-#: ../../mod/regmod.php:104
+#: mod/regmod.php:104
 msgid "Please login."
 msgstr "Пожалуйста, войдите с паролем."
 
-#: ../../mod/uimport.php:66
+#: mod/uimport.php:66
 msgid "Move account"
 msgstr "Удалить аккаунт"
 
-#: ../../mod/uimport.php:67
+#: mod/uimport.php:67
 msgid "You can import an account from another Friendica server."
 msgstr "Вы можете импортировать учетную запись с другого сервера Friendica."
 
-#: ../../mod/uimport.php:68
+#: mod/uimport.php:68
 msgid ""
 "You need to export your account from the old server and upload it here. We "
-"will recreate your old account here with all your contacts. We will try also"
-" to inform your friends that you moved here."
-msgstr "Вам нужно экспортировать свой ​​аккаунт со старого сервера и загрузить его сюда. Мы восстановим ваш ​​старый аккаунт здесь со всеми вашими контактами. Мы постараемся также сообщить друзьям, что вы переехали сюда."
+"will recreate your old account here with all your contacts. We will try also "
+"to inform your friends that you moved here."
+msgstr ""
+"Вам нужно экспортировать свой ​​аккаунт со старого сервера и загрузить его "
+"сюда. Мы восстановим ваш ​​старый аккаунт здесь со всеми вашими контактами. Мы "
+"постараемся также сообщить друзьям, что вы переехали сюда."
 
-#: ../../mod/uimport.php:69
+#: mod/uimport.php:69
 msgid ""
 "This feature is experimental. We can't import contacts from the OStatus "
-"network (statusnet/identi.ca) or from Diaspora"
-msgstr "Это экспериментальная функция. Мы не можем импортировать контакты из сети OStatus (StatusNet / identi.ca) или из Diaspora"
+"network (GNU Social/Statusnet) or from Diaspora"
+msgstr ""
 
-#: ../../mod/uimport.php:70
+#: mod/uimport.php:70
 msgid "Account file"
 msgstr "Файл аккаунта"
 
-#: ../../mod/uimport.php:70
+#: mod/uimport.php:70
 msgid ""
 "To export your account, go to \"Settings->Export your personal data\" and "
 "select \"Export account\""
-msgstr "Для экспорта аккаунта, перейдите в \"Настройки->Экспортировать ваши данные\" и выберите \"Экспорт аккаунта\""
+msgstr ""
+"Для экспорта аккаунта, перейдите в \"Настройки->Экспортировать ваши данные\" "
+"и выберите \"Экспорт аккаунта\""
 
-#: ../../mod/attach.php:8
+#: mod/attach.php:8
 msgid "Item not available."
 msgstr "Пункт не доступен."
 
-#: ../../mod/attach.php:20
+#: mod/attach.php:20
 msgid "Item was not found."
 msgstr "Пункт не был найден."
 
-#: ../../boot.php:749
+#: boot.php:868
 msgid "Delete this item?"
 msgstr "Удалить этот элемент?"
 
-#: ../../boot.php:752
+#: boot.php:871
 msgid "show fewer"
 msgstr "показать меньше"
 
-#: ../../boot.php:1122
+#: boot.php:1292
 #, php-format
 msgid "Update %s failed. See error logs."
 msgstr "Обновление %s не удалось. Смотрите журнал ошибок."
 
-#: ../../boot.php:1240
+#: boot.php:1404
 msgid "Create a New Account"
 msgstr "Создать новый аккаунт"
 
-#: ../../boot.php:1265 ../../include/nav.php:73
+#: boot.php:1429 include/nav.php:72
 msgid "Logout"
 msgstr "Выход"
 
-#: ../../boot.php:1268
+#: boot.php:1432
 msgid "Nickname or Email address: "
 msgstr "Ник или адрес электронной почты: "
 
-#: ../../boot.php:1269
+#: boot.php:1433
 msgid "Password: "
 msgstr "Пароль: "
 
-#: ../../boot.php:1270
+#: boot.php:1434
 msgid "Remember me"
 msgstr "Запомнить"
 
-#: ../../boot.php:1273
+#: boot.php:1437
 msgid "Or login using OpenID: "
 msgstr "Или зайти с OpenID: "
 
-#: ../../boot.php:1279
+#: boot.php:1443
 msgid "Forgot your password?"
 msgstr "Забыли пароль?"
 
-#: ../../boot.php:1282
+#: boot.php:1446
 msgid "Website Terms of Service"
 msgstr "Правила сайта"
 
-#: ../../boot.php:1283
+#: boot.php:1447
 msgid "terms of service"
 msgstr "правила"
 
-#: ../../boot.php:1285
+#: boot.php:1449
 msgid "Website Privacy Policy"
 msgstr "Политика конфиденциальности сервера"
 
-#: ../../boot.php:1286
+#: boot.php:1450
 msgid "privacy policy"
 msgstr "политика конфиденциальности"
 
-#: ../../boot.php:1419
-msgid "Requested account is not available."
-msgstr "Запрашиваемый профиль недоступен."
-
-#: ../../boot.php:1501 ../../boot.php:1635
-#: ../../include/profile_advanced.php:84
-msgid "Edit profile"
-msgstr "Редактировать профиль"
-
-#: ../../boot.php:1600
-msgid "Message"
-msgstr "Сообщение"
-
-#: ../../boot.php:1606 ../../include/nav.php:175
-msgid "Profiles"
-msgstr "Профили"
-
-#: ../../boot.php:1606
-msgid "Manage/edit profiles"
-msgstr "Управление / редактирование профилей"
+#: object/Item.php:95
+msgid "This entry was edited"
+msgstr "Эта запись была отредактирована"
 
-#: ../../boot.php:1706
-msgid "Network:"
+#: object/Item.php:191
+msgid "I will attend"
 msgstr ""
 
-#: ../../boot.php:1736 ../../boot.php:1822
-msgid "g A l F d"
-msgstr "g A l F d"
-
-#: ../../boot.php:1737 ../../boot.php:1823
-msgid "F d"
-msgstr "F d"
-
-#: ../../boot.php:1782 ../../boot.php:1863
-msgid "[today]"
-msgstr "[сегодня]"
-
-#: ../../boot.php:1794
-msgid "Birthday Reminders"
-msgstr "Напоминания о днях рождения"
-
-#: ../../boot.php:1795
-msgid "Birthdays this week:"
-msgstr "Дни рождения на этой неделе:"
-
-#: ../../boot.php:1856
-msgid "[No description]"
-msgstr "[без описания]"
-
-#: ../../boot.php:1874
-msgid "Event Reminders"
-msgstr "Напоминания о мероприятиях"
-
-#: ../../boot.php:1875
-msgid "Events this week:"
-msgstr "Мероприятия на этой неделе:"
-
-#: ../../boot.php:2112 ../../include/nav.php:76
-msgid "Status"
-msgstr "Статус"
-
-#: ../../boot.php:2115
-msgid "Status Messages and Posts"
-msgstr "Сообщение статуса и посты"
-
-#: ../../boot.php:2122
-msgid "Profile Details"
-msgstr "Детали профиля"
-
-#: ../../boot.php:2133 ../../boot.php:2136 ../../include/nav.php:79
-msgid "Videos"
-msgstr "Видео"
-
-#: ../../boot.php:2146
-msgid "Events and Calendar"
-msgstr "Календарь и события"
-
-#: ../../boot.php:2153
-msgid "Only You Can See This"
-msgstr "Только вы можете это видеть"
+#: object/Item.php:191
+msgid "I will not attend"
+msgstr ""
 
-#: ../../object/Item.php:94
-msgid "This entry was edited"
-msgstr "Эта запись была отредактирована"
+#: object/Item.php:191
+msgid "I might attend"
+msgstr ""
 
-#: ../../object/Item.php:208
+#: object/Item.php:230
 msgid "ignore thread"
 msgstr ""
 
-#: ../../object/Item.php:209
+#: object/Item.php:231
 msgid "unignore thread"
 msgstr ""
 
-#: ../../object/Item.php:210
+#: object/Item.php:232
 msgid "toggle ignore status"
 msgstr ""
 
-#: ../../object/Item.php:213
-msgid "ignored"
-msgstr ""
-
-#: ../../object/Item.php:316 ../../include/conversation.php:666
+#: object/Item.php:345 include/conversation.php:687
 msgid "Categories:"
 msgstr "Категории:"
 
-#: ../../object/Item.php:317 ../../include/conversation.php:667
+#: object/Item.php:346 include/conversation.php:688
 msgid "Filed under:"
 msgstr "В рубрике:"
 
-#: ../../object/Item.php:329
+#: object/Item.php:360
 msgid "via"
 msgstr "через"
 
-#: ../../include/dbstructure.php:26
+#: include/dbstructure.php:26
 #, php-format
 msgid ""
 "\n"
 "\t\t\tThe friendica developers released update %s recently,\n"
 "\t\t\tbut when I tried to install it, something went terribly wrong.\n"
 "\t\t\tThis needs to be fixed soon and I can't do it alone. Please contact a\n"
-"\t\t\tfriendica developer if you can not help me on your own. My database might be invalid."
+"\t\t\tfriendica developer if you can not help me on your own. My database "
+"might be invalid."
 msgstr ""
 
-#: ../../include/dbstructure.php:31
+#: include/dbstructure.php:31
 #, php-format
 msgid ""
 "The error message is\n"
 "[pre]%s[/pre]"
 msgstr ""
 
-#: ../../include/dbstructure.php:162
+#: include/dbstructure.php:153
 msgid "Errors encountered creating database tables."
 msgstr "Обнаружены ошибки при создании таблиц базы данных."
 
-#: ../../include/dbstructure.php:220
+#: include/dbstructure.php:230
 msgid "Errors encountered performing database changes."
 msgstr ""
 
-#: ../../include/auth.php:38
+#: include/auth.php:44
 msgid "Logged out."
 msgstr "Выход из системы."
 
-#: ../../include/auth.php:128 ../../include/user.php:67
+#: include/auth.php:134 include/user.php:75
 msgid ""
 "We encountered a problem while logging in with the OpenID you provided. "
 "Please check the correct spelling of the ID."
-msgstr "Мы столкнулись с проблемой при входе с OpenID, который вы указали. Пожалуйста, проверьте правильность написания ID."
+msgstr ""
+"Мы столкнулись с проблемой при входе с OpenID, который вы указали. "
+"Пожалуйста, проверьте правильность написания ID."
 
-#: ../../include/auth.php:128 ../../include/user.php:67
+#: include/auth.php:134 include/user.php:75
 msgid "The error message was:"
 msgstr "Сообщение об ошибке было:"
 
-#: ../../include/contact_widgets.php:6
+#: include/contact_widgets.php:6
 msgid "Add New Contact"
 msgstr "Добавить контакт"
 
-#: ../../include/contact_widgets.php:7
+#: include/contact_widgets.php:7
 msgid "Enter address or web location"
 msgstr "Введите адрес или веб-местонахождение"
 
-#: ../../include/contact_widgets.php:8
+#: include/contact_widgets.php:8
 msgid "Example: bob@example.com, http://example.com/barbara"
 msgstr "Пример: bob@example.com, http://example.com/barbara"
 
-#: ../../include/contact_widgets.php:24
+#: include/contact_widgets.php:24
 #, php-format
 msgid "%d invitation available"
 msgid_plural "%d invitations available"
 msgstr[0] "%d приглашение доступно"
 msgstr[1] "%d приглашений доступно"
 msgstr[2] "%d приглашений доступно"
+msgstr[3] "%d приглашений доступно"
 
-#: ../../include/contact_widgets.php:30
+#: include/contact_widgets.php:30
 msgid "Find People"
 msgstr "Поиск людей"
 
-#: ../../include/contact_widgets.php:31
+#: include/contact_widgets.php:31
 msgid "Enter name or interest"
 msgstr "Введите имя или интерес"
 
-#: ../../include/contact_widgets.php:32
-msgid "Connect/Follow"
-msgstr "Подключиться/Следовать"
-
-#: ../../include/contact_widgets.php:33
+#: include/contact_widgets.php:33
 msgid "Examples: Robert Morgenstein, Fishing"
 msgstr "Примеры: Роберт Morgenstein, Рыбалка"
 
-#: ../../include/contact_widgets.php:36 ../../view/theme/diabook/theme.php:526
+#: include/contact_widgets.php:36 view/theme/diabook/theme.php:526
+#: view/theme/vier/theme.php:206
 msgid "Similar Interests"
 msgstr "Похожие интересы"
 
-#: ../../include/contact_widgets.php:37
+#: include/contact_widgets.php:37
 msgid "Random Profile"
 msgstr "Случайный профиль"
 
-#: ../../include/contact_widgets.php:38 ../../view/theme/diabook/theme.php:528
+#: include/contact_widgets.php:38 view/theme/diabook/theme.php:528
+#: view/theme/vier/theme.php:208
 msgid "Invite Friends"
 msgstr "Пригласить друзей"
 
-#: ../../include/contact_widgets.php:71
+#: include/contact_widgets.php:108
 msgid "Networks"
 msgstr "Сети"
 
-#: ../../include/contact_widgets.php:74
+#: include/contact_widgets.php:111
 msgid "All Networks"
 msgstr "Все сети"
 
-#: ../../include/contact_widgets.php:104 ../../include/features.php:60
+#: include/contact_widgets.php:141 include/features.php:102
 msgid "Saved Folders"
 msgstr "Сохранённые папки"
 
-#: ../../include/contact_widgets.php:107 ../../include/contact_widgets.php:139
+#: include/contact_widgets.php:144 include/contact_widgets.php:176
 msgid "Everything"
 msgstr "Всё"
 
-#: ../../include/contact_widgets.php:136
+#: include/contact_widgets.php:173
 msgid "Categories"
 msgstr "Категории"
 
-#: ../../include/features.php:23
+#: include/contact_widgets.php:237
+#, php-format
+msgid "%d contact in common"
+msgid_plural "%d contacts in common"
+msgstr[0] "%d Контакт"
+msgstr[1] "%d Контактов"
+msgstr[2] "%d Контактов"
+msgstr[3] "%d Контактов"
+
+#: include/features.php:63
 msgid "General Features"
 msgstr "Основные возможности"
 
-#: ../../include/features.php:25
+#: include/features.php:65
 msgid "Multiple Profiles"
 msgstr "Несколько профилей"
 
-#: ../../include/features.php:25
+#: include/features.php:65
 msgid "Ability to create multiple profiles"
 msgstr "Возможность создания нескольких профилей"
 
-#: ../../include/features.php:30
-msgid "Post Composition Features"
+#: include/features.php:66
+msgid "Photo Location"
+msgstr ""
+
+#: include/features.php:66
+msgid ""
+"Photo metadata is normally stripped. This extracts the location (if present) "
+"prior to stripping metadata and links it to a map."
 msgstr ""
 
-#: ../../include/features.php:31
+#: include/features.php:71
+msgid "Post Composition Features"
+msgstr "Составление сообщений"
+
+#: include/features.php:72
 msgid "Richtext Editor"
 msgstr "Редактор RTF"
 
-#: ../../include/features.php:31
+#: include/features.php:72
 msgid "Enable richtext editor"
 msgstr "Включить редактор RTF"
 
-#: ../../include/features.php:32
+#: include/features.php:73
 msgid "Post Preview"
-msgstr "предварительный просмотр"
+msgstr "Ð\9fредварительный просмотр"
 
-#: ../../include/features.php:32
+#: include/features.php:73
 msgid "Allow previewing posts and comments before publishing them"
 msgstr "Разрешить предпросмотр сообщения и комментария перед их публикацией"
 
-#: ../../include/features.php:33
+#: include/features.php:74
 msgid "Auto-mention Forums"
 msgstr ""
 
-#: ../../include/features.php:33
+#: include/features.php:74
 msgid ""
 "Add/remove mention when a fourm page is selected/deselected in ACL window."
 msgstr ""
 
-#: ../../include/features.php:38
+#: include/features.php:79
 msgid "Network Sidebar Widgets"
 msgstr "Виджет боковой панели \"Сеть\""
 
-#: ../../include/features.php:39
+#: include/features.php:80
 msgid "Search by Date"
 msgstr "Поиск по датам"
 
-#: ../../include/features.php:39
+#: include/features.php:80
 msgid "Ability to select posts by date ranges"
 msgstr "Возможность выбора постов по диапазону дат"
 
-#: ../../include/features.php:40
+#: include/features.php:81 include/features.php:111
+msgid "List Forums"
+msgstr ""
+
+#: include/features.php:81
+msgid "Enable widget to display the forums your are connected with"
+msgstr ""
+
+#: include/features.php:82
 msgid "Group Filter"
 msgstr "Фильтр групп"
 
-#: ../../include/features.php:40
+#: include/features.php:82
 msgid "Enable widget to display Network posts only from selected group"
-msgstr "Включить виджет для отображения сообщений сети только от выбранной группы"
+msgstr ""
+"Включить виджет для отображения сообщений сети только от выбранной группы"
 
-#: ../../include/features.php:41
+#: include/features.php:83
 msgid "Network Filter"
 msgstr "Фильтр сети"
 
-#: ../../include/features.php:41
+#: include/features.php:83
 msgid "Enable widget to display Network posts only from selected network"
-msgstr "Включить виджет для отображения сообщений сети только от выбранной сети"
+msgstr ""
+"Включить виджет для отображения сообщений сети только от выбранной сети"
 
-#: ../../include/features.php:42
+#: include/features.php:84
 msgid "Save search terms for re-use"
 msgstr "Сохранить условия поиска для повторного использования"
 
-#: ../../include/features.php:47
+#: include/features.php:89
 msgid "Network Tabs"
 msgstr "Сетевые вкладки"
 
-#: ../../include/features.php:48
+#: include/features.php:90
 msgid "Network Personal Tab"
 msgstr "Персональные сетевые вкладки"
 
-#: ../../include/features.php:48
+#: include/features.php:90
 msgid "Enable tab to display only Network posts that you've interacted on"
-msgstr "Включить вкладку для отображения только сообщений сети, с которой вы взаимодействовали"
+msgstr ""
+"Включить вкладку для отображения только сообщений сети, с которой вы "
+"взаимодействовали"
 
-#: ../../include/features.php:49
+#: include/features.php:91
 msgid "Network New Tab"
 msgstr "Новая вкладка сеть"
 
-#: ../../include/features.php:49
+#: include/features.php:91
 msgid "Enable tab to display only new Network posts (from the last 12 hours)"
-msgstr "Включить вкладку для отображения только новых сообщений сети (за последние 12 часов)"
+msgstr ""
+"Включить вкладку для отображения только новых сообщений сети (за последние "
+"12 часов)"
 
-#: ../../include/features.php:50
+#: include/features.php:92
 msgid "Network Shared Links Tab"
 msgstr "Вкладка shared ссылок сети"
 
-#: ../../include/features.php:50
+#: include/features.php:92
 msgid "Enable tab to display only Network posts with links in them"
-msgstr "Включить вкладку для отображения только сообщений сети со ссылками на них"
+msgstr ""
+"Включить вкладку для отображения только сообщений сети со ссылками на них"
 
-#: ../../include/features.php:55
+#: include/features.php:97
 msgid "Post/Comment Tools"
 msgstr "Инструменты пост/комментарий"
 
-#: ../../include/features.php:56
+#: include/features.php:98
 msgid "Multiple Deletion"
 msgstr "Множественное удаление"
 
-#: ../../include/features.php:56
+#: include/features.php:98
 msgid "Select and delete multiple posts/comments at once"
 msgstr "Выбрать и удалить несколько постов/комментариев одновременно."
 
-#: ../../include/features.php:57
+#: include/features.php:99
 msgid "Edit Sent Posts"
 msgstr "Редактировать отправленные посты"
 
-#: ../../include/features.php:57
+#: include/features.php:99
 msgid "Edit and correct posts and comments after sending"
 msgstr "Редактировать и править посты и комментарии после отправления"
 
-#: ../../include/features.php:58
+#: include/features.php:100
 msgid "Tagging"
 msgstr "Отмеченное"
 
-#: ../../include/features.php:58
+#: include/features.php:100
 msgid "Ability to tag existing posts"
 msgstr "Возможность отмечать существующие посты"
 
-#: ../../include/features.php:59
+#: include/features.php:101
 msgid "Post Categories"
 msgstr "Категории постов"
 
-#: ../../include/features.php:59
+#: include/features.php:101
 msgid "Add categories to your posts"
 msgstr "Добавить категории вашего поста"
 
-#: ../../include/features.php:60
+#: include/features.php:102
 msgid "Ability to file posts under folders"
 msgstr ""
 
-#: ../../include/features.php:61
+#: include/features.php:103
 msgid "Dislike Posts"
 msgstr "Посты дизлайк"
 
-#: ../../include/features.php:61
+#: include/features.php:103
 msgid "Ability to dislike posts/comments"
 msgstr "Возможность дизлайка постов/комментариев"
 
-#: ../../include/features.php:62
+#: include/features.php:104
 msgid "Star Posts"
 msgstr "Популярные посты"
 
-#: ../../include/features.php:62
+#: include/features.php:104
 msgid "Ability to mark special posts with a star indicator"
 msgstr "Возможность отметить специальные сообщения индикатором популярности"
 
-#: ../../include/features.php:63
+#: include/features.php:105
 msgid "Mute Post Notifications"
 msgstr ""
 
-#: ../../include/features.php:63
+#: include/features.php:105
 msgid "Ability to mute notifications for a thread"
 msgstr ""
 
-#: ../../include/follow.php:32
+#: include/features.php:110
+msgid "Advanced Profile Settings"
+msgstr "Расширенные настройки профиля"
+
+#: include/features.php:111
+msgid "Show visitors public community forums at the Advanced Profile Page"
+msgstr ""
+
+#: include/follow.php:77
 msgid "Connect URL missing."
 msgstr "Connect-URL отсутствует."
 
-#: ../../include/follow.php:59
+#: include/follow.php:104
 msgid ""
 "This site is not configured to allow communications with other networks."
 msgstr "Данный сайт не настроен так, чтобы держать связь с другими сетями."
 
-#: ../../include/follow.php:60 ../../include/follow.php:80
+#: include/follow.php:105 include/follow.php:125
 msgid "No compatible communication protocols or feeds were discovered."
 msgstr "Обнаружены несовместимые протоколы связи или каналы."
 
-#: ../../include/follow.php:78
+#: include/follow.php:123
 msgid "The profile address specified does not provide adequate information."
 msgstr "Указанный адрес профиля не дает адекватной информации."
 
-#: ../../include/follow.php:82
+#: include/follow.php:127
 msgid "An author or name was not found."
 msgstr "Автор или имя не найдены."
 
-#: ../../include/follow.php:84
+#: include/follow.php:129
 msgid "No browser URL could be matched to this address."
 msgstr "Нет URL браузера, который соответствует этому адресу."
 
-#: ../../include/follow.php:86
+#: include/follow.php:131
 msgid ""
 "Unable to match @-style Identity Address with a known protocol or email "
 "contact."
 msgstr ""
 
-#: ../../include/follow.php:87
+#: include/follow.php:132
 msgid "Use mailto: in front of address to force email check."
 msgstr "Bcgjkmpeqnt mailto: перед адресом для быстрого доступа к email."
 
-#: ../../include/follow.php:93
+#: include/follow.php:138
 msgid ""
 "The profile address specified belongs to a network which has been disabled "
 "on this site."
 msgstr "Указанный адрес профиля принадлежит сети, недоступной на этом сайта."
 
-#: ../../include/follow.php:103
+#: include/follow.php:148
 msgid ""
 "Limited profile. This person will be unable to receive direct/personal "
 "notifications from you."
-msgstr "Ограниченный профиль. Этот человек не сможет получить прямые / личные уведомления от вас."
+msgstr ""
+"Ограниченный профиль. Этот человек не сможет получить прямые / личные "
+"уведомления от вас."
 
-#: ../../include/follow.php:205
+#: include/follow.php:249
 msgid "Unable to retrieve contact information."
 msgstr "Невозможно получить контактную информацию."
 
-#: ../../include/follow.php:258
+#: include/follow.php:302
 msgid "following"
 msgstr "следует"
 
-#: ../../include/group.php:25
+#: include/group.php:25
 msgid ""
 "A deleted group with this name was revived. Existing item permissions "
 "<strong>may</strong> apply to this group and any future members. If this is "
 "not what you intended, please create another group with a different name."
-msgstr "Удаленная группа с таким названием была восстановлена. Существующие права доступа <strong>могут</strong> применяться к этой группе и любым будущим участникам. Если это не то, что вы хотели, пожалуйста, создайте еще ​​одну группу с другим названием."
+msgstr ""
+"Удаленная группа с таким названием была восстановлена. Существующие права "
+"доступа <strong>могут</strong> применяться к этой группе и любым будущим "
+"участникам. Если это не то, что вы хотели, пожалуйста, создайте еще ​​одну "
+"группу с другим названием."
 
-#: ../../include/group.php:207
+#: include/group.php:209
 msgid "Default privacy group for new contacts"
 msgstr "Группа доступа по умолчанию для новых контактов"
 
-#: ../../include/group.php:226
+#: include/group.php:239
 msgid "Everybody"
 msgstr "Каждый"
 
-#: ../../include/group.php:249
+#: include/group.php:262
 msgid "edit"
 msgstr "редактировать"
 
-#: ../../include/group.php:271
+#: include/group.php:285
+msgid "Edit groups"
+msgstr ""
+
+#: include/group.php:287
 msgid "Edit group"
 msgstr "Редактировать группу"
 
-#: ../../include/group.php:272
+#: include/group.php:288
 msgid "Create a new group"
 msgstr "Создать новую группу"
 
-#: ../../include/group.php:273
+#: include/group.php:291
 msgid "Contacts not in any group"
 msgstr "Контакты не состоят в группе"
 
-#: ../../include/datetime.php:43 ../../include/datetime.php:45
+#: include/datetime.php:43 include/datetime.php:45
 msgid "Miscellaneous"
 msgstr "Разное"
 
-#: ../../include/datetime.php:153 ../../include/datetime.php:290
-msgid "year"
-msgstr "год"
-
-#: ../../include/datetime.php:158 ../../include/datetime.php:291
-msgid "month"
-msgstr "мес."
-
-#: ../../include/datetime.php:163 ../../include/datetime.php:293
-msgid "day"
-msgstr "день"
+#: include/datetime.php:141
+msgid "YYYY-MM-DD or MM-DD"
+msgstr ""
 
-#: ../../include/datetime.php:276
+#: include/datetime.php:271
 msgid "never"
 msgstr "никогда"
 
-#: ../../include/datetime.php:282
+#: include/datetime.php:277
 msgid "less than a second ago"
 msgstr "менее сек. назад"
 
-#: ../../include/datetime.php:290
+#: include/datetime.php:287
+msgid "year"
+msgstr "год"
+
+#: include/datetime.php:287
 msgid "years"
 msgstr "лет"
 
-#: ../../include/datetime.php:291
+#: include/datetime.php:288
 msgid "months"
 msgstr "мес."
 
-#: ../../include/datetime.php:292
-msgid "week"
-msgstr "неделя"
-
-#: ../../include/datetime.php:292
+#: include/datetime.php:289
 msgid "weeks"
 msgstr "недель"
 
-#: ../../include/datetime.php:293
+#: include/datetime.php:290
 msgid "days"
 msgstr "дней"
 
-#: ../../include/datetime.php:294
+#: include/datetime.php:291
 msgid "hour"
 msgstr "час"
 
-#: ../../include/datetime.php:294
+#: include/datetime.php:291
 msgid "hours"
 msgstr "час."
 
-#: ../../include/datetime.php:295
+#: include/datetime.php:292
 msgid "minute"
 msgstr "минута"
 
-#: ../../include/datetime.php:295
+#: include/datetime.php:292
 msgid "minutes"
 msgstr "мин."
 
-#: ../../include/datetime.php:296
+#: include/datetime.php:293
 msgid "second"
 msgstr "секунда"
 
-#: ../../include/datetime.php:296
+#: include/datetime.php:293
 msgid "seconds"
 msgstr "сек."
 
-#: ../../include/datetime.php:305
+#: include/datetime.php:302
 #, php-format
 msgid "%1$d %2$s ago"
 msgstr "%1$d %2$s назад"
 
-#: ../../include/datetime.php:477 ../../include/items.php:2211
+#: include/datetime.php:474 include/items.php:2500
 #, php-format
 msgid "%s's birthday"
 msgstr "день рождения %s"
 
-#: ../../include/datetime.php:478 ../../include/items.php:2212
+#: include/datetime.php:475 include/items.php:2501
 #, php-format
 msgid "Happy Birthday %s"
 msgstr "С днём рождения %s"
 
-#: ../../include/acl_selectors.php:333
+#: include/identity.php:42
+msgid "Requested account is not available."
+msgstr "Запрашиваемый профиль недоступен."
+
+#: include/identity.php:95 include/identity.php:284 include/identity.php:662
+msgid "Edit profile"
+msgstr "Редактировать профиль"
+
+#: include/identity.php:244
+msgid "Atom feed"
+msgstr ""
+
+#: include/identity.php:249
+msgid "Message"
+msgstr "Сообщение"
+
+#: include/identity.php:255 include/nav.php:185
+msgid "Profiles"
+msgstr "Профили"
+
+#: include/identity.php:255
+msgid "Manage/edit profiles"
+msgstr "Управление / редактирование профилей"
+
+#: include/identity.php:425 include/identity.php:509
+msgid "g A l F d"
+msgstr "g A l F d"
+
+#: include/identity.php:426 include/identity.php:510
+msgid "F d"
+msgstr "F d"
+
+#: include/identity.php:471 include/identity.php:556
+msgid "[today]"
+msgstr "[сегодня]"
+
+#: include/identity.php:483
+msgid "Birthday Reminders"
+msgstr "Напоминания о днях рождения"
+
+#: include/identity.php:484
+msgid "Birthdays this week:"
+msgstr "Дни рождения на этой неделе:"
+
+#: include/identity.php:543
+msgid "[No description]"
+msgstr "[без описания]"
+
+#: include/identity.php:567
+msgid "Event Reminders"
+msgstr "Напоминания о мероприятиях"
+
+#: include/identity.php:568
+msgid "Events this week:"
+msgstr "Мероприятия на этой неделе:"
+
+#: include/identity.php:595
+msgid "j F, Y"
+msgstr "j F, Y"
+
+#: include/identity.php:596
+msgid "j F"
+msgstr "j F"
+
+#: include/identity.php:603
+msgid "Birthday:"
+msgstr "День рождения:"
+
+#: include/identity.php:607
+msgid "Age:"
+msgstr "Возраст:"
+
+#: include/identity.php:616
+#, php-format
+msgid "for %1$d %2$s"
+msgstr ""
+
+#: include/identity.php:629
+msgid "Religion:"
+msgstr "Религия:"
+
+#: include/identity.php:633
+msgid "Hobbies/Interests:"
+msgstr "Хобби / Интересы:"
+
+#: include/identity.php:640
+msgid "Contact information and Social Networks:"
+msgstr "Информация о контакте и социальных сетях:"
+
+#: include/identity.php:642
+msgid "Musical interests:"
+msgstr "Музыкальные интересы:"
+
+#: include/identity.php:644
+msgid "Books, literature:"
+msgstr "Книги, литература:"
+
+#: include/identity.php:646
+msgid "Television:"
+msgstr "Телевидение:"
+
+#: include/identity.php:648
+msgid "Film/dance/culture/entertainment:"
+msgstr "Кино / Танцы / Культура / Развлечения:"
+
+#: include/identity.php:650
+msgid "Love/Romance:"
+msgstr "Любовь / Романтика:"
+
+#: include/identity.php:652
+msgid "Work/employment:"
+msgstr "Работа / Занятость:"
+
+#: include/identity.php:654
+msgid "School/education:"
+msgstr "Школа / Образование:"
+
+#: include/identity.php:658
+msgid "Forums:"
+msgstr ""
+
+#: include/identity.php:710 include/identity.php:713 include/nav.php:78
+msgid "Videos"
+msgstr "Видео"
+
+#: include/identity.php:725 include/nav.php:140
+msgid "Events and Calendar"
+msgstr "Календарь и события"
+
+#: include/identity.php:733
+msgid "Only You Can See This"
+msgstr "Только вы можете это видеть"
+
+#: include/like.php:167 include/conversation.php:122
+#: include/conversation.php:258 include/text.php:1998
+#: view/theme/diabook/theme.php:463
+msgid "event"
+msgstr "мероприятие"
+
+#: include/like.php:184 include/conversation.php:141 include/diaspora.php:2185
+#: view/theme/diabook/theme.php:480
+#, php-format
+msgid "%1$s likes %2$s's %3$s"
+msgstr "%1$s нравится %3$s от %2$s "
+
+#: include/like.php:186 include/conversation.php:144
+#, php-format
+msgid "%1$s doesn't like %2$s's %3$s"
+msgstr "%1$s не нравится %3$s от %2$s "
+
+#: include/like.php:188
+#, php-format
+msgid "%1$s is attending %2$s's %3$s"
+msgstr ""
+
+#: include/like.php:190
+#, php-format
+msgid "%1$s is not attending %2$s's %3$s"
+msgstr ""
+
+#: include/like.php:192
+#, php-format
+msgid "%1$s may attend %2$s's %3$s"
+msgstr ""
+
+#: include/acl_selectors.php:325
+msgid "Post to Email"
+msgstr "Отправить на Email"
+
+#: include/acl_selectors.php:330
+#, php-format
+msgid "Connectors disabled, since \"%s\" is enabled."
+msgstr ""
+
+#: include/acl_selectors.php:336
 msgid "Visible to everybody"
 msgstr "Видимо всем"
 
-#: ../../include/acl_selectors.php:334 ../../view/theme/diabook/config.php:142
-#: ../../view/theme/diabook/theme.php:621
+#: include/acl_selectors.php:337 view/theme/diabook/config.php:142
+#: view/theme/diabook/theme.php:621 view/theme/vier/config.php:103
 msgid "show"
 msgstr "показывать"
 
-#: ../../include/acl_selectors.php:335 ../../view/theme/diabook/config.php:142
-#: ../../view/theme/diabook/theme.php:621
+#: include/acl_selectors.php:338 view/theme/diabook/config.php:142
+#: view/theme/diabook/theme.php:621 view/theme/vier/config.php:103
 msgid "don't show"
 msgstr "не показывать"
 
-#: ../../include/message.php:15 ../../include/message.php:172
+#: include/acl_selectors.php:348
+msgid "Close"
+msgstr "Закрыть"
+
+#: include/message.php:15 include/message.php:173
 msgid "[no subject]"
 msgstr "[без темы]"
 
-#: ../../include/Contact.php:115
+#: include/Contact.php:119
 msgid "stopped following"
 msgstr "остановлено следование"
 
-#: ../../include/Contact.php:228 ../../include/conversation.php:882
-msgid "Poke"
-msgstr ""
-
-#: ../../include/Contact.php:229 ../../include/conversation.php:876
+#: include/Contact.php:337 include/conversation.php:911
 msgid "View Status"
-msgstr "Просмотреть статус"
-
-#: ../../include/Contact.php:230 ../../include/conversation.php:877
-msgid "View Profile"
-msgstr "Просмотреть профиль"
+msgstr "Просмотреть статус"
 
-#: ../../include/Contact.php:231 ../../include/conversation.php:878
+#: include/Contact.php:339 include/conversation.php:913
 msgid "View Photos"
 msgstr "Просмотреть фото"
 
-#: ../../include/Contact.php:232 ../../include/Contact.php:255
-#: ../../include/conversation.php:879
+#: include/Contact.php:340 include/conversation.php:914
 msgid "Network Posts"
 msgstr "Посты сети"
 
-#: ../../include/Contact.php:233 ../../include/Contact.php:255
-#: ../../include/conversation.php:880
+#: include/Contact.php:341 include/conversation.php:915
 msgid "Edit Contact"
 msgstr "Редактировать контакт"
 
-#: ../../include/Contact.php:234
+#: include/Contact.php:342
 msgid "Drop Contact"
 msgstr "Удалить контакт"
 
-#: ../../include/Contact.php:235 ../../include/Contact.php:255
-#: ../../include/conversation.php:881
+#: include/Contact.php:343 include/conversation.php:916
 msgid "Send PM"
 msgstr "Отправить ЛС"
 
-#: ../../include/security.php:22
+#: include/Contact.php:344 include/conversation.php:920
+msgid "Poke"
+msgstr ""
+
+#: include/security.php:22
 msgid "Welcome "
 msgstr "Добро пожаловать, "
 
-#: ../../include/security.php:23
+#: include/security.php:23
 msgid "Please upload a profile photo."
 msgstr "Пожалуйста, загрузите фотографию профиля."
 
-#: ../../include/security.php:26
+#: include/security.php:26
 msgid "Welcome back "
 msgstr "Добро пожаловать обратно, "
 
-#: ../../include/security.php:366
+#: include/security.php:375
 msgid ""
 "The form security token was not correct. This probably happened because the "
 "form has been opened for too long (>3 hours) before submitting it."
-msgstr "Ключ формы безопасности неправильный. Вероятно, это произошло потому, что форма была открыта слишком долго (более 3 часов) до её отправки."
+msgstr ""
+"Ключ формы безопасности неправильный. Вероятно, это произошло потому, что "
+"форма была открыта слишком долго (более 3 часов) до её отправки."
 
-#: ../../include/conversation.php:118 ../../include/conversation.php:246
-#: ../../include/text.php:1966 ../../view/theme/diabook/theme.php:463
-msgid "event"
-msgstr "мероприятие"
+#: include/conversation.php:147
+#, php-format
+msgid "%1$s attends %2$s's %3$s"
+msgstr ""
 
-#: ../../include/conversation.php:207
+#: include/conversation.php:150
 #, php-format
-msgid "%1$s poked %2$s"
+msgid "%1$s doesn't attend %2$s's %3$s"
 msgstr ""
 
-#: ../../include/conversation.php:211 ../../include/text.php:1005
-msgid "poked"
+#: include/conversation.php:153
+#, php-format
+msgid "%1$s attends maybe %2$s's %3$s"
+msgstr ""
+
+#: include/conversation.php:219
+#, php-format
+msgid "%1$s poked %2$s"
 msgstr ""
 
-#: ../../include/conversation.php:291
+#: include/conversation.php:303
 msgid "post/item"
 msgstr "пост/элемент"
 
-#: ../../include/conversation.php:292
+#: include/conversation.php:304
 #, php-format
 msgid "%1$s marked %2$s's %3$s as favorite"
 msgstr "%1$s пометил %2$s %3$s как Фаворит"
 
-#: ../../include/conversation.php:772
+#: include/conversation.php:792
 msgid "remove"
 msgstr "удалить"
 
-#: ../../include/conversation.php:776
+#: include/conversation.php:796
 msgid "Delete Selected Items"
 msgstr "Удалить выбранные позиции"
 
-#: ../../include/conversation.php:875
+#: include/conversation.php:910
 msgid "Follow Thread"
 msgstr ""
 
-#: ../../include/conversation.php:944
+#: include/conversation.php:1034
 #, php-format
 msgid "%s likes this."
 msgstr "%s нравится это."
 
-#: ../../include/conversation.php:944
+#: include/conversation.php:1037
 #, php-format
 msgid "%s doesn't like this."
 msgstr "%s не нравится это."
 
-#: ../../include/conversation.php:949
+#: include/conversation.php:1040
 #, php-format
-msgid "<span  %1$s>%2$d people</span> like this"
-msgstr "<span %1$s>%2$d людям</span> нравится это"
+msgid "%s attends."
+msgstr ""
 
-#: ../../include/conversation.php:952
+#: include/conversation.php:1043
 #, php-format
-msgid "<span  %1$s>%2$d people</span> don't like this"
-msgstr "<span %1$s>%2$d людям</span> не нравится это"
+msgid "%s doesn't attend."
+msgstr ""
+
+#: include/conversation.php:1046
+#, php-format
+msgid "%s attends maybe."
+msgstr ""
 
-#: ../../include/conversation.php:966
+#: include/conversation.php:1056
 msgid "and"
 msgstr "и"
 
-#: ../../include/conversation.php:972
+#: include/conversation.php:1062
 #, php-format
 msgid ", and %d other people"
 msgstr ", и %d других чел."
 
-#: ../../include/conversation.php:974
+#: include/conversation.php:1071
+#, php-format
+msgid "<span  %1$s>%2$d people</span> like this"
+msgstr "<span %1$s>%2$d людям</span> нравится это"
+
+#: include/conversation.php:1072
 #, php-format
 msgid "%s like this."
-msgstr "%s нравится это."
+msgstr ""
+
+#: include/conversation.php:1075
+#, php-format
+msgid "<span  %1$s>%2$d people</span> don't like this"
+msgstr "<span %1$s>%2$d людям</span> не нравится это"
 
-#: ../../include/conversation.php:974
+#: include/conversation.php:1076
 #, php-format
 msgid "%s don't like this."
-msgstr "%s не нравится это."
+msgstr ""
+
+#: include/conversation.php:1079
+#, php-format
+msgid "<span  %1$s>%2$d people</span> attend"
+msgstr ""
+
+#: include/conversation.php:1080
+#, php-format
+msgid "%s attend."
+msgstr ""
+
+#: include/conversation.php:1083
+#, php-format
+msgid "<span  %1$s>%2$d people</span> don't attend"
+msgstr ""
+
+#: include/conversation.php:1084
+#, php-format
+msgid "%s don't attend."
+msgstr ""
+
+#: include/conversation.php:1087
+#, php-format
+msgid "<span  %1$s>%2$d people</span> anttend maybe"
+msgstr ""
 
-#: ../../include/conversation.php:1001 ../../include/conversation.php:1019
+#: include/conversation.php:1088
+#, php-format
+msgid "%s anttend maybe."
+msgstr ""
+
+#: include/conversation.php:1127 include/conversation.php:1145
 msgid "Visible to <strong>everybody</strong>"
 msgstr "Видимое <strong>всем</strong>"
 
-#: ../../include/conversation.php:1003 ../../include/conversation.php:1021
+#: include/conversation.php:1129 include/conversation.php:1147
 msgid "Please enter a video link/URL:"
 msgstr "Введите  ссылку на видео link/URL:"
 
-#: ../../include/conversation.php:1004 ../../include/conversation.php:1022
+#: include/conversation.php:1130 include/conversation.php:1148
 msgid "Please enter an audio link/URL:"
 msgstr "Введите ссылку на аудио link/URL:"
 
-#: ../../include/conversation.php:1005 ../../include/conversation.php:1023
+#: include/conversation.php:1131 include/conversation.php:1149
 msgid "Tag term:"
 msgstr ""
 
-#: ../../include/conversation.php:1007 ../../include/conversation.php:1025
+#: include/conversation.php:1133 include/conversation.php:1151
 msgid "Where are you right now?"
 msgstr "И где вы сейчас?"
 
-#: ../../include/conversation.php:1008
+#: include/conversation.php:1134
 msgid "Delete item(s)?"
 msgstr "Удалить елемент(ты)?"
 
-#: ../../include/conversation.php:1051
-msgid "Post to Email"
-msgstr "Отправить на Email"
-
-#: ../../include/conversation.php:1056
-#, php-format
-msgid "Connectors disabled, since \"%s\" is enabled."
-msgstr ""
-
-#: ../../include/conversation.php:1111
+#: include/conversation.php:1203
 msgid "permissions"
 msgstr "разрешения"
 
-#: ../../include/conversation.php:1135
+#: include/conversation.php:1226
 msgid "Post to Groups"
 msgstr "Пост для групп"
 
-#: ../../include/conversation.php:1136
+#: include/conversation.php:1227
 msgid "Post to Contacts"
 msgstr "Пост для контактов"
 
-#: ../../include/conversation.php:1137
+#: include/conversation.php:1228
 msgid "Private post"
 msgstr "Личное сообщение"
 
-#: ../../include/network.php:895
+#: include/conversation.php:1385
+msgid "View all"
+msgstr ""
+
+#: include/conversation.php:1407
+msgid "Like"
+msgid_plural "Likes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+#: include/conversation.php:1410
+msgid "Dislike"
+msgid_plural "Dislikes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+#: include/conversation.php:1416
+msgid "Not Attending"
+msgid_plural "Not Attending"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+#: include/conversation.php:1419 include/profile_selectors.php:6
+msgid "Undecided"
+msgid_plural "Undecided"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+#: include/forums.php:105 include/text.php:1015 include/nav.php:126
+#: view/theme/vier/theme.php:259
+msgid "Forums"
+msgstr ""
+
+#: include/forums.php:107 view/theme/vier/theme.php:261
+msgid "External link to forum"
+msgstr ""
+
+#: include/network.php:967
 msgid "view full size"
 msgstr "посмотреть в полный размер"
 
-#: ../../include/text.php:297
+#: include/text.php:303
 msgid "newer"
 msgstr "новее"
 
-#: ../../include/text.php:299
+#: include/text.php:305
 msgid "older"
 msgstr "старее"
 
-#: ../../include/text.php:304
+#: include/text.php:310
 msgid "prev"
 msgstr "пред."
 
-#: ../../include/text.php:306
+#: include/text.php:312
 msgid "first"
 msgstr "первый"
 
-#: ../../include/text.php:338
+#: include/text.php:344
 msgid "last"
 msgstr "последний"
 
-#: ../../include/text.php:341
+#: include/text.php:347
 msgid "next"
 msgstr "след."
 
-#: ../../include/text.php:855
+#: include/text.php:402
+msgid "Loading more entries..."
+msgstr ""
+
+#: include/text.php:403
+msgid "The end"
+msgstr ""
+
+#: include/text.php:894
 msgid "No contacts"
 msgstr "Нет контактов"
 
-#: ../../include/text.php:864
+#: include/text.php:909
 #, php-format
 msgid "%d Contact"
 msgid_plural "%d Contacts"
 msgstr[0] "%d контакт"
 msgstr[1] "%d контактов"
 msgstr[2] "%d контактов"
+msgstr[3] "%d контактов"
+
+#: include/text.php:921
+msgid "View Contacts"
+msgstr "Просмотр контактов"
 
-#: ../../include/text.php:1005
+#: include/text.php:1010 include/nav.php:121
+msgid "Full Text"
+msgstr "Контент"
+
+#: include/text.php:1011 include/nav.php:122
+msgid "Tags"
+msgstr "Тэги"
+
+#: include/text.php:1066
 msgid "poke"
 msgstr "poke"
 
-#: ../../include/text.php:1006
+#: include/text.php:1066
+msgid "poked"
+msgstr ""
+
+#: include/text.php:1067
 msgid "ping"
 msgstr "пинг"
 
-#: ../../include/text.php:1006
+#: include/text.php:1067
 msgid "pinged"
 msgstr "пингуется"
 
-#: ../../include/text.php:1007
+#: include/text.php:1068
 msgid "prod"
 msgstr ""
 
-#: ../../include/text.php:1007
+#: include/text.php:1068
 msgid "prodded"
 msgstr ""
 
-#: ../../include/text.php:1008
+#: include/text.php:1069
 msgid "slap"
 msgstr ""
 
-#: ../../include/text.php:1008
+#: include/text.php:1069
 msgid "slapped"
 msgstr ""
 
-#: ../../include/text.php:1009
+#: include/text.php:1070
 msgid "finger"
 msgstr ""
 
-#: ../../include/text.php:1009
+#: include/text.php:1070
 msgid "fingered"
 msgstr ""
 
-#: ../../include/text.php:1010
+#: include/text.php:1071
 msgid "rebuff"
 msgstr ""
 
-#: ../../include/text.php:1010
+#: include/text.php:1071
 msgid "rebuffed"
 msgstr ""
 
-#: ../../include/text.php:1024
+#: include/text.php:1085
 msgid "happy"
 msgstr ""
 
-#: ../../include/text.php:1025
+#: include/text.php:1086
 msgid "sad"
 msgstr ""
 
-#: ../../include/text.php:1026
+#: include/text.php:1087
 msgid "mellow"
 msgstr ""
 
-#: ../../include/text.php:1027
+#: include/text.php:1088
 msgid "tired"
 msgstr ""
 
-#: ../../include/text.php:1028
+#: include/text.php:1089
 msgid "perky"
 msgstr ""
 
-#: ../../include/text.php:1029
+#: include/text.php:1090
 msgid "angry"
 msgstr ""
 
-#: ../../include/text.php:1030
+#: include/text.php:1091
 msgid "stupified"
 msgstr ""
 
-#: ../../include/text.php:1031
+#: include/text.php:1092
 msgid "puzzled"
 msgstr ""
 
-#: ../../include/text.php:1032
+#: include/text.php:1093
 msgid "interested"
 msgstr ""
 
-#: ../../include/text.php:1033
+#: include/text.php:1094
 msgid "bitter"
 msgstr ""
 
-#: ../../include/text.php:1034
+#: include/text.php:1095
 msgid "cheerful"
 msgstr ""
 
-#: ../../include/text.php:1035
+#: include/text.php:1096
 msgid "alive"
 msgstr ""
 
-#: ../../include/text.php:1036
+#: include/text.php:1097
 msgid "annoyed"
 msgstr ""
 
-#: ../../include/text.php:1037
+#: include/text.php:1098
 msgid "anxious"
 msgstr ""
 
-#: ../../include/text.php:1038
+#: include/text.php:1099
 msgid "cranky"
 msgstr ""
 
-#: ../../include/text.php:1039
+#: include/text.php:1100
 msgid "disturbed"
 msgstr ""
 
-#: ../../include/text.php:1040
+#: include/text.php:1101
 msgid "frustrated"
 msgstr ""
 
-#: ../../include/text.php:1041
+#: include/text.php:1102
 msgid "motivated"
 msgstr ""
 
-#: ../../include/text.php:1042
+#: include/text.php:1103
 msgid "relaxed"
 msgstr ""
 
-#: ../../include/text.php:1043
+#: include/text.php:1104
 msgid "surprised"
 msgstr ""
 
-#: ../../include/text.php:1213
-msgid "Monday"
-msgstr "Понедельник"
-
-#: ../../include/text.php:1213
-msgid "Tuesday"
-msgstr "Вторник"
-
-#: ../../include/text.php:1213
-msgid "Wednesday"
-msgstr "Среда"
-
-#: ../../include/text.php:1213
-msgid "Thursday"
-msgstr "Четверг"
-
-#: ../../include/text.php:1213
-msgid "Friday"
-msgstr "Пятница"
-
-#: ../../include/text.php:1213
-msgid "Saturday"
-msgstr "Суббота"
-
-#: ../../include/text.php:1213
-msgid "Sunday"
-msgstr "Воскресенье"
-
-#: ../../include/text.php:1217
-msgid "January"
-msgstr "Январь"
-
-#: ../../include/text.php:1217
-msgid "February"
-msgstr "Февраль"
-
-#: ../../include/text.php:1217
-msgid "March"
-msgstr "Март"
-
-#: ../../include/text.php:1217
-msgid "April"
-msgstr "Апрель"
-
-#: ../../include/text.php:1217
-msgid "May"
-msgstr "Май"
-
-#: ../../include/text.php:1217
-msgid "June"
-msgstr "Июнь"
-
-#: ../../include/text.php:1217
-msgid "July"
-msgstr "Июль"
-
-#: ../../include/text.php:1217
-msgid "August"
-msgstr "Август"
-
-#: ../../include/text.php:1217
-msgid "September"
-msgstr "Сентябрь"
-
-#: ../../include/text.php:1217
-msgid "October"
-msgstr "Октябрь"
-
-#: ../../include/text.php:1217
-msgid "November"
-msgstr "Ноябрь"
-
-#: ../../include/text.php:1217
-msgid "December"
-msgstr "Декабрь"
-
-#: ../../include/text.php:1437
+#: include/text.php:1504
 msgid "bytes"
 msgstr "байт"
 
-#: ../../include/text.php:1461 ../../include/text.php:1473
+#: include/text.php:1536 include/text.php:1548
 msgid "Click to open/close"
 msgstr "Нажмите, чтобы открыть / закрыть"
 
-#: ../../include/text.php:1702 ../../include/user.php:247
-#: ../../view/theme/duepuntozero/config.php:44
-msgid "default"
-msgstr "значение по умолчанию"
+#: include/text.php:1722
+msgid "View on separate page"
+msgstr ""
 
-#: ../../include/text.php:1714
-msgid "Select an alternate language"
-msgstr "Выбор альтернативного языка"
+#: include/text.php:1723
+msgid "view on separate page"
+msgstr ""
 
-#: ../../include/text.php:1970
+#: include/text.php:2002
 msgid "activity"
 msgstr "активность"
 
-#: ../../include/text.php:1973
+#: include/text.php:2005
 msgid "post"
 msgstr "сообщение"
 
-#: ../../include/text.php:2141
+#: include/text.php:2173
 msgid "Item filed"
 msgstr ""
 
-#: ../../include/bbcode.php:428 ../../include/bbcode.php:1047
-#: ../../include/bbcode.php:1048
+#: include/bbcode.php:482 include/bbcode.php:1157 include/bbcode.php:1158
 msgid "Image/photo"
 msgstr "Изображение / Фото"
 
-#: ../../include/bbcode.php:528
+#: include/bbcode.php:595
 #, php-format
 msgid "<a href=\"%1$s\" target=\"_blank\">%2$s</a> %3$s"
 msgstr ""
 
-#: ../../include/bbcode.php:562
+#: include/bbcode.php:629
 #, php-format
 msgid ""
-"<span><a href=\"%s\" target=\"_blank\">%s</a> wrote the following <a "
-"href=\"%s\" target=\"_blank\">post</a>"
+"<span><a href=\"%s\" target=\"_blank\">%s</a> wrote the following <a href="
+"\"%s\" target=\"_blank\">post</a>"
 msgstr ""
 
-#: ../../include/bbcode.php:1011 ../../include/bbcode.php:1031
+#: include/bbcode.php:1117 include/bbcode.php:1137
 msgid "$1 wrote:"
 msgstr "$1 написал:"
 
-#: ../../include/bbcode.php:1056 ../../include/bbcode.php:1057
+#: include/bbcode.php:1166 include/bbcode.php:1167
 msgid "Encrypted content"
 msgstr "Зашифрованный контент"
 
-#: ../../include/notifier.php:786 ../../include/delivery.php:456
-msgid "(no subject)"
-msgstr "(без темы)"
-
-#: ../../include/notifier.php:796 ../../include/delivery.php:467
-#: ../../include/enotify.php:33
-msgid "noreply"
-msgstr "без ответа"
-
-#: ../../include/dba_pdo.php:72 ../../include/dba.php:56
+#: include/dba_pdo.php:72 include/dba.php:55
 #, php-format
 msgid "Cannot locate DNS info for database server '%s'"
 msgstr "Не могу найти информацию для DNS-сервера базы данных '%s'"
 
-#: ../../include/contact_selectors.php:32
+#: include/contact_selectors.php:32
 msgid "Unknown | Not categorised"
 msgstr "Неизвестно | Не определено"
 
-#: ../../include/contact_selectors.php:33
+#: include/contact_selectors.php:33
 msgid "Block immediately"
 msgstr "Блокировать немедленно"
 
-#: ../../include/contact_selectors.php:34
+#: include/contact_selectors.php:34
 msgid "Shady, spammer, self-marketer"
 msgstr "Тролль, спаммер, рассылает рекламу"
 
-#: ../../include/contact_selectors.php:35
+#: include/contact_selectors.php:35
 msgid "Known to me, but no opinion"
 msgstr "Известные мне, но нет определенного мнения"
 
-#: ../../include/contact_selectors.php:36
+#: include/contact_selectors.php:36
 msgid "OK, probably harmless"
 msgstr "Хорошо, наверное, безвредные"
 
-#: ../../include/contact_selectors.php:37
+#: include/contact_selectors.php:37
 msgid "Reputable, has my trust"
 msgstr "Уважаемые, есть мое доверие"
 
-#: ../../include/contact_selectors.php:60
+#: include/contact_selectors.php:60
 msgid "Weekly"
 msgstr "Еженедельно"
 
-#: ../../include/contact_selectors.php:61
+#: include/contact_selectors.php:61
 msgid "Monthly"
 msgstr "Ежемесячно"
 
-#: ../../include/contact_selectors.php:77
+#: include/contact_selectors.php:77
 msgid "OStatus"
 msgstr "OStatus"
 
-#: ../../include/contact_selectors.php:78
+#: include/contact_selectors.php:78
 msgid "RSS/Atom"
 msgstr "RSS/Atom"
 
-#: ../../include/contact_selectors.php:82
+#: include/contact_selectors.php:81
+msgid "Facebook"
+msgstr "Facebook"
+
+#: include/contact_selectors.php:82
 msgid "Zot!"
 msgstr "Zot!"
 
-#: ../../include/contact_selectors.php:83
+#: include/contact_selectors.php:83
 msgid "LinkedIn"
 msgstr "LinkedIn"
 
-#: ../../include/contact_selectors.php:84
+#: include/contact_selectors.php:84
 msgid "XMPP/IM"
 msgstr "XMPP/IM"
 
-#: ../../include/contact_selectors.php:85
+#: include/contact_selectors.php:85
 msgid "MySpace"
 msgstr "MySpace"
 
-#: ../../include/contact_selectors.php:87
+#: include/contact_selectors.php:87
 msgid "Google+"
 msgstr "Google+"
 
-#: ../../include/contact_selectors.php:88
+#: include/contact_selectors.php:88
 msgid "pump.io"
 msgstr "pump.io"
 
-#: ../../include/contact_selectors.php:89
+#: include/contact_selectors.php:89
 msgid "Twitter"
 msgstr "Twitter"
 
-#: ../../include/contact_selectors.php:90
+#: include/contact_selectors.php:90
 msgid "Diaspora Connector"
 msgstr ""
 
-#: ../../include/contact_selectors.php:91
-msgid "Statusnet"
+#: include/contact_selectors.php:91
+msgid "GNU Social"
 msgstr ""
 
-#: ../../include/contact_selectors.php:92
+#: include/contact_selectors.php:92
 msgid "App.net"
 msgstr ""
 
-#: ../../include/Scrape.php:614
+#: include/contact_selectors.php:103
+msgid "Redmatrix"
+msgstr ""
+
+#: include/Scrape.php:624
 msgid " on Last.fm"
 msgstr "на Last.fm"
 
-#: ../../include/bb2diaspora.php:154 ../../include/event.php:20
+#: include/bb2diaspora.php:154 include/event.php:30 include/event.php:48
 msgid "Starts:"
 msgstr "Начало:"
 
-#: ../../include/bb2diaspora.php:162 ../../include/event.php:30
+#: include/bb2diaspora.php:162 include/event.php:33 include/event.php:54
 msgid "Finishes:"
 msgstr "Окончание:"
 
-#: ../../include/profile_advanced.php:22
-msgid "j F, Y"
-msgstr "j F, Y"
-
-#: ../../include/profile_advanced.php:23
-msgid "j F"
-msgstr "j F"
-
-#: ../../include/profile_advanced.php:30
-msgid "Birthday:"
-msgstr "День рождения:"
-
-#: ../../include/profile_advanced.php:34
-msgid "Age:"
-msgstr "Возраст:"
-
-#: ../../include/profile_advanced.php:43
-#, php-format
-msgid "for %1$d %2$s"
-msgstr ""
-
-#: ../../include/profile_advanced.php:52
-msgid "Tags:"
-msgstr "Ключевые слова: "
-
-#: ../../include/profile_advanced.php:56
-msgid "Religion:"
-msgstr "Религия:"
-
-#: ../../include/profile_advanced.php:60
-msgid "Hobbies/Interests:"
-msgstr "Хобби / Интересы:"
-
-#: ../../include/profile_advanced.php:67
-msgid "Contact information and Social Networks:"
-msgstr "Информация о контакте и социальных сетях:"
-
-#: ../../include/profile_advanced.php:69
-msgid "Musical interests:"
-msgstr "Музыкальные интересы:"
-
-#: ../../include/profile_advanced.php:71
-msgid "Books, literature:"
-msgstr "Книги, литература:"
-
-#: ../../include/profile_advanced.php:73
-msgid "Television:"
-msgstr "Телевидение:"
-
-#: ../../include/profile_advanced.php:75
-msgid "Film/dance/culture/entertainment:"
-msgstr "Кино / Танцы / Культура / Развлечения:"
-
-#: ../../include/profile_advanced.php:77
-msgid "Love/Romance:"
-msgstr "Любовь / Романтика:"
-
-#: ../../include/profile_advanced.php:79
-msgid "Work/employment:"
-msgstr "Работа / Занятость:"
-
-#: ../../include/profile_advanced.php:81
-msgid "School/education:"
-msgstr "Школа / Образование:"
-
-#: ../../include/plugin.php:455 ../../include/plugin.php:457
+#: include/plugin.php:522 include/plugin.php:524
 msgid "Click here to upgrade."
 msgstr "Нажмите для обновления."
 
-#: ../../include/plugin.php:463
+#: include/plugin.php:530
 msgid "This action exceeds the limits set by your subscription plan."
 msgstr "Это действие превышает лимиты, установленные вашим тарифным планом."
 
-#: ../../include/plugin.php:468
+#: include/plugin.php:535
 msgid "This action is not available under your subscription plan."
 msgstr "Это действие не доступно в соответствии с вашим планом подписки."
 
-#: ../../include/nav.php:73
+#: include/nav.php:72
 msgid "End this session"
-msgstr "Ð\9aонеÑ\86 Ñ\8dÑ\82ой Ñ\81еÑ\81Ñ\81ии"
+msgstr "Ð\97авеÑ\80Ñ\88иÑ\82Ñ\8c Ñ\8dÑ\82Ñ\83 Ñ\81еÑ\81Ñ\81иÑ\8e"
 
-#: ../../include/nav.php:76 ../../include/nav.php:148
-#: ../../view/theme/diabook/theme.php:123
+#: include/nav.php:75 include/nav.php:157 view/theme/diabook/theme.php:123
 msgid "Your posts and conversations"
-msgstr "Ð\92аÑ\88и Ñ\81ообÑ\89ениÑ\8f Ð¸ Ð±ÐµÑ\81едÑ\8b"
+msgstr "Ð\94аннÑ\8bе Ð²Ð°Ñ\88ей Ñ\83Ñ\87Ñ\91Ñ\82ной Ð·Ð°Ð¿Ð¸Ñ\81и"
 
-#: ../../include/nav.php:77 ../../view/theme/diabook/theme.php:124
+#: include/nav.php:76 view/theme/diabook/theme.php:124
 msgid "Your profile page"
-msgstr "СÑ\82Ñ\80аниÑ\86а Ð\92аÑ\88его Ð¿Ñ\80оÑ\84илÑ\8f"
+msgstr "Ð\98нÑ\84оÑ\80маÑ\86иÑ\8f Ð¾ Ð²Ð°Ñ\81"
 
-#: ../../include/nav.php:78 ../../view/theme/diabook/theme.php:126
+#: include/nav.php:77 view/theme/diabook/theme.php:126
 msgid "Your photos"
 msgstr "Ваши фотографии"
 
-#: ../../include/nav.php:79
+#: include/nav.php:78
 msgid "Your videos"
 msgstr ""
 
-#: ../../include/nav.php:80 ../../view/theme/diabook/theme.php:127
+#: include/nav.php:79 view/theme/diabook/theme.php:127
 msgid "Your events"
 msgstr "Ваши события"
 
-#: ../../include/nav.php:81 ../../view/theme/diabook/theme.php:128
+#: include/nav.php:80 view/theme/diabook/theme.php:128
 msgid "Personal notes"
 msgstr "Личные заметки"
 
-#: ../../include/nav.php:81
+#: include/nav.php:80
 msgid "Your personal notes"
 msgstr ""
 
-#: ../../include/nav.php:92
+#: include/nav.php:91
 msgid "Sign in"
 msgstr "Вход"
 
-#: ../../include/nav.php:105
+#: include/nav.php:104
 msgid "Home Page"
 msgstr "Главная страница"
 
-#: ../../include/nav.php:109
+#: include/nav.php:108
 msgid "Create an account"
 msgstr "Создать аккаунт"
 
-#: ../../include/nav.php:114
+#: include/nav.php:113
 msgid "Help and documentation"
 msgstr "Помощь и документация"
 
-#: ../../include/nav.php:117
+#: include/nav.php:116
 msgid "Apps"
 msgstr "Приложения"
 
-#: ../../include/nav.php:117
+#: include/nav.php:116
 msgid "Addon applications, utilities, games"
 msgstr "Дополнительные приложения, утилиты, игры"
 
-#: ../../include/nav.php:119
+#: include/nav.php:118
 msgid "Search site content"
 msgstr "Поиск по сайту"
 
-#: ../../include/nav.php:129
+#: include/nav.php:136
 msgid "Conversations on this site"
 msgstr "Беседы на этом сайте"
 
-#: ../../include/nav.php:131
+#: include/nav.php:138
 msgid "Conversations on the network"
 msgstr ""
 
-#: ../../include/nav.php:133
+#: include/nav.php:142
 msgid "Directory"
 msgstr "Каталог"
 
-#: ../../include/nav.php:133
+#: include/nav.php:142
 msgid "People directory"
 msgstr "Каталог участников"
 
-#: ../../include/nav.php:135
+#: include/nav.php:144
 msgid "Information"
-msgstr ""
+msgstr "Информация"
 
-#: ../../include/nav.php:135
+#: include/nav.php:144
 msgid "Information about this friendica instance"
 msgstr ""
 
-#: ../../include/nav.php:145
+#: include/nav.php:154
 msgid "Conversations from your friends"
-msgstr "Ð\91еÑ\81едÑ\8b Ñ\81 Ð´Ñ\80Ñ\83зÑ\8cÑ\8fми"
+msgstr "Ð\9fоÑ\81Ñ\82Ñ\8b Ð²Ð°Ñ\88иÑ\85 Ð´Ñ\80Ñ\83зей"
 
-#: ../../include/nav.php:146
+#: include/nav.php:155
 msgid "Network Reset"
 msgstr "Перезагрузка сети"
 
-#: ../../include/nav.php:146
+#: include/nav.php:155
 msgid "Load Network page with no filters"
 msgstr "Загрузить страницу сети без фильтров"
 
-#: ../../include/nav.php:154
+#: include/nav.php:162
 msgid "Friend Requests"
 msgstr "Запросы на добавление в список друзей"
 
-#: ../../include/nav.php:156
+#: include/nav.php:166
 msgid "See all notifications"
 msgstr "Посмотреть все уведомления"
 
-#: ../../include/nav.php:157
+#: include/nav.php:167
 msgid "Mark all system notifications seen"
 msgstr "Отметить все системные уведомления, как прочитанные"
 
-#: ../../include/nav.php:161
+#: include/nav.php:171
 msgid "Private mail"
 msgstr "Личная почта"
 
-#: ../../include/nav.php:162
+#: include/nav.php:172
 msgid "Inbox"
 msgstr "Входящие"
 
-#: ../../include/nav.php:163
+#: include/nav.php:173
 msgid "Outbox"
 msgstr "Исходящие"
 
-#: ../../include/nav.php:167
+#: include/nav.php:177
 msgid "Manage"
 msgstr "Управлять"
 
-#: ../../include/nav.php:167
+#: include/nav.php:177
 msgid "Manage other pages"
 msgstr "Управление другими страницами"
 
-#: ../../include/nav.php:172
+#: include/nav.php:182
 msgid "Account settings"
 msgstr "Настройки аккаунта"
 
-#: ../../include/nav.php:175
+#: include/nav.php:185
 msgid "Manage/Edit Profiles"
 msgstr "Управление/редактирование профилей"
 
-#: ../../include/nav.php:177
+#: include/nav.php:187
 msgid "Manage/edit friends and contacts"
 msgstr "Управление / редактирование друзей и контактов"
 
-#: ../../include/nav.php:184
+#: include/nav.php:194
 msgid "Site setup and configuration"
-msgstr "УÑ\81Ñ\82ановка Ð¸ Ðºонфигурация сайта"
+msgstr "Ð\9aонфигурация сайта"
 
-#: ../../include/nav.php:188
+#: include/nav.php:198
 msgid "Navigation"
 msgstr "Навигация"
 
-#: ../../include/nav.php:188
+#: include/nav.php:198
 msgid "Site map"
 msgstr "Карта сайта"
 
-#: ../../include/api.php:304 ../../include/api.php:315
-#: ../../include/api.php:416 ../../include/api.php:1063
-#: ../../include/api.php:1065
-msgid "User not found."
-msgstr "Пользователь не найден."
-
-#: ../../include/api.php:771
+#: include/api.php:878
 #, php-format
 msgid "Daily posting limit of %d posts reached. The post was rejected."
 msgstr ""
 
-#: ../../include/api.php:790
+#: include/api.php:897
 #, php-format
 msgid "Weekly posting limit of %d posts reached. The post was rejected."
 msgstr ""
 
-#: ../../include/api.php:809
+#: include/api.php:916
 #, php-format
 msgid "Monthly posting limit of %d posts reached. The post was rejected."
 msgstr ""
 
-#: ../../include/api.php:1272
-msgid "There is no status with this id."
-msgstr "Нет статуса с таким id."
-
-#: ../../include/api.php:1342
-msgid "There is no conversation with this id."
-msgstr ""
-
-#: ../../include/api.php:1614
-msgid "Invalid request."
-msgstr ""
-
-#: ../../include/api.php:1625
-msgid "Invalid item."
-msgstr ""
-
-#: ../../include/api.php:1635
-msgid "Invalid action. "
-msgstr ""
-
-#: ../../include/api.php:1643
-msgid "DB error"
-msgstr ""
-
-#: ../../include/user.php:40
+#: include/user.php:48
 msgid "An invitation is required."
 msgstr "Требуется приглашение."
 
-#: ../../include/user.php:45
+#: include/user.php:53
 msgid "Invitation could not be verified."
 msgstr "Приглашение не может быть проверено."
 
-#: ../../include/user.php:53
+#: include/user.php:61
 msgid "Invalid OpenID url"
 msgstr "Неверный URL OpenID"
 
-#: ../../include/user.php:74
+#: include/user.php:82
 msgid "Please enter the required information."
 msgstr "Пожалуйста, введите необходимую информацию."
 
-#: ../../include/user.php:88
+#: include/user.php:96
 msgid "Please use a shorter name."
 msgstr "Пожалуйста, используйте более короткое имя."
 
-#: ../../include/user.php:90
+#: include/user.php:98
 msgid "Name too short."
 msgstr "Имя слишком короткое."
 
-#: ../../include/user.php:105
+#: include/user.php:113
 msgid "That doesn't appear to be your full (First Last) name."
 msgstr "Кажется, что это ваше неполное (Имя Фамилия) имя."
 
-#: ../../include/user.php:110
+#: include/user.php:118
 msgid "Your email domain is not among those allowed on this site."
-msgstr "Домен вашего адреса электронной почты не относится к числу разрешенных на этом сайте."
+msgstr ""
+"Домен вашего адреса электронной почты не относится к числу разрешенных на "
+"этом сайте."
 
-#: ../../include/user.php:113
+#: include/user.php:121
 msgid "Not a valid email address."
 msgstr "Неверный адрес электронной почты."
 
-#: ../../include/user.php:126
+#: include/user.php:134
 msgid "Cannot use that email."
 msgstr "Нельзя использовать этот Email."
 
-#: ../../include/user.php:132
-msgid ""
-"Your \"nickname\" can only contain \"a-z\", \"0-9\", \"-\", and \"_\", and "
-"must also begin with a letter."
-msgstr "Ваш \"ник\" может содержать только \"a-z\", \"0-9\", \"-\", и \"_\", а также должен начинаться с буквы."
+#: include/user.php:140
+msgid "Your \"nickname\" can only contain \"a-z\", \"0-9\" and \"_\"."
+msgstr ""
 
-#: ../../include/user.php:138 ../../include/user.php:236
+#: include/user.php:147 include/user.php:245
 msgid "Nickname is already registered. Please choose another."
 msgstr "Такой ник уже зарегистрирован. Пожалуйста, выберите другой."
 
-#: ../../include/user.php:148
+#: include/user.php:157
 msgid ""
 "Nickname was once registered here and may not be re-used. Please choose "
 "another."
-msgstr "Ник уже зарегистрирован на этом сайте и не может быть изменён. Пожалуйста, выберите другой ник."
+msgstr ""
+"Ник уже зарегистрирован на этом сайте и не может быть изменён. Пожалуйста, "
+"выберите другой ник."
 
-#: ../../include/user.php:164
+#: include/user.php:173
 msgid "SERIOUS ERROR: Generation of security keys failed."
 msgstr "СЕРЬЕЗНАЯ ОШИБКА: генерация ключей безопасности не удалась."
 
-#: ../../include/user.php:222
+#: include/user.php:231
 msgid "An error occurred during registration. Please try again."
 msgstr "Ошибка при регистрации. Пожалуйста, попробуйте еще раз."
 
-#: ../../include/user.php:257
+#: include/user.php:256 view/theme/duepuntozero/config.php:44
+msgid "default"
+msgstr "значение по умолчанию"
+
+#: include/user.php:266
 msgid "An error occurred creating your default profile. Please try again."
 msgstr "Ошибка создания вашего профиля. Пожалуйста, попробуйте еще раз."
 
-#: ../../include/user.php:289 ../../include/user.php:293
-#: ../../include/profile_selectors.php:42
+#: include/user.php:299 include/user.php:303 include/profile_selectors.php:42
 msgid "Friends"
 msgstr "Друзья"
 
-#: ../../include/user.php:377
+#: include/user.php:387
 #, php-format
 msgid ""
 "\n"
@@ -7099,7 +8110,7 @@ msgid ""
 "\t"
 msgstr ""
 
-#: ../../include/user.php:381
+#: include/user.php:391
 #, php-format
 msgid ""
 "\n"
@@ -7108,20 +8119,25 @@ msgid ""
 "\t\t\tLogin Name:\t%1$s\n"
 "\t\t\tPassword:\t%5$s\n"
 "\n"
-"\t\tYou may change your password from your account \"Settings\" page after logging\n"
+"\t\tYou may change your password from your account \"Settings\" page after "
+"logging\n"
 "\t\tin.\n"
 "\n"
-"\t\tPlease take a few moments to review the other account settings on that page.\n"
+"\t\tPlease take a few moments to review the other account settings on that "
+"page.\n"
 "\n"
 "\t\tYou may also wish to add some basic information to your default profile\n"
 "\t\t(on the \"Profiles\" page) so that other people can easily find you.\n"
 "\n"
 "\t\tWe recommend setting your full name, adding a profile photo,\n"
-"\t\tadding some profile \"keywords\" (very useful in making new friends) - and\n"
-"\t\tperhaps what country you live in; if you do not wish to be more specific\n"
+"\t\tadding some profile \"keywords\" (very useful in making new friends) - "
+"and\n"
+"\t\tperhaps what country you live in; if you do not wish to be more "
+"specific\n"
 "\t\tthan that.\n"
 "\n"
-"\t\tWe fully respect your right to privacy, and none of these items are necessary.\n"
+"\t\tWe fully respect your right to privacy, and none of these items are "
+"necessary.\n"
 "\t\tIf you are new and do not know anybody here, they may help\n"
 "\t\tyou to make some new and interesting friends.\n"
 "\n"
@@ -7129,495 +8145,504 @@ msgid ""
 "\t\tThank you and welcome to %2$s."
 msgstr ""
 
-#: ../../include/diaspora.php:703
+#: include/diaspora.php:720
 msgid "Sharing notification from Diaspora network"
 msgstr "Делиться уведомлениями из сети Diaspora"
 
-#: ../../include/diaspora.php:2520
+#: include/diaspora.php:2625
 msgid "Attachments:"
 msgstr "Вложения:"
 
-#: ../../include/items.php:4555
+#: include/delivery.php:533
+msgid "(no subject)"
+msgstr "(без темы)"
+
+#: include/delivery.php:544 include/enotify.php:37
+msgid "noreply"
+msgstr "без ответа"
+
+#: include/items.php:4926
 msgid "Do you really want to delete this item?"
 msgstr "Вы действительно хотите удалить этот элемент?"
 
-#: ../../include/items.php:4778
+#: include/items.php:5201
 msgid "Archives"
 msgstr "Архивы"
 
-#: ../../include/profile_selectors.php:6
+#: include/profile_selectors.php:6
 msgid "Male"
 msgstr "Мужчина"
 
-#: ../../include/profile_selectors.php:6
+#: include/profile_selectors.php:6
 msgid "Female"
 msgstr "Женщина"
 
-#: ../../include/profile_selectors.php:6
+#: include/profile_selectors.php:6
 msgid "Currently Male"
 msgstr "В данный момент мужчина"
 
-#: ../../include/profile_selectors.php:6
+#: include/profile_selectors.php:6
 msgid "Currently Female"
 msgstr "В настоящее время женщина"
 
-#: ../../include/profile_selectors.php:6
+#: include/profile_selectors.php:6
 msgid "Mostly Male"
 msgstr "В основном мужчина"
 
-#: ../../include/profile_selectors.php:6
+#: include/profile_selectors.php:6
 msgid "Mostly Female"
 msgstr "В основном женщина"
 
-#: ../../include/profile_selectors.php:6
+#: include/profile_selectors.php:6
 msgid "Transgender"
 msgstr "Транссексуал"
 
-#: ../../include/profile_selectors.php:6
+#: include/profile_selectors.php:6
 msgid "Intersex"
 msgstr "Интерсексуал"
 
-#: ../../include/profile_selectors.php:6
+#: include/profile_selectors.php:6
 msgid "Transsexual"
 msgstr "Транссексуал"
 
-#: ../../include/profile_selectors.php:6
+#: include/profile_selectors.php:6
 msgid "Hermaphrodite"
 msgstr "Гермафродит"
 
-#: ../../include/profile_selectors.php:6
+#: include/profile_selectors.php:6
 msgid "Neuter"
 msgstr "Средний род"
 
-#: ../../include/profile_selectors.php:6
+#: include/profile_selectors.php:6
 msgid "Non-specific"
 msgstr "Не определен"
 
-#: ../../include/profile_selectors.php:6
+#: include/profile_selectors.php:6
 msgid "Other"
 msgstr "Другой"
 
-#: ../../include/profile_selectors.php:6
-msgid "Undecided"
-msgstr "Не решено"
-
-#: ../../include/profile_selectors.php:23
+#: include/profile_selectors.php:23
 msgid "Males"
 msgstr "Мужчины"
 
-#: ../../include/profile_selectors.php:23
+#: include/profile_selectors.php:23
 msgid "Females"
 msgstr "Женщины"
 
-#: ../../include/profile_selectors.php:23
+#: include/profile_selectors.php:23
 msgid "Gay"
 msgstr "Гей"
 
-#: ../../include/profile_selectors.php:23
+#: include/profile_selectors.php:23
 msgid "Lesbian"
 msgstr "Лесбиянка"
 
-#: ../../include/profile_selectors.php:23
+#: include/profile_selectors.php:23
 msgid "No Preference"
 msgstr "Без предпочтений"
 
-#: ../../include/profile_selectors.php:23
+#: include/profile_selectors.php:23
 msgid "Bisexual"
 msgstr "Бисексуал"
 
-#: ../../include/profile_selectors.php:23
+#: include/profile_selectors.php:23
 msgid "Autosexual"
 msgstr "Автосексуал"
 
-#: ../../include/profile_selectors.php:23
+#: include/profile_selectors.php:23
 msgid "Abstinent"
 msgstr "Воздержанный"
 
-#: ../../include/profile_selectors.php:23
+#: include/profile_selectors.php:23
 msgid "Virgin"
 msgstr "Девственница"
 
-#: ../../include/profile_selectors.php:23
+#: include/profile_selectors.php:23
 msgid "Deviant"
 msgstr "Deviant"
 
-#: ../../include/profile_selectors.php:23
+#: include/profile_selectors.php:23
 msgid "Fetish"
 msgstr "Фетиш"
 
-#: ../../include/profile_selectors.php:23
+#: include/profile_selectors.php:23
 msgid "Oodles"
 msgstr "Групповой"
 
-#: ../../include/profile_selectors.php:23
+#: include/profile_selectors.php:23
 msgid "Nonsexual"
 msgstr "Нет интереса к сексу"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Single"
 msgstr "Без пары"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Lonely"
 msgstr "Пока никого нет"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Available"
 msgstr "Доступный"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Unavailable"
 msgstr "Не ищу никого"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Has crush"
 msgstr "Имеет ошибку"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Infatuated"
 msgstr "Влюблён"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Dating"
 msgstr "Свидания"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Unfaithful"
 msgstr "Изменяю супругу"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Sex Addict"
 msgstr "Люблю секс"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Friends/Benefits"
 msgstr "Друзья / Предпочтения"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Casual"
 msgstr "Обычный"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Engaged"
 msgstr "Занят"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Married"
 msgstr "Женат / Замужем"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Imaginarily married"
 msgstr ""
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Partners"
 msgstr "Партнеры"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Cohabiting"
 msgstr "Партнерство"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Common law"
 msgstr ""
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Happy"
 msgstr "Счастлив/а/"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Not looking"
 msgstr "Не в поиске"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Swinger"
 msgstr "Свинг"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Betrayed"
 msgstr "Преданный"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Separated"
 msgstr "Разделенный"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Unstable"
 msgstr "Нестабильный"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Divorced"
 msgstr "Разведен(а)"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Imaginarily divorced"
 msgstr ""
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Widowed"
 msgstr "Овдовевший"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Uncertain"
 msgstr "Неопределенный"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "It's complicated"
 msgstr "влишком сложно"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Don't care"
 msgstr "Не беспокоить"
 
-#: ../../include/profile_selectors.php:42
+#: include/profile_selectors.php:42
 msgid "Ask me"
 msgstr "Спросите меня"
 
-#: ../../include/enotify.php:18
+#: include/enotify.php:18
 msgid "Friendica Notification"
 msgstr "Friendica уведомления"
 
-#: ../../include/enotify.php:21
+#: include/enotify.php:21
 msgid "Thank You,"
 msgstr "Спасибо,"
 
-#: ../../include/enotify.php:23
+#: include/enotify.php:24
 #, php-format
 msgid "%s Administrator"
 msgstr "%s администратор"
 
-#: ../../include/enotify.php:64
+#: include/enotify.php:26
+#, php-format
+msgid "%1$s, %2$s Administrator"
+msgstr ""
+
+#: include/enotify.php:68
 #, php-format
 msgid "%s <!item_type!>"
 msgstr "%s <!item_type!>"
 
-#: ../../include/enotify.php:68
+#: include/enotify.php:82
 #, php-format
 msgid "[Friendica:Notify] New mail received at %s"
 msgstr "[Friendica: Оповещение] Новое сообщение, пришедшее на %s"
 
-#: ../../include/enotify.php:70
+#: include/enotify.php:84
 #, php-format
 msgid "%1$s sent you a new private message at %2$s."
 msgstr "%1$s отправил вам новое личное сообщение на %2$s."
 
-#: ../../include/enotify.php:71
+#: include/enotify.php:85
 #, php-format
 msgid "%1$s sent you %2$s."
 msgstr "%1$s послал вам %2$s."
 
-#: ../../include/enotify.php:71
+#: include/enotify.php:85
 msgid "a private message"
 msgstr "личное сообщение"
 
-#: ../../include/enotify.php:72
+#: include/enotify.php:86
 #, php-format
 msgid "Please visit %s to view and/or reply to your private messages."
-msgstr "Пожалуйста, посетите %s для просмотра и/или ответа на личные сообщения."
+msgstr ""
+"Пожалуйста, посетите %s для просмотра и/или ответа на личные сообщения."
 
-#: ../../include/enotify.php:124
+#: include/enotify.php:138
 #, php-format
 msgid "%1$s commented on [url=%2$s]a %3$s[/url]"
 msgstr "%1$s прокомментировал [url=%2$s]a %3$s[/url]"
 
-#: ../../include/enotify.php:131
+#: include/enotify.php:145
 #, php-format
 msgid "%1$s commented on [url=%2$s]%3$s's %4$s[/url]"
 msgstr "%1$s прокомментировал [url=%2$s]%3$s's %4$s[/url]"
 
-#: ../../include/enotify.php:139
+#: include/enotify.php:153
 #, php-format
 msgid "%1$s commented on [url=%2$s]your %3$s[/url]"
 msgstr "%1$s прокомментировал [url=%2$s]your %3$s[/url]"
 
-#: ../../include/enotify.php:149
+#: include/enotify.php:163
 #, php-format
 msgid "[Friendica:Notify] Comment to conversation #%1$d by %2$s"
 msgstr ""
 
-#: ../../include/enotify.php:150
+#: include/enotify.php:164
 #, php-format
 msgid "%s commented on an item/conversation you have been following."
 msgstr ""
 
-#: ../../include/enotify.php:153 ../../include/enotify.php:168
-#: ../../include/enotify.php:181 ../../include/enotify.php:194
-#: ../../include/enotify.php:212 ../../include/enotify.php:225
+#: include/enotify.php:167 include/enotify.php:182 include/enotify.php:195
+#: include/enotify.php:208 include/enotify.php:226 include/enotify.php:239
 #, php-format
 msgid "Please visit %s to view and/or reply to the conversation."
 msgstr ""
 
-#: ../../include/enotify.php:160
+#: include/enotify.php:174
 #, php-format
 msgid "[Friendica:Notify] %s posted to your profile wall"
 msgstr "[Friendica:Оповещение] %s написал на стене вашего профиля"
 
-#: ../../include/enotify.php:162
+#: include/enotify.php:176
 #, php-format
 msgid "%1$s posted to your profile wall at %2$s"
 msgstr ""
 
-#: ../../include/enotify.php:164
+#: include/enotify.php:178
 #, php-format
 msgid "%1$s posted to [url=%2$s]your wall[/url]"
 msgstr ""
 
-#: ../../include/enotify.php:175
+#: include/enotify.php:189
 #, php-format
 msgid "[Friendica:Notify] %s tagged you"
 msgstr ""
 
-#: ../../include/enotify.php:176
+#: include/enotify.php:190
 #, php-format
 msgid "%1$s tagged you at %2$s"
 msgstr ""
 
-#: ../../include/enotify.php:177
+#: include/enotify.php:191
 #, php-format
 msgid "%1$s [url=%2$s]tagged you[/url]."
 msgstr ""
 
-#: ../../include/enotify.php:188
+#: include/enotify.php:202
 #, php-format
 msgid "[Friendica:Notify] %s shared a new post"
 msgstr ""
 
-#: ../../include/enotify.php:189
+#: include/enotify.php:203
 #, php-format
 msgid "%1$s shared a new post at %2$s"
 msgstr ""
 
-#: ../../include/enotify.php:190
+#: include/enotify.php:204
 #, php-format
 msgid "%1$s [url=%2$s]shared a post[/url]."
 msgstr ""
 
-#: ../../include/enotify.php:202
+#: include/enotify.php:216
 #, php-format
 msgid "[Friendica:Notify] %1$s poked you"
 msgstr ""
 
-#: ../../include/enotify.php:203
+#: include/enotify.php:217
 #, php-format
 msgid "%1$s poked you at %2$s"
 msgstr ""
 
-#: ../../include/enotify.php:204
+#: include/enotify.php:218
 #, php-format
 msgid "%1$s [url=%2$s]poked you[/url]."
 msgstr ""
 
-#: ../../include/enotify.php:219
+#: include/enotify.php:233
 #, php-format
 msgid "[Friendica:Notify] %s tagged your post"
 msgstr ""
 
-#: ../../include/enotify.php:220
+#: include/enotify.php:234
 #, php-format
 msgid "%1$s tagged your post at %2$s"
 msgstr ""
 
-#: ../../include/enotify.php:221
+#: include/enotify.php:235
 #, php-format
 msgid "%1$s tagged [url=%2$s]your post[/url]"
 msgstr ""
 
-#: ../../include/enotify.php:232
+#: include/enotify.php:246
 msgid "[Friendica:Notify] Introduction received"
 msgstr "[Friendica:Сообщение] получен запрос"
 
-#: ../../include/enotify.php:233
+#: include/enotify.php:247
 #, php-format
 msgid "You've received an introduction from '%1$s' at %2$s"
 msgstr ""
 
-#: ../../include/enotify.php:234
+#: include/enotify.php:248
 #, php-format
 msgid "You've received [url=%1$s]an introduction[/url] from %2$s."
 msgstr ""
 
-#: ../../include/enotify.php:237 ../../include/enotify.php:279
+#: include/enotify.php:251 include/enotify.php:293
 #, php-format
 msgid "You may visit their profile at %s"
 msgstr "Вы можете посмотреть его профиль здесь %s"
 
-#: ../../include/enotify.php:239
+#: include/enotify.php:253
 #, php-format
 msgid "Please visit %s to approve or reject the introduction."
 msgstr "Посетите %s для подтверждения или отказа запроса."
 
-#: ../../include/enotify.php:247
+#: include/enotify.php:261
 msgid "[Friendica:Notify] A new person is sharing with you"
 msgstr ""
 
-#: ../../include/enotify.php:248 ../../include/enotify.php:249
+#: include/enotify.php:262 include/enotify.php:263
 #, php-format
 msgid "%1$s is sharing with you at %2$s"
 msgstr ""
 
-#: ../../include/enotify.php:255
+#: include/enotify.php:269
 msgid "[Friendica:Notify] You have a new follower"
 msgstr ""
 
-#: ../../include/enotify.php:256 ../../include/enotify.php:257
+#: include/enotify.php:270 include/enotify.php:271
 #, php-format
 msgid "You have a new follower at %2$s : %1$s"
 msgstr ""
 
-#: ../../include/enotify.php:270
+#: include/enotify.php:284
 msgid "[Friendica:Notify] Friend suggestion received"
 msgstr "[Friendica: Оповещение] получено предложение дружбы"
 
-#: ../../include/enotify.php:271
+#: include/enotify.php:285
 #, php-format
 msgid "You've received a friend suggestion from '%1$s' at %2$s"
 msgstr "Вы получили предложение дружбы от '%1$s' на %2$s"
 
-#: ../../include/enotify.php:272
+#: include/enotify.php:286
 #, php-format
-msgid ""
-"You've received [url=%1$s]a friend suggestion[/url] for %2$s from %3$s."
+msgid "You've received [url=%1$s]a friend suggestion[/url] for %2$s from %3$s."
 msgstr ""
 
-#: ../../include/enotify.php:277
+#: include/enotify.php:291
 msgid "Name:"
 msgstr "Имя:"
 
-#: ../../include/enotify.php:278
+#: include/enotify.php:292
 msgid "Photo:"
 msgstr "Фото:"
 
-#: ../../include/enotify.php:281
+#: include/enotify.php:295
 #, php-format
 msgid "Please visit %s to approve or reject the suggestion."
 msgstr "Пожалуйста, посетите %s для подтверждения, или отказа запроса."
 
-#: ../../include/enotify.php:289 ../../include/enotify.php:302
+#: include/enotify.php:303 include/enotify.php:316
 msgid "[Friendica:Notify] Connection accepted"
 msgstr ""
 
-#: ../../include/enotify.php:290 ../../include/enotify.php:303
+#: include/enotify.php:304 include/enotify.php:317
 #, php-format
-msgid "'%1$s' has acepted your connection request at %2$s"
+msgid "'%1$s' has accepted your connection request at %2$s"
 msgstr ""
 
-#: ../../include/enotify.php:291 ../../include/enotify.php:304
+#: include/enotify.php:305 include/enotify.php:318
 #, php-format
 msgid "%2$s has accepted your [url=%1$s]connection request[/url]."
 msgstr ""
 
-#: ../../include/enotify.php:294
+#: include/enotify.php:308
 msgid ""
-"You are now mutual friends and may exchange status updates, photos, and email\n"
+"You are now mutual friends and may exchange status updates, photos, and "
+"email\n"
 "\twithout restriction."
 msgstr ""
 
-#: ../../include/enotify.php:297 ../../include/enotify.php:311
+#: include/enotify.php:311 include/enotify.php:325
 #, php-format
 msgid "Please visit %s  if you wish to make any changes to this relationship."
 msgstr ""
 
-#: ../../include/enotify.php:307
+#: include/enotify.php:321
 #, php-format
 msgid ""
 "'%1$s' has chosen to accept you a \"fan\", which restricts some forms of "
@@ -7626,266 +8651,261 @@ msgid ""
 "automatically."
 msgstr ""
 
-#: ../../include/enotify.php:309
+#: include/enotify.php:323
 #, php-format
 msgid ""
 "'%1$s' may choose to extend this into a two-way or more permissive "
 "relationship in the future. "
 msgstr ""
 
-#: ../../include/enotify.php:322
+#: include/enotify.php:336
 msgid "[Friendica System:Notify] registration request"
 msgstr ""
 
-#: ../../include/enotify.php:323
+#: include/enotify.php:337
 #, php-format
 msgid "You've received a registration request from '%1$s' at %2$s"
 msgstr ""
 
-#: ../../include/enotify.php:324
+#: include/enotify.php:338
 #, php-format
 msgid "You've received a [url=%1$s]registration request[/url] from %2$s."
 msgstr ""
 
-#: ../../include/enotify.php:327
+#: include/enotify.php:341
 #, php-format
 msgid "Full Name:\t%1$s\\nSite Location:\t%2$s\\nLogin Name:\t%3$s (%4$s)"
 msgstr ""
 
-#: ../../include/enotify.php:330
+#: include/enotify.php:344
 #, php-format
 msgid "Please visit %s to approve or reject the request."
 msgstr ""
 
-#: ../../include/oembed.php:212
+#: include/oembed.php:226
 msgid "Embedded content"
 msgstr "Встроенное содержание"
 
-#: ../../include/oembed.php:221
+#: include/oembed.php:235
 msgid "Embedding disabled"
 msgstr "Встраивание отключено"
 
-#: ../../include/uimport.php:94
+#: include/uimport.php:94
 msgid "Error decoding account file"
 msgstr "Ошибка расшифровки файла аккаунта"
 
-#: ../../include/uimport.php:100
+#: include/uimport.php:100
 msgid "Error! No version data in file! This is not a Friendica account file?"
-msgstr "Ошибка! Неправильная версия данных в файле! Это не файл аккаунта Friendica?"
+msgstr ""
+"Ошибка! Неправильная версия данных в файле! Это не файл аккаунта Friendica?"
 
-#: ../../include/uimport.php:116 ../../include/uimport.php:127
+#: include/uimport.php:116 include/uimport.php:127
 msgid "Error! Cannot check nickname"
 msgstr "Ошибка! Невозможно проверить никнейм"
 
-#: ../../include/uimport.php:120 ../../include/uimport.php:131
+#: include/uimport.php:120 include/uimport.php:131
 #, php-format
 msgid "User '%s' already exists on this server!"
 msgstr "Пользователь '%s' уже существует на этом сервере!"
 
-#: ../../include/uimport.php:153
+#: include/uimport.php:153
 msgid "User creation error"
 msgstr "Ошибка создания пользователя"
 
-#: ../../include/uimport.php:171
+#: include/uimport.php:173
 msgid "User profile creation error"
 msgstr "Ошибка создания профиля пользователя"
 
-#: ../../include/uimport.php:220
+#: include/uimport.php:222
 #, php-format
 msgid "%d contact not imported"
 msgid_plural "%d contacts not imported"
 msgstr[0] "%d контакт не импортирован"
 msgstr[1] "%d контакты не импортированы"
 msgstr[2] "%d контакты не импортированы"
+msgstr[3] "%d контакты не импортированы"
 
-#: ../../include/uimport.php:290
+#: include/uimport.php:292
 msgid "Done. You can now login with your username and password"
 msgstr "Завершено. Теперь вы можете войти с вашим логином и паролем"
 
-#: ../../index.php:428
+#: index.php:442
 msgid "toggle mobile"
 msgstr "мобильная версия"
 
-#: ../../view/theme/cleanzero/config.php:82
-#: ../../view/theme/dispy/config.php:72 ../../view/theme/quattro/config.php:66
-#: ../../view/theme/diabook/config.php:150 ../../view/theme/vier/config.php:55
-#: ../../view/theme/duepuntozero/config.php:61
-msgid "Theme settings"
-msgstr "Настройки темы"
-
-#: ../../view/theme/cleanzero/config.php:83
+#: view/theme/cleanzero/config.php:83
 msgid "Set resize level for images in posts and comments (width and height)"
-msgstr "Установить уровень изменения размера изображений в постах и ​​комментариях (ширина и высота)"
+msgstr ""
+"Установить уровень изменения размера изображений в постах и ​​комментариях "
+"(ширина и высота)"
 
-#: ../../view/theme/cleanzero/config.php:84
-#: ../../view/theme/dispy/config.php:73
-#: ../../view/theme/diabook/config.php:151
+#: view/theme/cleanzero/config.php:84 view/theme/dispy/config.php:73
+#: view/theme/diabook/config.php:151
 msgid "Set font-size for posts and comments"
 msgstr "Установить шрифт-размер для постов и комментариев"
 
-#: ../../view/theme/cleanzero/config.php:85
+#: view/theme/cleanzero/config.php:85
 msgid "Set theme width"
 msgstr "Установить ширину темы"
 
-#: ../../view/theme/cleanzero/config.php:86
-#: ../../view/theme/quattro/config.php:68
+#: view/theme/cleanzero/config.php:86 view/theme/quattro/config.php:68
 msgid "Color scheme"
 msgstr "Цветовая схема"
 
-#: ../../view/theme/dispy/config.php:74
-#: ../../view/theme/diabook/config.php:152
+#: view/theme/dispy/config.php:74 view/theme/diabook/config.php:152
 msgid "Set line-height for posts and comments"
 msgstr "Установить высоту строки для постов и комментариев"
 
-#: ../../view/theme/dispy/config.php:75
+#: view/theme/dispy/config.php:75
 msgid "Set colour scheme"
 msgstr "Установить цветовую схему"
 
-#: ../../view/theme/quattro/config.php:67
+#: view/theme/quattro/config.php:67
 msgid "Alignment"
 msgstr "Выравнивание"
 
-#: ../../view/theme/quattro/config.php:67
+#: view/theme/quattro/config.php:67
 msgid "Left"
 msgstr ""
 
-#: ../../view/theme/quattro/config.php:67
+#: view/theme/quattro/config.php:67
 msgid "Center"
 msgstr "Центр"
 
-#: ../../view/theme/quattro/config.php:69
+#: view/theme/quattro/config.php:69
 msgid "Posts font size"
 msgstr "Размер шрифта постов"
 
-#: ../../view/theme/quattro/config.php:70
+#: view/theme/quattro/config.php:70
 msgid "Textareas font size"
 msgstr "Размер шрифта текстовых полей"
 
-#: ../../view/theme/diabook/config.php:153
+#: view/theme/diabook/config.php:153
 msgid "Set resolution for middle column"
 msgstr "Установить разрешение для средней колонки"
 
-#: ../../view/theme/diabook/config.php:154
+#: view/theme/diabook/config.php:154
 msgid "Set color scheme"
 msgstr "Установить цветовую схему"
 
-#: ../../view/theme/diabook/config.php:155
+#: view/theme/diabook/config.php:155
 msgid "Set zoomfactor for Earth Layer"
 msgstr "Установить масштаб карты"
 
-#: ../../view/theme/diabook/config.php:156
-#: ../../view/theme/diabook/theme.php:585
+#: view/theme/diabook/config.php:156 view/theme/diabook/theme.php:585
 msgid "Set longitude (X) for Earth Layers"
 msgstr "Установить длину (X) карты"
 
-#: ../../view/theme/diabook/config.php:157
-#: ../../view/theme/diabook/theme.php:586
+#: view/theme/diabook/config.php:157 view/theme/diabook/theme.php:586
 msgid "Set latitude (Y) for Earth Layers"
 msgstr "Установить ширину (Y) карты"
 
-#: ../../view/theme/diabook/config.php:158
-#: ../../view/theme/diabook/theme.php:130
-#: ../../view/theme/diabook/theme.php:544
-#: ../../view/theme/diabook/theme.php:624
+#: view/theme/diabook/config.php:158 view/theme/diabook/theme.php:130
+#: view/theme/diabook/theme.php:544 view/theme/diabook/theme.php:624
+#: view/theme/vier/config.php:111
 msgid "Community Pages"
 msgstr "Страницы сообщества"
 
-#: ../../view/theme/diabook/config.php:159
-#: ../../view/theme/diabook/theme.php:579
-#: ../../view/theme/diabook/theme.php:625
+#: view/theme/diabook/config.php:159 view/theme/diabook/theme.php:579
+#: view/theme/diabook/theme.php:625
 msgid "Earth Layers"
 msgstr "Карта"
 
-#: ../../view/theme/diabook/config.php:160
-#: ../../view/theme/diabook/theme.php:391
-#: ../../view/theme/diabook/theme.php:626
+#: view/theme/diabook/config.php:160 view/theme/diabook/theme.php:391
+#: view/theme/diabook/theme.php:626 view/theme/vier/config.php:112
+#: view/theme/vier/theme.php:156
 msgid "Community Profiles"
 msgstr "Профили сообщества"
 
-#: ../../view/theme/diabook/config.php:161
-#: ../../view/theme/diabook/theme.php:599
-#: ../../view/theme/diabook/theme.php:627
+#: view/theme/diabook/config.php:161 view/theme/diabook/theme.php:599
+#: view/theme/diabook/theme.php:627 view/theme/vier/config.php:113
 msgid "Help or @NewHere ?"
 msgstr "Помощь"
 
-#: ../../view/theme/diabook/config.php:162
-#: ../../view/theme/diabook/theme.php:606
-#: ../../view/theme/diabook/theme.php:628
+#: view/theme/diabook/config.php:162 view/theme/diabook/theme.php:606
+#: view/theme/diabook/theme.php:628 view/theme/vier/config.php:114
+#: view/theme/vier/theme.php:377
 msgid "Connect Services"
 msgstr "Подключить службы"
 
-#: ../../view/theme/diabook/config.php:163
-#: ../../view/theme/diabook/theme.php:523
-#: ../../view/theme/diabook/theme.php:629
+#: view/theme/diabook/config.php:163 view/theme/diabook/theme.php:523
+#: view/theme/diabook/theme.php:629 view/theme/vier/config.php:115
+#: view/theme/vier/theme.php:203
 msgid "Find Friends"
 msgstr "Найти друзей"
 
-#: ../../view/theme/diabook/config.php:164
-#: ../../view/theme/diabook/theme.php:412
-#: ../../view/theme/diabook/theme.php:630
+#: view/theme/diabook/config.php:164 view/theme/diabook/theme.php:412
+#: view/theme/diabook/theme.php:630 view/theme/vier/config.php:116
+#: view/theme/vier/theme.php:185
 msgid "Last users"
 msgstr "Последние пользователи"
 
-#: ../../view/theme/diabook/config.php:165
-#: ../../view/theme/diabook/theme.php:486
-#: ../../view/theme/diabook/theme.php:631
+#: view/theme/diabook/config.php:165 view/theme/diabook/theme.php:486
+#: view/theme/diabook/theme.php:631
 msgid "Last photos"
 msgstr "Последние фото"
 
-#: ../../view/theme/diabook/config.php:166
-#: ../../view/theme/diabook/theme.php:441
-#: ../../view/theme/diabook/theme.php:632
+#: view/theme/diabook/config.php:166 view/theme/diabook/theme.php:441
+#: view/theme/diabook/theme.php:632
 msgid "Last likes"
 msgstr "Последние likes"
 
-#: ../../view/theme/diabook/theme.php:125
+#: view/theme/diabook/theme.php:125
 msgid "Your contacts"
 msgstr "Ваши контакты"
 
-#: ../../view/theme/diabook/theme.php:128
+#: view/theme/diabook/theme.php:128
 msgid "Your personal photos"
 msgstr "Ваши личные фотографии"
 
-#: ../../view/theme/diabook/theme.php:524
+#: view/theme/diabook/theme.php:524 view/theme/vier/theme.php:204
 msgid "Local Directory"
 msgstr "Локальный каталог"
 
-#: ../../view/theme/diabook/theme.php:584
+#: view/theme/diabook/theme.php:584
 msgid "Set zoomfactor for Earth Layers"
 msgstr "Установить масштаб карты"
 
-#: ../../view/theme/diabook/theme.php:622
+#: view/theme/diabook/theme.php:622
 msgid "Show/hide boxes at right-hand column:"
 msgstr "Показать/скрыть блоки в правой колонке:"
 
-#: ../../view/theme/vier/config.php:56
+#: view/theme/vier/config.php:64
+msgid "Comma separated list of helper forums"
+msgstr ""
+
+#: view/theme/vier/config.php:110
 msgid "Set style"
 msgstr ""
 
-#: ../../view/theme/duepuntozero/config.php:45
+#: view/theme/vier/theme.php:295
+msgid "Quick Start"
+msgstr "Быстрый запуск"
+
+#: view/theme/duepuntozero/config.php:45
 msgid "greenzero"
 msgstr ""
 
-#: ../../view/theme/duepuntozero/config.php:46
+#: view/theme/duepuntozero/config.php:46
 msgid "purplezero"
 msgstr ""
 
-#: ../../view/theme/duepuntozero/config.php:47
+#: view/theme/duepuntozero/config.php:47
 msgid "easterbunny"
 msgstr ""
 
-#: ../../view/theme/duepuntozero/config.php:48
+#: view/theme/duepuntozero/config.php:48
 msgid "darkzero"
 msgstr ""
 
-#: ../../view/theme/duepuntozero/config.php:49
+#: view/theme/duepuntozero/config.php:49
 msgid "comix"
 msgstr ""
 
-#: ../../view/theme/duepuntozero/config.php:50
+#: view/theme/duepuntozero/config.php:50
 msgid "slackr"
 msgstr ""
 
-#: ../../view/theme/duepuntozero/config.php:62
+#: view/theme/duepuntozero/config.php:62
 msgid "Variations"
 msgstr ""
index 09755ca7bb393ac19cc4281b5d1551bdb42e4457..81600c48f370d87e7e287400adf2ee378fb2a90d 100644 (file)
@@ -2,13 +2,16 @@
 
 if(! function_exists("string_plural_select_ru")) {
 function string_plural_select_ru($n){
-       return ($n%10==1 && $n%100!=11 ? 0 : $n%10>=2 && $n%10<=4 && ($n%100<10 || $n%100>=20) ? 1 : 2);;
+       return ;
 }}
 ;
+$a->strings["Network:"] = "Сеть:";
+$a->strings["Forum"] = "Форум";
 $a->strings["%d contact edited."] = array(
-       0 => "%d контакт изменён.",
-       1 => "%d контакты изменены",
-       2 => "%d контакты изменены",
+       0 => "",
+       1 => "",
+       2 => "",
+       3 => "",
 );
 $a->strings["Could not access contact record."] = "Не удалось получить доступ к записи контакта.";
 $a->strings["Could not locate selected profile."] = "Не удалось найти выбранный профиль.";
@@ -34,26 +37,12 @@ $a->strings["(Update was successful)"] = "(Обновление было усп
 $a->strings["(Update was not successful)"] = "(Обновление не удалось)";
 $a->strings["Suggest friends"] = "Предложить друзей";
 $a->strings["Network type: %s"] = "Сеть: %s";
-$a->strings["%d contact in common"] = array(
-       0 => "%d Контакт",
-       1 => "%d Контактов",
-       2 => "%d Контактов",
-);
-$a->strings["View all contacts"] = "Показать все контакты";
-$a->strings["Unblock"] = "Разблокировать";
-$a->strings["Block"] = "Заблокировать";
-$a->strings["Toggle Blocked status"] = "Изменить статус блокированности (заблокировать/разблокировать)";
-$a->strings["Unignore"] = "Не игнорировать";
-$a->strings["Ignore"] = "Игнорировать";
-$a->strings["Toggle Ignored status"] = "Изменить статус игнорирования";
-$a->strings["Unarchive"] = "Разархивировать";
-$a->strings["Archive"] = "Архивировать";
-$a->strings["Toggle Archive status"] = "Сменить статус архивации (архивирова/не архивировать)";
-$a->strings["Repair"] = "Восстановить";
-$a->strings["Advanced Contact Settings"] = "Дополнительные Настройки Контакта";
 $a->strings["Communications lost with this contact!"] = "Связь с контактом утеряна!";
-$a->strings["Contact Editor"] = "Редактор контакта";
-$a->strings["Submit"] = "Подтвердить";
+$a->strings["Fetch further information for feeds"] = "";
+$a->strings["Disabled"] = "Отключенный";
+$a->strings["Fetch information"] = "";
+$a->strings["Fetch information and keywords"] = "";
+$a->strings["Submit"] = "Добавить";
 $a->strings["Profile Visibility"] = "Видимость профиля";
 $a->strings["Please choose the profile you would like to display to %s when viewing your profile securely."] = "Пожалуйста, выберите профиль, который вы хотите отображать %s, когда просмотр вашего профиля безопасен.";
 $a->strings["Contact Information / Notes"] = "Информация о контакте / Заметки";
@@ -67,6 +56,11 @@ $a->strings["Delete contact"] = "Удалить контакт";
 $a->strings["Last update:"] = "Последнее обновление: ";
 $a->strings["Update public posts"] = "Обновить публичные сообщения";
 $a->strings["Update now"] = "Обновить сейчас";
+$a->strings["Connect/Follow"] = "Подключиться/Следовать";
+$a->strings["Unblock"] = "Разблокировать";
+$a->strings["Block"] = "Заблокировать";
+$a->strings["Unignore"] = "Не игнорировать";
+$a->strings["Ignore"] = "Игнорировать";
 $a->strings["Currently blocked"] = "В настоящее время заблокирован";
 $a->strings["Currently ignored"] = "В настоящее время игнорируется";
 $a->strings["Currently archived"] = "В данный момент архивирован";
@@ -74,12 +68,12 @@ $a->strings["Hide this contact from others"] = "Скрыть этот конта
 $a->strings["Replies/likes to your public posts <strong>may</strong> still be visible"] = "Ответы/лайки ваших публичных сообщений <strong>будут</strong> видимы.";
 $a->strings["Notification for new posts"] = "";
 $a->strings["Send a notification of every new post of this contact"] = "";
-$a->strings["Fetch further information for feeds"] = "";
-$a->strings["Disabled"] = "";
-$a->strings["Fetch information"] = "";
-$a->strings["Fetch information and keywords"] = "";
 $a->strings["Blacklisted keywords"] = "";
 $a->strings["Comma separated list of keywords that should not be converted to hashtags, when \"Fetch information and keywords\" is selected"] = "";
+$a->strings["Profile URL"] = "URL профиля";
+$a->strings["Location:"] = "Откуда:";
+$a->strings["About:"] = "О себе:";
+$a->strings["Tags:"] = "Ключевые слова: ";
 $a->strings["Suggestions"] = "Предложения";
 $a->strings["Suggest potential friends"] = "Предложить потенциального знакомого";
 $a->strings["All Contacts"] = "Все контакты";
@@ -94,16 +88,30 @@ $a->strings["Archived"] = "Архивированные";
 $a->strings["Only show archived contacts"] = "Показывать только архивные контакты";
 $a->strings["Hidden"] = "Скрытые";
 $a->strings["Only show hidden contacts"] = "Показывать только скрытые контакты";
-$a->strings["Mutual Friendship"] = "Взаимная дружба";
-$a->strings["is a fan of yours"] = "является вашим поклонником";
-$a->strings["you are a fan of"] = "Вы - поклонник";
-$a->strings["Edit contact"] = "Редактировать контакт";
 $a->strings["Contacts"] = "Контакты";
 $a->strings["Search your contacts"] = "Поиск ваших контактов";
 $a->strings["Finding: "] = "Результат поиска: ";
 $a->strings["Find"] = "Найти";
 $a->strings["Update"] = "Обновление";
+$a->strings["Archive"] = "Архивировать";
+$a->strings["Unarchive"] = "Разархивировать";
 $a->strings["Delete"] = "Удалить";
+$a->strings["Status"] = "Посты";
+$a->strings["Status Messages and Posts"] = "Ваши посты";
+$a->strings["Profile"] = "Информация";
+$a->strings["Profile Details"] = "Информация о вас";
+$a->strings["View all contacts"] = "Показать все контакты";
+$a->strings["Common Friends"] = "Общие друзья";
+$a->strings["View all common friends"] = "";
+$a->strings["Repair"] = "Восстановить";
+$a->strings["Advanced Contact Settings"] = "Дополнительные Настройки Контакта";
+$a->strings["Toggle Blocked status"] = "Изменить статус блокированности (заблокировать/разблокировать)";
+$a->strings["Toggle Ignored status"] = "Изменить статус игнорирования";
+$a->strings["Toggle Archive status"] = "Сменить статус архивации (архивирова/не архивировать)";
+$a->strings["Mutual Friendship"] = "Взаимная дружба";
+$a->strings["is a fan of yours"] = "является вашим поклонником";
+$a->strings["you are a fan of"] = "Вы - поклонник";
+$a->strings["Edit contact"] = "Редактировать контакт";
 $a->strings["No profile"] = "Нет профиля";
 $a->strings["Manage Identities and/or Pages"] = "Управление идентификацией и / или страницами";
 $a->strings["Toggle between different identities or community/group pages which share your account details or which you have been granted \"manage\" permissions"] = "";
@@ -112,7 +120,6 @@ $a->strings["Post successful."] = "Успешно добавлено.";
 $a->strings["Permission denied"] = "Доступ запрещен";
 $a->strings["Invalid profile identifier."] = "Недопустимый идентификатор профиля.";
 $a->strings["Profile Visibility Editor"] = "Редактор видимости профиля";
-$a->strings["Profile"] = "Профиль";
 $a->strings["Click on a contact to add or remove."] = "Нажмите на контакт, чтобы добавить или удалить.";
 $a->strings["Visible To"] = "Видимый для";
 $a->strings["All Contacts (with secure profile access)"] = "Все контакты (с безопасным доступом к профилю)";
@@ -137,9 +144,6 @@ $a->strings["Edit your <strong>default</strong> profile to your liking. Review t
 $a->strings["Profile Keywords"] = "Ключевые слова профиля";
 $a->strings["Set some public keywords for your default profile which describe your interests. We may be able to find other people with similar interests and suggest friendships."] = "Установите некоторые публичные ключевые слова для вашего профиля по умолчанию, которые описывают ваши интересы. Мы можем быть в состоянии найти других людей со схожими интересами и предложить дружбу.";
 $a->strings["Connecting"] = "Подключение";
-$a->strings["Facebook"] = "Facebook";
-$a->strings["Authorise the Facebook Connector if you currently have a Facebook account and we will (optionally) import all your Facebook friends and conversations."] = "Авторизуйте Facebook Connector , если у вас уже есть аккаунт на Facebook, и мы (по желанию) импортируем всех ваших друзей и беседы с Facebook.";
-$a->strings["<em>If</em> this is your own personal server, installing the Facebook addon may ease your transition to the free social web."] = "<em>Если</em> это ваш личный сервер, установите дополнение Facebook, это может облегчить ваш переход на свободную социальную сеть.";
 $a->strings["Importing Emails"] = "Импортирование Email-ов";
 $a->strings["Enter your email access information on your Connector Settings page if you wish to import and interact with friends or mailing lists from your email INBOX"] = "Введите информацию о доступе к вашему email на странице настроек вашего коннектора, если вы хотите импортировать, и общаться с друзьями или получать рассылки на ваш ящик электронной почты";
 $a->strings["Go to Your Contacts Page"] = "Перейти на страницу ваших контактов";
@@ -164,7 +168,7 @@ $a->strings["Profile Photos"] = "Фотографии профиля";
 $a->strings["Image size reduction [%s] failed."] = "Уменьшение размера изображения [%s] не удалось.";
 $a->strings["Shift-reload the page or clear browser cache if the new photo does not display immediately."] = "Перезагрузите страницу с зажатой клавишей \"Shift\" для того, чтобы увидеть свое новое фото немедленно.";
 $a->strings["Unable to process image"] = "Не удается обработать изображение";
-$a->strings["Image exceeds size limit of %d"] = "Изображение превышает предельный размер %d";
+$a->strings["Image exceeds size limit of %s"] = "";
 $a->strings["Unable to process image."] = "Невозможно обработать фото.";
 $a->strings["Upload File:"] = "Загрузить файл:";
 $a->strings["Select a profile:"] = "Выбрать этот профиль:";
@@ -184,9 +188,28 @@ $a->strings["Tag removed"] = "Ключевое слово удалено";
 $a->strings["Remove Item Tag"] = "Удалить ключевое слово";
 $a->strings["Select a tag to remove: "] = "Выберите ключевое слово для удаления: ";
 $a->strings["Remove"] = "Удалить";
+$a->strings["Subscribing to OStatus contacts"] = "";
+$a->strings["No contact provided."] = "";
+$a->strings["Couldn't fetch information for contact."] = "";
+$a->strings["Couldn't fetch friends for contact."] = "";
+$a->strings["Done"] = "Готово";
+$a->strings["success"] = "удачно";
+$a->strings["failed"] = "неудача";
+$a->strings["ignored"] = "";
+$a->strings["Keep this window open until done."] = "";
 $a->strings["Save to Folder:"] = "Сохранить в папку:";
 $a->strings["- select -"] = "- выбрать -";
 $a->strings["Save"] = "Сохранить";
+$a->strings["Submit Request"] = "Отправить запрос";
+$a->strings["You already added this contact."] = "";
+$a->strings["Diaspora support isn't enabled. Contact can't be added."] = "";
+$a->strings["OStatus support is disabled. Contact can't be added."] = "";
+$a->strings["The network type couldn't be detected. Contact can't be added."] = "";
+$a->strings["Please answer the following:"] = "Пожалуйста, ответьте следующее:";
+$a->strings["Does %s know you?"] = "%s знает вас?";
+$a->strings["No"] = "Нет";
+$a->strings["Add a personal note:"] = "Добавить личную заметку:";
+$a->strings["Your Identity Address:"] = "Ваш идентификационный адрес:";
 $a->strings["Contact added"] = "Контакт добавлен";
 $a->strings["Unable to locate original post."] = "Не удалось найти оригинальный пост.";
 $a->strings["Empty post discarded."] = "Пустое сообщение отбрасывается.";
@@ -207,6 +230,7 @@ $a->strings["Group removed."] = "Группа удалена.";
 $a->strings["Unable to remove group."] = "Не удается удалить группу.";
 $a->strings["Group Editor"] = "Редактор групп";
 $a->strings["Members"] = "Участники";
+$a->strings["Group is empty"] = "Группа пуста";
 $a->strings["You must be logged in to use addons. "] = "Вы должны войти в систему, чтобы использовать аддоны.";
 $a->strings["Applications"] = "Приложения";
 $a->strings["No installed applications."] = "Нет установленных приложений.";
@@ -233,6 +257,8 @@ $a->strings["[Name Withheld]"] = "[Имя не разглашается]";
 $a->strings["%1\$s has joined %2\$s"] = "%1\$s присоединился %2\$s";
 $a->strings["Requested profile is not available."] = "Запрашиваемый профиль недоступен.";
 $a->strings["Tips for New Members"] = "Советы для новых участников";
+$a->strings["Do you really want to delete this video?"] = "";
+$a->strings["Delete Video"] = "Удалить видео";
 $a->strings["No videos selected"] = "Видео не выбрано";
 $a->strings["Access to this item is restricted."] = "Доступ к этому пункту ограничен.";
 $a->strings["View Video"] = "Просмотреть видео";
@@ -243,6 +269,7 @@ $a->strings["%1\$s tagged %2\$s's %3\$s with %4\$s"] = "%1\$s tagged %2\$s's %3\
 $a->strings["Friend suggestion sent."] = "Приглашение в друзья отправлено.";
 $a->strings["Suggest Friends"] = "Предложить друзей";
 $a->strings["Suggest a friend for %s"] = "Предложить друга для %s.";
+$a->strings["Invalid request."] = "Неверный запрос.";
 $a->strings["No valid account found."] = "Не найдено действительного аккаунта.";
 $a->strings["Password reset request issued. Check your email."] = "Запрос на сброс пароля принят. Проверьте вашу электронную почту.";
 $a->strings["\n\t\tDear %1\$s,\n\t\t\tA request was recently received at \"%2\$s\" to reset your account\n\t\tpassword. In order to confirm this request, please select the verification link\n\t\tbelow or paste it into your web browser address bar.\n\n\t\tIf you did NOT request this change, please DO NOT follow the link\n\t\tprovided and ignore and/or delete this email.\n\n\t\tYour password will not be changed unless we can verify that you\n\t\tissued this request."] = "";
@@ -262,26 +289,16 @@ $a->strings["Forgot your Password?"] = "Забыли пароль?";
 $a->strings["Enter your email address and submit to have your password reset. Then check your email for further instructions."] = "Введите адрес электронной почты и подтвердите, что вы хотите сбросить ваш пароль. Затем проверьте свою электронную почту для получения дальнейших инструкций.";
 $a->strings["Nickname or Email: "] = "Ник или E-mail: ";
 $a->strings["Reset"] = "Сброс";
-$a->strings["%1\$s likes %2\$s's %3\$s"] = "%1\$s нравится %3\$s от %2\$s ";
-$a->strings["%1\$s doesn't like %2\$s's %3\$s"] = "%1\$s не нравится %3\$s от %2\$s ";
 $a->strings["{0} wants to be your friend"] = "{0} хочет стать Вашим другом";
 $a->strings["{0} sent you a message"] = "{0} отправил Вам сообщение";
 $a->strings["{0} requested registration"] = "{0} требуемая регистрация";
-$a->strings["{0} commented %s's post"] = "{0} прокомментировал сообщение от %s";
-$a->strings["{0} liked %s's post"] = "{0} нравится сообщение от %s";
-$a->strings["{0} disliked %s's post"] = "{0} не нравится сообщение от %s";
-$a->strings["{0} is now friends with %s"] = "{0} теперь друзья с %s";
-$a->strings["{0} posted"] = "{0} опубликовано";
-$a->strings["{0} tagged %s's post with #%s"] = "{0} пометил сообщение %s с #%s";
-$a->strings["{0} mentioned you in a post"] = "{0} упоменул Вас в сообщение";
 $a->strings["No contacts."] = "Нет контактов.";
-$a->strings["View Contacts"] = "Просмотр контактов";
 $a->strings["Invalid request identifier."] = "Неверный идентификатор запроса.";
 $a->strings["Discard"] = "Отказаться";
 $a->strings["System"] = "Система";
-$a->strings["Network"] = "СеÑ\82Ñ\8c";
+$a->strings["Network"] = "Ð\9dовоÑ\81Ñ\82и";
 $a->strings["Personal"] = "Персонал";
-$a->strings["Home"] = "Ð\93лавнаÑ\8f";
+$a->strings["Home"] = "Ð\9cой Ð¿Ñ\80оÑ\84илÑ\8c";
 $a->strings["Introductions"] = "Запросы";
 $a->strings["Show Ignored Requests"] = "Показать проигнорированные запросы";
 $a->strings["Hide Ignored Requests"] = "Скрыть проигнорированные запросы";
@@ -294,12 +311,14 @@ $a->strings["Approve"] = "Одобрить";
 $a->strings["Claims to be known to you: "] = "Утверждения, о которых должно быть вам известно: ";
 $a->strings["yes"] = "да";
 $a->strings["no"] = "нет";
-$a->strings["Approve as: "] = "Утвердить как: ";
+$a->strings["Shall your connection be bidirectional or not? \"Friend\" implies that you allow to read and you subscribe to their posts. \"Fan/Admirer\" means that you allow to read but you do not want to read theirs. Approve as: "] = "";
+$a->strings["Shall your connection be bidirectional or not? \"Friend\" implies that you allow to read and you subscribe to their posts. \"Sharer\" means that you allow to read but you do not want to read theirs. Approve as: "] = "";
 $a->strings["Friend"] = "Друг";
 $a->strings["Sharer"] = "Участник";
 $a->strings["Fan/Admirer"] = "Фанат / Поклонник";
 $a->strings["Friend/Connect Request"] = "Запрос в друзья / на подключение";
 $a->strings["New Follower"] = "Новый фолловер";
+$a->strings["Gender:"] = "Пол:";
 $a->strings["No introductions."] = "Запросов нет.";
 $a->strings["Notifications"] = "Уведомления";
 $a->strings["%s liked %s's post"] = "%s нравится %s сообшение";
@@ -348,30 +367,31 @@ $a->strings["Upload photo"] = "Загрузить фото";
 $a->strings["Insert web link"] = "Вставить веб-ссылку";
 $a->strings["Please wait"] = "Пожалуйста, подождите";
 $a->strings["No messages."] = "Нет сообщений.";
+$a->strings["Message not available."] = "Сообщение не доступно.";
+$a->strings["Delete message"] = "Удалить сообщение";
+$a->strings["Delete conversation"] = "Удалить историю общения";
+$a->strings["No secure communications available. You <strong>may</strong> be able to respond from the sender's profile page."] = "Невозможно защищённое соединение. Вы <strong>имеете</strong> возможность ответить со страницы профиля отправителя.";
+$a->strings["Send Reply"] = "Отправить ответ";
 $a->strings["Unknown sender - %s"] = "Неизвестный отправитель - %s";
 $a->strings["You and %s"] = "Вы и %s";
 $a->strings["%s and You"] = "%s и Вы";
-$a->strings["Delete conversation"] = "Удалить историю общения";
 $a->strings["D, d M Y - g:i A"] = "D, d M Y - g:i A";
 $a->strings["%d message"] = array(
        0 => "%d сообщение",
        1 => "%d сообщений",
        2 => "%d сообщений",
+       3 => "%d сообщений",
 );
-$a->strings["Message not available."] = "Сообщение не доступно.";
-$a->strings["Delete message"] = "Удалить сообщение";
-$a->strings["No secure communications available. You <strong>may</strong> be able to respond from the sender's profile page."] = "Невозможно защищённое соединение. Вы <strong>имеете</strong> возможность ответить со страницы профиля отправителя.";
-$a->strings["Send Reply"] = "Отправить ответ";
 $a->strings["[Embedded content - reload page to view]"] = "[Встроенное содержание - перезагрузите страницу для просмотра]";
 $a->strings["Contact settings applied."] = "Установки контакта приняты.";
 $a->strings["Contact update failed."] = "Обновление контакта неудачное.";
-$a->strings["Repair Contact Settings"] = "Восстановить установки контакта";
 $a->strings["<strong>WARNING: This is highly advanced</strong> and if you enter incorrect information your communications with this contact may stop working."] = "<strong>ВНИМАНИЕ: Это крайне важно!</strong> Если вы введете неверную информацию, ваша связь с этим контактом перестанет работать.";
 $a->strings["Please use your browser 'Back' button <strong>now</strong> if you are uncertain what to do on this page."] = "Пожалуйста, нажмите клавишу вашего браузера 'Back' или 'Назад' <strong>сейчас</strong>, если вы не уверены, что делаете на этой странице.";
-$a->strings["Return to contact editor"] = "Возврат к редактору контакта";
 $a->strings["No mirroring"] = "";
 $a->strings["Mirror as forwarded posting"] = "";
 $a->strings["Mirror as my own posting"] = "";
+$a->strings["Return to contact editor"] = "Возврат к редактору контакта";
+$a->strings["Refetch contact data"] = "";
 $a->strings["Name"] = "Имя";
 $a->strings["Account Nickname"] = "Ник аккаунта";
 $a->strings["@Tagname - overrides Name/Nickname"] = "";
@@ -387,9 +407,12 @@ $a->strings["Mark this contact as remote_self, this will cause friendica to repo
 $a->strings["Login"] = "Вход";
 $a->strings["The post was created"] = "";
 $a->strings["Access denied."] = "Доступ запрещен.";
-$a->strings["People Search"] = "Поиск людей";
+$a->strings["Connect"] = "Подключить";
+$a->strings["View Profile"] = "Просмотреть профиль";
+$a->strings["People Search - %s"] = "";
 $a->strings["No matches"] = "Нет соответствий";
 $a->strings["Photos"] = "Фото";
+$a->strings["Contact Photos"] = "Фотографии контакта";
 $a->strings["Files"] = "Файлы";
 $a->strings["Contacts who are not members of a group"] = "Контакты, которые не являются членами группы";
 $a->strings["Theme settings updated."] = "Настройки темы обновлены.";
@@ -397,14 +420,28 @@ $a->strings["Site"] = "Сайт";
 $a->strings["Users"] = "Пользователи";
 $a->strings["Plugins"] = "Плагины";
 $a->strings["Themes"] = "Темы";
+$a->strings["Additional features"] = "Дополнительные возможности";
 $a->strings["DB updates"] = "Обновление БД";
+$a->strings["Inspect Queue"] = "";
+$a->strings["Federation Statistics"] = "";
 $a->strings["Logs"] = "Журналы";
+$a->strings["View Logs"] = "Просмотр логов";
 $a->strings["probe address"] = "";
 $a->strings["check webfinger"] = "";
 $a->strings["Admin"] = "Администратор";
 $a->strings["Plugin Features"] = "Возможности плагина";
-$a->strings["diagnostics"] = "";
+$a->strings["diagnostics"] = "Диагностика";
 $a->strings["User registrations waiting for confirmation"] = "Регистрации пользователей, ожидающие подтверждения";
+$a->strings["This page offers you some numbers to the known part of the federated social network your Friendica node is part of. These numbers are not complete but only reflect the part of the network your node is aware of."] = "";
+$a->strings["The <em>Auto Discovered Contact Directory</em> feature is not enabled, it will improve the data displayed here."] = "";
+$a->strings["Administration"] = "Администрация";
+$a->strings["Currently this node is aware of %d nodes from the following platforms:"] = "";
+$a->strings["ID"] = "";
+$a->strings["Recipient Name"] = "";
+$a->strings["Recipient Profile"] = "";
+$a->strings["Created"] = "";
+$a->strings["Last Tried"] = "";
+$a->strings["This page lists the content of the queue for outgoing postings. These are postings the initial delivery failed for. They will be resend later and eventually deleted if the delivery fails permanently."] = "";
 $a->strings["Normal Account"] = "Обычный аккаунт";
 $a->strings["Soapbox Account"] = "Аккаунт Витрина";
 $a->strings["Community/Celebrity Account"] = "Аккаунт Сообщество / Знаменитость";
@@ -412,13 +449,13 @@ $a->strings["Automatic Friend Account"] = "\"Автоматический дру
 $a->strings["Blog Account"] = "Аккаунт блога";
 $a->strings["Private Forum"] = "Личный форум";
 $a->strings["Message queues"] = "Очереди сообщений";
-$a->strings["Administration"] = "Администрация";
 $a->strings["Summary"] = "Резюме";
 $a->strings["Registered users"] = "Зарегистрированные пользователи";
 $a->strings["Pending registrations"] = "Ожидающие регистрации";
 $a->strings["Version"] = "Версия";
 $a->strings["Active plugins"] = "Активные плагины";
 $a->strings["Can not parse base url. Must have at least <scheme>://<domain>"] = "Невозможно определить базовый URL. Он должен иметь следующий вид - <scheme>://<domain>";
+$a->strings["RINO2 needs mcrypt php extension to work."] = "Для функционирования RINO2 необходим пакет php5-mcrypt";
 $a->strings["Site settings updated."] = "Установки сайта обновлены.";
 $a->strings["No special theme for mobile devices"] = "Нет специальной темы для мобильных устройств";
 $a->strings["No community page"] = "";
@@ -429,6 +466,12 @@ $a->strings["Frequently"] = "Часто";
 $a->strings["Hourly"] = "Раз в час";
 $a->strings["Twice daily"] = "Два раза в день";
 $a->strings["Daily"] = "Ежедневно";
+$a->strings["Users, Global Contacts"] = "";
+$a->strings["Users, Global Contacts/fallback"] = "";
+$a->strings["One month"] = "Один месяц";
+$a->strings["Three months"] = "Три месяца";
+$a->strings["Half a year"] = "Пол года";
+$a->strings["One year"] = "Один год";
 $a->strings["Multi user instance"] = "Многопользовательский вид";
 $a->strings["Closed"] = "Закрыто";
 $a->strings["Requires approval"] = "Требуется подтверждение";
@@ -441,16 +484,20 @@ $a->strings["Registration"] = "Регистрация";
 $a->strings["File upload"] = "Загрузка файлов";
 $a->strings["Policies"] = "Политики";
 $a->strings["Advanced"] = "Расширенный";
+$a->strings["Auto Discovered Contact Directory"] = "";
 $a->strings["Performance"] = "Производительность";
 $a->strings["Relocate - WARNING: advanced function. Could make this server unreachable."] = "Переместить - ПРЕДУПРЕЖДЕНИЕ: расширеная функция. Может сделать этот сервер недоступным.";
 $a->strings["Site name"] = "Название сайта";
-$a->strings["Host name"] = "";
-$a->strings["Sender Email"] = "";
+$a->strings["Host name"] = "Имя хоста";
+$a->strings["Sender Email"] = "Системный Email";
+$a->strings["The email address your server shall use to send notification emails from."] = "Адрес с которого будут приходить письма пользователям.";
 $a->strings["Banner/Logo"] = "Баннер/Логотип";
 $a->strings["Shortcut icon"] = "";
+$a->strings["Link to an icon that will be used for browsers."] = "";
 $a->strings["Touch icon"] = "";
+$a->strings["Link to an icon that will be used for tablets and mobiles."] = "";
 $a->strings["Additional Info"] = "Дополнительная информация";
-$a->strings["For public servers: you can add additional information here that will be listed at dir.friendica.com/siteinfo."] = "Для публичных серверов: вы можете добавить дополнительную информацию, которая будет перечислена в dir.friendica.com/siteinfo.";
+$a->strings["For public servers: you can add additional information here that will be listed at %s/siteinfo."] = "";
 $a->strings["System language"] = "Системный язык";
 $a->strings["System theme"] = "Системная тема";
 $a->strings["Default system theme - may be over-ridden by user profiles - <a href='#' id='cnftheme'>change theme settings</a>"] = "Тема системы по умолчанию - может быть переопределена пользователем - <a href='#' id='cnftheme'>изменить настройки темы</a>";
@@ -458,7 +505,7 @@ $a->strings["Mobile system theme"] = "Мобильная тема системы
 $a->strings["Theme for mobile devices"] = "Тема для мобильных устройств";
 $a->strings["SSL link policy"] = "Политика SSL";
 $a->strings["Determines whether generated links should be forced to use SSL"] = "Ссылки должны быть вынуждены использовать SSL";
-$a->strings["Force SSL"] = "";
+$a->strings["Force SSL"] = "SSL принудительно";
 $a->strings["Force all Non-SSL requests to SSL - Attention: on some systems it could lead to endless loops."] = "";
 $a->strings["Old style 'Share'"] = "Старый стиль 'Share'";
 $a->strings["Deactivates the bbcode element 'share' for repeating items."] = "Отключение BBCode элемента 'share' для повторяющихся элементов.";
@@ -487,8 +534,8 @@ $a->strings["Block public"] = "Блокировать общественный 
 $a->strings["Check to block public access to all otherwise public personal pages on this site unless you are currently logged in."] = "Отметьте, чтобы заблокировать публичный доступ ко всем иным публичным персональным страницам на этом сайте, если вы не вошли на сайт.";
 $a->strings["Force publish"] = "Принудительная публикация";
 $a->strings["Check to force all profiles on this site to be listed in the site directory."] = "Отметьте, чтобы принудительно заставить все профили на этом сайте, быть перечислеными в каталоге сайта.";
-$a->strings["Global directory update URL"] = "URL обновления глобального каталога";
-$a->strings["URL to update the global directory. If this is not set, the global directory is completely unavailable to the application."] = "URL для обновления глобального каталога. Если он не установлен, глобальный каталог полностью недоступен для приложения.";
+$a->strings["Global directory URL"] = "";
+$a->strings["URL to the global directory. If this is not set, the global directory is completely unavailable to the application."] = "";
 $a->strings["Allow threaded items"] = "Разрешить темы в обсуждении";
 $a->strings["Allow infinite level threading for items on this site."] = "Разрешить бесконечный уровень для тем на этом сайте.";
 $a->strings["Private posts by default for new users"] = "Частные сообщения по умолчанию для новых пользователей";
@@ -517,6 +564,8 @@ $a->strings["Enable OStatus support"] = "Включить поддержку OSt
 $a->strings["Provide built-in OStatus (StatusNet, GNU Social etc.) compatibility. All communications in OStatus are public, so privacy warnings will be occasionally displayed."] = "";
 $a->strings["OStatus conversation completion interval"] = "";
 $a->strings["How often shall the poller check for new entries in OStatus conversations? This can be a very ressource task."] = "Как часто процессы должны проверять наличие новых записей в OStatus разговорах? Это может быть очень ресурсоёмкой задачей.";
+$a->strings["OStatus support can only be enabled if threading is enabled."] = "";
+$a->strings["Diaspora support can't be enabled because Friendica was installed into a sub directory."] = "";
 $a->strings["Enable Diaspora support"] = "Включить поддержку Diaspora";
 $a->strings["Provide built-in Diaspora network compatibility."] = "Обеспечить встроенную поддержку сети Diaspora.";
 $a->strings["Only allow Friendica contacts"] = "Позвольть только  Friendica контакты";
@@ -533,6 +582,24 @@ $a->strings["Poll interval"] = "Интервал опроса";
 $a->strings["Delay background polling processes by this many seconds to reduce system load. If 0, use delivery interval."] = "Установить задержку фоновых процессов опросов путем ограничения количества секунд, чтобы уменьшить нагрузку на систему. Если 0, используется интервал доставки.";
 $a->strings["Maximum Load Average"] = "Средняя максимальная нагрузка";
 $a->strings["Maximum system load before delivery and poll processes are deferred - default 50."] = "Максимальная нагрузка на систему перед приостановкой процессов доставки и опросов - по умолчанию 50.";
+$a->strings["Maximum Load Average (Frontend)"] = "";
+$a->strings["Maximum system load before the frontend quits service - default 50."] = "";
+$a->strings["Maximum table size for optimization"] = "";
+$a->strings["Maximum table size (in MB) for the automatic optimization - default 100 MB. Enter -1 to disable it."] = "";
+$a->strings["Minimum level of fragmentation"] = "";
+$a->strings["Minimum fragmenation level to start the automatic optimization - default value is 30%."] = "";
+$a->strings["Periodical check of global contacts"] = "";
+$a->strings["If enabled, the global contacts are checked periodically for missing or outdated data and the vitality of the contacts and servers."] = "";
+$a->strings["Days between requery"] = "";
+$a->strings["Number of days after which a server is requeried for his contacts."] = "";
+$a->strings["Discover contacts from other servers"] = "";
+$a->strings["Periodically query other servers for contacts. You can choose between 'users': the users on the remote system, 'Global Contacts': active contacts that are known on the system. The fallback is meant for Redmatrix servers and older friendica servers, where global contacts weren't available. The fallback increases the server load, so the recommened setting is 'Users, Global Contacts'."] = "";
+$a->strings["Timeframe for fetching global contacts"] = "";
+$a->strings["When the discovery is activated, this value defines the timeframe for the activity of the global contacts that are fetched from other servers."] = "";
+$a->strings["Search the local directory"] = "";
+$a->strings["Search the local directory instead of the global directory. When searching locally, every search will be executed on the global directory in the background. This improves the search results when the search is repeated."] = "";
+$a->strings["Publish server information"] = "";
+$a->strings["If enabled, general server and usage data will be published. The data contains the name and version of the server, number of users with public profiles, number of posts and the activated protocols and connectors. See <a href='http://the-federation.info/'>the-federation.info</a> for details."] = "";
 $a->strings["Use MySQL full text engine"] = "Использовать систему полнотексного поиска MySQL";
 $a->strings["Activates the full text engine. Speeds up search - but can only search for four and more characters."] = "Активизирует систему полнотексного поиска. Ускоряет поиск - но может искать только при указании четырех и более символов.";
 $a->strings["Suppress Language"] = "";
@@ -540,13 +607,17 @@ $a->strings["Suppress language information in meta information about a posting."
 $a->strings["Suppress Tags"] = "";
 $a->strings["Suppress showing a list of hashtags at the end of the posting."] = "";
 $a->strings["Path to item cache"] = "Путь к элементам кэша";
+$a->strings["The item caches buffers generated bbcode and external images."] = "";
 $a->strings["Cache duration in seconds"] = "Время жизни кэша в секундах";
 $a->strings["How long should the cache files be hold? Default value is 86400 seconds (One day). To disable the item cache, set the value to -1."] = "";
 $a->strings["Maximum numbers of comments per post"] = "";
 $a->strings["How much comments should be shown for each post? Default value is 100."] = "";
 $a->strings["Path for lock file"] = "Путь к файлу блокировки";
+$a->strings["The lock file is used to avoid multiple pollers at one time. Only define a folder here."] = "";
 $a->strings["Temp path"] = "Временная папка";
+$a->strings["If you have a restricted system where the webserver can't access the system temp path, enter another path here."] = "";
 $a->strings["Base path to installation"] = "Путь для установки";
+$a->strings["If the system cannot detect the correct path to your installation, enter the correct path here. This setting should only be set if you are using a restricted system and symbolic links to your webroot."] = "";
 $a->strings["Disable picture proxy"] = "";
 $a->strings["The picture proxy increases performance and privacy. It shouldn't be used on systems with very low bandwith."] = "";
 $a->strings["Enable old style pager"] = "";
@@ -554,6 +625,11 @@ $a->strings["The old style pager has page numbers but slows down massively the p
 $a->strings["Only search in tags"] = "";
 $a->strings["On large systems the text search can slow down the system extremely."] = "";
 $a->strings["New base url"] = "Новый базовый url";
+$a->strings["Change base url for this server. Sends relocate message to all DFRN contacts of all users."] = "";
+$a->strings["RINO Encryption"] = "RINO шифрование";
+$a->strings["Encryption layer between nodes."] = "Слой шифрования между узлами.";
+$a->strings["Embedly API key"] = "";
+$a->strings["<a href='http://embed.ly'>Embedly</a> is used to fetch additional data for web pages. This is an optional parameter."] = "";
 $a->strings["Update has been marked successful"] = "Обновление было успешно отмечено";
 $a->strings["Database structure update %s was successfully applied."] = "";
 $a->strings["Executing of database structure update %s failed with error: %s"] = "";
@@ -574,11 +650,13 @@ $a->strings["%s user blocked/unblocked"] = array(
        0 => "%s пользователь заблокирован/разблокирован",
        1 => "%s пользователей заблокировано/разблокировано",
        2 => "%s пользователей заблокировано/разблокировано",
+       3 => "%s пользователей заблокировано/разблокировано",
 );
 $a->strings["%s user deleted"] = array(
        0 => "%s человек удален",
        1 => "%s чел. удалено",
        2 => "%s чел. удалено",
+       3 => "%s чел. удалено",
 );
 $a->strings["User '%s' deleted"] = "Пользователь '%s' удален";
 $a->strings["User '%s' unblocked"] = "Пользователь '%s' разблокирован";
@@ -612,8 +690,12 @@ $a->strings["Enable"] = "Включить";
 $a->strings["Toggle"] = "Переключить";
 $a->strings["Author: "] = "Автор:";
 $a->strings["Maintainer: "] = "Программа обслуживания: ";
+$a->strings["Reload active plugins"] = "";
+$a->strings["There are currently no plugins available on your node. You can find the official plugin repository at %1\$s and might find other interesting plugins in the open plugin registry at %2\$s"] = "";
 $a->strings["No themes found."] = "Темы не найдены.";
 $a->strings["Screenshot"] = "Скриншот";
+$a->strings["Reload active themes"] = "";
+$a->strings["No themes found on the system. They should be paced in %1\$s"] = "";
 $a->strings["[Experimental]"] = "[экспериментально]";
 $a->strings["[Unsupported]"] = "[Неподдерживаемое]";
 $a->strings["Log settings updated."] = "Настройки журнала обновлены.";
@@ -622,18 +704,19 @@ $a->strings["Enable Debugging"] = "Включить отладку";
 $a->strings["Log file"] = "Лог-файл";
 $a->strings["Must be writable by web server. Relative to your Friendica top-level directory."] = "Должно быть доступно для записи в веб-сервере. Относительно вашего Friendica каталога верхнего уровня.";
 $a->strings["Log level"] = "Уровень лога";
-$a->strings["Close"] = "Закрыть";
-$a->strings["FTP Host"] = "FTP хост";
-$a->strings["FTP Path"] = "Путь FTP";
-$a->strings["FTP User"] = "FTP пользователь";
-$a->strings["FTP Password"] = "FTP пароль";
-$a->strings["Search Results For:"] = "Результаты поиска для:";
+$a->strings["PHP logging"] = "PHP логирование";
+$a->strings["To enable logging of PHP errors and warnings you can add the following to the .htconfig.php file of your installation. The filename set in the 'error_log' line is relative to the friendica top-level directory and must be writeable by the web server. The option '1' for 'log_errors' and 'display_errors' is to enable these options, set to '0' to disable them."] = "";
+$a->strings["Off"] = "Выкл.";
+$a->strings["On"] = "Вкл.";
+$a->strings["Lock feature %s"] = "";
+$a->strings["Manage Additional Features"] = "";
+$a->strings["Search Results For: %s"] = "";
 $a->strings["Remove term"] = "Удалить элемент";
 $a->strings["Saved Searches"] = "запомненные поиски";
 $a->strings["add"] = "добавить";
-$a->strings["Commented Order"] = "Прокомментированный запрос";
+$a->strings["Commented Order"] = "Последние комментарии";
 $a->strings["Sort by Comment Date"] = "Сортировать по дате комментария";
-$a->strings["Posted Order"] = "Ð\9eÑ\82пÑ\80авленнÑ\8bй Ð·Ð°Ð¿Ñ\80оÑ\81";
+$a->strings["Posted Order"] = "Ð\9bенÑ\82а Ð·Ð°Ð¿Ð¸Ñ\81ей";
 $a->strings["Sort by Post Date"] = "Сортировать по дате отправки";
 $a->strings["Posts that mention or involve you"] = "";
 $a->strings["New"] = "Новый";
@@ -646,36 +729,77 @@ $a->strings["Warning: This group contains %s member from an insecure network."]
        0 => "Внимание: Эта группа содержит %s участника с незащищенной сети.",
        1 => "Внимание: Эта группа содержит %s участников с незащищенной сети.",
        2 => "Внимание: Эта группа содержит %s участников с незащищенной сети.",
+       3 => "Внимание: Эта группа содержит %s участников с незащищенной сети.",
 );
 $a->strings["Private messages to this group are at risk of public disclosure."] = "Личные сообщения к этой группе находятся под угрозой обнародования.";
 $a->strings["No such group"] = "Нет такой группы";
-$a->strings["Group is empty"] = "Группа пуста";
-$a->strings["Group: "] = "Группа: ";
-$a->strings["Contact: "] = "Контакт: ";
+$a->strings["Group: %s"] = "Группа: %s";
 $a->strings["Private messages to this person are at risk of public disclosure."] = "Личные сообщения этому человеку находятся под угрозой обнародования.";
 $a->strings["Invalid contact."] = "Недопустимый контакт.";
-$a->strings["Friends of %s"] = "%s Друзья";
 $a->strings["No friends to display."] = "Нет друзей.";
+$a->strings["Event can not end before it has started."] = "";
 $a->strings["Event title and start time are required."] = "Название мероприятия и время начала обязательны для заполнения.";
+$a->strings["Sun"] = "Вс";
+$a->strings["Mon"] = "Пн";
+$a->strings["Tue"] = "Вт";
+$a->strings["Wed"] = "Ср";
+$a->strings["Thu"] = "Чт";
+$a->strings["Fri"] = "Пт";
+$a->strings["Sat"] = "Сб";
+$a->strings["Sunday"] = "Воскресенье";
+$a->strings["Monday"] = "Понедельник";
+$a->strings["Tuesday"] = "Вторник";
+$a->strings["Wednesday"] = "Среда";
+$a->strings["Thursday"] = "Четверг";
+$a->strings["Friday"] = "Пятница";
+$a->strings["Saturday"] = "Суббота";
+$a->strings["Jan"] = "Янв";
+$a->strings["Feb"] = "Фев";
+$a->strings["Mar"] = "Мрт";
+$a->strings["Apr"] = "Апр";
+$a->strings["May"] = "Май";
+$a->strings["Jun"] = "Июн";
+$a->strings["Jul"] = "Июл";
+$a->strings["Aug"] = "Авг";
+$a->strings["Sept"] = "Сен";
+$a->strings["Oct"] = "Окт";
+$a->strings["Nov"] = "Нбр";
+$a->strings["Dec"] = "Дек";
+$a->strings["January"] = "Январь";
+$a->strings["February"] = "Февраль";
+$a->strings["March"] = "Март";
+$a->strings["April"] = "Апрель";
+$a->strings["June"] = "Июнь";
+$a->strings["July"] = "Июль";
+$a->strings["August"] = "Август";
+$a->strings["September"] = "Сентябрь";
+$a->strings["October"] = "Октябрь";
+$a->strings["November"] = "Ноябрь";
+$a->strings["December"] = "Декабрь";
+$a->strings["today"] = "сегодня";
+$a->strings["month"] = "мес.";
+$a->strings["week"] = "неделя";
+$a->strings["day"] = "день";
 $a->strings["l, F j"] = "l, j F";
 $a->strings["Edit event"] = "Редактировать мероприятие";
-$a->strings["link to source"] = "ссылка на источник";
+$a->strings["link to source"] = "ссылка на сообщение";
 $a->strings["Events"] = "Мероприятия";
 $a->strings["Create New Event"] = "Создать новое мероприятие";
 $a->strings["Previous"] = "Назад";
 $a->strings["Next"] = "Далее";
-$a->strings["hour:minute"] = "час:минута";
 $a->strings["Event details"] = "Сведения о мероприятии";
-$a->strings["Format is %s %s. Starting date and Title are required."] = "Формат %s %s. Необхлдима дата старта и заголовок.";
+$a->strings["Starting date and Title are required."] = "";
 $a->strings["Event Starts:"] = "Начало мероприятия:";
 $a->strings["Required"] = "Требуется";
 $a->strings["Finish date/time is not known or not relevant"] = "Дата/время окончания не известны, или не указаны";
 $a->strings["Event Finishes:"] = "Окончание мероприятия:";
 $a->strings["Adjust for viewer timezone"] = "Настройка часового пояса";
 $a->strings["Description:"] = "Описание:";
-$a->strings["Location:"] = "Откуда:";
 $a->strings["Title:"] = "Титул:";
 $a->strings["Share this event"] = "Поделитесь этим мероприятием";
+$a->strings["Preview"] = "Предварительный просмотр";
+$a->strings["Credits"] = "";
+$a->strings["Friendica is a community project, that would not be possible without the help of many people. Here is a list of those who have contributed to the code or the translation of Friendica. Thank you all!"] = "";
 $a->strings["Select"] = "Выберите";
 $a->strings["View %s's profile @ %s"] = "Просмотреть профиль %s [@ %s]";
 $a->strings["%s from %s"] = "%s с %s";
@@ -684,11 +808,13 @@ $a->strings["%d comment"] = array(
        0 => "%d комментарий",
        1 => "%d комментариев",
        2 => "%d комментариев",
+       3 => "%d комментариев",
 );
 $a->strings["comment"] = array(
        0 => "",
        1 => "",
        2 => "комментарий",
+       3 => "комментарий",
 );
 $a->strings["show more"] = "показать больше";
 $a->strings["Private Message"] = "Личное сообщение";
@@ -699,7 +825,7 @@ $a->strings["dislike"] = "не нравитса";
 $a->strings["Share this"] = "Поделитесь этим";
 $a->strings["share"] = "делиться";
 $a->strings["This is you"] = "Это вы";
-$a->strings["Comment"] = "Ð\9aомментарий";
+$a->strings["Comment"] = "Ð\9eÑ\81Ñ\82авиÑ\82Ñ\8c Ðºомментарий";
 $a->strings["Bold"] = "Жирный";
 $a->strings["Italic"] = "Kурсивный";
 $a->strings["Underline"] = "Подчеркнутый";
@@ -708,7 +834,6 @@ $a->strings["Code"] = "Код";
 $a->strings["Image"] = "Изображение / Фото";
 $a->strings["Link"] = "Ссылка";
 $a->strings["Video"] = "Видео";
-$a->strings["Preview"] = "предварительный просмотр";
 $a->strings["Edit"] = "Редактировать";
 $a->strings["add star"] = "пометить";
 $a->strings["remove star"] = "убрать метку";
@@ -728,6 +853,7 @@ $a->strings["Could not create table."] = "Не удалось создать т
 $a->strings["Your Friendica site database has been installed."] = "База данных сайта установлена.";
 $a->strings["You may need to import the file \"database.sql\" manually using phpmyadmin or mysql."] = "Вам может понадобиться импортировать файл \"database.sql\" вручную с помощью PhpMyAdmin или MySQL.";
 $a->strings["Please see the file \"INSTALL.txt\"."] = "Пожалуйста, смотрите файл \"INSTALL.txt\".";
+$a->strings["Database already in use."] = "";
 $a->strings["System check"] = "Проверить систему";
 $a->strings["Check again"] = "Проверить еще раз";
 $a->strings["Database connection"] = "Подключение к базе данных";
@@ -743,7 +869,7 @@ $a->strings["Your account email address must match this in order to use the web
 $a->strings["Please select a default timezone for your website"] = "Пожалуйста, выберите часовой пояс по умолчанию для вашего сайта";
 $a->strings["Site settings"] = "Настройки сайта";
 $a->strings["Could not find a command line version of PHP in the web server PATH."] = "Не удалось найти PATH веб-сервера в установках PHP.";
-$a->strings["If you don't have a command line version of PHP installed on server, you will not be able to run background polling via cron. See <a href='http://friendica.com/node/27'>'Activating scheduled tasks'</a>"] = "Если на вашем сервере не установлена версия командной строки PHP, вы не будете иметь возможность запускать фоновые опросы через крон. См. <a href='http://friendica.com/node/27'> 'Активация запланированных задачах' </a>";
+$a->strings["If you don't have a command line version of PHP installed on server, you will not be able to run background polling via cron. See <a href='https://github.com/friendica/friendica/blob/master/doc/Install.md#set-up-the-poller'>'Setup the poller'</a>"] = "";
 $a->strings["PHP executable path"] = "PHP executable path";
 $a->strings["Enter full path to php executable. You can leave this blank to continue the installation."] = "Введите полный путь к исполняемому файлу PHP. Вы можете оставить это поле пустым, чтобы продолжить установку.";
 $a->strings["Command line PHP"] = "Command line PHP";
@@ -761,6 +887,7 @@ $a->strings["GD graphics PHP module"] = "GD graphics PHP модуль";
 $a->strings["OpenSSL PHP module"] = "OpenSSL PHP модуль";
 $a->strings["mysqli PHP module"] = "mysqli PHP модуль";
 $a->strings["mb_string PHP module"] = "mb_string PHP модуль";
+$a->strings["mcrypt PHP module"] = "";
 $a->strings["Apache mod_rewrite module"] = "Apache mod_rewrite module";
 $a->strings["Error: Apache webserver mod-rewrite module is required but not installed."] = "Ошибка: необходим модуль веб-сервера Apache mod-rewrite, но он не установлен.";
 $a->strings["Error: libCURL PHP module required but not installed."] = "Ошибка: необходим libCURL PHP модуль, но он не установлен.";
@@ -768,6 +895,9 @@ $a->strings["Error: GD graphics PHP module with JPEG support required but not in
 $a->strings["Error: openssl PHP module required but not installed."] = "Ошибка: необходим PHP модуль OpenSSL, но он не установлен.";
 $a->strings["Error: mysqli PHP module required but not installed."] = "Ошибка: необходим PHP модуль MySQLi, но он не установлен.";
 $a->strings["Error: mb_string PHP module required but not installed."] = "Ошибка: необходим PHP модуль mb_string, но он не установлен.";
+$a->strings["Error: mcrypt PHP module required but not installed."] = "";
+$a->strings["Function mcrypt_create_iv() is not defined. This is needed to enable RINO2 encryption layer."] = "";
+$a->strings["mcrypt_create_iv() function"] = "";
 $a->strings["The web installer needs to be able to create a file called \".htconfig.php\" in the top folder of your web server and it is unable to do so."] = "Веб-инсталлятору требуется создать файл с именем \". htconfig.php\" в верхней папке веб-сервера, но он не в состоянии это сделать.";
 $a->strings["This is most often a permission setting, as the web server may not be able to write files in your folder - even if you can."] = "Это наиболее частые параметры разрешений, когда веб-сервер не может записать файлы в папке - даже если вы можете.";
 $a->strings["At the end of this procedure, we will give you a text to save in a file named .htconfig.php in your Friendica top folder."] = "В конце этой процедуры, мы дадим вам текст, для сохранения в файле с именем .htconfig.php в корневой папке, где установлена Friendica.";
@@ -780,6 +910,8 @@ $a->strings["Note: as a security measure, you should give the web server write a
 $a->strings["view/smarty3 is writable"] = "view/smarty3 доступен для записи";
 $a->strings["Url rewrite in .htaccess is not working. Check your server configuration."] = "Url rewrite в .htaccess не работает. Проверьте конфигурацию вашего сервера..";
 $a->strings["Url rewrite is working"] = "Url rewrite работает";
+$a->strings["ImageMagick PHP extension is installed"] = "";
+$a->strings["ImageMagick supports GIF"] = "";
 $a->strings["The database configuration file \".htconfig.php\" could not be written. Please use the enclosed text to create a configuration file in your web server root."] = "Файл конфигурации базы данных \".htconfig.php\" не могла быть записан. Пожалуйста, используйте приложенный текст, чтобы создать конфигурационный файл в корневом каталоге веб-сервера.";
 $a->strings["<h1>What next</h1>"] = "<h1>Что далее</h1>";
 $a->strings["IMPORTANT: You will need to [manually] setup a scheduled task for the poller."] = "ВАЖНО: Вам нужно будет [вручную] установить запланированное задание для регистратора.";
@@ -795,21 +927,19 @@ $a->strings["%1\$s welcomes %2\$s"] = "%1\$s добро пожаловать %2\
 $a->strings["Welcome to %s"] = "Добро пожаловать на %s!";
 $a->strings["Sorry, maybe your upload is bigger than the PHP configuration allows"] = "";
 $a->strings["Or - did you try to upload an empty file?"] = "";
-$a->strings["File exceeds size limit of %d"] = "Файл превышает предельный размер %d";
+$a->strings["File exceeds size limit of %s"] = "";
 $a->strings["File upload failed."] = "Загрузка файла не удалась.";
-$a->strings["Profile Match"] = "Похожие профили";
 $a->strings["No keywords to match. Please add keywords to your default profile."] = "Нет соответствующих ключевых слов. Пожалуйста, добавьте ключевые слова для вашего профиля по умолчанию.";
 $a->strings["is interested in:"] = "интересуется:";
-$a->strings["Connect"] = "Подключить";
+$a->strings["Profile Match"] = "Похожие профили";
 $a->strings["link"] = "ссылка";
 $a->strings["Not available."] = "Недоступно.";
 $a->strings["Community"] = "Сообщество";
 $a->strings["No results."] = "Нет результатов.";
 $a->strings["everybody"] = "каждый";
-$a->strings["Additional features"] = "Дополнительные возможности";
-$a->strings["Display"] = "";
-$a->strings["Social Networks"] = "";
-$a->strings["Delegations"] = "";
+$a->strings["Display"] = "Внешний вид";
+$a->strings["Social Networks"] = "Социальные сети";
+$a->strings["Delegations"] = "Делегирование";
 $a->strings["Connected apps"] = "Подключенные приложения";
 $a->strings["Export personal data"] = "Экспорт личных данных";
 $a->strings["Remove account"] = "Удалить аккаунт";
@@ -843,14 +973,20 @@ $a->strings["No name"] = "Нет имени";
 $a->strings["Remove authorization"] = "Удалить авторизацию";
 $a->strings["No Plugin settings configured"] = "Нет сконфигурированных настроек плагина";
 $a->strings["Plugin Settings"] = "Настройки плагина";
-$a->strings["Off"] = "";
-$a->strings["On"] = "";
 $a->strings["Additional Features"] = "Дополнительные возможности";
+$a->strings["General Social Media Settings"] = "";
+$a->strings["Disable intelligent shortening"] = "";
+$a->strings["Normally the system tries to find the best link to add to shortened posts. If this option is enabled then every shortened post will always point to the original friendica post."] = "";
+$a->strings["Automatically follow any GNU Social (OStatus) followers/mentioners"] = "";
+$a->strings["If you receive a message from an unknown OStatus user, this option decides what to do. If it is checked, a new contact will be created for every unknown user."] = "";
+$a->strings["Your legacy GNU Social account"] = "";
+$a->strings["If you enter your old GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done."] = "";
+$a->strings["Repair OStatus subscriptions"] = "";
 $a->strings["Built-in support for %s connectivity is %s"] = "Встроенная  поддержка для %s подключение %s";
 $a->strings["Diaspora"] = "Diaspora";
 $a->strings["enabled"] = "подключено";
 $a->strings["disabled"] = "отключено";
-$a->strings["StatusNet"] = "StatusNet";
+$a->strings["GNU Social (OStatus)"] = "";
 $a->strings["Email access is disabled on this site."] = "Доступ эл. почты отключен на этом сайте.";
 $a->strings["Email/Mailbox Setup"] = "Настройка эл. почты / почтового ящика";
 $a->strings["If you wish to communicate with email contacts using this service (optional), please specify how to connect to your mailbox."] = "Если вы хотите общаться с Email контактами, используя этот сервис (по желанию), пожалуйста, уточните, как подключиться к вашему почтовому ящику.";
@@ -871,14 +1007,17 @@ $a->strings["Display Settings"] = "Параметры дисплея";
 $a->strings["Display Theme:"] = "Показать тему:";
 $a->strings["Mobile Theme:"] = "Мобильная тема:";
 $a->strings["Update browser every xx seconds"] = "Обновление браузера каждые хх секунд";
-$a->strings["Minimum of 10 seconds, no maximum"] = "Минимум 10 секунд, максимума нет";
+$a->strings["Minimum of 10 seconds. Enter -1 to disable it."] = "";
 $a->strings["Number of items to display per page:"] = "Количество элементов, отображаемых на одной странице:";
 $a->strings["Maximum of 100 items"] = "Максимум 100 элементов";
 $a->strings["Number of items to display per page when viewed from mobile device:"] = "Количество элементов на странице, когда просмотр осуществляется с мобильных устройств:";
 $a->strings["Don't show emoticons"] = "не показывать emoticons";
+$a->strings["Calendar"] = "";
+$a->strings["Beginning of week:"] = "";
 $a->strings["Don't show notices"] = "";
 $a->strings["Infinite scroll"] = "Бесконечная прокрутка";
 $a->strings["Automatic updates only at the top of the network page"] = "";
+$a->strings["Theme settings"] = "Настройки темы";
 $a->strings["User Types"] = "";
 $a->strings["Community Types"] = "";
 $a->strings["Normal Account Page"] = "Стандартная страница аккаунта";
@@ -894,7 +1033,6 @@ $a->strings["Private forum - approved members only"] = "Приватный фо
 $a->strings["OpenID:"] = "OpenID:";
 $a->strings["(Optional) Allow this OpenID to login to this account."] = "(Необязательно) Разрешить этому OpenID входить в этот аккаунт";
 $a->strings["Publish your default profile in your local site directory?"] = "Публиковать ваш профиль по умолчанию в вашем локальном каталоге на сайте?";
-$a->strings["No"] = "Нет";
 $a->strings["Publish your default profile in the global social directory?"] = "Публиковать ваш профиль по умолчанию в глобальном социальном каталоге?";
 $a->strings["Hide your contact/friend list from viewers of your default profile?"] = "Скрывать ваш список контактов/друзей от посетителей вашего профиля по умолчанию?";
 $a->strings["Hide your profile details from unknown viewers?"] = "Скрыть данные профиля из неизвестных зрителей?";
@@ -904,7 +1042,7 @@ $a->strings["Allow friends to tag your posts?"] = "Разрешить друзь
 $a->strings["Allow us to suggest you as a potential friend to new members?"] = "Позвольть предлогать Вам потенциальных друзей?";
 $a->strings["Permit unknown people to send you private mail?"] = "Разрешить незнакомым людям отправлять вам личные сообщения?";
 $a->strings["Profile is <strong>not published</strong>."] = "Профиль <strong>не публикуется</strong>.";
-$a->strings["Your Identity Address is"] = "Ваш идентификационный адрес";
+$a->strings["Your Identity Address is <strong>'%s'</strong> or '%s'."] = "";
 $a->strings["Automatically expire posts after this many days:"] = "Автоматическое истекание срока действия сообщения после стольких дней:";
 $a->strings["If empty, posts will not expire. Expired posts will be deleted"] = "Если пусто, срок действия сообщений не будет ограничен. Сообщения с истекшим сроком действия будут удалены";
 $a->strings["Advanced expiration settings"] = "Настройки расширенного окончания срока действия";
@@ -915,7 +1053,7 @@ $a->strings["Expire starred posts:"] = "Срок хранения усеянны
 $a->strings["Expire photos:"] = "Срок хранения фотографий:";
 $a->strings["Only expire posts by others:"] = "Только устаревшие посты других:";
 $a->strings["Account Settings"] = "Настройки аккаунта";
-$a->strings["Password Settings"] = "Ð\9dаÑ\81Ñ\82Ñ\80ойка пароля";
+$a->strings["Password Settings"] = "Смена пароля";
 $a->strings["New Password:"] = "Новый пароль:";
 $a->strings["Confirm:"] = "Подтвердите:";
 $a->strings["Leave password fields blank unless changing"] = "Оставьте поля пароля пустыми, если он не изменяется";
@@ -926,6 +1064,8 @@ $a->strings["Basic Settings"] = "Основные параметры";
 $a->strings["Full Name:"] = "Полное имя:";
 $a->strings["Email Address:"] = "Адрес электронной почты:";
 $a->strings["Your Timezone:"] = "Ваш часовой пояс:";
+$a->strings["Your Language:"] = "";
+$a->strings["Set the language we use to show you friendica interface and to send you emails"] = "";
 $a->strings["Default Post Location:"] = "Местонахождение по умолчанию:";
 $a->strings["Use Browser Location:"] = "Использовать определение местоположения браузером:";
 $a->strings["Security and Privacy Settings"] = "Параметры безопасности и конфиденциальности";
@@ -953,11 +1093,13 @@ $a->strings["You receive a private message"] = "Вы получаете личн
 $a->strings["You receive a friend suggestion"] = "Вы полулили предложение о добавлении в друзья";
 $a->strings["You are tagged in a post"] = "Вы отмечены в посте";
 $a->strings["You are poked/prodded/etc. in a post"] = "";
+$a->strings["Activate desktop notifications"] = "";
+$a->strings["Show desktop popup on new notifications"] = "";
 $a->strings["Text-only notification emails"] = "";
 $a->strings["Send text only notification emails, without the html part"] = "";
-$a->strings["Advanced Account/Page Type Settings"] = "РаÑ\81Ñ\88иÑ\80еннÑ\8bе Ð½Ð°Ñ\81Ñ\82Ñ\80ойки Ñ\82ипа Ð°ÐºÐºÐ°Ñ\83нÑ\82а/Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b";
+$a->strings["Advanced Account/Page Type Settings"] = "РаÑ\81Ñ\88иÑ\80еннÑ\8bе Ð½Ð°Ñ\81Ñ\82Ñ\80ойки Ñ\83Ñ\87Ñ\91Ñ\82ной Ð·Ð°Ð¿Ð¸Ñ\81и";
 $a->strings["Change the behaviour of this account for special situations"] = "Измените поведение этого аккаунта в специальных ситуациях";
-$a->strings["Relocate"] = "Ð\9fеÑ\80емеÑ\81Ñ\82иÑ\82Ñ\8c";
+$a->strings["Relocate"] = "Ð\9fеÑ\80емеÑ\89ение";
 $a->strings["If you have moved this profile from another server, and some of your contacts don't receive your updates, try pushing this button."] = "Если вы переместили эту анкету с другого сервера, и некоторые из ваших контактов не получили ваши обновления, попробуйте нажать эту кнопку.";
 $a->strings["Resend relocate message to contacts"] = "Отправить перемещённые сообщения контактам";
 $a->strings["This introduction has already been accepted."] = "Этот запрос был уже принят.";
@@ -968,6 +1110,7 @@ $a->strings["%d required parameter was not found at the given location"] = array
        0 => "%d требуемый параметр не был найден в заданном месте",
        1 => "%d требуемых параметров не были найдены в заданном месте",
        2 => "%d требуемых параметров не были найдены в заданном месте",
+       3 => "%d требуемых параметров не были найдены в заданном месте",
 );
 $a->strings["Introduction complete."] = "Запрос создан.";
 $a->strings["Unrecoverable protocol error."] = "Неисправимая ошибка протокола.";
@@ -978,32 +1121,28 @@ $a->strings["Friends are advised to please try again in 24 hours."] = "Друз
 $a->strings["Invalid locator"] = "Недопустимый локатор";
 $a->strings["Invalid email address."] = "Неверный адрес электронной почты.";
 $a->strings["This account has not been configured for email. Request failed."] = "Этот аккаунт не настроен для электронной почты. Запрос не удался.";
-$a->strings["Unable to resolve your name at the provided location."] = "Не удается установить ваше имя на предложенном местоположении.";
 $a->strings["You have already introduced yourself here."] = "Вы уже ввели информацию о себе здесь.";
 $a->strings["Apparently you are already friends with %s."] = "Похоже, что вы уже друзья с %s.";
 $a->strings["Invalid profile URL."] = "Неверный URL профиля.";
 $a->strings["Disallowed profile URL."] = "Запрещенный URL профиля.";
 $a->strings["Your introduction has been sent."] = "Ваш запрос отправлен.";
+$a->strings["Remote subscription can't be done for your network. Please subscribe directly on your system."] = "";
 $a->strings["Please login to confirm introduction."] = "Для подтверждения запроса войдите пожалуйста с паролем.";
 $a->strings["Incorrect identity currently logged in. Please login to <strong>this</strong> profile."] = "Неверно идентифицирован вход. Пожалуйста, войдите в <strong>этот</strong> профиль.";
+$a->strings["Confirm"] = "Подтвердить";
 $a->strings["Hide this contact"] = "Скрыть этот контакт";
 $a->strings["Welcome home %s."] = "Добро пожаловать домой, %s!";
 $a->strings["Please confirm your introduction/connection request to %s."] = "Пожалуйста, подтвердите краткую информацию / запрос на подключение к %s.";
-$a->strings["Confirm"] = "Подтвердить";
 $a->strings["Please enter your 'Identity Address' from one of the following supported communications networks:"] = "Пожалуйста, введите ваш 'идентификационный адрес' одной из следующих поддерживаемых социальных сетей:";
-$a->strings["If you are not yet a member of the free social web, <a href=\"http://dir.friendica.com/siteinfo\">follow this link to find a public Friendica site and join us today</a>."] = "Если вы еще не являетесь членом свободной социальной сети, перейдите по <a href=\"http://dir.friendica.com/siteinfo\"> этой ссылке, чтобы найти публичный сервер Friendica и присоединиться к нам сейчас </a>.";
+$a->strings["If you are not yet a member of the free social web, <a href=\"%s/siteinfo\">follow this link to find a public Friendica site and join us today</a>."] = "";
 $a->strings["Friend/Connection Request"] = "Запрос в друзья / на подключение";
 $a->strings["Examples: jojo@demo.friendica.com, http://demo.friendica.com/profile/jojo, testuser@identi.ca"] = "Примеры: jojo@demo.friendica.com, http://demo.friendica.com/profile/jojo, testuser@identi.ca";
-$a->strings["Please answer the following:"] = "Пожалуйста, ответьте следующее:";
-$a->strings["Does %s know you?"] = "%s знает вас?";
-$a->strings["Add a personal note:"] = "Добавить личную заметку:";
 $a->strings["Friendica"] = "Friendica";
 $a->strings["StatusNet/Federated Social Web"] = "StatusNet / Federated Social Web";
 $a->strings[" - please do not use this form.  Instead, enter %s into your Diaspora search bar."] = "Участники сети Diaspora: пожалуйста, не пользуйтесь этой формой. Вместо этого введите  %s в строке поиска Diaspora";
-$a->strings["Your Identity Address:"] = "Ваш идентификационный адрес:";
-$a->strings["Submit Request"] = "Отправить запрос";
 $a->strings["Registration successful. Please check your email for further instructions."] = "Регистрация успешна. Пожалуйста, проверьте свою электронную почту для получения дальнейших инструкций.";
 $a->strings["Failed to send email message. Here your accout details:<br> login: %s<br> password: %s<br><br>You can change your password after login."] = "";
+$a->strings["Registration successful."] = "";
 $a->strings["Your registration can not be processed."] = "Ваша регистрация не может быть обработана.";
 $a->strings["Your registration is pending approval by the site owner."] = "Ваша регистрация в ожидании одобрения владельцем сайта.";
 $a->strings["This site has exceeded the number of allowed daily account registrations. Please try again tomorrow."] = "Этот сайт превысил допустимое количество ежедневных регистраций. Пожалуйста, повторите попытку завтра.";
@@ -1013,24 +1152,27 @@ $a->strings["Your OpenID (optional): "] = "Ваш OpenID (необязатель
 $a->strings["Include your profile in member directory?"] = "Включить ваш профиль в каталог участников?";
 $a->strings["Membership on this site is by invitation only."] = "Членство на сайте только по приглашению.";
 $a->strings["Your invitation ID: "] = "ID вашего приглашения:";
-$a->strings["Your Full Name (e.g. Joe Smith): "] = "Ваше полное имя (например, Joe Smith): ";
+$a->strings["Your Full Name (e.g. Joe Smith, real or real-looking): "] = "";
 $a->strings["Your Email Address: "] = "Ваш адрес электронной почты: ";
+$a->strings["Leave empty for an auto generated password."] = "";
 $a->strings["Choose a profile nickname. This must begin with a text character. Your profile address on this site will then be '<strong>nickname@\$sitename</strong>'."] = "Выбор псевдонима профиля. Он должен начинаться с буквы. Адрес вашего профиля на данном сайте будет в этом случае '<strong>nickname@\$sitename</strong>'.";
 $a->strings["Choose a nickname: "] = "Выберите псевдоним: ";
 $a->strings["Register"] = "Регистрация";
 $a->strings["Import"] = "Импорт";
 $a->strings["Import your profile to this friendica instance"] = "Импорт своего профиля в этот экземпляр friendica";
 $a->strings["System down for maintenance"] = "Система закрыта на техническое обслуживание";
+$a->strings["Only logged in users are permitted to perform a search."] = "";
+$a->strings["Too Many Requests"] = "";
+$a->strings["Only one search per minute is permitted for not logged in users."] = "";
 $a->strings["Search"] = "Поиск";
+$a->strings["Items tagged with: %s"] = "";
+$a->strings["Search results for: %s"] = "";
+$a->strings["Status:"] = "Статус:";
+$a->strings["Homepage:"] = "Домашняя страничка:";
 $a->strings["Global Directory"] = "Глобальный каталог";
 $a->strings["Find on this site"] = "Найти на этом сайте";
+$a->strings["Finding:"] = "";
 $a->strings["Site Directory"] = "Каталог сайта";
-$a->strings["Age: "] = "Возраст: ";
-$a->strings["Gender: "] = "Пол: ";
-$a->strings["Gender:"] = "Пол:";
-$a->strings["Status:"] = "Статус:";
-$a->strings["Homepage:"] = "Домашняя страничка:";
-$a->strings["About:"] = "О себе:";
 $a->strings["No entries (some entries may be hidden)."] = "Нет записей (некоторые записи могут быть скрыты).";
 $a->strings["No potential page delegates located."] = "";
 $a->strings["Delegate Page Management"] = "Делегировать управление страницей";
@@ -1040,7 +1182,6 @@ $a->strings["Existing Page Delegates"] = "Существующие уполно
 $a->strings["Potential Delegates"] = "Возможные доверенные лица";
 $a->strings["Add"] = "Добавить";
 $a->strings["No entries."] = "Нет записей.";
-$a->strings["Common Friends"] = "Общие друзья";
 $a->strings["No contacts in common."] = "Нет общих контактов.";
 $a->strings["Export account"] = "Экспорт аккаунта";
 $a->strings["Export your account info and contacts. Use this to make a backup of your account and/or to move it to another server."] = "Экспорт ваших регистрационных данные и контактов. Используйте, чтобы создать резервную копию вашего аккаунта и/или переместить его на другой сервер.";
@@ -1050,9 +1191,9 @@ $a->strings["%1\$s is currently %2\$s"] = "";
 $a->strings["Mood"] = "Настроение";
 $a->strings["Set your current mood and tell your friends"] = "Напишите о вашем настроении и расскажите своим друзьям";
 $a->strings["Do you really want to delete this suggestion?"] = "Вы действительно хотите удалить это предложение?";
-$a->strings["Friend Suggestions"] = "Предложения друзей";
 $a->strings["No suggestions available. If this is a new site, please try again in 24 hours."] = "Нет предложений. Если это новый сайт, пожалуйста, попробуйте снова через 24 часа.";
 $a->strings["Ignore/Hide"] = "Проигнорировать/Скрыть";
+$a->strings["Friend Suggestions"] = "Предложения друзей";
 $a->strings["Profile deleted."] = "Профиль удален.";
 $a->strings["Profile-"] = "Профиль-";
 $a->strings["New profile created."] = "Новый профиль создан.";
@@ -1079,6 +1220,7 @@ $a->strings[" - Visit %1\$s's %2\$s"] = " - Посетить профиль %1\$
 $a->strings["%1\$s has an updated %2\$s, changing %3\$s."] = "";
 $a->strings["Hide contacts and friends:"] = "";
 $a->strings["Hide your contact/friend list from viewers of this profile?"] = "Скрывать ваш список контактов / друзей от посетителей этого профиля?";
+$a->strings["Show more profile fields:"] = "";
 $a->strings["Edit Profile Details"] = "Редактировать детали профиля";
 $a->strings["Change Profile Photo"] = "Изменить фото профиля";
 $a->strings["View this profile"] = "Просмотреть этот профиль";
@@ -1094,7 +1236,7 @@ $a->strings["Profile Name:"] = "Имя профиля:";
 $a->strings["Your Full Name:"] = "Ваше полное имя:";
 $a->strings["Title/Description:"] = "Заголовок / Описание:";
 $a->strings["Your Gender:"] = "Ваш пол:";
-$a->strings["Birthday (%s):"] = "День рождения (%s):";
+$a->strings["Birthday :"] = "";
 $a->strings["Street Address:"] = "Адрес:";
 $a->strings["Locality/City:"] = "Город / Населенный пункт:";
 $a->strings["Postal/Zip Code:"] = "Почтовый индекс:";
@@ -1127,6 +1269,7 @@ $a->strings["Love/romance"] = "Любовь / романтика";
 $a->strings["Work/employment"] = "Работа / занятость";
 $a->strings["School/education"] = "Школа / образование";
 $a->strings["This is your <strong>public</strong> profile.<br />It <strong>may</strong> be visible to anybody using the internet."] = "Это ваш <strong>публичный</strong> профиль. <br /> Он <strong>может</strong> быть виден каждому через Интернет.";
+$a->strings["Age: "] = "Возраст: ";
 $a->strings["Edit/Manage Profiles"] = "Редактировать профиль";
 $a->strings["Change profile photo"] = "Изменить фото профиля";
 $a->strings["Create New Profile"] = "Создать новый профиль";
@@ -1136,7 +1279,7 @@ $a->strings["Edit visibility"] = "Редактировать видимость"
 $a->strings["Item not found"] = "Элемент не найден";
 $a->strings["Edit post"] = "Редактировать сообщение";
 $a->strings["upload photo"] = "загрузить фото";
-$a->strings["Attach file"] = "Ð\9fÑ\80иложить файл";
+$a->strings["Attach file"] = "Ð\9fÑ\80икÑ\80епить файл";
 $a->strings["attach file"] = "приложить файл";
 $a->strings["web link"] = "веб-ссылка";
 $a->strings["Insert video link"] = "Вставить ссылку видео";
@@ -1157,6 +1300,7 @@ $a->strings["This is Friendica, version"] = "Это Friendica, версия";
 $a->strings["running at web location"] = "работает на веб-узле";
 $a->strings["Please visit <a href=\"http://friendica.com\">Friendica.com</a> to learn more about the Friendica project."] = "Пожалуйста, посетите сайт <a href=\"http://friendica.com\">Friendica.com</a>, чтобы узнать больше о проекте Friendica.";
 $a->strings["Bug reports and issues: please visit"] = "Отчет об ошибках и проблемах: пожалуйста, посетите";
+$a->strings["the bugtracker at github"] = "";
 $a->strings["Suggestions, praise, donations, etc. - please email \"Info\" at Friendica - dot com"] = "Предложения, похвала, пожертвования? Пишите на \"info\" на Friendica - точка com";
 $a->strings["Installed plugins/addons/apps:"] = "Установленные плагины / добавки / приложения:";
 $a->strings["No installed plugins/addons/apps"] = "Нет установленных плагинов / добавок / приложений";
@@ -1179,6 +1323,8 @@ $a->strings["poke, prod or do other things to somebody"] = "";
 $a->strings["Recipient"] = "Получатель";
 $a->strings["Choose what you wish to do to recipient"] = "Выберите действия для получателя";
 $a->strings["Make this post private"] = "Сделать эту запись личной";
+$a->strings["Resubscribing to OStatus contacts"] = "";
+$a->strings["Error"] = "Ошибка";
 $a->strings["Total invitation limit exceeded."] = "Превышен общий лимит приглашений.";
 $a->strings["%s : Not a valid email address."] = "%s: Неверный адрес электронной почты.";
 $a->strings["Please join us on Friendica"] = "Пожалуйста, присоединяйтесь к нам на Friendica";
@@ -1188,6 +1334,7 @@ $a->strings["%d message sent."] = array(
        0 => "%d сообщение отправлено.",
        1 => "%d сообщений отправлено.",
        2 => "%d сообщений отправлено.",
+       3 => "%d сообщений отправлено.",
 );
 $a->strings["You have no more invitations available"] = "У вас нет больше приглашений";
 $a->strings["Visit %s for a list of public sites that you can join. Friendica members on other sites can all connect with each other, as well as with members of many other social networks."] = "Посетите %s со списком общедоступных сайтов, к которым вы можете присоединиться. Все участники Friendica на других сайтах могут соединиться друг с другом, а также с участниками многих других социальных сетей.";
@@ -1201,7 +1348,7 @@ $a->strings["You will need to supply this invitation code: \$invite_code"] = "В
 $a->strings["Once you have registered, please connect with me via my profile page at:"] = "После того как вы зарегистрировались, пожалуйста, свяжитесь со мной через мою страницу профиля по адресу:";
 $a->strings["For more information about the Friendica project and why we feel it is important, please visit http://friendica.com"] = "Для получения более подробной информации о проекте Friendica, пожалуйста, посетите http://friendica.com";
 $a->strings["Photo Albums"] = "Фотоальбомы";
-$a->strings["Contact Photos"] = "Фотографии контакта";
+$a->strings["Recent Photos"] = "Последние фото";
 $a->strings["Upload New Photos"] = "Загрузить новые фото";
 $a->strings["Contact information unavailable"] = "Информация о контакте недоступна";
 $a->strings["Album not found."] = "Альбом не найден.";
@@ -1211,7 +1358,6 @@ $a->strings["Delete Photo"] = "Удалить фото";
 $a->strings["Do you really want to delete this photo?"] = "Вы действительно хотите удалить эту фотографию?";
 $a->strings["%1\$s was tagged in %2\$s by %3\$s"] = "%1\$s отмечен/а/ в %2\$s by %3\$s";
 $a->strings["a photo"] = "фото";
-$a->strings["Image exceeds size limit of "] = "Размер фото превышает лимит ";
 $a->strings["Image file is empty."] = "Файл изображения пуст.";
 $a->strings["No photos selected"] = "Не выбрано фото.";
 $a->strings["You have used %1$.2f Mbytes of %2$.2f Mbytes photo storage."] = "Вы использовали %1$.2f мегабайт из %2$.2f возможных для хранения фотографий.";
@@ -1234,23 +1380,33 @@ $a->strings["Use as profile photo"] = "Использовать как фото
 $a->strings["View Full Size"] = "Просмотреть полный размер";
 $a->strings["Tags: "] = "Ключевые слова: ";
 $a->strings["[Remove any tag]"] = "[Удалить любое ключевое слово]";
-$a->strings["Rotate CW (right)"] = "Поворот по часовой стрелке (направо)";
-$a->strings["Rotate CCW (left)"] = "Поворот против часовой стрелки (налево)";
 $a->strings["New album name"] = "Название нового альбома";
 $a->strings["Caption"] = "Подпись";
 $a->strings["Add a Tag"] = "Добавить ключевое слово (таг)";
 $a->strings["Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping"] = "Пример: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping";
+$a->strings["Do not rotate"] = "";
+$a->strings["Rotate CW (right)"] = "Поворот по часовой стрелке (направо)";
+$a->strings["Rotate CCW (left)"] = "Поворот против часовой стрелки (налево)";
 $a->strings["Private photo"] = "Личное фото";
 $a->strings["Public photo"] = "Публичное фото";
 $a->strings["Share"] = "Поделиться";
-$a->strings["Recent Photos"] = "Последние фото";
+$a->strings["Attending"] = array(
+       0 => "",
+       1 => "",
+       2 => "",
+       3 => "",
+);
+$a->strings["Not attending"] = "";
+$a->strings["Might attend"] = "";
+$a->strings["Map"] = "Карта";
+$a->strings["Not Extended"] = "";
 $a->strings["Account approved."] = "Аккаунт утвержден.";
 $a->strings["Registration revoked for %s"] = "Регистрация отменена для %s";
 $a->strings["Please login."] = "Пожалуйста, войдите с паролем.";
 $a->strings["Move account"] = "Удалить аккаунт";
 $a->strings["You can import an account from another Friendica server."] = "Вы можете импортировать учетную запись с другого сервера Friendica.";
 $a->strings["You need to export your account from the old server and upload it here. We will recreate your old account here with all your contacts. We will try also to inform your friends that you moved here."] = "Вам нужно экспортировать свой ​​аккаунт со старого сервера и загрузить его сюда. Мы восстановим ваш ​​старый аккаунт здесь со всеми вашими контактами. Мы постараемся также сообщить друзьям, что вы переехали сюда.";
-$a->strings["This feature is experimental. We can't import contacts from the OStatus network (statusnet/identi.ca) or from Diaspora"] = "Это экспериментальная функция. Мы не можем импортировать контакты из сети OStatus (StatusNet / identi.ca) или из Diaspora";
+$a->strings["This feature is experimental. We can't import contacts from the OStatus network (GNU Social/Statusnet) or from Diaspora"] = "";
 $a->strings["Account file"] = "Файл аккаунта";
 $a->strings["To export your account, go to \"Settings->Export your personal data\" and select \"Export account\""] = "Для экспорта аккаунта, перейдите в \"Настройки->Экспортировать ваши данные\" и выберите \"Экспорт аккаунта\"";
 $a->strings["Item not available."] = "Пункт не доступен.";
@@ -1269,31 +1425,13 @@ $a->strings["Website Terms of Service"] = "Правила сайта";
 $a->strings["terms of service"] = "правила";
 $a->strings["Website Privacy Policy"] = "Политика конфиденциальности сервера";
 $a->strings["privacy policy"] = "политика конфиденциальности";
-$a->strings["Requested account is not available."] = "Запрашиваемый профиль недоступен.";
-$a->strings["Edit profile"] = "Редактировать профиль";
-$a->strings["Message"] = "Сообщение";
-$a->strings["Profiles"] = "Профили";
-$a->strings["Manage/edit profiles"] = "Управление / редактирование профилей";
-$a->strings["Network:"] = "";
-$a->strings["g A l F d"] = "g A l F d";
-$a->strings["F d"] = "F d";
-$a->strings["[today]"] = "[сегодня]";
-$a->strings["Birthday Reminders"] = "Напоминания о днях рождения";
-$a->strings["Birthdays this week:"] = "Дни рождения на этой неделе:";
-$a->strings["[No description]"] = "[без описания]";
-$a->strings["Event Reminders"] = "Напоминания о мероприятиях";
-$a->strings["Events this week:"] = "Мероприятия на этой неделе:";
-$a->strings["Status"] = "Статус";
-$a->strings["Status Messages and Posts"] = "Сообщение статуса и посты";
-$a->strings["Profile Details"] = "Детали профиля";
-$a->strings["Videos"] = "Видео";
-$a->strings["Events and Calendar"] = "Календарь и события";
-$a->strings["Only You Can See This"] = "Только вы можете это видеть";
 $a->strings["This entry was edited"] = "Эта запись была отредактирована";
+$a->strings["I will attend"] = "";
+$a->strings["I will not attend"] = "";
+$a->strings["I might attend"] = "";
 $a->strings["ignore thread"] = "";
 $a->strings["unignore thread"] = "";
 $a->strings["toggle ignore status"] = "";
-$a->strings["ignored"] = "";
 $a->strings["Categories:"] = "Категории:";
 $a->strings["Filed under:"] = "В рубрике:";
 $a->strings["via"] = "через";
@@ -1311,10 +1449,10 @@ $a->strings["%d invitation available"] = array(
        0 => "%d приглашение доступно",
        1 => "%d приглашений доступно",
        2 => "%d приглашений доступно",
+       3 => "%d приглашений доступно",
 );
 $a->strings["Find People"] = "Поиск людей";
 $a->strings["Enter name or interest"] = "Введите имя или интерес";
-$a->strings["Connect/Follow"] = "Подключиться/Следовать";
 $a->strings["Examples: Robert Morgenstein, Fishing"] = "Примеры: Роберт Morgenstein, Рыбалка";
 $a->strings["Similar Interests"] = "Похожие интересы";
 $a->strings["Random Profile"] = "Случайный профиль";
@@ -1324,19 +1462,29 @@ $a->strings["All Networks"] = "Все сети";
 $a->strings["Saved Folders"] = "Сохранённые папки";
 $a->strings["Everything"] = "Всё";
 $a->strings["Categories"] = "Категории";
+$a->strings["%d contact in common"] = array(
+       0 => "%d Контакт",
+       1 => "%d Контактов",
+       2 => "%d Контактов",
+       3 => "%d Контактов",
+);
 $a->strings["General Features"] = "Основные возможности";
 $a->strings["Multiple Profiles"] = "Несколько профилей";
 $a->strings["Ability to create multiple profiles"] = "Возможность создания нескольких профилей";
-$a->strings["Post Composition Features"] = "";
+$a->strings["Photo Location"] = "";
+$a->strings["Photo metadata is normally stripped. This extracts the location (if present) prior to stripping metadata and links it to a map."] = "";
+$a->strings["Post Composition Features"] = "Составление сообщений";
 $a->strings["Richtext Editor"] = "Редактор RTF";
 $a->strings["Enable richtext editor"] = "Включить редактор RTF";
-$a->strings["Post Preview"] = "предварительный просмотр";
+$a->strings["Post Preview"] = "Ð\9fредварительный просмотр";
 $a->strings["Allow previewing posts and comments before publishing them"] = "Разрешить предпросмотр сообщения и комментария перед их публикацией";
 $a->strings["Auto-mention Forums"] = "";
 $a->strings["Add/remove mention when a fourm page is selected/deselected in ACL window."] = "";
 $a->strings["Network Sidebar Widgets"] = "Виджет боковой панели \"Сеть\"";
 $a->strings["Search by Date"] = "Поиск по датам";
 $a->strings["Ability to select posts by date ranges"] = "Возможность выбора постов по диапазону дат";
+$a->strings["List Forums"] = "";
+$a->strings["Enable widget to display the forums your are connected with"] = "";
 $a->strings["Group Filter"] = "Фильтр групп";
 $a->strings["Enable widget to display Network posts only from selected group"] = "Включить виджет для отображения сообщений сети только от выбранной группы";
 $a->strings["Network Filter"] = "Фильтр сети";
@@ -1365,6 +1513,8 @@ $a->strings["Star Posts"] = "Популярные посты";
 $a->strings["Ability to mark special posts with a star indicator"] = "Возможность отметить специальные сообщения индикатором популярности";
 $a->strings["Mute Post Notifications"] = "";
 $a->strings["Ability to mute notifications for a thread"] = "";
+$a->strings["Advanced Profile Settings"] = "Расширенные настройки профиля";
+$a->strings["Show visitors public community forums at the Advanced Profile Page"] = "";
 $a->strings["Connect URL missing."] = "Connect-URL отсутствует.";
 $a->strings["This site is not configured to allow communications with other networks."] = "Данный сайт не настроен так, чтобы держать связь с другими сетями.";
 $a->strings["No compatible communication protocols or feeds were discovered."] = "Обнаружены несовместимые протоколы связи или каналы.";
@@ -1381,18 +1531,17 @@ $a->strings["A deleted group with this name was revived. Existing item permissio
 $a->strings["Default privacy group for new contacts"] = "Группа доступа по умолчанию для новых контактов";
 $a->strings["Everybody"] = "Каждый";
 $a->strings["edit"] = "редактировать";
+$a->strings["Edit groups"] = "";
 $a->strings["Edit group"] = "Редактировать группу";
 $a->strings["Create a new group"] = "Создать новую группу";
 $a->strings["Contacts not in any group"] = "Контакты не состоят в группе";
 $a->strings["Miscellaneous"] = "Разное";
-$a->strings["year"] = "год";
-$a->strings["month"] = "мес.";
-$a->strings["day"] = "день";
+$a->strings["YYYY-MM-DD or MM-DD"] = "";
 $a->strings["never"] = "никогда";
 $a->strings["less than a second ago"] = "менее сек. назад";
+$a->strings["year"] = "год";
 $a->strings["years"] = "лет";
 $a->strings["months"] = "мес.";
-$a->strings["week"] = "неделя";
 $a->strings["weeks"] = "недель";
 $a->strings["days"] = "дней";
 $a->strings["hour"] = "час";
@@ -1404,26 +1553,68 @@ $a->strings["seconds"] = "сек.";
 $a->strings["%1\$d %2\$s ago"] = "%1\$d %2\$s назад";
 $a->strings["%s's birthday"] = "день рождения %s";
 $a->strings["Happy Birthday %s"] = "С днём рождения %s";
+$a->strings["Requested account is not available."] = "Запрашиваемый профиль недоступен.";
+$a->strings["Edit profile"] = "Редактировать профиль";
+$a->strings["Atom feed"] = "";
+$a->strings["Message"] = "Сообщение";
+$a->strings["Profiles"] = "Профили";
+$a->strings["Manage/edit profiles"] = "Управление / редактирование профилей";
+$a->strings["g A l F d"] = "g A l F d";
+$a->strings["F d"] = "F d";
+$a->strings["[today]"] = "[сегодня]";
+$a->strings["Birthday Reminders"] = "Напоминания о днях рождения";
+$a->strings["Birthdays this week:"] = "Дни рождения на этой неделе:";
+$a->strings["[No description]"] = "[без описания]";
+$a->strings["Event Reminders"] = "Напоминания о мероприятиях";
+$a->strings["Events this week:"] = "Мероприятия на этой неделе:";
+$a->strings["j F, Y"] = "j F, Y";
+$a->strings["j F"] = "j F";
+$a->strings["Birthday:"] = "День рождения:";
+$a->strings["Age:"] = "Возраст:";
+$a->strings["for %1\$d %2\$s"] = "";
+$a->strings["Religion:"] = "Религия:";
+$a->strings["Hobbies/Interests:"] = "Хобби / Интересы:";
+$a->strings["Contact information and Social Networks:"] = "Информация о контакте и социальных сетях:";
+$a->strings["Musical interests:"] = "Музыкальные интересы:";
+$a->strings["Books, literature:"] = "Книги, литература:";
+$a->strings["Television:"] = "Телевидение:";
+$a->strings["Film/dance/culture/entertainment:"] = "Кино / Танцы / Культура / Развлечения:";
+$a->strings["Love/Romance:"] = "Любовь / Романтика:";
+$a->strings["Work/employment:"] = "Работа / Занятость:";
+$a->strings["School/education:"] = "Школа / Образование:";
+$a->strings["Forums:"] = "";
+$a->strings["Videos"] = "Видео";
+$a->strings["Events and Calendar"] = "Календарь и события";
+$a->strings["Only You Can See This"] = "Только вы можете это видеть";
+$a->strings["event"] = "мероприятие";
+$a->strings["%1\$s likes %2\$s's %3\$s"] = "%1\$s нравится %3\$s от %2\$s ";
+$a->strings["%1\$s doesn't like %2\$s's %3\$s"] = "%1\$s не нравится %3\$s от %2\$s ";
+$a->strings["%1\$s is attending %2\$s's %3\$s"] = "";
+$a->strings["%1\$s is not attending %2\$s's %3\$s"] = "";
+$a->strings["%1\$s may attend %2\$s's %3\$s"] = "";
+$a->strings["Post to Email"] = "Отправить на Email";
+$a->strings["Connectors disabled, since \"%s\" is enabled."] = "";
 $a->strings["Visible to everybody"] = "Видимо всем";
 $a->strings["show"] = "показывать";
 $a->strings["don't show"] = "не показывать";
+$a->strings["Close"] = "Закрыть";
 $a->strings["[no subject]"] = "[без темы]";
 $a->strings["stopped following"] = "остановлено следование";
-$a->strings["Poke"] = "";
 $a->strings["View Status"] = "Просмотреть статус";
-$a->strings["View Profile"] = "Просмотреть профиль";
 $a->strings["View Photos"] = "Просмотреть фото";
 $a->strings["Network Posts"] = "Посты сети";
 $a->strings["Edit Contact"] = "Редактировать контакт";
 $a->strings["Drop Contact"] = "Удалить контакт";
 $a->strings["Send PM"] = "Отправить ЛС";
+$a->strings["Poke"] = "";
 $a->strings["Welcome "] = "Добро пожаловать, ";
 $a->strings["Please upload a profile photo."] = "Пожалуйста, загрузите фотографию профиля.";
 $a->strings["Welcome back "] = "Добро пожаловать обратно, ";
 $a->strings["The form security token was not correct. This probably happened because the form has been opened for too long (>3 hours) before submitting it."] = "Ключ формы безопасности неправильный. Вероятно, это произошло потому, что форма была открыта слишком долго (более 3 часов) до её отправки.";
-$a->strings["event"] = "мероприятие";
+$a->strings["%1\$s attends %2\$s's %3\$s"] = "";
+$a->strings["%1\$s doesn't attend %2\$s's %3\$s"] = "";
+$a->strings["%1\$s attends maybe %2\$s's %3\$s"] = "";
 $a->strings["%1\$s poked %2\$s"] = "";
-$a->strings["poked"] = "";
 $a->strings["post/item"] = "пост/элемент";
 $a->strings["%1\$s marked %2\$s's %3\$s as favorite"] = "%1\$s пометил %2\$s %3\$s как Фаворит";
 $a->strings["remove"] = "удалить";
@@ -1431,24 +1622,58 @@ $a->strings["Delete Selected Items"] = "Удалить выбранные поз
 $a->strings["Follow Thread"] = "";
 $a->strings["%s likes this."] = "%s нравится это.";
 $a->strings["%s doesn't like this."] = "%s не нравится это.";
-$a->strings["<span  %1\$s>%2\$d people</span> like this"] = "<span %1\$s>%2\$d людям</span> нравится это";
-$a->strings["<span  %1\$s>%2\$d people</span> don't like this"] = "<span %1\$s>%2\$d людям</span> не нравится это";
+$a->strings["%s attends."] = "";
+$a->strings["%s doesn't attend."] = "";
+$a->strings["%s attends maybe."] = "";
 $a->strings["and"] = "и";
 $a->strings[", and %d other people"] = ", и %d других чел.";
-$a->strings["%s like this."] = "%s нравится это.";
-$a->strings["%s don't like this."] = "%s не нравится это.";
+$a->strings["<span  %1\$s>%2\$d people</span> like this"] = "<span %1\$s>%2\$d людям</span> нравится это";
+$a->strings["%s like this."] = "";
+$a->strings["<span  %1\$s>%2\$d people</span> don't like this"] = "<span %1\$s>%2\$d людям</span> не нравится это";
+$a->strings["%s don't like this."] = "";
+$a->strings["<span  %1\$s>%2\$d people</span> attend"] = "";
+$a->strings["%s attend."] = "";
+$a->strings["<span  %1\$s>%2\$d people</span> don't attend"] = "";
+$a->strings["%s don't attend."] = "";
+$a->strings["<span  %1\$s>%2\$d people</span> anttend maybe"] = "";
+$a->strings["%s anttend maybe."] = "";
 $a->strings["Visible to <strong>everybody</strong>"] = "Видимое <strong>всем</strong>";
 $a->strings["Please enter a video link/URL:"] = "Введите  ссылку на видео link/URL:";
 $a->strings["Please enter an audio link/URL:"] = "Введите ссылку на аудио link/URL:";
 $a->strings["Tag term:"] = "";
 $a->strings["Where are you right now?"] = "И где вы сейчас?";
 $a->strings["Delete item(s)?"] = "Удалить елемент(ты)?";
-$a->strings["Post to Email"] = "Отправить на Email";
-$a->strings["Connectors disabled, since \"%s\" is enabled."] = "";
 $a->strings["permissions"] = "разрешения";
 $a->strings["Post to Groups"] = "Пост для групп";
 $a->strings["Post to Contacts"] = "Пост для контактов";
 $a->strings["Private post"] = "Личное сообщение";
+$a->strings["View all"] = "";
+$a->strings["Like"] = array(
+       0 => "",
+       1 => "",
+       2 => "",
+       3 => "",
+);
+$a->strings["Dislike"] = array(
+       0 => "",
+       1 => "",
+       2 => "",
+       3 => "",
+);
+$a->strings["Not Attending"] = array(
+       0 => "",
+       1 => "",
+       2 => "",
+       3 => "",
+);
+$a->strings["Undecided"] = array(
+       0 => "",
+       1 => "",
+       2 => "",
+       3 => "",
+);
+$a->strings["Forums"] = "";
+$a->strings["External link to forum"] = "";
 $a->strings["view full size"] = "посмотреть в полный размер";
 $a->strings["newer"] = "новее";
 $a->strings["older"] = "старее";
@@ -1456,13 +1681,20 @@ $a->strings["prev"] = "пред.";
 $a->strings["first"] = "первый";
 $a->strings["last"] = "последний";
 $a->strings["next"] = "след.";
+$a->strings["Loading more entries..."] = "";
+$a->strings["The end"] = "";
 $a->strings["No contacts"] = "Нет контактов";
 $a->strings["%d Contact"] = array(
        0 => "%d контакт",
        1 => "%d контактов",
        2 => "%d контактов",
+       3 => "%d контактов",
 );
+$a->strings["View Contacts"] = "Просмотр контактов";
+$a->strings["Full Text"] = "Контент";
+$a->strings["Tags"] = "Тэги";
 $a->strings["poke"] = "poke";
+$a->strings["poked"] = "";
 $a->strings["ping"] = "пинг";
 $a->strings["pinged"] = "пингуется";
 $a->strings["prod"] = "";
@@ -1493,29 +1725,10 @@ $a->strings["frustrated"] = "";
 $a->strings["motivated"] = "";
 $a->strings["relaxed"] = "";
 $a->strings["surprised"] = "";
-$a->strings["Monday"] = "Понедельник";
-$a->strings["Tuesday"] = "Вторник";
-$a->strings["Wednesday"] = "Среда";
-$a->strings["Thursday"] = "Четверг";
-$a->strings["Friday"] = "Пятница";
-$a->strings["Saturday"] = "Суббота";
-$a->strings["Sunday"] = "Воскресенье";
-$a->strings["January"] = "Январь";
-$a->strings["February"] = "Февраль";
-$a->strings["March"] = "Март";
-$a->strings["April"] = "Апрель";
-$a->strings["May"] = "Май";
-$a->strings["June"] = "Июнь";
-$a->strings["July"] = "Июль";
-$a->strings["August"] = "Август";
-$a->strings["September"] = "Сентябрь";
-$a->strings["October"] = "Октябрь";
-$a->strings["November"] = "Ноябрь";
-$a->strings["December"] = "Декабрь";
 $a->strings["bytes"] = "байт";
 $a->strings["Click to open/close"] = "Нажмите, чтобы открыть / закрыть";
-$a->strings["default"] = "значение по умолчанию";
-$a->strings["Select an alternate language"] = "Выбор альтернативного языка";
+$a->strings["View on separate page"] = "";
+$a->strings["view on separate page"] = "";
 $a->strings["activity"] = "активность";
 $a->strings["post"] = "сообщение";
 $a->strings["Item filed"] = "";
@@ -1524,8 +1737,6 @@ $a->strings["<a href=\"%1\$s\" target=\"_blank\">%2\$s</a> %3\$s"] = "";
 $a->strings["<span><a href=\"%s\" target=\"_blank\">%s</a> wrote the following <a href=\"%s\" target=\"_blank\">post</a>"] = "";
 $a->strings["$1 wrote:"] = "$1 написал:";
 $a->strings["Encrypted content"] = "Зашифрованный контент";
-$a->strings["(no subject)"] = "(без темы)";
-$a->strings["noreply"] = "без ответа";
 $a->strings["Cannot locate DNS info for database server '%s'"] = "Не могу найти информацию для DNS-сервера базы данных '%s'";
 $a->strings["Unknown | Not categorised"] = "Неизвестно | Не определено";
 $a->strings["Block immediately"] = "Блокировать немедленно";
@@ -1537,6 +1748,7 @@ $a->strings["Weekly"] = "Еженедельно";
 $a->strings["Monthly"] = "Ежемесячно";
 $a->strings["OStatus"] = "OStatus";
 $a->strings["RSS/Atom"] = "RSS/Atom";
+$a->strings["Facebook"] = "Facebook";
 $a->strings["Zot!"] = "Zot!";
 $a->strings["LinkedIn"] = "LinkedIn";
 $a->strings["XMPP/IM"] = "XMPP/IM";
@@ -1545,33 +1757,18 @@ $a->strings["Google+"] = "Google+";
 $a->strings["pump.io"] = "pump.io";
 $a->strings["Twitter"] = "Twitter";
 $a->strings["Diaspora Connector"] = "";
-$a->strings["Statusnet"] = "";
+$a->strings["GNU Social"] = "";
 $a->strings["App.net"] = "";
+$a->strings["Redmatrix"] = "";
 $a->strings[" on Last.fm"] = "на Last.fm";
 $a->strings["Starts:"] = "Начало:";
 $a->strings["Finishes:"] = "Окончание:";
-$a->strings["j F, Y"] = "j F, Y";
-$a->strings["j F"] = "j F";
-$a->strings["Birthday:"] = "День рождения:";
-$a->strings["Age:"] = "Возраст:";
-$a->strings["for %1\$d %2\$s"] = "";
-$a->strings["Tags:"] = "Ключевые слова: ";
-$a->strings["Religion:"] = "Религия:";
-$a->strings["Hobbies/Interests:"] = "Хобби / Интересы:";
-$a->strings["Contact information and Social Networks:"] = "Информация о контакте и социальных сетях:";
-$a->strings["Musical interests:"] = "Музыкальные интересы:";
-$a->strings["Books, literature:"] = "Книги, литература:";
-$a->strings["Television:"] = "Телевидение:";
-$a->strings["Film/dance/culture/entertainment:"] = "Кино / Танцы / Культура / Развлечения:";
-$a->strings["Love/Romance:"] = "Любовь / Романтика:";
-$a->strings["Work/employment:"] = "Работа / Занятость:";
-$a->strings["School/education:"] = "Школа / Образование:";
 $a->strings["Click here to upgrade."] = "Нажмите для обновления.";
 $a->strings["This action exceeds the limits set by your subscription plan."] = "Это действие превышает лимиты, установленные вашим тарифным планом.";
 $a->strings["This action is not available under your subscription plan."] = "Это действие не доступно в соответствии с вашим планом подписки.";
-$a->strings["End this session"] = "Ð\9aонеÑ\86 Ñ\8dÑ\82ой Ñ\81еÑ\81Ñ\81ии";
-$a->strings["Your posts and conversations"] = "Ð\92аÑ\88и Ñ\81ообÑ\89ениÑ\8f Ð¸ Ð±ÐµÑ\81едÑ\8b";
-$a->strings["Your profile page"] = "СÑ\82Ñ\80аниÑ\86а Ð\92аÑ\88его Ð¿Ñ\80оÑ\84илÑ\8f";
+$a->strings["End this session"] = "Ð\97авеÑ\80Ñ\88иÑ\82Ñ\8c Ñ\8dÑ\82Ñ\83 Ñ\81еÑ\81Ñ\81иÑ\8e";
+$a->strings["Your posts and conversations"] = "Ð\94аннÑ\8bе Ð²Ð°Ñ\88ей Ñ\83Ñ\87Ñ\91Ñ\82ной Ð·Ð°Ð¿Ð¸Ñ\81и";
+$a->strings["Your profile page"] = "Ð\98нÑ\84оÑ\80маÑ\86иÑ\8f Ð¾ Ð²Ð°Ñ\81";
 $a->strings["Your photos"] = "Ваши фотографии";
 $a->strings["Your videos"] = "";
 $a->strings["Your events"] = "Ваши события";
@@ -1588,9 +1785,9 @@ $a->strings["Conversations on this site"] = "Беседы на этом сайт
 $a->strings["Conversations on the network"] = "";
 $a->strings["Directory"] = "Каталог";
 $a->strings["People directory"] = "Каталог участников";
-$a->strings["Information"] = "";
+$a->strings["Information"] = "Информация";
 $a->strings["Information about this friendica instance"] = "";
-$a->strings["Conversations from your friends"] = "Ð\91еÑ\81едÑ\8b Ñ\81 Ð´Ñ\80Ñ\83зÑ\8cÑ\8fми";
+$a->strings["Conversations from your friends"] = "Ð\9fоÑ\81Ñ\82Ñ\8b Ð²Ð°Ñ\88иÑ\85 Ð´Ñ\80Ñ\83зей";
 $a->strings["Network Reset"] = "Перезагрузка сети";
 $a->strings["Load Network page with no filters"] = "Загрузить страницу сети без фильтров";
 $a->strings["Friend Requests"] = "Запросы на добавление в список друзей";
@@ -1604,19 +1801,12 @@ $a->strings["Manage other pages"] = "Управление другими стр
 $a->strings["Account settings"] = "Настройки аккаунта";
 $a->strings["Manage/Edit Profiles"] = "Управление/редактирование профилей";
 $a->strings["Manage/edit friends and contacts"] = "Управление / редактирование друзей и контактов";
-$a->strings["Site setup and configuration"] = "УÑ\81Ñ\82ановка Ð¸ Ðºонфигурация сайта";
+$a->strings["Site setup and configuration"] = "Ð\9aонфигурация сайта";
 $a->strings["Navigation"] = "Навигация";
 $a->strings["Site map"] = "Карта сайта";
-$a->strings["User not found."] = "Пользователь не найден.";
 $a->strings["Daily posting limit of %d posts reached. The post was rejected."] = "";
 $a->strings["Weekly posting limit of %d posts reached. The post was rejected."] = "";
 $a->strings["Monthly posting limit of %d posts reached. The post was rejected."] = "";
-$a->strings["There is no status with this id."] = "Нет статуса с таким id.";
-$a->strings["There is no conversation with this id."] = "";
-$a->strings["Invalid request."] = "";
-$a->strings["Invalid item."] = "";
-$a->strings["Invalid action. "] = "";
-$a->strings["DB error"] = "";
 $a->strings["An invitation is required."] = "Требуется приглашение.";
 $a->strings["Invitation could not be verified."] = "Приглашение не может быть проверено.";
 $a->strings["Invalid OpenID url"] = "Неверный URL OpenID";
@@ -1627,17 +1817,20 @@ $a->strings["That doesn't appear to be your full (First Last) name."] = "Каж
 $a->strings["Your email domain is not among those allowed on this site."] = "Домен вашего адреса электронной почты не относится к числу разрешенных на этом сайте.";
 $a->strings["Not a valid email address."] = "Неверный адрес электронной почты.";
 $a->strings["Cannot use that email."] = "Нельзя использовать этот Email.";
-$a->strings["Your \"nickname\" can only contain \"a-z\", \"0-9\", \"-\", and \"_\", and must also begin with a letter."] = "Ваш \"ник\" может содержать только \"a-z\", \"0-9\", \"-\", и \"_\", а также должен начинаться с буквы.";
+$a->strings["Your \"nickname\" can only contain \"a-z\", \"0-9\" and \"_\"."] = "";
 $a->strings["Nickname is already registered. Please choose another."] = "Такой ник уже зарегистрирован. Пожалуйста, выберите другой.";
 $a->strings["Nickname was once registered here and may not be re-used. Please choose another."] = "Ник уже зарегистрирован на этом сайте и не может быть изменён. Пожалуйста, выберите другой ник.";
 $a->strings["SERIOUS ERROR: Generation of security keys failed."] = "СЕРЬЕЗНАЯ ОШИБКА: генерация ключей безопасности не удалась.";
 $a->strings["An error occurred during registration. Please try again."] = "Ошибка при регистрации. Пожалуйста, попробуйте еще раз.";
+$a->strings["default"] = "значение по умолчанию";
 $a->strings["An error occurred creating your default profile. Please try again."] = "Ошибка создания вашего профиля. Пожалуйста, попробуйте еще раз.";
 $a->strings["Friends"] = "Друзья";
 $a->strings["\n\t\tDear %1\$s,\n\t\t\tThank you for registering at %2\$s. Your account has been created.\n\t"] = "";
 $a->strings["\n\t\tThe login details are as follows:\n\t\t\tSite Location:\t%3\$s\n\t\t\tLogin Name:\t%1\$s\n\t\t\tPassword:\t%5\$s\n\n\t\tYou may change your password from your account \"Settings\" page after logging\n\t\tin.\n\n\t\tPlease take a few moments to review the other account settings on that page.\n\n\t\tYou may also wish to add some basic information to your default profile\n\t\t(on the \"Profiles\" page) so that other people can easily find you.\n\n\t\tWe recommend setting your full name, adding a profile photo,\n\t\tadding some profile \"keywords\" (very useful in making new friends) - and\n\t\tperhaps what country you live in; if you do not wish to be more specific\n\t\tthan that.\n\n\t\tWe fully respect your right to privacy, and none of these items are necessary.\n\t\tIf you are new and do not know anybody here, they may help\n\t\tyou to make some new and interesting friends.\n\n\n\t\tThank you and welcome to %2\$s."] = "";
 $a->strings["Sharing notification from Diaspora network"] = "Делиться уведомлениями из сети Diaspora";
 $a->strings["Attachments:"] = "Вложения:";
+$a->strings["(no subject)"] = "(без темы)";
+$a->strings["noreply"] = "без ответа";
 $a->strings["Do you really want to delete this item?"] = "Вы действительно хотите удалить этот элемент?";
 $a->strings["Archives"] = "Архивы";
 $a->strings["Male"] = "Мужчина";
@@ -1653,7 +1846,6 @@ $a->strings["Hermaphrodite"] = "Гермафродит";
 $a->strings["Neuter"] = "Средний род";
 $a->strings["Non-specific"] = "Не определен";
 $a->strings["Other"] = "Другой";
-$a->strings["Undecided"] = "Не решено";
 $a->strings["Males"] = "Мужчины";
 $a->strings["Females"] = "Женщины";
 $a->strings["Gay"] = "Гей";
@@ -1700,6 +1892,7 @@ $a->strings["Ask me"] = "Спросите меня";
 $a->strings["Friendica Notification"] = "Friendica уведомления";
 $a->strings["Thank You,"] = "Спасибо,";
 $a->strings["%s Administrator"] = "%s администратор";
+$a->strings["%1\$s, %2\$s Administrator"] = "";
 $a->strings["%s <!item_type!>"] = "%s <!item_type!>";
 $a->strings["[Friendica:Notify] New mail received at %s"] = "[Friendica: Оповещение] Новое сообщение, пришедшее на %s";
 $a->strings["%1\$s sent you a new private message at %2\$s."] = "%1\$s отправил вам новое личное сообщение на %2\$s.";
@@ -1743,7 +1936,7 @@ $a->strings["Name:"] = "Имя:";
 $a->strings["Photo:"] = "Фото:";
 $a->strings["Please visit %s to approve or reject the suggestion."] = "Пожалуйста, посетите %s для подтверждения, или отказа запроса.";
 $a->strings["[Friendica:Notify] Connection accepted"] = "";
-$a->strings["'%1\$s' has acepted your connection request at %2\$s"] = "";
+$a->strings["'%1\$s' has accepted your connection request at %2\$s"] = "";
 $a->strings["%2\$s has accepted your [url=%1\$s]connection request[/url]."] = "";
 $a->strings["You are now mutual friends and may exchange status updates, photos, and email\n\twithout restriction."] = "";
 $a->strings["Please visit %s  if you wish to make any changes to this relationship."] = "";
@@ -1766,10 +1959,10 @@ $a->strings["%d contact not imported"] = array(
        0 => "%d контакт не импортирован",
        1 => "%d контакты не импортированы",
        2 => "%d контакты не импортированы",
+       3 => "%d контакты не импортированы",
 );
 $a->strings["Done. You can now login with your username and password"] = "Завершено. Теперь вы можете войти с вашим логином и паролем";
 $a->strings["toggle mobile"] = "мобильная версия";
-$a->strings["Theme settings"] = "Настройки темы";
 $a->strings["Set resize level for images in posts and comments (width and height)"] = "Установить уровень изменения размера изображений в постах и ​​комментариях (ширина и высота)";
 $a->strings["Set font-size for posts and comments"] = "Установить шрифт-размер для постов и комментариев";
 $a->strings["Set theme width"] = "Установить ширину темы";
@@ -1800,7 +1993,9 @@ $a->strings["Your personal photos"] = "Ваши личные фотографи
 $a->strings["Local Directory"] = "Локальный каталог";
 $a->strings["Set zoomfactor for Earth Layers"] = "Установить масштаб карты";
 $a->strings["Show/hide boxes at right-hand column:"] = "Показать/скрыть блоки в правой колонке:";
+$a->strings["Comma separated list of helper forums"] = "";
 $a->strings["Set style"] = "";
+$a->strings["Quick Start"] = "Быстрый запуск";
 $a->strings["greenzero"] = "";
 $a->strings["purplezero"] = "";
 $a->strings["easterbunny"] = "";
index fad87da5b5b2e32a21dc61ceb18d9494c13b06f5..ee33df09b279ea32efe17e6d9e607b2da3153ccf 100644 (file)
@@ -19,7 +19,7 @@
 <script>
 var FedData = [
 {{foreach $counts as $c}}
-    { value: {{$c[0]['total']}}, label: "{{$c[0]['platform']}}", color: "#90EE90", highlight: "#EE90A1", },
+    { value: {{$c[0]['total']}}, label: "{{$c[0]['platform']}}", color: '{{$c[3]}}', highlight: "#EE90A1", },
 {{/foreach}}
 ];
 var ctx = document.getElementById("FederationChart").getContext("2d");
@@ -40,7 +40,7 @@ var myDoughnutChart = new Chart(ctx).Doughnut(FedData, { animateRotate : false,
     <script>
     var {{$c[2]}}data = [
     {{foreach $c[1] as $v}}
-       { value: {{$v['total']}}, label: '{{$v['version']}}', color: "#90EE90", highlight: "#EE90A1",},
+       { value: {{$v['total']}}, label: '{{$v['version']}}', color: "{{$c[3]}}", highlight: "#EE90A1",},
     {{/foreach}}
     ];
     var ctx = document.getElementById("{{$c[2]}}Chart").getContext("2d");
index b08e5f935f6982ebd94a4bb358c06861d4a73f79..f22319b695ec7029a7fde8ddcc261569edd24ee6 100644 (file)
@@ -87,6 +87,7 @@
        {{if $thread_allow.2}}
                {{include file="field_checkbox.tpl" field=$ostatus_disabled}}
                {{include file="field_select.tpl" field=$ostatus_poll_interval}}
+               {{include file="field_checkbox.tpl" field=$ostatus_full_threads}}
        {{else}}
                <div class='field checkbox' id='div_id_{{$ostatus_disabled.0}}'>
                        <label for='id_{{$ostatus_disabled.0}}'>{{$ostatus_disabled.1}}</label>
        {{include file="field_checkbox.tpl" field=$old_pager}}
        <div class="submit"><input type="submit" name="page_site" value="{{$submit|escape:'html'}}" /></div>
 
+       <h3>{{$worker_title}}</h3>
+       {{include file="field_checkbox.tpl" field=$worker}}
+       {{include file="field_input.tpl" field=$worker_queues}}
+       {{include file="field_checkbox.tpl" field=$worker_dont_fork}}
+       <div class="submit"><input type="submit" name="page_site" value="{{$submit|escape:'html'}}" /></div>
+
        </form>
        
        {{* separate form for relocate... *}}
index 15863b6a272227a4ece28b1153058beca943a996..93999a860c6095a904a332a632a2e336eb997403 100644 (file)
+
 {{if $header}}<h2>{{$header}}</h2>{{/if}}
 
 <div id="contact-edit-wrapper" >
 
+       {{* Insert Tab-Nav *}}
        {{$tab_str}}
 
-       <div id="contact-edit-drop-link" >
-               <a href="contacts/{{$contact_id}}/drop" class="icon drophide" id="contact-edit-drop-link" onclick="return confirmDelete();"  title="{{$delete}}" onmouseover="imgbright(this);" onmouseout="imgdull(this);"></a>
-       </div>
-
-       <div id="contact-edit-drop-link-end"></div>
-
 
        <div id="contact-edit-nav-wrapper" >
                <div id="contact-edit-links">
-                       <ul>
-                               {{if $relation_text}}
-                                       <li><div id="contact-edit-rel">{{$relation_text}}</div></li>
-                               {{/if}}
-                               {{if $lost_contact}}
-                                       <li><div id="lost-contact-message">{{$lost_contact}}</div></li>
-                               {{/if}}
-                               {{if $insecure}}
-                                       <li><div id="insecure-message">{{$insecure}}</div></li>
-                               {{/if}}
-                               {{if $blocked}}
-                                       <li><div id="block-message">{{$blocked}}</div></li>
-                               {{/if}}
-                               {{if $ignored}}
-                                       <li><div id="ignore-message">{{$ignored}}</div></li>
-                               {{/if}}
-                               {{if $archived}}
-                                       <li><div id="archive-message">{{$archived}}</div></li>
-                               {{/if}}
-                       </ul>
-
-                       <ul>
-
-                               {{if $common_text}}
-                                       <li><div id="contact-edit-common"><a href="{{$common_link}}">{{$common_text}}</a></div></li>
-                               {{/if}}
-                               {{if $all_friends}}
-                                       <li><div id="contact-edit-allfriends"><a href="allfriends/{{$contact_id}}">{{$all_friends}}</a></div></li>
-                               {{/if}}
-
-
-                               <!-- <li><a href="network/0?nets=all&cid={{$contact_id}}" id="contact-edit-view-recent">{{$lblrecent}}</a></li> -->
-                               {{if $lblsuggest}}
-                                       <li><a href="fsuggest/{{$contact_id}}" id="contact-edit-suggest">{{$lblsuggest}}</a></li>
-                               {{/if}}
-                               {{if $follow}}
-                                       <li><div id="contact-edit-follow"><a href="{{$follow}}">{{$follow_text}}</a></div></li>
-                               {{/if}}
-
-                       </ul>
-
+                       <div id="contact-edit-status-wrapper">
+                               <span id="contact-edit-contact-status">{{$contact_status}}</span>
+
+                               {{* This is the Action menu where contact related actions like 'ignore', 'hide' can be performed *}}
+                               <div id="contact-edit-actions">
+                                       <a class="btn" rel="#contact-actions-menu" href="#" id="contact-edit-actions-button">{{$contact_action_button}}</a>
+
+                                       <ul role="menu" aria-haspopup="true" id="contact-actions-menu" class="menu-popup" >
+                                               {{if $lblsuggest}}<li role="menuitem"><a  href="#" title="{{$contact_actions.suggest.title}}" onclick="window.location.href='{{$contact_actions.suggest.url}}'; return false;">{{$contact_actions.suggest.label}}</a></li>{{/if}}
+                                               {{if $poll_enabled}}<li role="menuitem"><a  href="#" title="{{$contact_actions.update.title}}" onclick="window.location.href='{{$contact_actions.update.url}}'; return false;">{{$contact_actions.update.label}}</a></li>{{/if}}
+                                               <li class="divider"></li>
+                                               <li role="menuitem"><a  href="#" title="{{$contact_actions.block.title}}" onclick="window.location.href='{{$contact_actions.block.url}}'; return false;">{{$contact_actions.block.label}}</a></li>
+                                               <li role="menuitem"><a  href="#" title="{{$contact_actions.ignore.title}}" onclick="window.location.href='{{$contact_actions.ignore.url}}'; return false;">{{$contact_actions.ignore.label}}</a></li>
+                                               <li role="menuitem"><a  href="#" title="{{$contact_actions.archive.title}}" onclick="window.location.href='{{$contact_actions.archive.url}}'; return false;">{{$contact_actions.archive.label}}</a></li>
+                                               <li role="menuitem"><a  href="#" title="{{$contact_actions.delete.title}}" onclick="return confirmDelete();">{{$contact_actions.delete.label}}</a></li>
+                                       </ul>
+                               </div>
+
+                               {{* Block with status information about the contact *}}
+                               <ul>
+                                       {{if $relation_text}}<li><div id="contact-edit-rel">{{$relation_text}}</div></li>{{/if}}
+
+                                       {{if $poll_enabled}}
+                                               <li><div id="contact-edit-last-update-text">{{$lastupdtext}} <span id="contact-edit-last-updated">{{$last_update}}</span></div>
+                                               {{if $poll_interval}}
+                                                       <span id="contact-edit-poll-text">{{$updpub}}</span> {{$poll_interval}}
+                                               {{/if}}
+                                               </li>
+                                       {{/if}}
+
+                                       {{if $lost_contact}}<li><div id="lost-contact-message">{{$lost_contact}}</div></li>{{/if}}
+                                       {{if $insecure}}<li><div id="insecure-message">{{$insecure}}</div></li> {{/if}}
+                                       {{if $blocked}}<li><div id="block-message">{{$blocked}}</div></li>{{/if}}
+                                       {{if $ignored}}<li><div id="ignore-message">{{$ignored}}</div></li>{{/if}}
+                                       {{if $archived}}<li><div id="archive-message">{{$archived}}</div></li>{{/if}}
+                               </ul>
+
+                               <ul>
+                                       <!-- <li><a href="network/0?nets=all&cid={{$contact_id}}" id="contact-edit-view-recent">{{$lblrecent}}</a></li> -->
+                                       {{if $follow}}<li><div id="contact-edit-follow"><a href="{{$follow}}">{{$follow_text}}</a></div></li>{{/if}}
+                               </ul>
+                       </div> {{* End of contact-edit-status-wrapper *}}
+
+                       {{* Some information about the contact from the profile *}}
                        <dl><dt>{{$profileurllabel}}</dt><dd><a target="blank" href="{{$url}}">{{$profileurl}}</a></dd></dl>
                        {{if $location}}<dl><dt>{{$location_label}}</dt><dd>{{$location}}</dd></dl>{{/if}}
                        {{if $keywords}}<dl><dt>{{$keywords_label}}</dt><dd>{{$keywords}}</dd></dl>{{/if}}
                        {{if $about}}<dl><dt>{{$about_label}}</dt><dd>{{$about}}</dd></dl>{{/if}}
-               </div>
-       </div>
-       <div id="contact-edit-nav-end"></div>
+               </div>{{* End of contact-edit-links *}}
+
+               <div id="contact-edit-links-end"></div>
 
-<hr />
+               <hr />
 
-<form action="contacts/{{$contact_id}}" method="post" >
-<input type="hidden" name="contact_id" value="{{$contact_id}}">
+               <h4 id="contact-edit-settings-label" class="fakelink" onclick="openClose('contact-edit-settings')">{{$contact_settings_label}}</h4>
+               <div id="contact-edit-settings">
+                       <form action="contacts/{{$contact_id}}" method="post" >
+                       <input type="hidden" name="contact_id" value="{{$contact_id}}">
 
-       <div id="contact-edit-poll-wrapper">
-               {{if $poll_enabled}}
-                       <div id="contact-edit-last-update-text">{{$lastupdtext}} <span id="contact-edit-last-updated">{{$last_update}}</span></div>
-                       {{if $poll_interval}}
-                               <span id="contact-edit-poll-text">{{$updpub}}</span> {{$poll_interval}}
+                               <div id="contact-edit-end" ></div>
+                               {{include file="field_checkbox.tpl" field=$notify}}
+                               {{if $fetch_further_information}}
+                                       {{include file="field_select.tpl" field=$fetch_further_information}}
+                                       {{if $fetch_further_information.2 == 2 }} {{include file="field_textarea.tpl" field=$ffi_keyword_blacklist}} {{/if}}
+                               {{/if}}
+                               {{include file="field_checkbox.tpl" field=$hidden}}
+
+                       <div id="contact-edit-info-wrapper">
+                               <h4>{{$lbl_info1}}</h4>
+                               <textarea id="contact-edit-info" rows="8" cols="60" name="info">{{$info}}</textarea>
+                               <input class="contact-edit-submit" type="submit" name="submit" value="{{$submit|escape:'html'}}" />
+                       </div>
+                       <div id="contact-edit-info-end"></div>
+
+                       {{if $profile_select}}
+                               <div id="contact-edit-profile-select-text">
+                               <h4>{{$lbl_vis1}}</h4>
+                               <p>{{$lbl_vis2}}</p> 
+                               </div>
+                               {{$profile_select}}
+                               <div id="contact-edit-profile-select-end"></div>
+                               <input class="contact-edit-submit" type="submit" name="submit" value="{{$submit|escape:'html'}}" />
                        {{/if}}
-                       <span id="contact-edit-update-now" class="button"><a href="contacts/{{$contact_id}}/update" >{{$udnow}}</a></span>
-               {{/if}}
-       </div>
-       <div id="contact-edit-end" ></div>
-       {{include file="field_checkbox.tpl" field=$notify}}
-       {{if $fetch_further_information}}
-               {{include file="field_select.tpl" field=$fetch_further_information}}
-               {{if $fetch_further_information.2 == 2 }} {{include file="field_textarea.tpl" field=$ffi_keyword_blacklist}} {{/if}}
-       {{/if}}
-       {{include file="field_checkbox.tpl" field=$hidden}}
-
-<div id="contact-edit-info-wrapper">
-<h4>{{$lbl_info1}}</h4>
-       <textarea id="contact-edit-info" rows="8" cols="60" name="info">{{$info}}</textarea>
-       <input class="contact-edit-submit" type="submit" name="submit" value="{{$submit|escape:'html'}}" />
-</div>
-<div id="contact-edit-info-end"></div>
-
-{{if $profile_select}}
-       <div id="contact-edit-profile-select-text">
-       <h4>{{$lbl_vis1}}</h4>
-       <p>{{$lbl_vis2}}</p> 
-       </div>
-       {{$profile_select}}
-       <div id="contact-edit-profile-select-end"></div>
-       <input class="contact-edit-submit" type="submit" name="submit" value="{{$submit|escape:'html'}}" />
-{{/if}}
-</form>
+                       </form>
+               </div>
+       </div>{{* End of contact-edit-nav-wrapper *}}
 </div>
index 3b96d3eefd6afb536a1e9095e9489b271dafa658..5225bd60b22743f45b88c26c4be0601ac0608882 100644 (file)
 {{/if}}
 
 {{if $request}}
-<form action="{{$request}}" method="post" />
+<form action="{{$request}}" method="post">
 {{else}}
-<form action="dfrn_request/{{$nickname}}" method="post" />
+<form action="dfrn_request/{{$nickname}}" method="post">
 {{/if}}
 
 {{if $photo}}
-<img src="{{$photo}}" alt="" id="dfrn-request-photo">
+<img src="{{$photo}}" alt="" id="dfrn-request-photo" />
 {{/if}}
 
 {{if $url}}<dl><dt>{{$url_label}}</dt><dd><a target="blank" href="{{$zrl}}">{{$url}}</a></dd></dl>{{/if}}
diff --git a/view/templates/diasp_dec_hdr.tpl b/view/templates/diasp_dec_hdr.tpl
deleted file mode 100644 (file)
index 136d1ca..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-
-<decrypted_hdeader>
-  <iv>{{$inner_iv}}</iv>
-  <aes_key>{{$inner_key}}</aes_key>
-  <author>
-    <name>{{$author_name}}</name>
-    <uri>{{$author_uri}}</uri>
-  </author>
-</decrypted_header>
diff --git a/view/templates/diaspora_comment.tpl b/view/templates/diaspora_comment.tpl
deleted file mode 100644 (file)
index 107cc73..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-
-<XML>
-  <post>
-    <comment>
-      <guid>{{$guid}}</guid>
-      <parent_guid>{{$parent_guid}}</parent_guid>
-      <author_signature>{{$authorsig}}</author_signature>
-      <text>{{$body}}</text>
-      <diaspora_handle>{{$handle}}</diaspora_handle>
-    </comment>
-  </post>
-</XML>
\ No newline at end of file
diff --git a/view/templates/diaspora_comment_relay.tpl b/view/templates/diaspora_comment_relay.tpl
deleted file mode 100644 (file)
index b4f84dc..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-
-<XML>
-  <post>
-    <comment>
-      <guid>{{$guid}}</guid>
-      <parent_guid>{{$parent_guid}}</parent_guid>
-      <parent_author_signature>{{$parentsig}}</parent_author_signature>
-      <author_signature>{{$authorsig}}</author_signature>
-      <text>{{$body}}</text>
-      <diaspora_handle>{{$handle}}</diaspora_handle>
-    </comment>
-  </post>
-</XML>
\ No newline at end of file
diff --git a/view/templates/diaspora_conversation.tpl b/view/templates/diaspora_conversation.tpl
deleted file mode 100644 (file)
index 28e4cdb..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-
-<XML>
-  <post>
-    <conversation>
-      <guid>{{$conv.guid}}</guid>
-      <subject>{{$conv.subject}}</subject>
-      <created_at>{{$conv.created_at}}</created_at>
-
-      {{foreach $conv.messages as $msg}}
-
-      <message>
-        <guid>{{$msg.guid}}</guid>
-        <parent_guid>{{$msg.parent_guid}}</parent_guid>
-        {{if $msg.parent_author_signature}}
-        <parent_author_signature>{{$msg.parent_author_signature}}</parent_author_signature>
-        {{/if}}
-        <author_signature>{{$msg.author_signature}}</author_signature>
-        <text>{{$msg.text}}</text>
-        <created_at>{{$msg.created_at}}</created_at>
-        <diaspora_handle>{{$msg.diaspora_handle}}</diaspora_handle>
-        <conversation_guid>{{$msg.conversation_guid}}</conversation_guid>
-      </message>
-
-      {{/foreach}}
-
-      <diaspora_handle>{{$conv.diaspora_handle}}</diaspora_handle>
-      <participant_handles>{{$conv.participant_handles}}</participant_handles>
-    </conversation>
-  </post>
-</XML>
diff --git a/view/templates/diaspora_like.tpl b/view/templates/diaspora_like.tpl
deleted file mode 100644 (file)
index 165b0f5..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-
-<XML>
-  <post>
-    <like>
-      <positive>{{$positive}}</positive>
-      <guid>{{$guid}}</guid>
-      <target_type>{{$target_type}}</target_type>
-      <parent_guid>{{$parent_guid}}</parent_guid>
-      <author_signature>{{$authorsig}}</author_signature>
-      <diaspora_handle>{{$handle}}</diaspora_handle>
-    </like>
-  </post>
-</XML>
diff --git a/view/templates/diaspora_like_relay.tpl b/view/templates/diaspora_like_relay.tpl
deleted file mode 100644 (file)
index e1696e7..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-
-<XML>
-  <post>
-    <like>
-      <positive>{{$positive}}</positive>
-      <guid>{{$guid}}</guid>
-      <target_type>{{$target_type}}</target_type>
-      <parent_guid>{{$parent_guid}}</parent_guid>
-      <parent_author_signature>{{$parentsig}}</parent_author_signature>
-      <author_signature>{{$authorsig}}</author_signature>
-      <diaspora_handle>{{$handle}}</diaspora_handle>
-    </like>
-  </post>
-</XML>
diff --git a/view/templates/diaspora_message.tpl b/view/templates/diaspora_message.tpl
deleted file mode 100644 (file)
index f9adb83..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-
-<XML>
-  <post>
-      <message>
-        <guid>{{$msg.guid}}</guid>
-        <parent_guid>{{$msg.parent_guid}}</parent_guid>
-       {{if $msg.parent_author_signature}}
-        <parent_author_signature>{{$msg.parent_author_signature}}</parent_author_signature>
-        {{/if}}
-        <author_signature>{{$msg.author_signature}}</author_signature>
-        <text>{{$msg.text}}</text>
-        <created_at>{{$msg.created_at}}</created_at>
-        <diaspora_handle>{{$msg.diaspora_handle}}</diaspora_handle>
-        <conversation_guid>{{$msg.conversation_guid}}</conversation_guid>
-      </message>
-  </post>
-</XML>
diff --git a/view/templates/diaspora_photo.tpl b/view/templates/diaspora_photo.tpl
deleted file mode 100644 (file)
index 0330499..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-
-<XML>
-  <post>
-    <photo>
-      <guid>{{$guid}}</guid>
-      <diaspora_handle>{{$handle}}</diaspora_handle>
-      <public>{{$public}}</public>
-      <created_at>{{$created_at}}</created_at>
-      <remote_photo_path>{{$path}}</remote_photo_path>
-      <remote_photo_name>{{$filename}}</remote_photo_name>
-      <status_message_guid>{{$msg_guid}}</status_message_guid>
-    </photo>
-  </post>
-</XML>
diff --git a/view/templates/diaspora_post.tpl b/view/templates/diaspora_post.tpl
deleted file mode 100644 (file)
index d6ba973..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-
-<XML>
-  <post>
-    <status_message>
-      <raw_message>{{$body}}</raw_message>
-      <guid>{{$guid}}</guid>
-      <diaspora_handle>{{$handle}}</diaspora_handle>
-      <public>{{$public}}</public>
-      <created_at>{{$created}}</created_at>
-      <provider_display_name>{{$provider}}</provider_display_name>
-    </status_message>
-  </post>
-</XML>
diff --git a/view/templates/diaspora_profile.tpl b/view/templates/diaspora_profile.tpl
deleted file mode 100644 (file)
index afbbb1e..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-
-<XML>
- <post><profile>
-  <diaspora_handle>{{$handle}}</diaspora_handle>
-  <first_name>{{$first}}</first_name>
-  <last_name>{{$last}}</last_name>
-  <image_url>{{$large}}</image_url>
-  <image_url_medium>{{$medium}}</image_url_medium>
-  <image_url_small>{{$small}}</image_url_small>
-  <birthday>{{$dob}}</birthday>
-  <gender>{{$gender}}</gender>
-  <bio>{{$about}}</bio>
-  <location>{{$location}}</location>
-  <searchable>{{$searchable}}</searchable>
-  <tag_string>{{$tags}}</tag_string>
- </profile></post>
-</XML>
diff --git a/view/templates/diaspora_relay_retraction.tpl b/view/templates/diaspora_relay_retraction.tpl
deleted file mode 100644 (file)
index b3f97a2..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-
-<XML>
-  <post>
-    <relayable_retraction>
-      <target_type>{{$type}}</target_type>
-      <target_guid>{{$guid}}</target_guid>
-      <target_author_signature>{{$signature}}</target_author_signature>
-      <sender_handle>{{$handle}}</sender_handle>
-    </relayable_retraction>
-  </post>
-</XML>
diff --git a/view/templates/diaspora_relayable_retraction.tpl b/view/templates/diaspora_relayable_retraction.tpl
deleted file mode 100644 (file)
index 2ae776d..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-
-<XML>
-  <post>
-    <relayable_retraction>
-      <parent_author_signature>{{$parentsig}}</parent_author_signature>
-      <target_guid>{{$guid}}</target_guid>
-      <target_type>{{$target_type}}</target_type>
-      <sender_handle>{{$handle}}</sender_handle>
-      <target_author_signature>{{$authorsig}}</target_author_signature>
-    </relayable_retraction>
-  </post>
-</XML>
diff --git a/view/templates/diaspora_reshare.tpl b/view/templates/diaspora_reshare.tpl
deleted file mode 100644 (file)
index 6a4776b..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-
-<XML>
-  <post>
-    <reshare>
-      <root_diaspora_id>{{$root_handle}}</root_diaspora_id>
-      <root_guid>{{$root_guid}}</root_guid>
-      <guid>{{$guid}}</guid>
-      <diaspora_handle>{{$handle}}</diaspora_handle>
-      <public>{{$public}}</public>
-      <created_at>{{$created}}</created_at>
-      <provider_display_name>{{$provider}}</provider_display_name>
-    </reshare>
-  </post>
-</XML>
diff --git a/view/templates/diaspora_retract.tpl b/view/templates/diaspora_retract.tpl
deleted file mode 100644 (file)
index 3ddfcab..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-
-<XML>
-  <post>
-    <retraction>
-      <post_guid>{{$guid}}</post_guid>
-      <diaspora_handle>{{$handle}}</diaspora_handle>
-      <type>{{$type}}</type>
-    </retraction>
-  </post>
-</XML>
diff --git a/view/templates/diaspora_share.tpl b/view/templates/diaspora_share.tpl
deleted file mode 100644 (file)
index 0142ab3..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-
-<XML>
-  <post>
-    <request>
-      <sender_handle>{{$sender}}</sender_handle>
-      <recipient_handle>{{$recipient}}</recipient_handle>
-    </request>
-  </post>
-</XML>
\ No newline at end of file
diff --git a/view/templates/diaspora_signed_retract.tpl b/view/templates/diaspora_signed_retract.tpl
deleted file mode 100644 (file)
index 83d0def..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-
-<XML>
-  <post>
-    <signed_retraction>
-      <target_guid>{{$guid}}</target_guid>
-      <target_type>{{$type}}</target_type>
-      <sender_handle>{{$handle}}</sender_handle>
-      <target_author_signature>{{$signature}}</target_author_signature>
-    </signed_retraction>
-  </post>
-</XML>
index e476c07d722978696c5d7efafc5f1872abdfd5b8..06796376b60a5f4d52f2c94de7f1d5cbe1f77f1c 100644 (file)
@@ -1,5 +1,5 @@
        <div class='field checkbox' id='div_id_{{$field.0}}'>
                <label for='id_{{$field.0}}'>{{$field.1}}</label>
-               <input type="checkbox" name='{{$field.0}}' id='id_{{$field.0}}' value="1" {{if $field.2}}checked="checked"{{/if}}>
-               <span class='field_help'>{{$field.3}}</span>
+               <input type="checkbox" name='{{$field.0}}' id='id_{{$field.0}}' aria-describedby='{{$field.0}}_tip' value="1" {{if $field.2}}checked="checked"{{/if}}>
+               <span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
        </div>
index a2f7c3f27e9a52d6aeb9dbd5cf9808df0e6f1fb7..45865501662a7d1e1ba7e6a5347659eaaca0339c 100644 (file)
@@ -7,12 +7,12 @@
                   {{foreach $field.4 as $opt=>$val}}<option value="{{$val|escape:'html'}}">{{/foreach}}
                </datalist> *}}
                
-               <input id="id_{{$field.0}}" type="text" value="{{$field.2}}">
+               <input id="id_{{$field.0}}" type="text" value="{{$field.2}}" aria-describedby='{{$field.0}}_tip'>
                <select id="select_{{$field.0}}" onChange="$('#id_{{$field.0}}').val($(this).val())">
                        <option value="">{{$field.5}}</option>
                        {{foreach $field.4 as $opt=>$val}}<option value="{{$val|escape:'html'}}">{{$val}}</option>{{/foreach}}
                </select>
                
-               <span class='field_help'>{{$field.3}}</span>
+               <span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
        </div>
 
index 6a3328c5cc28dafe2dde8fba89d1baa6ba1d1bf7..8db8e545f34644531821b9744875923003bf2c04 100644 (file)
@@ -1,6 +1,6 @@
        
        <div class='field input' id='wrapper_{{$field.0}}'>
                <label for='id_{{$field.0}}'>{{$field.1}}</label>
-               <input{{if $field.6 eq 'email'}} type='email'{{elseif $field.6 eq 'url'}} type='url'{{/if}} name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}"{{if $field.4 eq 'required'}} required{{/if}}{{if $field.5 eq 'autofocus'}} autofocus{{/if}}>
-               <span class='field_help'>{{$field.3}}</span>
+               <input{{if $field.6 eq 'email'}} type='email'{{elseif $field.6 eq 'url'}} type='url'{{/if}} name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}"{{if $field.4 eq 'required'}} required{{/if}}{{if $field.5 eq 'autofocus'}} autofocus{{/if}} aria-describedby='{{$field.0}}_tip'>
+               <span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
        </div>
index 2f3c27d920cb860649e01ca4b7980920b02d3ae1..73bdf604170085b9c404cec81d11d65d9ae4cf1d 100644 (file)
@@ -2,6 +2,6 @@
        
        <div class='field checkbox'>
                <label for='id_{{$field.0}}'>{{$field.1}}</label>
-               <input type="checkbox" name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.3|escape:'html'}}" {{if $field.2}}checked="true"{{/if}}>
-               <span class='field_help'>{{$field.4}}</span>
+               <input type="checkbox" name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.3|escape:'html'}}" {{if $field.2}}checked="true"{{/if}} aria-describedby='{{$field.0}}_tip'>
+               <span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.4}}</span>
        </div>
index e5f236c6791a08a475e0323ffbb8fb4fec67eba6..25773189f88bb8973914c350c2062a03f6bbfd6e 100644 (file)
@@ -1,6 +1,6 @@
        
        <div class='field input openid' id='wrapper_{{$field.0}}'>
                <label for='id_{{$field.0}}'>{{$field.1}}</label>
-               <input name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}">
-               <span class='field_help'>{{$field.3}}</span>
+               <input name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}" aria-describedby='{{$field.0}}_tip'>
+               <span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
        </div>
index 8a9f0dc330e89ddf8fdb4609268b0dbc3851fd55..333ce67c3839ff6cbea8dcfd6cbc185502bfbcda 100644 (file)
@@ -1,6 +1,6 @@
        
        <div class='field password' id='wrapper_{{$field.0}}'>
                <label for='id_{{$field.0}}'>{{$field.1}}</label>
-               <input type='password' name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}"{{if $field.4 eq 'required'}} required{{/if}}{{if $field.5 eq 'autofocus'}} autofocus{{/if}}>
-               <span class='field_help'>{{$field.3}}</span>
+               <input type='password' name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}"{{if $field.4 eq 'required'}} required{{/if}}{{if $field.5 eq 'autofocus'}} autofocus{{/if}} aria-describedby='{{$field.0}}_tip'>
+               <span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
        </div>
index 86cc8fc47e15239189a7b2cbb7978463c6f43436..ef8ec4f9a6372e7c13ea191703e7ccf2e40b1776 100644 (file)
@@ -2,6 +2,6 @@
        
        <div class='field radio'>
                <label for='id_{{$field.0}}_{{$field.2}}'>{{$field.1}}</label>
-               <input type="radio" name='{{$field.0}}' id='id_{{$field.0}}_{{$field.2}}' value="{{$field.2|escape:'html'}}" {{if $field.4}}checked="true"{{/if}}>
-               <span class='field_help'>{{$field.3}}</span>
+               <input type="radio" name='{{$field.0}}' id='id_{{$field.0}}_{{$field.2}}' value="{{$field.2|escape:'html'}}" {{if $field.4}}checked="true"{{/if}} aria-describedby={{$field.0}}_tip'>
+               <span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
        </div>
index f0ea50b58b88d03a6cf6a73039fa48c968103e70..67553bb95a2bca3460d02767c7dd4bf4aefc0342 100644 (file)
@@ -2,6 +2,6 @@
        
        <div class='field richtext'>
                <label for='id_{{$field.0}}'>{{$field.1}}</label>
-               <textarea name='{{$field.0}}' id='id_{{$field.0}}' class="fieldRichtext">{{$field.2}}</textarea>
-               <span class='field_help'>{{$field.3}}</span>
+               <textarea name='{{$field.0}}' id='id_{{$field.0}}' class="fieldRichtext" aria-describedby='{{$field.0}}_tip'>{{$field.2}}</textarea>
+               <span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
        </div>
index 4fbbd4beb0a5423a2df00e5fb0479facbd0e9a95..2d037439df6b644dc92a7e10af58109beff6566c 100644 (file)
@@ -2,8 +2,8 @@
        
        <div class='field select'>
                <label for='id_{{$field.0}}'>{{$field.1}}</label>
-               <select name='{{$field.0}}' id='id_{{$field.0}}'>
+               <select name='{{$field.0}}' id='id_{{$field.0}}' aria-describedby='{{$field.0}}_tip'>
                        {{foreach $field.4 as $opt=>$val}}<option value="{{$opt|escape:'html'}}" {{if $opt==$field.2}}selected="selected"{{/if}}>{{$val}}</option>{{/foreach}}
                </select>
-               <span class='field_help'>{{$field.3}}</span>
+               <span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
        </div>
index 02308e2f4b70c0b60b59d7989d8533a8337e6f13..4c826a042749e593ec685a9af94bfa035c7daf16 100644 (file)
@@ -2,8 +2,8 @@
        
        <div class='field select'>
                <label for='id_{{$field.0}}'>{{$field.1}}</label>
-               <select name='{{$field.0}}' id='id_{{$field.0}}'>
+               <select name='{{$field.0}}' id='id_{{$field.0}}' aria-describedby='{{$field.0}}_tip'>
                        {{$field.4}}
                </select>
-               <span class='field_help'>{{$field.3}}</span>
+               <span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
        </div>
index 29d3b7a7c6f7672bd286b0741d0e26729e72472a..c37537d6ec57a21bffe900ff4e92be1ea16da799 100644 (file)
@@ -2,6 +2,6 @@
        
        <div class='field textarea'>
                <label for='id_{{$field.0}}'>{{$field.1}}</label>
-               <textarea name='{{$field.0}}' id='id_{{$field.0}}'>{{$field.2}}</textarea>
-               <span class='field_help'>{{$field.3}}</span>
+               <textarea name='{{$field.0}}' id='id_{{$field.0}}' aria-describedby='{{$field.0}}_tip'>{{$field.2}}</textarea>
+               <span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
        </div>
index edd25dbe0f8cc77a66b2410d9f48fa55f8a3d60b..51f6057ae3b25c2de05acfc98f4a6930230ed128 100644 (file)
@@ -2,9 +2,9 @@
        {{if $field.5}}<script>$(function(){ previewTheme($("#id_{{$field.0}}")[0]); });</script>{{/if}}
        <div class='field select'>
                <label for='id_{{$field.0}}'>{{$field.1}}</label>
-               <select name='{{$field.0}}' id='id_{{$field.0}}' {{if $field.5}}onchange="previewTheme(this);"{{/if}} >
+               <select name='{{$field.0}}' id='id_{{$field.0}}' {{if $field.5}}onchange="previewTheme(this);"{{/if}} aria-describedby='{{$field.0}}_tip'>
                        {{foreach $field.4 as $opt=>$val}}<option value="{{$opt|escape:'html'}}" {{if $opt==$field.2}}selected="selected"{{/if}}>{{$val}}</option>{{/foreach}}
                </select>
-               <span class='field_help'>{{$field.3}}</span>
+               <span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
                {{if $field.5}}<div id="theme-preview"></div>{{/if}}
        </div>
index de70c5ae6dccdfc967500b51f025d72a2d529963..155d0488b32563a606ce805b2a3166d8a73efd3e 100644 (file)
@@ -2,7 +2,7 @@
        <div class='field yesno'>
                <label for='id_{{$field.0}}'>{{$field.1}}</label>
                <div class='onoff' id="id_{{$field.0}}_onoff">
-                       <input  type="hidden" name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}">
+                       <input  type="hidden" name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}" aria-describedby='{{$field.0}}_tip'>
                        <a href="#" class='off'>
                                {{if $field.4}}{{$field.4.0}}{{else}}OFF{{/if}}
                        </a>
@@ -10,5 +10,5 @@
                                {{if $field.4}}{{$field.4.1}}{{else}}ON{{/if}}
                        </a>
                </div>
-               <span class='field_help'>{{$field.3}}</span>
+               <span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
        </div>
index 17c459c4d8a17bd90053ba2361e60963a8d545fa..fdf9a7716e0572c35bfc68c3cee7ff532ec4dd54 100644 (file)
@@ -2,17 +2,17 @@
 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
 <base href="{{$baseurl}}/" />
 <meta name="generator" content="{{$generator}}" />
-<link rel="stylesheet" href="{{$baseurl}}/view/global.css" type="text/css" media="all" />
-<link rel="stylesheet" href="{{$baseurl}}/library/colorbox/colorbox.css" type="text/css" media="screen" />
-<link rel="stylesheet" href="{{$baseurl}}/library/jgrowl/jquery.jgrowl.css" type="text/css" media="screen" />
-<link rel="stylesheet" href="{{$baseurl}}/library/datetimepicker/jquery.datetimepicker.css" type="text/css" media="screen" />
-<link rel="stylesheet" href="{{$baseurl}}/library/perfect-scrollbar/perfect-scrollbar.min.css" type="text/css" media="screen" />
+<link rel="stylesheet" href="view/global.css" type="text/css" media="all" />
+<link rel="stylesheet" href="library/colorbox/colorbox.css" type="text/css" media="screen" />
+<link rel="stylesheet" href="library/jgrowl/jquery.jgrowl.css" type="text/css" media="screen" />
+<link rel="stylesheet" href="library/datetimepicker/jquery.datetimepicker.css" type="text/css" media="screen" />
+<link rel="stylesheet" href="library/perfect-scrollbar/perfect-scrollbar.min.css" type="text/css" media="screen" />
 
 <link rel="stylesheet" type="text/css" href="{{$stylesheet}}" media="all" />
 
 <!--
-<link rel="shortcut icon" href="{{$baseurl}}/images/friendica-32.png" />
-<link rel="apple-touch-icon" href="{{$baseurl}}/images/friendica-128.png"/>
+<link rel="shortcut icon" href="images/friendica-32.png" />
+<link rel="apple-touch-icon" href="images/friendica-128.png"/>
 -->
 <link rel="shortcut icon" href="{{$shortcut_icon}}" />
 <link rel="apple-touch-icon" href="{{$touch_icon}}"/>
 <!--[if IE]>
 <script type="text/javascript" src="https://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
 <![endif]-->
-<script type="text/javascript" src="{{$baseurl}}/js/modernizr.js" ></script>
-<script type="text/javascript" src="{{$baseurl}}/js/jquery.js" ></script>
-<!-- <script type="text/javascript" src="{{$baseurl}}/js/jquery-migrate.js" ></script>-->
-<script type="text/javascript" src="{{$baseurl}}/js/jquery-migrate.js" ></script>
-<script type="text/javascript" src="{{$baseurl}}/js/jquery.textinputs.js" ></script>
-<script type="text/javascript" src="{{$baseurl}}/js/fk.autocomplete.js" ></script>
-<script type="text/javascript" src="{{$baseurl}}/library/colorbox/jquery.colorbox-min.js"></script>
-<script type="text/javascript" src="{{$baseurl}}/library/jgrowl/jquery.jgrowl_minimized.js"></script>
-<script type="text/javascript" src="{{$baseurl}}/library/datetimepicker/jquery.datetimepicker.js"></script>
-<script type="text/javascript" src="{{$baseurl}}/library/tinymce/jscripts/tiny_mce/tiny_mce_src.js" ></script>
-<script type="text/javascript" src="{{$baseurl}}/library/perfect-scrollbar/perfect-scrollbar.jquery.js" ></script>
-<script type="text/javascript" src="{{$baseurl}}/js/acl.js" ></script>
-<script type="text/javascript" src="{{$baseurl}}/js/webtoolkit.base64.js" ></script>
-<script type="text/javascript" src="{{$baseurl}}/js/main.js" ></script>
+<script type="text/javascript" src="js/modernizr.js" ></script>
+<script type="text/javascript" src="js/jquery.js" ></script>
+<!-- <script type="text/javascript" src="js/jquery-migrate.js" ></script>-->
+<script type="text/javascript" src="js/jquery-migrate.js" ></script>
+<script type="text/javascript" src="js/jquery.textinputs.js" ></script>
+<script type="text/javascript" src="js/fk.autocomplete.js" ></script>
+<script type="text/javascript" src="library/colorbox/jquery.colorbox-min.js"></script>
+<script type="text/javascript" src="library/jgrowl/jquery.jgrowl_minimized.js"></script>
+<script type="text/javascript" src="library/datetimepicker/jquery.datetimepicker.js"></script>
+<script type="text/javascript" src="library/tinymce/jscripts/tiny_mce/tiny_mce_src.js" ></script>
+<script type="text/javascript" src="library/perfect-scrollbar/perfect-scrollbar.jquery.js" ></script>
+<script type="text/javascript" src="js/acl.js" ></script>
+<script type="text/javascript" src="js/webtoolkit.base64.js" ></script>
+<script type="text/javascript" src="js/main.js" ></script>
 <script>
 
        var updateInterval = {{$update_interval}};
index 37d105c087b0c53367ee7dd6d3d81970c9dc3201..caa2e74a4bce134540ed6870e5ac720bf96b92bc 100644 (file)
@@ -1,6 +1,7 @@
 
 
 <form action="{{$dest_url}}" method="post" >
+<fieldset>
        <input type="hidden" name="auth-params" value="login" />
 
        <div id="login_standard">
@@ -29,7 +30,7 @@
                <input type="hidden" name="{{$k}}" value="{{$v|escape:'html'}}" />
        {{/foreach}}
        
-       
+</fieldset>
 </form>
 
 
index 0b7e77a07bb7b47b64f4a25db38c699a164c3220..54f9de0c7a37385abcde31d023aaf5dce03d0ef6 100644 (file)
@@ -2,7 +2,7 @@
 
 <h1>{{$notif_header}}</h1>
 
-{{include file="common_tabs.tpl"}}
+{{if $tabs }}{{include file="common_tabs.tpl"}}{{/if}}
 
 <div class="notif-network-wrapper">
        {{$notif_content}}
index 382c7fc6a355fe280762f203b9496c28719e7bcd..b9e177af268c4e87694e1087ad6130256c03f643 100644 (file)
@@ -1,10 +1,10 @@
 
-<h3>{{$title}}</h3>\r
-\r
-\r
-{{foreach $options as $o}}\r
-<dl>\r
-    <dt><a href="{{$baseurl}}/{{$o.0}}">{{$o.1}}</a></dt>\r
-    <dd>{{$o.2}}</dd>\r
-</dl>\r
-{{/foreach}}
\ No newline at end of file
+<h3>{{$title}}</h3>
+
+
+{{foreach $options as $o}}
+<dl>
+    <dt><a href="{{$o.0}}">{{$o.1}}</a></dt>
+    <dd>{{$o.2}}</dd>
+</dl>
+{{/foreach}}
index 1a32fb724dc18a16acf6dae1aabf1dd47bbda7a4..9b1c1daa619f6284f628f5e577ed68395e1c5804 100644 (file)
@@ -10,7 +10,6 @@
  */
 
 function decaf_mobile_init(&$a) {
-       $a->theme_info = array();
        $a->sourcename = 'Friendica mobile web';
        $a->videowidth = 250;
        $a->videoheight = 200;
index c004eb53d0ce85c4c1af77be3431bbd4750235e7..787e52600c4303071820f9a10c64e2f32dbfe440 100644 (file)
@@ -83,6 +83,26 @@ blockquote {
        margin-right: 5px;
 }
 
+ul.menu-popup {
+       position: absolute;
+       display: none;
+       width: auto;
+       margin: 2px 0 0;
+       padding: 0px;
+       list-style: none;
+       z-index: 100000;
+       border: 2px solid #444444;
+       background: #FFFFFF;
+}
+.menu-popup li a {
+       padding: 2px;
+       white-space: nowrap;
+}
+
+a.btn, a.btn:hover {
+       text-decoration: none;
+       color: inherit;
+}
 
 
 /* nav */
@@ -140,12 +160,12 @@ nav #banner #logo-text a:hover { text-decoration: none; }
 
 
 .nav-commlink, .nav-login-link {
-    display: block;
-    height: 15px;
+       display: block;
+       height: 15px;
        margin-top: 67px;
        margin-right: 2px;
-       //padding: 6px 10px;
-       padding: 6px 3px;
+       /*padding: 6px 10px;*/
+       padding: 6px 3px;
        float: left;
        bottom: 140px;
        border: 1px solid #babdb6;
@@ -244,7 +264,7 @@ section {
        display:block;
        float:left;
        padding: 0.4em;
-       //margin-right: 1em;
+       /*margin-right: 1em;*/
        margin-right: 3px ;
 }
 .tab.active {
@@ -3371,17 +3391,6 @@ div.jGrowl div.info {
 .nav-notify.show {
        display: block;
 }
-ul.menu-popup {
-       position: absolute;
-       display: none;
-       width: 10em;
-       margin: 0px;
-       padding: 0px;
-       list-style: none;
-       z-index: 100000;
-       top: 90px;
-       left: 200px;
-}
 #nav-notifications-menu {
        width: 320px;
        max-height: 400px;
@@ -3391,6 +3400,8 @@ ul.menu-popup {
        -webkit-border-radius: 5px;
        border-radius:5px;
        border: 1px solid #888;
+       top: 90px;
+       left: 200px;
 }
 #nav-notifications-menu .contactname { font-weight: bold; font-size: 0.9em; }
 #nav-notifications-menu img { float: left; margin-right: 5px; }
index ba3f25d3e26e002b461e8d97ac433d3ddca286e4..50d57f91e5759041f463284537ddcfcc1495ad9d 100644 (file)
@@ -2,7 +2,6 @@
 
 function duepuntozero_init(&$a) {
 
-$a->theme_info = array();
 set_template_engine($a, 'smarty3');
 
     $colorset = get_pconfig( local_user(), 'duepuntozero','colorset');
index e7203c66416b5ca17bb09d79ccf672c4681ac02d..e6255f118a60b9e5c3be8655bea40dc8d78cfd50 100644 (file)
@@ -1,7 +1,6 @@
 <?php
 
 function facepark_init(&$a) {
-$a->theme_info = array();
 set_template_engine($a, 'smarty3');
 
 $a->page['htmlhead'] .= <<< EOT
index ad464760f74156ed93df0dcb311582e914612088..332291ca9266d442839a46c4f90daf028b6e060e 100644 (file)
@@ -1,5 +1,5 @@
 <!DOCTYPE html >\r
-<html>\r
+<html lang="<?php echo $lang; ?>">\r
 <head>\r
   <title><?php if(x($page,'title')) echo $page['title'] ?></title>\r
   <script>var baseurl="<?php echo $a->get_baseurl() ?>";</script>\r
@@ -28,7 +28,9 @@
 <!--           <div class='main-content-container'>-->\r
                <div class='section-wrapper'>\r
                <?php if( ($a->module === 'settings' || $a->module === 'message' || $a->module === 'profile') && x($page,'aside')) echo $page['aside']; ?>\r
-               <section><?php if(x($page,'content')) echo $page['content']; ?>\r
+               <section>\r
+                       <?php if(x($page,'content')) echo $page['content']; ?>\r
+                       <div id="pause"></div> <!-- The pause/resume Ajax indicator -->\r
                        <div id="page-footer"></div>\r
                </section>\r
                </div>\r
@@ -41,4 +43,3 @@
        <?php if(x($page,'end')) echo $page['end']; ?>\r
 </body>\r
 </html>\r
-\r
index 9eac71be8344d1767aec5413b18ebcecb3f36da8..7e2880594d81c9310317525515b177cd47f8fe70 100644 (file)
@@ -13,7 +13,6 @@
                if($(listID).is(":visible")) {
                        $(listID).hide();
                        $(listID+"-wrapper").show();
-                       alert($(listID+"-wrapper").attr("id"));
                }
                else {
                        $(listID).show();
index 400b23c10b38b2f2e4f73e335c2d833a31bcf476..a99cc17a910d3d5e27238368a52a9b70ddbf28a1 100644 (file)
@@ -139,6 +139,47 @@ blockquote {
        margin-right: 5px;\r
 }\r
 \r
+.btn {\r
+       outline: none;\r
+       -moz-box-shadow: inset 0px 1px 0px 0px #ffffff;\r
+       -webkit-box-shadow: inset 0px 1px 0px 0px #ffffff;\r
+       box-shadow: inset 0px 1px 0px 0px #ffffff;\r
+       background-color: #ededed;\r
+       text-indent: 0;\r
+       border: 1px solid #dcdcdc;\r
+       display: inline-block;\r
+       color: #777777;\r
+       padding: 5px 10px;\r
+       text-align: center;\r
+       border-radius: 8px;\r
+}\r
+\r
+.menu-popup {\r
+       width: auto;\r
+       border: 2px solid #444444;\r
+       background: #FFFFFF;\r
+       position: absolute;\r
+       margin: 2px 0 0;\r
+       display: none;\r
+       z-index: 10000;\r
+}\r
+\r
+.menu-popup li a {\r
+       display: block;\r
+       padding: 2px;\r
+}\r
+\r
+.menu-popup li a:hover {\r
+       color: #FFFFFF;\r
+       background: #3465A4;\r
+       text-decoration: none;\r
+}\r
+ul.menu-popup li.divider {\r
+       height: 1px;\r
+       margin: 3px 0;\r
+       overflow: hidden;\r
+       background-color: #2d2d2d;\r
+}\r
 \r
 \r
 /* nav */\r
@@ -2045,6 +2086,19 @@ input#profile-jot-email {
        margin-left: 15px;\r
 }\r
 \r
+#contact-edit-status-wrapper {\r
+       padding: 10px;\r
+       border: 1px solid #aaa;\r
+       border-radius: 8px;\r
+}\r
+\r
+#contact-edit-contact-status {\r
+       font-weight: bold;\r
+}\r
+#contact-edit-actions {\r
+       float: right;\r
+       display: inline-block;\r
+}\r
 #contact-edit-wrapper {\r
        margin-top: 10px;\r
 }\r
@@ -2059,14 +2113,10 @@ input#profile-jot-email {
 }\r
 \r
 #contact-edit-last-update-text {\r
-       float: left;\r
-       clear: left;\r
        margin-top: 30px;\r
 }\r
 \r
 #contact-edit-poll-text {\r
-       float: left;\r
-       clear: left;\r
        margin-top: 15px;\r
        margin-bottom: 0px;\r
 }\r
index e6401de606258a9839d329389764870ff7fbeccb..79dc7da408ffb29b627d448f37f8bfcf43db0eec 100644 (file)
@@ -6,12 +6,6 @@
 
        {{$tab_str}}
 
-       <div id="contact-edit-drop-link" >
-               <a href="contacts/{{$contact_id}}/drop" class="icon drophide" id="contact-edit-drop-link" onclick="return confirmDelete();"  title="{{$delete}}" {{*onmouseover="imgbright(this);" onmouseout="imgdull(this);"*}}></a>
-       </div>
-
-       <div id="contact-edit-drop-link-end"></div>
-
        <div class="vcard">
                <div class="fn">{{$name}}</div>
                <div id="profile-photo-wrapper"><img class="photo" style="width: 175px; height: 175px;" src="{{$photo}}" alt="{{$name}}" /></div>
 
        <div id="contact-edit-nav-wrapper" >
                <div id="contact-edit-links">
-                       <ul>
-                               <li><div id="contact-edit-rel">{{$relation_text}}</div></li>
-                               <li><div id="contact-edit-nettype">{{$nettype}}</div></li>
-                               {{if $lost_contact}}
-                                       <li><div id="lost-contact-message">{{$lost_contact}}</div></li>
-                               {{/if}}
-                               {{if $insecure}}
-                                       <li><div id="insecure-message">{{$insecure}}</div></li>
-                               {{/if}}
-                               {{if $blocked}}
-                                       <li><div id="block-message">{{$blocked}}</div></li>
-                               {{/if}}
-                               {{if $ignored}}
-                                       <li><div id="ignore-message">{{$ignored}}</div></li>
-                               {{/if}}
-                               {{if $archived}}
-                                       <li><div id="archive-message">{{$archived}}</div></li>
-                               {{/if}}
-
-                               <li>&nbsp;</li>
-
-                               {{if $common_text}}
-                                       <li><div id="contact-edit-common"><a href="{{$common_link}}">{{$common_text}}</a></div></li>
-                               {{/if}}
-                               {{if $all_friends}}
-                                       <li><div id="contact-edit-allfriends"><a href="allfriends/{{$contact_id}}">{{$all_friends}}</a></div></li>
-                               {{/if}}
-
-
-                               <li><a href="network/0?nets=all&cid={{$contact_id}}" id="contact-edit-view-recent">{{$lblrecent}}</a></li>
-                               {{if $lblsuggest}}
-                                       <li><a href="fsuggest/{{$contact_id}}" id="contact-edit-suggest">{{$lblsuggest}}</a></li>
-                               {{/if}}
-
-                       </ul>
+                       <div id="contact-edit-status-wrapper">
+                               <span id="contact-edit-contact-status">{{$contact_status}}</span>
+
+                               <div id="contact-edit-actions">
+                                       <div class="btn" id="contact-edit-actions-button" onclick="openClose('contact-actions-menu')">{{$contact_action_button}}</div>
+
+                                       <ul role="menu" aria-haspopup="true" id="contact-actions-menu" class="menu-popup" >
+                                               {{if $lblsuggest}}<li role="menuitem"><a  href="#" title="{{$contact_actions.suggest.title}}" onclick="window.location.href='{{$contact_actions.suggest.url}}'; return false;">{{$contact_actions.suggest.label}}</a></li>{{/if}}
+                                               {{if $poll_enabled}}<li role="menuitem"><a  href="#" title="{{$contact_actions.update.title}}" onclick="window.location.href='{{$contact_actions.update.url}}'; return false;">{{$contact_actions.update.label}}</a></li>{{/if}}
+                                               <li class="divider"></li>
+                                               <li role="menuitem"><a  href="#" title="{{$contact_actions.block.title}}" onclick="window.location.href='{{$contact_actions.block.url}}'; return false;">{{$contact_actions.block.label}}</a></li>
+                                               <li role="menuitem"><a  href="#" title="{{$contact_actions.ignore.title}}" onclick="window.location.href='{{$contact_actions.ignore.url}}'; return false;">{{$contact_actions.ignore.label}}</a></li>
+                                               <li role="menuitem"><a  href="#" title="{{$contact_actions.archive.title}}" onclick="window.location.href='{{$contact_actions.archive.url}}'; return false;">{{$contact_actions.archive.label}}</a></li>
+                                               <li role="menuitem"><a  href="#" title="{{$contact_actions.delete.title}}" onclick="return confirmDelete();">{{$contact_actions.delete.label}}</a></li>
+                                       </ul>
+                               </div>
+
+                               <ul>
+                                       <li><div id="contact-edit-rel">{{$relation_text}}</div></li>
+                                       <li><div id="contact-edit-nettype">{{$nettype}}</div></li>
+                                       {{if $poll_enabled}}
+                                               <div id="contact-edit-poll-wrapper">
+                                                       <div id="contact-edit-last-update-text">{{$lastupdtext}} <span id="contact-edit-last-updated">{{$last_update}}</span></div>
+                                               </div>
+                                       {{/if}}
+                                       {{if $lost_contact}}
+                                               <li><div id="lost-contact-message">{{$lost_contact}}</div></li>
+                                       {{/if}}
+                                       {{if $insecure}}
+                                               <li><div id="insecure-message">{{$insecure}}</div></li>
+                                       {{/if}}
+                                       {{if $blocked}}
+                                               <li><div id="block-message">{{$blocked}}</div></li>
+                                       {{/if}}
+                                       {{if $ignored}}
+                                               <li><div id="ignore-message">{{$ignored}}</div></li>
+                                       {{/if}}
+                                       {{if $archived}}
+                                               <li><div id="archive-message">{{$archived}}</div></li>
+                                       {{/if}}
+
+                               </ul>
+                       </div>
                </div>
        </div>
        <div id="contact-edit-nav-end"></div>
 <form action="contacts/{{$contact_id}}" method="post" >
 <input type="hidden" name="contact_id" value="{{$contact_id}}">
 
-       {{if $poll_enabled}}
-               <div id="contact-edit-poll-wrapper">
-                       <div id="contact-edit-last-update-text">{{$lastupdtext}} <span id="contact-edit-last-updated">{{$last_update}}</span></div>
-                       <span id="contact-edit-poll-text">{{$updpub}} {{$poll_interval}}</span> <span id="contact-edit-update-now" class="button"><a id="update_now_link" href="contacts/{{$contact_id}}/update" >{{$udnow}}</a></span>
-               </div>
-       {{/if}}
        <div id="contact-edit-end" ></div>
 
        {{include file="field_checkbox.tpl" field=$hidden}}
index beec9249348c70e15d43351bffc77cb32f019b60..29a990f7b8604c5bbbf3af20cd9c3d8e84710be4 100644 (file)
@@ -10,7 +10,6 @@
  */
 
 function frost_mobile_init(&$a) {
-       $a->theme_info = array();
        $a->sourcename = 'Friendica mobile web';
        $a->videowidth = 250;
        $a->videoheight = 200;
index 95a9b1e8c7b7403e4adc1362f1c9101811c94e61..c67bdcf20ee8eaf86cdd5cea38a0d40d574a8ec9 100644 (file)
@@ -1,5 +1,5 @@
 <!DOCTYPE html >\r
-<html>\r
+<html lang="<?php echo $lang; ?>">\r
 <head>\r
   <title><?php if(x($page,'title')) echo $page['title'] ?></title>\r
   <script>var baseurl="<?php echo $a->get_baseurl() ?>";</script>\r
@@ -28,7 +28,9 @@
                <!--<div class='main-content-loading'><img src="/view/theme/frost/images/ajax-loader.gif" alt="Please wait..."></div>-->\r
                <div class='main-content-container'>\r
                <aside><?php if(x($page,'aside')) echo $page['aside']; ?></aside>\r
-               <section><?php if(x($page,'content')) echo $page['content']; ?>\r
+               <section>\r
+                       <?php if(x($page,'content')) echo $page['content']; ?>\r
+                       <div id="pause"></div> <!-- The pause/resume Ajax indicator -->\r
                        <div id="page-footer"></div>\r
                </section>\r
                <right_aside><?php if(x($page,'right_aside')) echo $page['right_aside']; ?></right_aside>\r
@@ -39,4 +41,3 @@
        <?php if(x($page,'end')) echo $page['end']; ?>\r
 </body>\r
 </html>\r
-\r
index 3dd400c76b396e2d752304cdb05f93c5212d136a..1054b55c11a0ed2703993579b08ffb2c7ec8fd5d 100644 (file)
@@ -113,6 +113,51 @@ blockquote {
 
 .pull-right { float: right }
 
+.btn {
+       outline: none;
+       -moz-box-shadow: inset 0px 1px 0px 0px #ffffff;
+       -webkit-box-shadow: inset 0px 1px 0px 0px #ffffff;
+       box-shadow: inset 0px 1px 0px 0px #ffffff;
+       background-color: #ededed;
+       text-indent: 0;
+       border: 1px solid #dcdcdc;
+       display: inline-block;
+       color: #777777;
+       padding: 5px 10px;
+       text-align: center;
+       border-radius: 8px;
+}
+a.btn {
+       text-decoration: none;
+       color: inherit;
+}
+
+.menu-popup {
+       width: auto;
+       border: 2px solid #444444;
+       background: #FFFFFF;
+       position: absolute;
+       margin: 2px 0 0;
+       display: none;
+       z-index: 10000;
+}
+
+.menu-popup li a {
+       display: block;
+       padding: 2px;
+}
+
+.menu-popup li a:hover {
+       color: #FFFFFF;
+       background: #3465A4;
+       text-decoration: none;
+}
+ul.menu-popup li.divider {
+       height: 1px;
+       margin: 3px 0;
+       overflow: hidden;
+       background-color: #2d2d2d;
+}
 
 
 /* nav */
@@ -1952,6 +1997,20 @@ input#dfrn-url {
        margin-left: 15px;
 }
 
+#contact-edit-status-wrapper {
+       padding: 10px;
+       border: 1px solid #aaa;
+       border-radius: 8px;
+}
+
+#contact-edit-contact-status {
+       font-weight: bold;
+}
+#contact-edit-actions {
+       float: right;
+       display: inline-block;
+}
+
 #contact-edit-wrapper {
        margin-top: 10px;
 }
@@ -1989,11 +2048,6 @@ input#dfrn-url {
        margin-top: 5px;
 }
 
-#contact-edit-drop-link {
-       float: right;
-       margin-right: 20px;
-}
-
 #contact-edit-nav-end {
        clear: both;
 }
index 731c5e0d4c155220ab082433ccffcf619fa14758..44e55b1cd813874585f9c80bde0d6574af6680a6 100644 (file)
@@ -6,50 +6,51 @@
 
        {{$tab_str}}
 
-       <div id="contact-edit-drop-link" >
-               <a href="contacts/{{$contact_id}}/drop" class="icon drophide" id="contact-edit-drop-link" onclick="return confirmDelete();"  title="{{$delete}}" {{*onmouseover="imgbright(this);" onmouseout="imgdull(this);"*}}></a>
-       </div>
-
-       <div id="contact-edit-drop-link-end"></div>
-
-
        <div id="contact-edit-nav-wrapper" >
                <div id="contact-edit-links">
-                       <ul>
-                               <li><div id="contact-edit-rel">{{$relation_text}}</div></li>
-                               <li><div id="contact-edit-nettype">{{$nettype}}</div></li>
-                               {{if $lost_contact}}
-                                       <li><div id="lost-contact-message">{{$lost_contact}}</div></li>
-                               {{/if}}
-                               {{if $insecure}}
-                                       <li><div id="insecure-message">{{$insecure}}</div></li>
-                               {{/if}}
-                               {{if $blocked}}
-                                       <li><div id="block-message">{{$blocked}}</div></li>
-                               {{/if}}
-                               {{if $ignored}}
-                                       <li><div id="ignore-message">{{$ignored}}</div></li>
-                               {{/if}}
-                               {{if $archived}}
-                                       <li><div id="archive-message">{{$archived}}</div></li>
-                               {{/if}}
-
-                               <li>&nbsp;</li>
-
-                               {{if $common_text}}
-                                       <li><div id="contact-edit-common"><a href="{{$common_link}}">{{$common_text}}</a></div></li>
-                               {{/if}}
-                               {{if $all_friends}}
-                                       <li><div id="contact-edit-allfriends"><a href="allfriends/{{$contact_id}}">{{$all_friends}}</a></div></li>
-                               {{/if}}
-
-
-                               <li><a href="network/?cid={{$contact_id}}" id="contact-edit-view-recent">{{$lblrecent}}</a></li>
-                               {{if $lblsuggest}}
-                                       <li><a href="fsuggest/{{$contact_id}}" id="contact-edit-suggest">{{$lblsuggest}}</a></li>
-                               {{/if}}
-
-                       </ul>
+                       <div id="contact-edit-status-wrapper">
+                               <span id="contact-edit-contact-status">{{$contact_status}}</span>
+
+                               <div id="contact-edit-actions">
+                                       <a class="btn" rel="#contact-actions-menu" href="#" id="contact-edit-actions-button">{{$contact_action_button}}</a>
+
+                                       <ul role="menu" aria-haspopup="true" id="contact-actions-menu" class="menu-popup" >
+                                               {{if $lblsuggest}}<li role="menuitem"><a  href="#" title="{{$contact_actions.suggest.title}}" onclick="window.location.href='{{$contact_actions.suggest.url}}'; return false;">{{$contact_actions.suggest.label}}</a></li>{{/if}}
+                                               {{if $poll_enabled}}<li role="menuitem"><a  href="#" title="{{$contact_actions.update.title}}" onclick="window.location.href='{{$contact_actions.update.url}}'; return false;">{{$contact_actions.update.label}}</a></li>{{/if}}
+                                               <li class="divider"></li>
+                                               <li role="menuitem"><a  href="#" title="{{$contact_actions.block.title}}" onclick="window.location.href='{{$contact_actions.block.url}}'; return false;">{{$contact_actions.block.label}}</a></li>
+                                               <li role="menuitem"><a  href="#" title="{{$contact_actions.ignore.title}}" onclick="window.location.href='{{$contact_actions.ignore.url}}'; return false;">{{$contact_actions.ignore.label}}</a></li>
+                                               <li role="menuitem"><a  href="#" title="{{$contact_actions.archive.title}}" onclick="window.location.href='{{$contact_actions.archive.url}}'; return false;">{{$contact_actions.archive.label}}</a></li>
+                                               <li role="menuitem"><a  href="#" title="{{$contact_actions.delete.title}}" onclick="return confirmDelete();">{{$contact_actions.delete.label}}</a></li>
+                                       </ul>
+                               </div>
+
+                               <ul>
+                                       <li><div id="contact-edit-rel">{{$relation_text}}</div></li>
+                                       <li><div id="contact-edit-nettype">{{$nettype}}</div></li>
+                                       {{if $poll_enabled}}
+                                               <div id="contact-edit-poll-wrapper">
+                                                       <div id="contact-edit-last-update-text">{{$lastupdtext}} <span id="contact-edit-last-updated">{{$last_update}}</span></div>
+                                               </div>
+                                       {{/if}}
+                                       {{if $lost_contact}}
+                                               <li><div id="lost-contact-message">{{$lost_contact}}</div></li>
+                                       {{/if}}
+                                       {{if $insecure}}
+                                               <li><div id="insecure-message">{{$insecure}}</div></li>
+                                       {{/if}}
+                                       {{if $blocked}}
+                                               <li><div id="block-message">{{$blocked}}</div></li>
+                                       {{/if}}
+                                       {{if $ignored}}
+                                               <li><div id="ignore-message">{{$ignored}}</div></li>
+                                       {{/if}}
+                                       {{if $archived}}
+                                               <li><div id="archive-message">{{$archived}}</div></li>
+                                       {{/if}}
+
+                               </ul>
+                       </div>
                </div>
        </div>
        <div id="contact-edit-nav-end"></div>
 <form action="contacts/{{$contact_id}}" method="post" >
 <input type="hidden" name="contact_id" value="{{$contact_id}}">
 
-       {{if $poll_enabled}}
-               <div id="contact-edit-poll-wrapper">
-                       <div id="contact-edit-last-update-text">{{$lastupdtext}} <span id="contact-edit-last-updated">{{$last_update}}</span></div>
-                       <span id="contact-edit-poll-text">{{$updpub}}</span> {{$poll_interval}} <span id="contact-edit-update-now" class="button"><a href="contacts/{{$contact_id}}/update" >{{$udnow}}</a></span>
-               </div>
-       {{/if}}
        <div id="contact-edit-end" ></div>
 
        {{include file="field_checkbox.tpl" field=$hidden}}
index 868a840dee0ed93a6f774f7074fb34bffa66f742..1093a04729c27be483097069bccd656044601849 100644 (file)
@@ -10,7 +10,6 @@
  */
 
 function frost_init(&$a) {
-       $a->theme_info = array();
        $a->videowidth = 400;
        $a->videoheight = 330;
        $a->theme_thread_allow = false;
index 847017ee5bf36b361b3bacafae855ea930329285..aed53fdac6bb46db7af656848fde5eeffd14534e 100644 (file)
@@ -463,7 +463,7 @@ a:hover {
   text-decoration: underline;
 }
 blockquote {
-  background: #FFFFFF;
+  background: #ffffff;
   padding: 1em;
   margin-left: 1em;
   border-left: 1em solid #e6e6e6;
@@ -1655,6 +1655,9 @@ span[id^="showmore-wrap"] {
   overflow: hidden;
   text-overflow: ellipsis;
 }
+#contact-edit-status-wrapper {
+  border-color: #364e59;
+}
 /* editor */
 .jothidden {
   display: none;
index 4cfcb5927311f34fb4312faa8c8c436d4bf691dd..74ab5b9cd0bd8e27318b019ff15121d916a02677 100644 (file)
@@ -463,7 +463,7 @@ a:hover {
   text-decoration: underline;
 }
 blockquote {
-  background: #FFFFFF;
+  background: #ffffff;
   padding: 1em;
   margin-left: 1em;
   border-left: 1em solid #e6e6e6;
@@ -1655,6 +1655,9 @@ span[id^="showmore-wrap"] {
   overflow: hidden;
   text-overflow: ellipsis;
 }
+#contact-edit-status-wrapper {
+  border-color: #9ade00;
+}
 /* editor */
 .jothidden {
   display: none;
index 2ff7cfcb0c4b271d1dd2a6310777c7bb5b5518f1..327309fa5e8a83f4ebd14d586a4a4e05f10c3350 100644 (file)
 body {
   font-family: Liberation Sans, helvetica, arial, clean, sans-serif;
   font-size: 11px;
-  background-color: #F6ECF9;
+  background-color: #f6ecf9;
   color: #2d2d2d;
   margin: 50px 0 0 0;
   display: table;
@@ -463,7 +463,7 @@ a:hover {
   text-decoration: underline;
 }
 blockquote {
-  background: #FFFFFF;
+  background: #ffffff;
   padding: 1em;
   margin-left: 1em;
   border-left: 1em solid #e6e6e6;
@@ -1655,6 +1655,9 @@ span[id^="showmore-wrap"] {
   overflow: hidden;
   text-overflow: ellipsis;
 }
+#contact-edit-status-wrapper {
+  border-color: #86608e;
+}
 /* editor */
 .jothidden {
   display: none;
@@ -1753,7 +1756,7 @@ span[id^="showmore-wrap"] {
   height: 20px;
   width: 500px;
   font-weight: bold;
-  border: 1px solid #F6ECF9;
+  border: 1px solid #f6ecf9;
 }
 #jot #jot-title:-webkit-input-placeholder {
   font-weight: normal;
@@ -1780,7 +1783,7 @@ span[id^="showmore-wrap"] {
   margin: 0;
   height: 20px;
   width: 200px;
-  border: 1px solid #F6ECF9;
+  border: 1px solid #f6ecf9;
 }
 #jot #jot-category:hover {
   border: 1px solid #999999;
index 681cfcc3748634cf6eb0e9114b42ee1cd7873997..d81aedf41a9ac468b10fcf638a57505cd631a645 100644 (file)
@@ -408,19 +408,19 @@ aside {
 .group-delete-wrapper {
        float: right;
        margin-right: 50px;
-        .drophide {
-            background-image: url('../../../images/icons/22/delete.png');
-            display: block; width: 22px; height: 22px;
-            opacity: 0.3;
-            position: relative;
-            top: -50px;
-        }
-        .drop {
-            background-image: url('../../../images/icons/22/delete.png');
-            display: block; width: 22px; height: 22px;
-            position: relative;
-            top: -50px;
-        }
+       .drophide {
+               background-image: url('../../../images/icons/22/delete.png');
+               display: block; width: 22px; height: 22px;
+               opacity: 0.3;
+               position: relative;
+               top: -50px;
+       }
+       .drop {
+               background-image: url('../../../images/icons/22/delete.png');
+               display: block; width: 22px; height: 22px;
+               position: relative;
+               top: -50px;
+       }
 }
 /*
 #group-members {
@@ -502,7 +502,7 @@ section {
 }
 
 .sparkle {
-  cursor: url('icons/lock.cur'), pointer;
+       cursor: url('icons/lock.cur'), pointer;
 }
 
 /* wall item */
@@ -959,6 +959,7 @@ span[id^="showmore-wrap"] {
        text-overflow: ellipsis;
 }
 
+#contact-edit-status-wrapper { border-color: @JotToolsOverBackgroundColor;}
 /* editor */
 .jothidden { display: none; }
 #jot {
index a1cd29ee72ad84c1ff2fe3da4850e46b9daec213..0b67c6b49a8282a023eeabd274b836f6aa1abcae 100644 (file)
@@ -8,8 +8,6 @@
  */
 
 function quattro_init(&$a) {
-       $a->theme_info = array();
-
        $a->page['htmlhead'] .= '<script src="'.$a->get_baseurl().'/view/theme/quattro/tinycon.min.js"></script>';
        $a->page['htmlhead'] .= '<script src="'.$a->get_baseurl().'/view/theme/quattro/js/quattro.js"></script>';;
 }
index 9ac49820aad7cfd205a59deb16788f22d701d621..405e1cad34c4461b1f268bd1072a098ca6ad28c5 100644 (file)
@@ -19,7 +19,9 @@
 
        <aside><?php if(x($page,'aside')) echo $page['aside']; ?></aside>
 
-       <section><?php if(x($page,'content')) echo $page['content']; ?>
+       <section>
+               <?php if(x($page,'content')) echo $page['content']; ?>
+               <div id="pause"></div> <!-- The pause/resume Ajax indicator -->
                <div id="page-footer"></div>
        </section>
 
@@ -41,4 +43,3 @@
        <?php if (x($page, 'bottom')) echo $page['bottom']; ?>
 </body>
 </html>
-
index b9f094932ddb1c52949116df9ef1302c0bd00c70..87c7342c9dc9bd235b12e542c7045a988e93058e 100644 (file)
@@ -236,6 +236,39 @@ section {
        color: #efefef;
 }
 
+ul.menu-popup {
+       position: absolute;
+       display: none;
+       width: auto;
+       margin: 2px 0 0;
+       padding: 0px;
+       list-style: none;
+       z-index: 100000;
+       color: #2e3436;
+       border-top: 1px;
+       background: #eeeeee;
+       border: 1px solid #7C7D7B;
+       border-radius: 0px 0px 5px 5px;
+       -webkit-border-radius: 0px 0px 5px 5px;
+       -moz-border-radius: 0px 0px 5px 5px;
+       box-shadow: 5px 5px 10px #242424;
+       -moz-box-shadow: 5px 5px 10px #242424;
+       -webkit-box-shadow: 5px 5px 10px #242424;
+}
+ul.menu-popup li a {
+       white-space: nowrap;
+       display: block;
+       padding: 5px 2px;
+       color: #2e3436;
+}
+ul.menu-popup li a:hover {
+       color: #efefef;
+       background: -webkit-gradient( linear, left top, left bottom, color-stop(0.05, #1873a2), color-stop(1, #6da6c4) );
+       background: -moz-linear-gradient( center top, #1873a2 5%, #6da6c4 100% );
+       filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#1873a2', endColorstr='#6da6c4');
+       background-color: #1873a2;
+}
+
 /* ========= */
 /* = Login = */
 /* ========= */
@@ -4271,16 +4304,6 @@ a.active {
 .nav-notify.show {
        display: block;
 }
-ul.menu-popup {
-       position: absolute;
-       display: none;
-       width: 10em;
-       margin: 0px;
-       padding: 0px;
-       list-style: none;
-       z-index: 100000;
-       top: 40px;
-}
 #nav-notifications-menu {
        width: 320px;
        max-height: 400px;
@@ -4298,6 +4321,7 @@ ul.menu-popup {
        box-shadow: 5px 5px 10px #242424;
                -moz-box-shadow: 5px 5px 10px #242424;
                -webkit-box-shadow: 5px 5px 10px #242424;
+       top: 40px;
 }
 
 #nav-notifications-menu .contactname {
@@ -4406,6 +4430,10 @@ ul.menu-popup {
        background: #000000;
 }
 
+.notify-seen a {
+       color: #efefef !important;
+}
+
 /* Pages profile widget ----------------------------------------------------------- */
 
 #page-profile,
index d3ebbc1d159d3b7bc9092e1ce719d90019224a03..0dae3a6e473b606a3034bf6cd4f78a1cf49fa649 100644 (file)
@@ -11,7 +11,6 @@
  */
 
 function smoothly_init(&$a) {
-       $a->theme_info = array();
        set_template_engine($a, 'smarty3');
 
        $cssFile = null;
index 4300a910c07dc2ae80dd6c516d4bfc47938a45ea..a62cffc00c2cac88bbde4080658f026d04bce8a1 100644 (file)
@@ -228,6 +228,7 @@ li.icon.icon-large:before {
 .icon-key:before                  { content: "\f084"; }
 .icon.gears:before                { content: "\f085"; }
 .icon-comments:before             { content: "\f086"; }
+.icon-commenting:before           { content: "\f27a"; }
 .icon.like:before                 { content: "\f087"; }
 .icon.dislike:before              { content: "\f088"; }
 .icon-star-half:before            { content: "\f089"; }
index 023e419464d7015c6dd22386ae14a26077b3e545..99850f2826310ebbcd548f66dadd0049e3fdc556 100644 (file)
@@ -8,6 +8,12 @@ hr { background-color: #343434 !important; }
 a, .wall-item-name, .fakelink { 
        color: #989898 !important; 
 }
+.btn, .btn:hover{
+       color: #989898;
+       border: 2px solid #0C1116;
+       background-color: #0C1116;
+       text-shadow: none;
+}
 
 nav { 
        color: #989898 !important;
@@ -36,7 +42,7 @@ body, section, blockquote, blockquote.shared_content, #profile-jot-form,
 }
 
 #profile-jot-acl-wrapper, #event-notice, #event-wrapper,
-#cboxLoadedContent, .contact-photo-menu {
+#cboxLoadedContent, .contact-photo-menu, #contact-edit-status-wrapper {
        background-color: #252C33 !important;
 }
 
index 81c9ad949b47f64afeca5642ee2494b6e3147f44..3ed7f8b48ad9bfab52eb03822fefcd6b77d2e680 100644 (file)
Binary files a/view/theme/vier/font/FontAwesome.otf and b/view/theme/vier/font/FontAwesome.otf differ
old mode 100755 (executable)
new mode 100644 (file)
index 84677bc..9b6afae
Binary files a/view/theme/vier/font/fontawesome-webfont.eot and b/view/theme/vier/font/fontawesome-webfont.eot differ
old mode 100755 (executable)
new mode 100644 (file)
index d907b25..d05688e
 <glyph unicode="&#xf077;" horiz-adv-x="1792" d="M1683 205l-166 -165q-19 -19 -45 -19t-45 19l-531 531l-531 -531q-19 -19 -45 -19t-45 19l-166 165q-19 19 -19 45.5t19 45.5l742 741q19 19 45 19t45 -19l742 -741q19 -19 19 -45.5t-19 -45.5z" />
 <glyph unicode="&#xf078;" horiz-adv-x="1792" d="M1683 728l-742 -741q-19 -19 -45 -19t-45 19l-742 741q-19 19 -19 45.5t19 45.5l166 165q19 19 45 19t45 -19l531 -531l531 531q19 19 45 19t45 -19l166 -165q19 -19 19 -45.5t-19 -45.5z" />
 <glyph unicode="&#xf079;" horiz-adv-x="1920" d="M1280 32q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-8 0 -13.5 2t-9 7t-5.5 8t-3 11.5t-1 11.5v13v11v160v416h-192q-26 0 -45 19t-19 45q0 24 15 41l320 384q19 22 49 22t49 -22l320 -384q15 -17 15 -41q0 -26 -19 -45t-45 -19h-192v-384h576q16 0 25 -11l160 -192q7 -11 7 -21 zM1920 448q0 -24 -15 -41l-320 -384q-20 -23 -49 -23t-49 23l-320 384q-15 17 -15 41q0 26 19 45t45 19h192v384h-576q-16 0 -25 12l-160 192q-7 9 -7 20q0 13 9.5 22.5t22.5 9.5h960q8 0 13.5 -2t9 -7t5.5 -8t3 -11.5t1 -11.5v-13v-11v-160v-416h192q26 0 45 -19t19 -45z " />
-<glyph unicode="&#xf07a;" horiz-adv-x="1664" d="M640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5 l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5 t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf07a;" horiz-adv-x="1664" d="M640 0q0 -52 -38 -90t-90 -38t-90 38t-38 90t38 90t90 38t90 -38t38 -90zM1536 0q0 -52 -38 -90t-90 -38t-90 38t-38 90t38 90t90 38t90 -38t38 -90zM1664 1088v-512q0 -24 -16.5 -42.5t-40.5 -21.5l-1044 -122q13 -60 13 -70q0 -16 -24 -64h920q26 0 45 -19t19 -45 t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 11 8 31.5t16 36t21.5 40t15.5 29.5l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t19.5 -15.5t13 -24.5t8 -26t5.5 -29.5t4.5 -26h1201q26 0 45 -19t19 -45z" />
 <glyph unicode="&#xf07b;" horiz-adv-x="1664" d="M1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
 <glyph unicode="&#xf07c;" horiz-adv-x="1920" d="M1879 584q0 -31 -31 -66l-336 -396q-43 -51 -120.5 -86.5t-143.5 -35.5h-1088q-34 0 -60.5 13t-26.5 43q0 31 31 66l336 396q43 51 120.5 86.5t143.5 35.5h1088q34 0 60.5 -13t26.5 -43zM1536 928v-160h-832q-94 0 -197 -47.5t-164 -119.5l-337 -396l-5 -6q0 4 -0.5 12.5 t-0.5 12.5v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158z" />
 <glyph unicode="&#xf07d;" horiz-adv-x="768" d="M704 1216q0 -26 -19 -45t-45 -19h-128v-1024h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v1024h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45z" />
 <glyph unicode="&#xf07e;" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-1024v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h1024v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
 <glyph unicode="&#xf080;" horiz-adv-x="2048" d="M640 640v-512h-256v512h256zM1024 1152v-1024h-256v1024h256zM2048 0v-128h-2048v1536h128v-1408h1920zM1408 896v-768h-256v768h256zM1792 1280v-1152h-256v1152h256z" />
 <glyph unicode="&#xf081;" d="M1280 926q-56 -25 -121 -34q68 40 93 117q-65 -38 -134 -51q-61 66 -153 66q-87 0 -148.5 -61.5t-61.5 -148.5q0 -29 5 -48q-129 7 -242 65t-192 155q-29 -50 -29 -106q0 -114 91 -175q-47 1 -100 26v-2q0 -75 50 -133.5t123 -72.5q-29 -8 -51 -8q-13 0 -39 4 q21 -63 74.5 -104t121.5 -42q-116 -90 -261 -90q-26 0 -50 3q148 -94 322 -94q112 0 210 35.5t168 95t120.5 137t75 162t24.5 168.5q0 18 -1 27q63 45 105 109zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5 t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf082;" d="M1536 160q0 -119 -84.5 -203.5t-203.5 -84.5h-192v608h203l30 224h-233v143q0 54 28 83t96 29l132 1v207q-96 9 -180 9q-136 0 -218 -80.5t-82 -225.5v-166h-224v-224h224v-608h-544q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960 q119 0 203.5 -84.5t84.5 -203.5v-960z" />
+<glyph unicode="&#xf082;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-188v595h199l30 232h-229v148q0 56 23.5 84t91.5 28l122 1v207q-63 9 -178 9q-136 0 -217.5 -80t-81.5 -226v-171h-200v-232h200v-595h-532q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960z" />
 <glyph unicode="&#xf083;" horiz-adv-x="1792" d="M928 704q0 14 -9 23t-23 9q-66 0 -113 -47t-47 -113q0 -14 9 -23t23 -9t23 9t9 23q0 40 28 68t68 28q14 0 23 9t9 23zM1152 574q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM128 0h1536v128h-1536v-128zM1280 574q0 159 -112.5 271.5 t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM256 1216h384v128h-384v-128zM128 1024h1536v118v138h-828l-64 -128h-644v-128zM1792 1280v-1280q0 -53 -37.5 -90.5t-90.5 -37.5h-1536q-53 0 -90.5 37.5t-37.5 90.5v1280 q0 53 37.5 90.5t90.5 37.5h1536q53 0 90.5 -37.5t37.5 -90.5z" />
 <glyph unicode="&#xf084;" horiz-adv-x="1792" d="M832 1024q0 80 -56 136t-136 56t-136 -56t-56 -136q0 -42 19 -83q-41 19 -83 19q-80 0 -136 -56t-56 -136t56 -136t136 -56t136 56t56 136q0 42 -19 83q41 -19 83 -19q80 0 136 56t56 136zM1683 320q0 -17 -49 -66t-66 -49q-9 0 -28.5 16t-36.5 33t-38.5 40t-24.5 26 l-96 -96l220 -220q28 -28 28 -68q0 -42 -39 -81t-81 -39q-40 0 -68 28l-671 671q-176 -131 -365 -131q-163 0 -265.5 102.5t-102.5 265.5q0 160 95 313t248 248t313 95q163 0 265.5 -102.5t102.5 -265.5q0 -189 -131 -365l355 -355l96 96q-3 3 -26 24.5t-40 38.5t-33 36.5 t-16 28.5q0 17 49 66t66 49q13 0 23 -10q6 -6 46 -44.5t82 -79.5t86.5 -86t73 -78t28.5 -41z" />
 <glyph unicode="&#xf085;" horiz-adv-x="1920" d="M896 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1664 128q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1152q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1280 731v-185q0 -10 -7 -19.5t-16 -10.5l-155 -24q-11 -35 -32 -76q34 -48 90 -115q7 -10 7 -20q0 -12 -7 -19q-23 -30 -82.5 -89.5t-78.5 -59.5q-11 0 -21 7l-115 90q-37 -19 -77 -31q-11 -108 -23 -155q-7 -24 -30 -24h-186q-11 0 -20 7.5t-10 17.5 l-23 153q-34 10 -75 31l-118 -89q-7 -7 -20 -7q-11 0 -21 8q-144 133 -144 160q0 9 7 19q10 14 41 53t47 61q-23 44 -35 82l-152 24q-10 1 -17 9.5t-7 19.5v185q0 10 7 19.5t16 10.5l155 24q11 35 32 76q-34 48 -90 115q-7 11 -7 20q0 12 7 20q22 30 82 89t79 59q11 0 21 -7 l115 -90q34 18 77 32q11 108 23 154q7 24 30 24h186q11 0 20 -7.5t10 -17.5l23 -153q34 -10 75 -31l118 89q8 7 20 7q11 0 21 -8q144 -133 144 -160q0 -9 -7 -19q-12 -16 -42 -54t-45 -60q23 -48 34 -82l152 -23q10 -2 17 -10.5t7 -19.5zM1920 198v-140q0 -16 -149 -31 q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20 t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31zM1920 1222v-140q0 -16 -149 -31q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68 q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70 q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31z" />
 <glyph unicode="&#xf0d1;" horiz-adv-x="1792" d="M640 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM256 640h384v256h-158q-13 0 -22 -9l-195 -195q-9 -9 -9 -22v-30zM1536 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1792 1216v-1024q0 -15 -4 -26.5t-13.5 -18.5 t-16.5 -11.5t-23.5 -6t-22.5 -2t-25.5 0t-22.5 0.5q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-64q-3 0 -22.5 -0.5t-25.5 0t-22.5 2t-23.5 6t-16.5 11.5t-13.5 18.5t-4 26.5q0 26 19 45t45 19v320q0 8 -0.5 35t0 38 t2.5 34.5t6.5 37t14 30.5t22.5 30l198 198q19 19 50.5 32t58.5 13h160v192q0 26 19 45t45 19h1024q26 0 45 -19t19 -45z" />
 <glyph unicode="&#xf0d2;" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103q-111 0 -218 32q59 93 78 164q9 34 54 211q20 -39 73 -67.5t114 -28.5q121 0 216 68.5t147 188.5t52 270q0 114 -59.5 214t-172.5 163t-255 63q-105 0 -196 -29t-154.5 -77t-109 -110.5t-67 -129.5t-21.5 -134 q0 -104 40 -183t117 -111q30 -12 38 20q2 7 8 31t8 30q6 23 -11 43q-51 61 -51 151q0 151 104.5 259.5t273.5 108.5q151 0 235.5 -82t84.5 -213q0 -170 -68.5 -289t-175.5 -119q-61 0 -98 43.5t-23 104.5q8 35 26.5 93.5t30 103t11.5 75.5q0 50 -27 83t-77 33 q-62 0 -105 -57t-43 -142q0 -73 25 -122l-99 -418q-17 -70 -13 -177q-206 91 -333 281t-127 423q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
 <glyph unicode="&#xf0d3;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-725q85 122 108 210q9 34 53 209q21 -39 73.5 -67t112.5 -28q181 0 295.5 147.5t114.5 373.5q0 84 -35 162.5t-96.5 139t-152.5 97t-197 36.5q-104 0 -194.5 -28.5t-153 -76.5 t-107.5 -109.5t-66.5 -128t-21.5 -132.5q0 -102 39.5 -180t116.5 -110q13 -5 23.5 0t14.5 19q10 44 15 61q6 23 -11 42q-50 62 -50 150q0 150 103.5 256.5t270.5 106.5q149 0 232.5 -81t83.5 -210q0 -168 -67.5 -286t-173.5 -118q-60 0 -97 43.5t-23 103.5q8 34 26.5 92.5 t29.5 102t11 74.5q0 49 -26.5 81.5t-75.5 32.5q-61 0 -103.5 -56.5t-42.5 -139.5q0 -72 24 -121l-98 -414q-24 -100 -7 -254h-183q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960z" />
-<glyph unicode="&#xf0d4;" d="M829 318q0 -76 -58.5 -112.5t-139.5 -36.5q-41 0 -80.5 9.5t-75.5 28.5t-58 53t-22 78q0 46 25 80t65.5 51.5t82 25t84.5 7.5q20 0 31 -2q2 -1 23 -16.5t26 -19t23 -18t24.5 -22t19 -22.5t17 -26t9 -26.5t4.5 -31.5zM755 863q0 -60 -33 -99.5t-92 -39.5q-53 0 -93 42.5 t-57.5 96.5t-17.5 106q0 61 32 104t92 43q53 0 93.5 -45t58 -101t17.5 -107zM861 1120l88 64h-265q-85 0 -161 -32t-127.5 -98t-51.5 -153q0 -93 64.5 -154.5t158.5 -61.5q22 0 43 3q-13 -29 -13 -54q0 -44 40 -94q-175 -12 -257 -63q-47 -29 -75.5 -73t-28.5 -95 q0 -43 18.5 -77.5t48.5 -56.5t69 -37t77.5 -21t76.5 -6q60 0 120.5 15.5t113.5 46t86 82.5t33 117q0 49 -20 89.5t-49 66.5t-58 47.5t-49 44t-20 44.5t15.5 42.5t37.5 39.5t44 42t37.5 59.5t15.5 82.5q0 60 -22.5 99.5t-72.5 90.5h83zM1152 672h128v64h-128v128h-64v-128 h-128v-64h128v-160h64v160zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf0d5;" horiz-adv-x="1664" d="M735 740q0 -36 32 -70.5t77.5 -68t90.5 -73.5t77 -104t32 -142q0 -90 -48 -173q-72 -122 -211 -179.5t-298 -57.5q-132 0 -246.5 41.5t-171.5 137.5q-37 60 -37 131q0 81 44.5 150t118.5 115q131 82 404 100q-32 42 -47.5 74t-15.5 73q0 36 21 85q-46 -4 -68 -4 q-148 0 -249.5 96.5t-101.5 244.5q0 82 36 159t99 131q77 66 182.5 98t217.5 32h418l-138 -88h-131q74 -63 112 -133t38 -160q0 -72 -24.5 -129.5t-59 -93t-69.5 -65t-59.5 -61.5t-24.5 -66zM589 836q38 0 78 16.5t66 43.5q53 57 53 159q0 58 -17 125t-48.5 129.5 t-84.5 103.5t-117 41q-42 0 -82.5 -19.5t-65.5 -52.5q-47 -59 -47 -160q0 -46 10 -97.5t31.5 -103t52 -92.5t75 -67t96.5 -26zM591 -37q58 0 111.5 13t99 39t73 73t27.5 109q0 25 -7 49t-14.5 42t-27 41.5t-29.5 35t-38.5 34.5t-36.5 29t-41.5 30t-36.5 26q-16 2 -48 2 q-53 0 -105 -7t-107.5 -25t-97 -46t-68.5 -74.5t-27 -105.5q0 -70 35 -123.5t91.5 -83t119 -44t127.5 -14.5zM1401 839h213v-108h-213v-219h-105v219h-212v108h212v217h105v-217z" />
+<glyph unicode="&#xf0d4;" d="M917 631q0 26 -6 64h-362v-132h217q-3 -24 -16.5 -50t-37.5 -53t-66.5 -44.5t-96.5 -17.5q-99 0 -169 71t-70 171t70 171t169 71q92 0 153 -59l104 101q-108 100 -257 100q-160 0 -272 -112.5t-112 -271.5t112 -271.5t272 -112.5q165 0 266.5 105t101.5 270zM1262 585 h109v110h-109v110h-110v-110h-110v-110h110v-110h110v110zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf0d5;" horiz-adv-x="2304" d="M1437 623q0 -208 -87 -370.5t-248 -254t-369 -91.5q-149 0 -285 58t-234 156t-156 234t-58 285t58 285t156 234t234 156t285 58q286 0 491 -192l-199 -191q-117 113 -292 113q-123 0 -227.5 -62t-165.5 -168.5t-61 -232.5t61 -232.5t165.5 -168.5t227.5 -62 q83 0 152.5 23t114.5 57.5t78.5 78.5t49 83t21.5 74h-416v252h692q12 -63 12 -122zM2304 745v-210h-209v-209h-210v209h-209v210h209v209h210v-209h209z" />
 <glyph unicode="&#xf0d6;" horiz-adv-x="1920" d="M768 384h384v96h-128v448h-114l-148 -137l77 -80q42 37 55 57h2v-288h-128v-96zM1280 640q0 -70 -21 -142t-59.5 -134t-101.5 -101t-138 -39t-138 39t-101.5 101t-59.5 134t-21 142t21 142t59.5 134t101.5 101t138 39t138 -39t101.5 -101t59.5 -134t21 -142zM1792 384 v512q-106 0 -181 75t-75 181h-1152q0 -106 -75 -181t-181 -75v-512q106 0 181 -75t75 -181h1152q0 106 75 181t181 75zM1920 1216v-1152q0 -26 -19 -45t-45 -19h-1792q-26 0 -45 19t-19 45v1152q0 26 19 45t45 19h1792q26 0 45 -19t19 -45z" />
 <glyph unicode="&#xf0d7;" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
 <glyph unicode="&#xf0d8;" horiz-adv-x="1024" d="M1024 320q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
 <glyph unicode="&#xf10c;" d="M768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103 t279.5 -279.5t103 -385.5z" />
 <glyph unicode="&#xf10d;" horiz-adv-x="1664" d="M768 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z M1664 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z" />
 <glyph unicode="&#xf10e;" horiz-adv-x="1664" d="M768 1216v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136zM1664 1216 v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136z" />
-<glyph unicode="&#xf110;" horiz-adv-x="1568" d="M496 192q0 -60 -42.5 -102t-101.5 -42q-60 0 -102 42t-42 102t42 102t102 42q59 0 101.5 -42t42.5 -102zM928 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM320 640q0 -66 -47 -113t-113 -47t-113 47t-47 113 t47 113t113 47t113 -47t47 -113zM1360 192q0 -46 -33 -79t-79 -33t-79 33t-33 79t33 79t79 33t79 -33t33 -79zM528 1088q0 -73 -51.5 -124.5t-124.5 -51.5t-124.5 51.5t-51.5 124.5t51.5 124.5t124.5 51.5t124.5 -51.5t51.5 -124.5zM992 1280q0 -80 -56 -136t-136 -56 t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1536 640q0 -40 -28 -68t-68 -28t-68 28t-28 68t28 68t68 28t68 -28t28 -68zM1328 1088q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5z" />
+<glyph unicode="&#xf110;" horiz-adv-x="1792" d="M526 142q0 -53 -37.5 -90.5t-90.5 -37.5q-52 0 -90 38t-38 90q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1024 -64q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM320 640q0 -53 -37.5 -90.5t-90.5 -37.5 t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1522 142q0 -52 -38 -90t-90 -38q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM558 1138q0 -66 -47 -113t-113 -47t-113 47t-47 113t47 113t113 47t113 -47t47 -113z M1728 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1088 1344q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1618 1138q0 -93 -66 -158.5t-158 -65.5q-93 0 -158.5 65.5t-65.5 158.5 q0 92 65.5 158t158.5 66q92 0 158 -66t66 -158z" />
 <glyph unicode="&#xf111;" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
 <glyph unicode="&#xf112;" horiz-adv-x="1792" d="M1792 416q0 -166 -127 -451q-3 -7 -10.5 -24t-13.5 -30t-13 -22q-12 -17 -28 -17q-15 0 -23.5 10t-8.5 25q0 9 2.5 26.5t2.5 23.5q5 68 5 123q0 101 -17.5 181t-48.5 138.5t-80 101t-105.5 69.5t-133 42.5t-154 21.5t-175.5 6h-224v-256q0 -26 -19 -45t-45 -19t-45 19 l-512 512q-19 19 -19 45t19 45l512 512q19 19 45 19t45 -19t19 -45v-256h224q713 0 875 -403q53 -134 53 -333z" />
 <glyph unicode="&#xf113;" horiz-adv-x="1664" d="M640 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1280 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1440 320 q0 120 -69 204t-187 84q-41 0 -195 -21q-71 -11 -157 -11t-157 11q-152 21 -195 21q-118 0 -187 -84t-69 -204q0 -88 32 -153.5t81 -103t122 -60t140 -29.5t149 -7h168q82 0 149 7t140 29.5t122 60t81 103t32 153.5zM1664 496q0 -207 -61 -331q-38 -77 -105.5 -133t-141 -86 t-170 -47.5t-171.5 -22t-167 -4.5q-78 0 -142 3t-147.5 12.5t-152.5 30t-137 51.5t-121 81t-86 115q-62 123 -62 331q0 237 136 396q-27 82 -27 170q0 116 51 218q108 0 190 -39.5t189 -123.5q147 35 309 35q148 0 280 -32q105 82 187 121t189 39q51 -102 51 -218 q0 -87 -27 -168q136 -160 136 -398z" />
 <glyph unicode="&#xf169;" d="M685 771q0 1 -126 222q-21 34 -52 34h-184q-18 0 -26 -11q-7 -12 1 -29l125 -216v-1l-196 -346q-9 -14 0 -28q8 -13 24 -13h185q31 0 50 36zM1309 1268q-7 12 -24 12h-187q-30 0 -49 -35l-411 -729q1 -2 262 -481q20 -35 52 -35h184q18 0 25 12q8 13 -1 28l-260 476v1 l409 723q8 16 0 28zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
 <glyph unicode="&#xf16a;" horiz-adv-x="1792" d="M1280 640q0 37 -30 54l-512 320q-31 20 -65 2q-33 -18 -33 -56v-640q0 -38 33 -56q16 -8 31 -8q20 0 34 10l512 320q30 17 30 54zM1792 640q0 -96 -1 -150t-8.5 -136.5t-22.5 -147.5q-16 -73 -69 -123t-124 -58q-222 -25 -671 -25t-671 25q-71 8 -124.5 58t-69.5 123 q-14 65 -21.5 147.5t-8.5 136.5t-1 150t1 150t8.5 136.5t22.5 147.5q16 73 69 123t124 58q222 25 671 25t671 -25q71 -8 124.5 -58t69.5 -123q14 -65 21.5 -147.5t8.5 -136.5t1 -150z" />
 <glyph unicode="&#xf16b;" horiz-adv-x="1792" d="M402 829l494 -305l-342 -285l-490 319zM1388 274v-108l-490 -293v-1l-1 1l-1 -1v1l-489 293v108l147 -96l342 284v2l1 -1l1 1v-2l343 -284zM554 1418l342 -285l-494 -304l-338 270zM1390 829l338 -271l-489 -319l-343 285zM1239 1418l489 -319l-338 -270l-494 304z" />
-<glyph unicode="&#xf16c;" horiz-adv-x="1408" d="M928 135v-151l-707 -1v151zM1169 481v-701l-1 -35v-1h-1132l-35 1h-1v736h121v-618h928v618h120zM241 393l704 -65l-13 -150l-705 65zM309 709l683 -183l-39 -146l-683 183zM472 1058l609 -360l-77 -130l-609 360zM832 1389l398 -585l-124 -85l-399 584zM1285 1536 l121 -697l-149 -26l-121 697z" />
+<glyph unicode="&#xf16c;" d="M1289 -96h-1118v480h-160v-640h1438v640h-160v-480zM347 428l33 157l783 -165l-33 -156zM450 802l67 146l725 -339l-67 -145zM651 1158l102 123l614 -513l-102 -123zM1048 1536l477 -641l-128 -96l-477 641zM330 65v159h800v-159h-800z" />
 <glyph unicode="&#xf16d;" d="M1362 110v648h-135q20 -63 20 -131q0 -126 -64 -232.5t-174 -168.5t-240 -62q-197 0 -337 135.5t-140 327.5q0 68 20 131h-141v-648q0 -26 17.5 -43.5t43.5 -17.5h1069q25 0 43 17.5t18 43.5zM1078 643q0 124 -90.5 211.5t-218.5 87.5q-127 0 -217.5 -87.5t-90.5 -211.5 t90.5 -211.5t217.5 -87.5q128 0 218.5 87.5t90.5 211.5zM1362 1003v165q0 28 -20 48.5t-49 20.5h-174q-29 0 -49 -20.5t-20 -48.5v-165q0 -29 20 -49t49 -20h174q29 0 49 20t20 49zM1536 1211v-1142q0 -81 -58 -139t-139 -58h-1142q-81 0 -139 58t-58 139v1142q0 81 58 139 t139 58h1142q81 0 139 -58t58 -139z" />
 <glyph unicode="&#xf16e;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM698 640q0 88 -62 150t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150zM1262 640q0 88 -62 150 t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150z" />
 <glyph unicode="&#xf170;" d="M768 914l201 -306h-402zM1133 384h94l-459 691l-459 -691h94l104 160h522zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
 <glyph unicode="&#xf191;" d="M1024 960v-640q0 -26 -19 -45t-45 -19q-20 0 -37 12l-448 320q-27 19 -27 52t27 52l448 320q17 12 37 12q26 0 45 -19t19 -45zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5z M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
 <glyph unicode="&#xf192;" d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5 t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
 <glyph unicode="&#xf193;" horiz-adv-x="1664" d="M1023 349l102 -204q-58 -179 -210 -290t-339 -111q-156 0 -288.5 77.5t-210 210t-77.5 288.5q0 181 104.5 330t274.5 211l17 -131q-122 -54 -195 -165.5t-73 -244.5q0 -185 131.5 -316.5t316.5 -131.5q126 0 232.5 65t165 175.5t49.5 236.5zM1571 249l58 -114l-256 -128 q-13 -7 -29 -7q-40 0 -57 35l-239 477h-472q-24 0 -42.5 16.5t-21.5 40.5l-96 779q-2 16 6 42q14 51 57 82.5t97 31.5q66 0 113 -47t47 -113q0 -69 -52 -117.5t-120 -41.5l37 -289h423v-128h-407l16 -128h455q40 0 57 -35l228 -455z" />
-<glyph unicode="&#xf194;" d="M1254 899q16 85 -21 132q-52 65 -187 45q-17 -3 -41 -12.5t-57.5 -30.5t-64.5 -48.5t-59.5 -70t-44.5 -91.5q80 7 113.5 -16t26.5 -99q-5 -52 -52 -143q-43 -78 -71 -99q-44 -32 -87 14q-23 24 -37.5 64.5t-19 73t-10 84t-8.5 71.5q-23 129 -34 164q-12 37 -35.5 69 t-50.5 40q-57 16 -127 -25q-54 -32 -136.5 -106t-122.5 -102v-7q16 -8 25.5 -26t21.5 -20q21 -3 54.5 8.5t58 10.5t41.5 -30q11 -18 18.5 -38.5t15 -48t12.5 -40.5q17 -46 53 -187q36 -146 57 -197q42 -99 103 -125q43 -12 85 -1.5t76 31.5q131 77 250 237 q104 139 172.5 292.5t82.5 226.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf194;" d="M1292 898q10 216 -161 222q-231 8 -312 -261q44 19 82 19q85 0 74 -96q-4 -57 -74 -167t-105 -110q-43 0 -82 169q-13 54 -45 255q-30 189 -160 177q-59 -7 -164 -100l-81 -72l-81 -72l52 -67q76 52 87 52q57 0 107 -179q15 -55 45 -164.5t45 -164.5q68 -179 164 -179 q157 0 383 294q220 283 226 444zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
 <glyph unicode="&#xf195;" horiz-adv-x="1152" d="M1152 704q0 -191 -94.5 -353t-256.5 -256.5t-353 -94.5h-160q-14 0 -23 9t-9 23v611l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v93l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v250q0 14 9 23t23 9h160 q14 0 23 -9t9 -23v-181l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-93l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-487q188 13 318 151t130 328q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" />
 <glyph unicode="&#xf196;" horiz-adv-x="1408" d="M1152 736v-64q0 -14 -9 -23t-23 -9h-352v-352q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v352h-352q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h352v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-352h352q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832 q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" />
 <glyph unicode="&#xf197;" horiz-adv-x="2176" d="M620 416q-110 -64 -268 -64h-128v64h-64q-13 0 -22.5 23.5t-9.5 56.5q0 24 7 49q-58 2 -96.5 10.5t-38.5 20.5t38.5 20.5t96.5 10.5q-7 25 -7 49q0 33 9.5 56.5t22.5 23.5h64v64h128q158 0 268 -64h1113q42 -7 106.5 -18t80.5 -14q89 -15 150 -40.5t83.5 -47.5t22.5 -40 t-22.5 -40t-83.5 -47.5t-150 -40.5q-16 -3 -80.5 -14t-106.5 -18h-1113zM1739 668q53 -36 53 -92t-53 -92l81 -30q68 48 68 122t-68 122zM625 400h1015q-217 -38 -456 -80q-57 0 -113 -24t-83 -48l-28 -24l-288 -288q-26 -26 -70.5 -45t-89.5 -19h-96l-93 464h29 q157 0 273 64zM352 816h-29l93 464h96q46 0 90 -19t70 -45l288 -288q4 -4 11 -10.5t30.5 -23t48.5 -29t61.5 -23t72.5 -10.5l456 -80h-1015q-116 64 -273 64z" />
 <glyph unicode="&#xf19c;" horiz-adv-x="2048" d="M960 1536l960 -384v-128h-128q0 -26 -20.5 -45t-48.5 -19h-1526q-28 0 -48.5 19t-20.5 45h-128v128zM256 896h256v-768h128v768h256v-768h128v768h256v-768h128v768h256v-768h59q28 0 48.5 -19t20.5 -45v-64h-1664v64q0 26 20.5 45t48.5 19h59v768zM1851 -64 q28 0 48.5 -19t20.5 -45v-128h-1920v128q0 26 20.5 45t48.5 19h1782z" />
 <glyph unicode="&#xf19d;" horiz-adv-x="2304" d="M1774 700l18 -316q4 -69 -82 -128t-235 -93.5t-323 -34.5t-323 34.5t-235 93.5t-82 128l18 316l574 -181q22 -7 48 -7t48 7zM2304 1024q0 -23 -22 -31l-1120 -352q-4 -1 -10 -1t-10 1l-652 206q-43 -34 -71 -111.5t-34 -178.5q63 -36 63 -109q0 -69 -58 -107l58 -433 q2 -14 -8 -25q-9 -11 -24 -11h-192q-15 0 -24 11q-10 11 -8 25l58 433q-58 38 -58 107q0 73 65 111q11 207 98 330l-333 104q-22 8 -22 31t22 31l1120 352q4 1 10 1t10 -1l1120 -352q22 -8 22 -31z" />
 <glyph unicode="&#xf19e;" d="M859 579l13 -707q-62 11 -105 11q-41 0 -105 -11l13 707q-40 69 -168.5 295.5t-216.5 374.5t-181 287q58 -15 108 -15q43 0 111 15q63 -111 133.5 -229.5t167 -276.5t138.5 -227q37 61 109.5 177.5t117.5 190t105 176t107 189.5q54 -14 107 -14q56 0 114 14v0 q-28 -39 -60 -88.5t-49.5 -78.5t-56.5 -96t-49 -84q-146 -248 -353 -610z" />
-<glyph unicode="&#xf1a0;" horiz-adv-x="1280" d="M981 197q0 25 -7 49t-14.5 42t-27 41.5t-29.5 35t-38.5 34.5t-36.5 29t-41.5 30t-36.5 26q-16 2 -49 2q-53 0 -104.5 -7t-107 -25t-97 -46t-68.5 -74.5t-27 -105.5q0 -56 23.5 -102t61 -75.5t87 -50t100 -29t101.5 -8.5q58 0 111.5 13t99 39t73 73t27.5 109zM864 1055 q0 59 -17 125.5t-48 129t-84 103.5t-117 41q-42 0 -82.5 -19.5t-66.5 -52.5q-46 -59 -46 -160q0 -46 10 -97.5t31.5 -103t52 -92.5t75 -67t96.5 -26q37 0 77.5 16.5t65.5 43.5q53 56 53 159zM752 1536h417l-137 -88h-132q75 -63 113 -133t38 -160q0 -72 -24.5 -129.5 t-59.5 -93t-69.5 -65t-59 -61.5t-24.5 -66q0 -36 32 -70.5t77 -68t90.5 -73.5t77.5 -104t32 -142q0 -91 -49 -173q-71 -122 -209.5 -179.5t-298.5 -57.5q-132 0 -246.5 41.5t-172.5 137.5q-36 59 -36 131q0 81 44.5 150t118.5 115q131 82 404 100q-32 41 -47.5 73.5 t-15.5 73.5q0 40 21 85q-46 -4 -68 -4q-148 0 -249.5 96.5t-101.5 244.5q0 82 36 159t99 131q76 66 182 98t218 32z" />
-<glyph unicode="&#xf1a1;" horiz-adv-x="1984" d="M831 572q0 -56 -40.5 -96t-96.5 -40q-57 0 -98 40t-41 96q0 57 41.5 98t97.5 41t96.5 -41t40.5 -98zM1292 711q56 0 96.5 -41t40.5 -98q0 -56 -40.5 -96t-96.5 -40q-57 0 -98 40t-41 96q0 57 41.5 98t97.5 41zM1984 722q0 -62 -31 -114t-83 -82q5 -33 5 -61 q0 -121 -68.5 -230.5t-197.5 -193.5q-125 -82 -285.5 -125.5t-335.5 -43.5q-176 0 -336.5 43.5t-284.5 125.5q-129 84 -197.5 193t-68.5 231q0 29 5 66q-48 31 -77 81.5t-29 109.5q0 94 66 160t160 66q83 0 148 -55q248 158 592 164l134 423q4 14 17.5 21.5t28.5 4.5 l347 -82q22 50 68.5 81t102.5 31q77 0 131.5 -54.5t54.5 -131.5t-54.5 -132t-131.5 -55q-76 0 -130.5 54t-55.5 131l-315 74l-116 -366q327 -14 560 -166q64 58 151 58q94 0 160 -66t66 -160zM1664 1459q-45 0 -77 -32t-32 -77t32 -77t77 -32t77 32t32 77t-32 77t-77 32z M77 722q0 -67 51 -111q49 131 180 235q-36 25 -82 25q-62 0 -105.5 -43.5t-43.5 -105.5zM1567 105q112 73 171.5 166t59.5 194t-59.5 193.5t-171.5 165.5q-116 75 -265.5 115.5t-313.5 40.5t-313.5 -40.5t-265.5 -115.5q-112 -73 -171.5 -165.5t-59.5 -193.5t59.5 -194 t171.5 -166q116 -75 265.5 -115.5t313.5 -40.5t313.5 40.5t265.5 115.5zM1850 605q57 46 57 117q0 62 -43.5 105.5t-105.5 43.5q-49 0 -86 -28q131 -105 178 -238zM1258 237q11 11 27 11t27 -11t11 -27.5t-11 -27.5q-99 -99 -319 -99h-2q-220 0 -319 99q-11 11 -11 27.5 t11 27.5t27 11t27 -11q77 -77 265 -77h2q188 0 265 77z" />
-<glyph unicode="&#xf1a2;" d="M950 393q7 7 17.5 7t17.5 -7t7 -18t-7 -18q-65 -64 -208 -64h-1h-1q-143 0 -207 64q-8 7 -8 18t8 18q7 7 17.5 7t17.5 -7q49 -51 172 -51h1h1q122 0 173 51zM671 613q0 -37 -26 -64t-63 -27t-63 27t-26 64t26 63t63 26t63 -26t26 -63zM1214 1049q-29 0 -50 21t-21 50 q0 30 21 51t50 21q30 0 51 -21t21 -51q0 -29 -21 -50t-51 -21zM1216 1408q132 0 226 -94t94 -227v-894q0 -133 -94 -227t-226 -94h-896q-132 0 -226 94t-94 227v894q0 133 94 227t226 94h896zM1321 596q35 14 57 45.5t22 70.5q0 51 -36 87.5t-87 36.5q-60 0 -98 -48 q-151 107 -375 115l83 265l206 -49q1 -50 36.5 -85t84.5 -35q50 0 86 35.5t36 85.5t-36 86t-86 36q-36 0 -66 -20.5t-45 -53.5l-227 54q-9 2 -17.5 -2.5t-11.5 -14.5l-95 -302q-224 -4 -381 -113q-36 43 -93 43q-51 0 -87 -36.5t-36 -87.5q0 -37 19.5 -67.5t52.5 -45.5 q-7 -25 -7 -54q0 -98 74 -181.5t201.5 -132t278.5 -48.5q150 0 277.5 48.5t201.5 132t74 181.5q0 27 -6 54zM971 702q37 0 63 -26t26 -63t-26 -64t-63 -27t-63 27t-26 64t26 63t63 26z" />
+<glyph unicode="&#xf1a0;" d="M768 750h725q12 -67 12 -128q0 -217 -91 -387.5t-259.5 -266.5t-386.5 -96q-157 0 -299 60.5t-245 163.5t-163.5 245t-60.5 299t60.5 299t163.5 245t245 163.5t299 60.5q300 0 515 -201l-209 -201q-123 119 -306 119q-129 0 -238.5 -65t-173.5 -176.5t-64 -243.5 t64 -243.5t173.5 -176.5t238.5 -65q87 0 160 24t120 60t82 82t51.5 87t22.5 78h-436v264z" />
+<glyph unicode="&#xf1a1;" horiz-adv-x="1792" d="M1095 369q16 -16 0 -31q-62 -62 -199 -62t-199 62q-16 15 0 31q6 6 15 6t15 -6q48 -49 169 -49q120 0 169 49q6 6 15 6t15 -6zM788 550q0 -37 -26 -63t-63 -26t-63.5 26t-26.5 63q0 38 26.5 64t63.5 26t63 -26.5t26 -63.5zM1183 550q0 -37 -26.5 -63t-63.5 -26t-63 26 t-26 63t26 63.5t63 26.5t63.5 -26t26.5 -64zM1434 670q0 49 -35 84t-85 35t-86 -36q-130 90 -311 96l63 283l200 -45q0 -37 26 -63t63 -26t63.5 26.5t26.5 63.5t-26.5 63.5t-63.5 26.5q-54 0 -80 -50l-221 49q-19 5 -25 -16l-69 -312q-180 -7 -309 -97q-35 37 -87 37 q-50 0 -85 -35t-35 -84q0 -35 18.5 -64t49.5 -44q-6 -27 -6 -56q0 -142 140 -243t337 -101q198 0 338 101t140 243q0 32 -7 57q30 15 48 43.5t18 63.5zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191 t348 71t348 -71t286 -191t191 -286t71 -348z" />
+<glyph unicode="&#xf1a2;" d="M939 407q13 -13 0 -26q-53 -53 -171 -53t-171 53q-13 13 0 26q5 6 13 6t13 -6q42 -42 145 -42t145 42q5 6 13 6t13 -6zM676 563q0 -31 -23 -54t-54 -23t-54 23t-23 54q0 32 22.5 54.5t54.5 22.5t54.5 -22.5t22.5 -54.5zM1014 563q0 -31 -23 -54t-54 -23t-54 23t-23 54 q0 32 22.5 54.5t54.5 22.5t54.5 -22.5t22.5 -54.5zM1229 666q0 42 -30 72t-73 30q-42 0 -73 -31q-113 78 -267 82l54 243l171 -39q1 -32 23.5 -54t53.5 -22q32 0 54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5q-48 0 -69 -43l-189 42q-17 5 -21 -13l-60 -268q-154 -6 -265 -83 q-30 32 -74 32q-43 0 -73 -30t-30 -72q0 -30 16 -55t42 -38q-5 -25 -5 -48q0 -122 120 -208.5t289 -86.5q170 0 290 86.5t120 208.5q0 25 -6 49q25 13 40.5 37.5t15.5 54.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
 <glyph unicode="&#xf1a3;" d="M866 697l90 27v62q0 79 -58 135t-138 56t-138 -55.5t-58 -134.5v-283q0 -20 -14 -33.5t-33 -13.5t-32.5 13.5t-13.5 33.5v120h-151v-122q0 -82 57.5 -139t139.5 -57q81 0 138.5 56.5t57.5 136.5v280q0 19 13.5 33t33.5 14q19 0 32.5 -14t13.5 -33v-54zM1199 502v122h-150 v-126q0 -20 -13.5 -33.5t-33.5 -13.5q-19 0 -32.5 14t-13.5 33v123l-90 -26l-60 28v-123q0 -80 58 -137t139 -57t138.5 57t57.5 139zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103 t385.5 -103t279.5 -279.5t103 -385.5z" />
 <glyph unicode="&#xf1a4;" horiz-adv-x="1920" d="M1062 824v118q0 42 -30 72t-72 30t-72 -30t-30 -72v-612q0 -175 -126 -299t-303 -124q-178 0 -303.5 125.5t-125.5 303.5v266h328v-262q0 -43 30 -72.5t72 -29.5t72 29.5t30 72.5v620q0 171 126.5 292t301.5 121q176 0 302 -122t126 -294v-136l-195 -58zM1592 602h328 v-266q0 -178 -125.5 -303.5t-303.5 -125.5q-177 0 -303 124.5t-126 300.5v268l131 -61l195 58v-270q0 -42 30 -71.5t72 -29.5t72 29.5t30 71.5v275z" />
 <glyph unicode="&#xf1a5;" d="M1472 160v480h-704v704h-480q-93 0 -158.5 -65.5t-65.5 -158.5v-480h704v-704h480q93 0 158.5 65.5t65.5 158.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z" />
 <glyph unicode="&#xf1ba;" horiz-adv-x="2048" d="M1824 640q93 0 158.5 -65.5t65.5 -158.5v-384q0 -14 -9 -23t-23 -9h-96v-64q0 -80 -56 -136t-136 -56t-136 56t-56 136v64h-1024v-64q0 -80 -56 -136t-136 -56t-136 56t-56 136v64h-96q-14 0 -23 9t-9 23v384q0 93 65.5 158.5t158.5 65.5h28l105 419q23 94 104 157.5 t179 63.5h128v224q0 14 9 23t23 9h448q14 0 23 -9t9 -23v-224h128q98 0 179 -63.5t104 -157.5l105 -419h28zM320 160q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47zM516 640h1016l-89 357q-2 8 -14 17.5t-21 9.5h-768q-9 0 -21 -9.5t-14 -17.5z M1728 160q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47z" />
 <glyph unicode="&#xf1bb;" d="M1504 64q0 -26 -19 -45t-45 -19h-462q1 -17 6 -87.5t5 -108.5q0 -25 -18 -42.5t-43 -17.5h-320q-25 0 -43 17.5t-18 42.5q0 38 5 108.5t6 87.5h-462q-26 0 -45 19t-19 45t19 45l402 403h-229q-26 0 -45 19t-19 45t19 45l402 403h-197q-26 0 -45 19t-19 45t19 45l384 384 q19 19 45 19t45 -19l384 -384q19 -19 19 -45t-19 -45t-45 -19h-197l402 -403q19 -19 19 -45t-19 -45t-45 -19h-229l402 -403q19 -19 19 -45z" />
 <glyph unicode="&#xf1bc;" d="M1127 326q0 32 -30 51q-193 115 -447 115q-133 0 -287 -34q-42 -9 -42 -52q0 -20 13.5 -34.5t35.5 -14.5q5 0 37 8q132 27 243 27q226 0 397 -103q19 -11 33 -11q19 0 33 13.5t14 34.5zM1223 541q0 40 -35 61q-237 141 -548 141q-153 0 -303 -42q-48 -13 -48 -64 q0 -25 17.5 -42.5t42.5 -17.5q7 0 37 8q122 33 251 33q279 0 488 -124q24 -13 38 -13q25 0 42.5 17.5t17.5 42.5zM1331 789q0 47 -40 70q-126 73 -293 110.5t-343 37.5q-204 0 -364 -47q-23 -7 -38.5 -25.5t-15.5 -48.5q0 -31 20.5 -52t51.5 -21q11 0 40 8q133 37 307 37 q159 0 309.5 -34t253.5 -95q21 -12 40 -12q29 0 50.5 20.5t21.5 51.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf1bd;" d="M1397 1408q58 0 98.5 -40.5t40.5 -98.5v-1258q0 -58 -40.5 -98.5t-98.5 -40.5h-1258q-58 0 -98.5 40.5t-40.5 98.5v1258q0 58 40.5 98.5t98.5 40.5h1258zM1465 11v1258q0 28 -20 48t-48 20h-1258q-28 0 -48 -20t-20 -48v-1258q0 -28 20 -48t48 -20h1258q28 0 48 20t20 48 zM694 749l188 -387l533 145v-496q0 -7 -5.5 -12.5t-12.5 -5.5h-1258q-7 0 -12.5 5.5t-5.5 12.5v141l711 195l-212 439q4 1 12 2.5t12 1.5q170 32 303.5 21.5t221 -46t143.5 -94.5q27 -28 -25 -42q-64 -16 -256 -62l-97 198q-111 7 -240 -16zM1397 1287q7 0 12.5 -5.5 t5.5 -12.5v-428q-85 30 -188 52q-294 64 -645 12l-18 -3l-65 134h-233l85 -190q-132 -51 -230 -137v560q0 7 5.5 12.5t12.5 5.5h1258zM286 387q-14 -3 -26 4.5t-14 21.5q-24 203 166 305l129 -270z" />
+<glyph unicode="&#xf1bd;" horiz-adv-x="1024" d="M1024 1233l-303 -582l24 -31h279v-415h-507l-44 -30l-142 -273l-30 -30h-301v303l303 583l-24 30h-279v415h507l44 30l142 273l30 30h301v-303z" />
 <glyph unicode="&#xf1be;" horiz-adv-x="2304" d="M784 164l16 241l-16 523q-1 10 -7.5 17t-16.5 7q-9 0 -16 -7t-7 -17l-14 -523l14 -241q1 -10 7.5 -16.5t15.5 -6.5q22 0 24 23zM1080 193l11 211l-12 586q0 16 -13 24q-8 5 -16 5t-16 -5q-13 -8 -13 -24l-1 -6l-10 -579q0 -1 11 -236v-1q0 -10 6 -17q9 -11 23 -11 q11 0 20 9q9 7 9 20zM35 533l20 -128l-20 -126q-2 -9 -9 -9t-9 9l-17 126l17 128q2 9 9 9t9 -9zM121 612l26 -207l-26 -203q-2 -9 -10 -9q-9 0 -9 10l-23 202l23 207q0 9 9 9q8 0 10 -9zM401 159zM213 650l25 -245l-25 -237q0 -11 -11 -11q-10 0 -12 11l-21 237l21 245 q2 12 12 12q11 0 11 -12zM307 657l23 -252l-23 -244q-2 -13 -14 -13q-13 0 -13 13l-21 244l21 252q0 13 13 13q12 0 14 -13zM401 639l21 -234l-21 -246q-2 -16 -16 -16q-6 0 -10.5 4.5t-4.5 11.5l-20 246l20 234q0 6 4.5 10.5t10.5 4.5q14 0 16 -15zM784 164zM495 785 l21 -380l-21 -246q0 -7 -5 -12.5t-12 -5.5q-16 0 -18 18l-18 246l18 380q2 18 18 18q7 0 12 -5.5t5 -12.5zM589 871l19 -468l-19 -244q0 -8 -5.5 -13.5t-13.5 -5.5q-18 0 -20 19l-16 244l16 468q2 19 20 19q8 0 13.5 -5.5t5.5 -13.5zM687 911l18 -506l-18 -242 q-2 -21 -22 -21q-19 0 -21 21l-16 242l16 506q0 9 6.5 15.5t14.5 6.5q9 0 15 -6.5t7 -15.5zM1079 169v0v0zM881 915l15 -510l-15 -239q0 -10 -7.5 -17.5t-17.5 -7.5t-17 7t-8 18l-14 239l14 510q0 11 7.5 18t17.5 7t17.5 -7t7.5 -18zM980 896l14 -492l-14 -236q0 -11 -8 -19 t-19 -8t-19 8t-9 19l-12 236l12 492q1 12 9 20t19 8t18.5 -8t8.5 -20zM1192 404l-14 -231v0q0 -13 -9 -22t-22 -9t-22 9t-10 22l-6 114l-6 117l12 636v3q2 15 12 24q9 7 20 7q8 0 15 -5q14 -8 16 -26zM2304 423q0 -117 -83 -199.5t-200 -82.5h-786q-13 2 -22 11t-9 22v899 q0 23 28 33q85 34 181 34q195 0 338 -131.5t160 -323.5q53 22 110 22q117 0 200 -83t83 -201z" />
 <glyph unicode="&#xf1c0;" d="M768 768q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127t443 -43zM768 0q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127 t443 -43zM768 384q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127t443 -43zM768 1536q208 0 385 -34.5t280 -93.5t103 -128v-128q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5 t-103 128v128q0 69 103 128t280 93.5t385 34.5z" />
 <glyph unicode="&#xf1c1;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M894 465q33 -26 84 -56q59 7 117 7q147 0 177 -49q16 -22 2 -52q0 -1 -1 -2l-2 -2v-1q-6 -38 -71 -38q-48 0 -115 20t-130 53q-221 -24 -392 -83q-153 -262 -242 -262q-15 0 -28 7l-24 12q-1 1 -6 5q-10 10 -6 36q9 40 56 91.5t132 96.5q14 9 23 -6q2 -2 2 -4q52 85 107 197 q68 136 104 262q-24 82 -30.5 159.5t6.5 127.5q11 40 42 40h21h1q23 0 35 -15q18 -21 9 -68q-2 -6 -4 -8q1 -3 1 -8v-30q-2 -123 -14 -192q55 -164 146 -238zM318 54q52 24 137 158q-51 -40 -87.5 -84t-49.5 -74zM716 974q-15 -42 -2 -132q1 7 7 44q0 3 7 43q1 4 4 8 q-1 1 -1 2t-0.5 1.5t-0.5 1.5q-1 22 -13 36q0 -1 -1 -2v-2zM592 313q135 54 284 81q-2 1 -13 9.5t-16 13.5q-76 67 -127 176q-27 -86 -83 -197q-30 -56 -45 -83zM1238 329q-24 24 -140 24q76 -28 124 -28q14 0 18 1q0 1 -2 3z" />
 <glyph unicode="&#xf1cb;" horiz-adv-x="1792" d="M216 367l603 -402v359l-334 223zM154 511l193 129l-193 129v-258zM973 -35l603 402l-269 180l-334 -223v-359zM896 458l272 182l-272 182l-272 -182zM485 733l334 223v359l-603 -402zM1445 640l193 -129v258zM1307 733l269 180l-603 402v-359zM1792 913v-546 q0 -41 -34 -64l-819 -546q-21 -13 -43 -13t-43 13l-819 546q-34 23 -34 64v546q0 41 34 64l819 546q21 13 43 13t43 -13l819 -546q34 -23 34 -64z" />
 <glyph unicode="&#xf1cc;" horiz-adv-x="2048" d="M1800 764q111 -46 179.5 -145.5t68.5 -221.5q0 -164 -118 -280.5t-285 -116.5q-4 0 -11.5 0.5t-10.5 0.5h-1209h-1h-2h-5q-170 10 -288 125.5t-118 280.5q0 110 55 203t147 147q-12 39 -12 82q0 115 82 196t199 81q95 0 172 -58q75 154 222.5 248t326.5 94 q166 0 306 -80.5t221.5 -218.5t81.5 -301q0 -6 -0.5 -18t-0.5 -18zM468 498q0 -122 84 -193t208 -71q137 0 240 99q-16 20 -47.5 56.5t-43.5 50.5q-67 -65 -144 -65q-55 0 -93.5 33.5t-38.5 87.5q0 53 38.5 87t91.5 34q44 0 84.5 -21t73 -55t65 -75t69 -82t77 -75t97 -55 t121.5 -21q121 0 204.5 71.5t83.5 190.5q0 121 -84 192t-207 71q-143 0 -241 -97q14 -16 29.5 -34t34.5 -40t29 -34q66 64 142 64q52 0 92 -33t40 -84q0 -57 -37 -91.5t-94 -34.5q-43 0 -82.5 21t-72 55t-65.5 75t-69.5 82t-77.5 75t-96.5 55t-118.5 21q-122 0 -207 -70.5 t-85 -189.5z" />
 <glyph unicode="&#xf1cd;" horiz-adv-x="1792" d="M896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM896 1408q-190 0 -361 -90l194 -194q82 28 167 28t167 -28l194 194q-171 90 -361 90zM218 279l194 194 q-28 82 -28 167t28 167l-194 194q-90 -171 -90 -361t90 -361zM896 -128q190 0 361 90l-194 194q-82 -28 -167 -28t-167 28l-194 -194q171 -90 361 -90zM896 256q159 0 271.5 112.5t112.5 271.5t-112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5 t271.5 -112.5zM1380 473l194 -194q90 171 90 361t-90 361l-194 -194q28 -82 28 -167t-28 -167z" />
-<glyph unicode="&#xf1ce;" horiz-adv-x="1792" d="M1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348q0 222 101 414.5t276.5 317t390.5 155.5v-260q-221 -45 -366.5 -221t-145.5 -406q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5 q0 230 -145.5 406t-366.5 221v260q215 -31 390.5 -155.5t276.5 -317t101 -414.5z" />
+<glyph unicode="&#xf1ce;" horiz-adv-x="1792" d="M1760 640q0 -176 -68.5 -336t-184 -275.5t-275.5 -184t-336 -68.5t-336 68.5t-275.5 184t-184 275.5t-68.5 336q0 213 97 398.5t265 305.5t374 151v-228q-221 -45 -366.5 -221t-145.5 -406q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5 t136.5 204t51 248.5q0 230 -145.5 406t-366.5 221v228q206 -31 374 -151t265 -305.5t97 -398.5z" />
 <glyph unicode="&#xf1d0;" horiz-adv-x="1792" d="M19 662q8 217 116 406t305 318h5q0 -1 -1 -3q-8 -8 -28 -33.5t-52 -76.5t-60 -110.5t-44.5 -135.5t-14 -150.5t39 -157.5t108.5 -154q50 -50 102 -69.5t90.5 -11.5t69.5 23.5t47 32.5l16 16q39 51 53 116.5t6.5 122.5t-21 107t-26.5 80l-14 29q-10 25 -30.5 49.5t-43 41 t-43.5 29.5t-35 19l-13 6l104 115q39 -17 78 -52t59 -61l19 -27q1 48 -18.5 103.5t-40.5 87.5l-20 31l161 183l160 -181q-33 -46 -52.5 -102.5t-22.5 -90.5l-4 -33q22 37 61.5 72.5t67.5 52.5l28 17l103 -115q-44 -14 -85 -50t-60 -65l-19 -29q-31 -56 -48 -133.5t-7 -170 t57 -156.5q33 -45 77.5 -60.5t85 -5.5t76 26.5t57.5 33.5l21 16q60 53 96.5 115t48.5 121.5t10 121.5t-18 118t-37 107.5t-45.5 93t-45 72t-34.5 47.5l-13 17q-14 13 -7 13l10 -3q40 -29 62.5 -46t62 -50t64 -58t58.5 -65t55.5 -77t45.5 -88t38 -103t23.5 -117t10.5 -136 q3 -259 -108 -465t-312 -321t-456 -115q-185 0 -351 74t-283.5 198t-184 293t-60.5 353z" />
 <glyph unicode="&#xf1d1;" horiz-adv-x="1792" d="M874 -102v-66q-208 6 -385 109.5t-283 275.5l58 34q29 -49 73 -99l65 57q148 -168 368 -212l-17 -86q65 -12 121 -13zM276 428l-83 -28q22 -60 49 -112l-57 -33q-98 180 -98 385t98 385l57 -33q-30 -56 -49 -112l82 -28q-35 -100 -35 -212q0 -109 36 -212zM1528 251 l58 -34q-106 -172 -283 -275.5t-385 -109.5v66q56 1 121 13l-17 86q220 44 368 212l65 -57q44 50 73 99zM1377 805l-233 -80q14 -42 14 -85t-14 -85l232 -80q-31 -92 -98 -169l-185 162q-57 -67 -147 -85l48 -241q-52 -10 -98 -10t-98 10l48 241q-90 18 -147 85l-185 -162 q-67 77 -98 169l232 80q-14 42 -14 85t14 85l-233 80q33 93 99 169l185 -162q59 68 147 86l-48 240q44 10 98 10t98 -10l-48 -240q88 -18 147 -86l185 162q66 -76 99 -169zM874 1448v-66q-65 -2 -121 -13l17 -86q-220 -42 -368 -211l-65 56q-38 -42 -73 -98l-57 33 q106 172 282 275.5t385 109.5zM1705 640q0 -205 -98 -385l-57 33q27 52 49 112l-83 28q36 103 36 212q0 112 -35 212l82 28q-19 56 -49 112l57 33q98 -180 98 -385zM1585 1063l-57 -33q-35 56 -73 98l-65 -56q-148 169 -368 211l17 86q-56 11 -121 13v66q209 -6 385 -109.5 t282 -275.5zM1748 640q0 173 -67.5 331t-181.5 272t-272 181.5t-331 67.5t-331 -67.5t-272 -181.5t-181.5 -272t-67.5 -331t67.5 -331t181.5 -272t272 -181.5t331 -67.5t331 67.5t272 181.5t181.5 272t67.5 331zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71 t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
 <glyph unicode="&#xf1d2;" d="M582 228q0 -66 -93 -66q-107 0 -107 63q0 64 98 64q102 0 102 -61zM546 694q0 -85 -74 -85q-77 0 -77 84q0 90 77 90q36 0 55 -25.5t19 -63.5zM712 769v125q-78 -29 -135 -29q-50 29 -110 29q-86 0 -145 -57t-59 -143q0 -50 29.5 -102t73.5 -67v-3q-38 -17 -38 -85 q0 -53 41 -77v-3q-113 -37 -113 -139q0 -45 20 -78.5t54 -51t72 -25.5t81 -8q224 0 224 188q0 67 -48 99t-126 46q-27 5 -51.5 20.5t-24.5 39.5q0 44 49 52q77 15 122 70t45 134q0 24 -10 52q37 9 49 13zM771 350h137q-2 27 -2 82v387q0 46 2 69h-137q3 -23 3 -71v-392 q0 -50 -3 -75zM1280 366v121q-30 -21 -68 -21q-53 0 -53 82v225h52q9 0 26.5 -1t26.5 -1v117h-105q0 82 3 102h-140q4 -24 4 -55v-47h-60v-117q36 3 37 3q3 0 11 -0.5t12 -0.5v-2h-2v-217q0 -37 2.5 -64t11.5 -56.5t24.5 -48.5t43.5 -31t66 -12q64 0 108 24zM924 1072 q0 36 -24 63.5t-60 27.5t-60.5 -27t-24.5 -64q0 -36 25 -62.5t60 -26.5t59.5 27t24.5 62zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
 <glyph unicode="&#xf1d3;" horiz-adv-x="1792" d="M595 22q0 100 -165 100q-158 0 -158 -104q0 -101 172 -101q151 0 151 105zM536 777q0 61 -30 102t-89 41q-124 0 -124 -145q0 -135 124 -135q119 0 119 137zM805 1101v-202q-36 -12 -79 -22q16 -43 16 -84q0 -127 -73 -216.5t-197 -112.5q-40 -8 -59.5 -27t-19.5 -58 q0 -31 22.5 -51.5t58 -32t78.5 -22t86 -25.5t78.5 -37.5t58 -64t22.5 -98.5q0 -304 -363 -304q-69 0 -130 12.5t-116 41t-87.5 82t-32.5 127.5q0 165 182 225v4q-67 41 -67 126q0 109 63 137v4q-72 24 -119.5 108.5t-47.5 165.5q0 139 95 231.5t235 92.5q96 0 178 -47 q98 0 218 47zM1123 220h-222q4 45 4 134v609q0 94 -4 128h222q-4 -33 -4 -124v-613q0 -89 4 -134zM1724 442v-196q-71 -39 -174 -39q-62 0 -107 20t-70 50t-39.5 78t-18.5 92t-4 103v351h2v4q-7 0 -19 1t-18 1q-21 0 -59 -6v190h96v76q0 54 -6 89h227q-6 -41 -6 -165h171 v-190q-15 0 -43.5 2t-42.5 2h-85v-365q0 -131 87 -131q61 0 109 33zM1148 1389q0 -58 -39 -101.5t-96 -43.5q-58 0 -98 43.5t-40 101.5q0 59 39.5 103t98.5 44q58 0 96.5 -44.5t38.5 -102.5z" />
-<glyph unicode="&#xf1d4;" d="M825 547l343 588h-150q-21 -39 -63.5 -118.5t-68 -128.5t-59.5 -118.5t-60 -128.5h-3q-21 48 -44.5 97t-52 105.5t-46.5 92t-54 104.5t-49 95h-150l323 -589v-435h134v436zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf1d4;" d="M809 532l266 499h-112l-157 -312q-24 -48 -44 -92l-42 92l-155 312h-120l263 -493v-324h101v318zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
 <glyph unicode="&#xf1d5;" horiz-adv-x="1280" d="M842 964q0 -80 -57 -136.5t-136 -56.5q-60 0 -111 35q-62 -67 -115 -146q-247 -371 -202 -859q1 -22 -12.5 -38.5t-34.5 -18.5h-5q-20 0 -35 13.5t-17 33.5q-14 126 -3.5 247.5t29.5 217t54 186t69 155.5t74 125q61 90 132 165q-16 35 -16 77q0 80 56.5 136.5t136.5 56.5 t136.5 -56.5t56.5 -136.5zM1223 953q0 -158 -78 -292t-212.5 -212t-292.5 -78q-64 0 -131 14q-21 5 -32.5 23.5t-6.5 39.5q5 20 23 31.5t39 7.5q51 -13 108 -13q97 0 186 38t153 102t102 153t38 186t-38 186t-102 153t-153 102t-186 38t-186 -38t-153 -102t-102 -153 t-38 -186q0 -114 52 -218q10 -20 3.5 -40t-25.5 -30t-39.5 -3t-30.5 26q-64 123 -64 265q0 119 46.5 227t124.5 186t186 124t226 46q158 0 292.5 -78t212.5 -212.5t78 -292.5z" />
 <glyph unicode="&#xf1d6;" horiz-adv-x="1792" d="M270 730q-8 19 -8 52q0 20 11 49t24 45q-1 22 7.5 53t22.5 43q0 139 92.5 288.5t217.5 209.5q139 66 324 66q133 0 266 -55q49 -21 90 -48t71 -56t55 -68t42 -74t32.5 -84.5t25.5 -89.5t22 -98l1 -5q55 -83 55 -150q0 -14 -9 -40t-9 -38q0 -1 1.5 -3.5t3.5 -5t2 -3.5 q77 -114 120.5 -214.5t43.5 -208.5q0 -43 -19.5 -100t-55.5 -57q-9 0 -19.5 7.5t-19 17.5t-19 26t-16 26.5t-13.5 26t-9 17.5q-1 1 -3 1l-5 -4q-59 -154 -132 -223q20 -20 61.5 -38.5t69 -41.5t35.5 -65q-2 -4 -4 -16t-7 -18q-64 -97 -302 -97q-53 0 -110.5 9t-98 20 t-104.5 30q-15 5 -23 7q-14 4 -46 4.5t-40 1.5q-41 -45 -127.5 -65t-168.5 -20q-35 0 -69 1.5t-93 9t-101 20.5t-74.5 40t-32.5 64q0 40 10 59.5t41 48.5q11 2 40.5 13t49.5 12q4 0 14 2q2 2 2 4l-2 3q-48 11 -108 105.5t-73 156.5l-5 3q-4 0 -12 -20q-18 -41 -54.5 -74.5 t-77.5 -37.5h-1q-4 0 -6 4.5t-5 5.5q-23 54 -23 100q0 275 252 466z" />
 <glyph unicode="&#xf1d7;" horiz-adv-x="2048" d="M580 1075q0 41 -25 66t-66 25q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 66 24.5t25 65.5zM1323 568q0 28 -25.5 50t-65.5 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q40 0 65.5 22t25.5 51zM1087 1075q0 41 -24.5 66t-65.5 25 q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 65.5 24.5t24.5 65.5zM1722 568q0 28 -26 50t-65 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q39 0 65 22t26 51zM1456 965q-31 4 -70 4q-169 0 -311 -77t-223.5 -208.5t-81.5 -287.5 q0 -78 23 -152q-35 -3 -68 -3q-26 0 -50 1.5t-55 6.5t-44.5 7t-54.5 10.5t-50 10.5l-253 -127l72 218q-290 203 -290 490q0 169 97.5 311t264 223.5t363.5 81.5q176 0 332.5 -66t262 -182.5t136.5 -260.5zM2048 404q0 -117 -68.5 -223.5t-185.5 -193.5l55 -181l-199 109 q-150 -37 -218 -37q-169 0 -311 70.5t-223.5 191.5t-81.5 264t81.5 264t223.5 191.5t311 70.5q161 0 303 -70.5t227.5 -192t85.5 -263.5z" />
 <glyph unicode="&#xf1ea;" horiz-adv-x="2048" d="M1024 1024h-384v-384h384v384zM1152 384v-128h-640v128h640zM1152 1152v-640h-640v640h640zM1792 384v-128h-512v128h512zM1792 640v-128h-512v128h512zM1792 896v-128h-512v128h512zM1792 1152v-128h-512v128h512zM256 192v960h-128v-960q0 -26 19 -45t45 -19t45 19 t19 45zM1920 192v1088h-1536v-1088q0 -33 -11 -64h1483q26 0 45 19t19 45zM2048 1408v-1216q0 -80 -56 -136t-136 -56h-1664q-80 0 -136 56t-56 136v1088h256v128h1792z" />
 <glyph unicode="&#xf1eb;" horiz-adv-x="2048" d="M1024 13q-20 0 -93 73.5t-73 93.5q0 32 62.5 54t103.5 22t103.5 -22t62.5 -54q0 -20 -73 -93.5t-93 -73.5zM1294 284q-2 0 -40 25t-101.5 50t-128.5 25t-128.5 -25t-101 -50t-40.5 -25q-18 0 -93.5 75t-75.5 93q0 13 10 23q78 77 196 121t233 44t233 -44t196 -121 q10 -10 10 -23q0 -18 -75.5 -93t-93.5 -75zM1567 556q-11 0 -23 8q-136 105 -252 154.5t-268 49.5q-85 0 -170.5 -22t-149 -53t-113.5 -62t-79 -53t-31 -22q-17 0 -92 75t-75 93q0 12 10 22q132 132 320 205t380 73t380 -73t320 -205q10 -10 10 -22q0 -18 -75 -93t-92 -75z M1838 827q-11 0 -22 9q-179 157 -371.5 236.5t-420.5 79.5t-420.5 -79.5t-371.5 -236.5q-11 -9 -22 -9q-17 0 -92.5 75t-75.5 93q0 13 10 23q187 186 445 288t527 102t527 -102t445 -288q10 -10 10 -23q0 -18 -75.5 -93t-92.5 -75z" />
 <glyph unicode="&#xf1ec;" horiz-adv-x="1792" d="M384 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5 t37.5 90.5zM384 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 768q0 53 -37.5 90.5t-90.5 37.5 t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1536 0v384q0 52 -38 90t-90 38t-90 -38t-38 -90v-384q0 -52 38 -90t90 -38t90 38t38 90zM1152 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5z M1536 1088v256q0 26 -19 45t-45 19h-1280q-26 0 -45 -19t-19 -45v-256q0 -26 19 -45t45 -19h1280q26 0 45 19t19 45zM1536 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1408v-1536q0 -52 -38 -90t-90 -38 h-1408q-52 0 -90 38t-38 90v1536q0 52 38 90t90 38h1408q52 0 90 -38t38 -90z" />
-<glyph unicode="&#xf1ed;" horiz-adv-x="1792" d="M1112 1090q0 159 -237 159h-70q-32 0 -59.5 -21.5t-34.5 -52.5l-63 -276q-2 -5 -2 -16q0 -24 17 -39.5t41 -15.5h53q69 0 128.5 13t112.5 41t83.5 81.5t30.5 126.5zM1716 938q0 -265 -220 -428q-219 -161 -612 -161h-61q-32 0 -59 -21.5t-34 -52.5l-73 -316 q-8 -36 -40.5 -61.5t-69.5 -25.5h-213q-31 0 -53 20t-22 51q0 10 13 65h151q34 0 64 23.5t38 56.5l73 316q8 33 37.5 57t63.5 24h61q390 0 607 160t217 421q0 129 -51 207q183 -92 183 -335zM1533 1123q0 -264 -221 -428q-218 -161 -612 -161h-60q-32 0 -59.5 -22t-34.5 -53 l-73 -315q-8 -36 -40 -61.5t-69 -25.5h-214q-31 0 -52.5 19.5t-21.5 51.5q0 8 2 20l300 1301q8 36 40.5 61.5t69.5 25.5h444q68 0 125 -4t120.5 -15t113.5 -30t96.5 -50.5t77.5 -74t49.5 -103.5t18.5 -136z" />
+<glyph unicode="&#xf1ed;" d="M1519 890q18 -84 -4 -204q-87 -444 -565 -444h-44q-25 0 -44 -16.5t-24 -42.5l-4 -19l-55 -346l-2 -15q-5 -26 -24.5 -42.5t-44.5 -16.5h-251q-21 0 -33 15t-9 36q9 56 26.5 168t26.5 168t27 167.5t27 167.5q5 37 43 37h131q133 -2 236 21q175 39 287 144q102 95 155 246 q24 70 35 133q1 6 2.5 7.5t3.5 1t6 -3.5q79 -59 98 -162zM1347 1172q0 -107 -46 -236q-80 -233 -302 -315q-113 -40 -252 -42q0 -1 -90 -1l-90 1q-100 0 -118 -96q-2 -8 -85 -530q-1 -10 -12 -10h-295q-22 0 -36.5 16.5t-11.5 38.5l232 1471q5 29 27.5 48t51.5 19h598 q34 0 97.5 -13t111.5 -32q107 -41 163.5 -123t56.5 -196z" />
 <glyph unicode="&#xf1ee;" horiz-adv-x="1792" d="M602 949q19 -61 31 -123.5t17 -141.5t-14 -159t-62 -145q-21 81 -67 157t-95.5 127t-99 90.5t-78.5 57.5t-33 19q-62 34 -81.5 100t14.5 128t101 81.5t129 -14.5q138 -83 238 -177zM927 1236q11 -25 20.5 -46t36.5 -100.5t42.5 -150.5t25.5 -179.5t0 -205.5t-47.5 -209.5 t-105.5 -208.5q-51 -72 -138 -72q-54 0 -98 31q-57 40 -69 109t28 127q60 85 81 195t13 199.5t-32 180.5t-39 128t-22 52q-31 63 -8.5 129.5t85.5 97.5q34 17 75 17q47 0 88.5 -25t63.5 -69zM1248 567q-17 -160 -72 -311q-17 131 -63 246q25 174 -5 361q-27 178 -94 342 q114 -90 212 -211q9 -37 15 -80q26 -179 7 -347zM1520 1440q9 -17 23.5 -49.5t43.5 -117.5t50.5 -178t34 -227.5t5 -269t-47 -300t-112.5 -323.5q-22 -48 -66 -75.5t-95 -27.5q-39 0 -74 16q-67 31 -92.5 100t4.5 136q58 126 90 257.5t37.5 239.5t-3.5 213.5t-26.5 180.5 t-38.5 138.5t-32.5 90t-15.5 32.5q-34 65 -11.5 135.5t87.5 104.5q37 20 81 20q49 0 91.5 -25.5t66.5 -70.5z" />
 <glyph unicode="&#xf1f0;" horiz-adv-x="2304" d="M1975 546h-138q14 37 66 179l3 9q4 10 10 26t9 26l12 -55zM531 611l-58 295q-11 54 -75 54h-268l-2 -13q311 -79 403 -336zM710 960l-162 -438l-17 89q-26 70 -85 129.5t-131 88.5l135 -510h175l261 641h-176zM849 318h166l104 642h-166zM1617 944q-69 27 -149 27 q-123 0 -201 -59t-79 -153q-1 -102 145 -174q48 -23 67 -41t19 -39q0 -30 -30 -46t-69 -16q-86 0 -156 33l-22 11l-23 -144q74 -34 185 -34q130 -1 208.5 59t80.5 160q0 106 -140 174q-49 25 -71 42t-22 38q0 22 24.5 38.5t70.5 16.5q70 1 124 -24l15 -8zM2042 960h-128 q-65 0 -87 -54l-246 -588h174l35 96h212q5 -22 20 -96h154zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
 <glyph unicode="&#xf1f1;" horiz-adv-x="2304" d="M671 603h-13q-47 0 -47 -32q0 -22 20 -22q17 0 28 15t12 39zM1066 639h62v3q1 4 0.5 6.5t-1 7t-2 8t-4.5 6.5t-7.5 5t-11.5 2q-28 0 -36 -38zM1606 603h-12q-48 0 -48 -32q0 -22 20 -22q17 0 28 15t12 39zM1925 629q0 41 -30 41q-19 0 -31 -20t-12 -51q0 -42 28 -42 q20 0 32.5 20t12.5 52zM480 770h87l-44 -262h-56l32 201l-71 -201h-39l-4 200l-34 -200h-53l44 262h81l2 -163zM733 663q0 -6 -4 -42q-16 -101 -17 -113h-47l1 22q-20 -26 -58 -26q-23 0 -37.5 16t-14.5 42q0 39 26 60.5t73 21.5q14 0 23 -1q0 3 0.5 5.5t1 4.5t0.5 3 q0 20 -36 20q-29 0 -59 -10q0 4 7 48q38 11 67 11q74 0 74 -62zM889 721l-8 -49q-22 3 -41 3q-27 0 -27 -17q0 -8 4.5 -12t21.5 -11q40 -19 40 -60q0 -72 -87 -71q-34 0 -58 6q0 2 7 49q29 -8 51 -8q32 0 32 19q0 7 -4.5 11.5t-21.5 12.5q-43 20 -43 59q0 72 84 72 q30 0 50 -4zM977 721h28l-7 -52h-29q-2 -17 -6.5 -40.5t-7 -38.5t-2.5 -18q0 -16 19 -16q8 0 16 2l-8 -47q-21 -7 -40 -7q-43 0 -45 47q0 12 8 56q3 20 25 146h55zM1180 648q0 -23 -7 -52h-111q-3 -22 10 -33t38 -11q30 0 58 14l-9 -54q-30 -8 -57 -8q-95 0 -95 95 q0 55 27.5 90.5t69.5 35.5q35 0 55.5 -21t20.5 -56zM1319 722q-13 -23 -22 -62q-22 2 -31 -24t-25 -128h-56l3 14q22 130 29 199h51l-3 -33q14 21 25.5 29.5t28.5 4.5zM1506 763l-9 -57q-28 14 -50 14q-31 0 -51 -27.5t-20 -70.5q0 -30 13.5 -47t38.5 -17q21 0 48 13 l-10 -59q-28 -8 -50 -8q-45 0 -71.5 30.5t-26.5 82.5q0 70 35.5 114.5t91.5 44.5q26 0 61 -13zM1668 663q0 -18 -4 -42q-13 -79 -17 -113h-46l1 22q-20 -26 -59 -26q-23 0 -37 16t-14 42q0 39 25.5 60.5t72.5 21.5q15 0 23 -1q2 7 2 13q0 20 -36 20q-29 0 -59 -10q0 4 8 48 q38 11 67 11q73 0 73 -62zM1809 722q-14 -24 -21 -62q-23 2 -31.5 -23t-25.5 -129h-56l3 14q19 104 29 199h52q0 -11 -4 -33q15 21 26.5 29.5t27.5 4.5zM1950 770h56l-43 -262h-53l3 19q-23 -23 -52 -23q-31 0 -49.5 24t-18.5 64q0 53 27.5 92t64.5 39q31 0 53 -29z M2061 640q0 148 -72.5 273t-198 198t-273.5 73q-181 0 -328 -110q127 -116 171 -284h-50q-44 150 -158 253q-114 -103 -158 -253h-50q44 168 171 284q-147 110 -328 110q-148 0 -273.5 -73t-198 -198t-72.5 -273t72.5 -273t198 -198t273.5 -73q181 0 328 110 q-120 111 -165 264h50q46 -138 152 -233q106 95 152 233h50q-45 -153 -165 -264q147 -110 328 -110q148 0 273.5 73t198 198t72.5 273zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
 <glyph unicode="&#xf1f2;" horiz-adv-x="2304" d="M313 759q0 -51 -36 -84q-29 -26 -89 -26h-17v220h17q61 0 89 -27q36 -31 36 -83zM2089 824q0 -52 -64 -52h-19v101h20q63 0 63 -49zM380 759q0 74 -50 120.5t-129 46.5h-95v-333h95q74 0 119 38q60 51 60 128zM410 593h65v333h-65v-333zM730 694q0 40 -20.5 62t-75.5 42 q-29 10 -39.5 19t-10.5 23q0 16 13.5 26.5t34.5 10.5q29 0 53 -27l34 44q-41 37 -98 37q-44 0 -74 -27.5t-30 -67.5q0 -35 18 -55.5t64 -36.5q37 -13 45 -19q19 -12 19 -34q0 -20 -14 -33.5t-36 -13.5q-48 0 -71 44l-42 -40q44 -64 115 -64q51 0 83 30.5t32 79.5zM1008 604 v77q-37 -37 -78 -37q-49 0 -80.5 32.5t-31.5 82.5q0 48 31.5 81.5t77.5 33.5q43 0 81 -38v77q-40 20 -80 20q-74 0 -125.5 -50.5t-51.5 -123.5t51 -123.5t125 -50.5q42 0 81 19zM2240 0v527q-65 -40 -144.5 -84t-237.5 -117t-329.5 -137.5t-417.5 -134.5t-504 -118h1569 q26 0 45 19t19 45zM1389 757q0 75 -53 128t-128 53t-128 -53t-53 -128t53 -128t128 -53t128 53t53 128zM1541 584l144 342h-71l-90 -224l-89 224h-71l142 -342h35zM1714 593h184v56h-119v90h115v56h-115v74h119v57h-184v-333zM2105 593h80l-105 140q76 16 76 94q0 47 -31 73 t-87 26h-97v-333h65v133h9zM2304 1274v-1268q0 -56 -38.5 -95t-93.5 -39h-2040q-55 0 -93.5 39t-38.5 95v1268q0 56 38.5 95t93.5 39h2040q55 0 93.5 -39t38.5 -95z" />
 <glyph unicode="&#xf1f3;" horiz-adv-x="2304" d="M119 854h89l-45 108zM740 328l74 79l-70 79h-163v-49h142v-55h-142v-54h159zM898 406l99 -110v217zM1186 453q0 33 -40 33h-84v-69h83q41 0 41 36zM1475 457q0 29 -42 29h-82v-61h81q43 0 43 32zM1197 923q0 29 -42 29h-82v-60h81q43 0 43 31zM1656 854h89l-44 108z M699 1009v-271h-66v212l-94 -212h-57l-94 212v-212h-132l-25 60h-135l-25 -60h-70l116 271h96l110 -257v257h106l85 -184l77 184h108zM1255 453q0 -20 -5.5 -35t-14 -25t-22.5 -16.5t-26 -10t-31.5 -4.5t-31.5 -1t-32.5 0.5t-29.5 0.5v-91h-126l-80 90l-83 -90h-256v271h260 l80 -89l82 89h207q109 0 109 -89zM964 794v-56h-217v271h217v-57h-152v-49h148v-55h-148v-54h152zM2304 235v-229q0 -55 -38.5 -94.5t-93.5 -39.5h-2040q-55 0 -93.5 39.5t-38.5 94.5v678h111l25 61h55l25 -61h218v46l19 -46h113l20 47v-47h541v99l10 1q10 0 10 -14v-86h279 v23q23 -12 55 -18t52.5 -6.5t63 0.5t51.5 1l25 61h56l25 -61h227v58l34 -58h182v378h-180v-44l-25 44h-185v-44l-23 44h-249q-69 0 -109 -22v22h-172v-22q-24 22 -73 22h-628l-43 -97l-43 97h-198v-44l-22 44h-169l-78 -179v391q0 55 38.5 94.5t93.5 39.5h2040 q55 0 93.5 -39.5t38.5 -94.5v-678h-120q-51 0 -81 -22v22h-177q-55 0 -78 -22v22h-316v-22q-31 22 -87 22h-209v-22q-23 22 -91 22h-234l-54 -58l-50 58h-349v-378h343l55 59l52 -59h211v89h21q59 0 90 13v-102h174v99h8q8 0 10 -2t2 -10v-87h529q57 0 88 24v-24h168 q60 0 95 17zM1546 469q0 -23 -12 -43t-34 -29q25 -9 34 -26t9 -46v-54h-65v45q0 33 -12 43.5t-46 10.5h-69v-99h-65v271h154q48 0 77 -15t29 -58zM1269 936q0 -24 -12.5 -44t-33.5 -29q26 -9 34.5 -25.5t8.5 -46.5v-53h-65q0 9 0.5 26.5t0 25t-3 18.5t-8.5 16t-17.5 8.5 t-29.5 3.5h-70v-98h-64v271l153 -1q49 0 78 -14.5t29 -57.5zM1798 327v-56h-216v271h216v-56h-151v-49h148v-55h-148v-54zM1372 1009v-271h-66v271h66zM2065 357q0 -86 -102 -86h-126v58h126q34 0 34 25q0 16 -17 21t-41.5 5t-49.5 3.5t-42 22.5t-17 55q0 39 26 60t66 21 h130v-57h-119q-36 0 -36 -25q0 -16 17.5 -20.5t42 -4t49 -2.5t42 -21.5t17.5 -54.5zM2304 407v-101q-24 -35 -88 -35h-125v58h125q33 0 33 25q0 13 -12.5 19t-31 5.5t-40 2t-40 8t-31 24t-12.5 48.5q0 39 26.5 60t66.5 21h129v-57h-118q-36 0 -36 -25q0 -20 29 -22t68.5 -5 t56.5 -26zM2139 1008v-270h-92l-122 203v-203h-132l-26 60h-134l-25 -60h-75q-129 0 -129 133q0 138 133 138h63v-59q-7 0 -28 1t-28.5 0.5t-23 -2t-21.5 -6.5t-14.5 -13.5t-11.5 -23t-3 -33.5q0 -38 13.5 -58t49.5 -20h29l92 213h97l109 -256v256h99l114 -188v188h66z" />
-<glyph unicode="&#xf1f4;" horiz-adv-x="2304" d="M322 689h-15q-19 0 -19 18q0 28 19 85q5 15 15 19.5t28 4.5q77 0 77 -49q0 -41 -30.5 -59.5t-74.5 -18.5zM664 528q-47 0 -47 29q0 62 123 62l3 -3q-5 -88 -79 -88zM1438 687h-15q-19 0 -19 19q0 28 19 85q5 15 14.5 19t28.5 4q77 0 77 -49q0 -41 -30.5 -59.5 t-74.5 -18.5zM1780 527q-47 0 -47 30q0 62 123 62l3 -3q-5 -89 -79 -89zM373 894h-128q-8 0 -14.5 -4t-8.5 -7.5t-7 -12.5q-3 -7 -45 -190t-42 -192q0 -7 5.5 -12.5t13.5 -5.5h62q25 0 32.5 34.5l15 69t32.5 34.5q47 0 87.5 7.5t80.5 24.5t63.5 52.5t23.5 84.5 q0 36 -14.5 61t-41 36.5t-53.5 15.5t-62 4zM719 798q-38 0 -74 -6q-2 0 -8.5 -1t-9 -1.5l-7.5 -1.5t-7.5 -2t-6.5 -3t-6.5 -4t-5 -5t-4.5 -7t-4 -9q-9 -29 -9 -39t9 -10q5 0 21.5 5t19.5 6q30 8 58 8q74 0 74 -36q0 -11 -10 -14q-8 -2 -18 -3t-21.5 -1.5t-17.5 -1.5 q-38 -4 -64.5 -10t-56.5 -19.5t-45.5 -39t-15.5 -62.5q0 -38 26 -59.5t64 -21.5q24 0 45.5 6.5t33 13t38.5 23.5q-3 -7 -3 -15t5.5 -13.5t12.5 -5.5h56q1 1 7 3.5t7.5 3.5t5 3.5t5 5.5t2.5 8l45 194q4 13 4 30q0 81 -145 81zM1247 793h-74q-22 0 -39 -23q-5 -7 -29.5 -51 t-46.5 -81.5t-26 -38.5l-5 4q0 77 -27 166q-1 5 -3.5 8.5t-6 6.5t-6.5 5t-8.5 3t-8.5 1.5t-9.5 1t-9 0.5h-10h-8.5q-38 0 -38 -21l1 -5q5 -53 25 -151t25 -143q2 -16 2 -24q0 -19 -30.5 -61.5t-30.5 -58.5q0 -13 40 -13q61 0 76 25l245 415q10 20 10 26q0 9 -8 9zM1489 892 h-129q-18 0 -29 -23q-6 -13 -46.5 -191.5t-40.5 -190.5q0 -20 43 -20h7.5h9h9t9.5 1t8.5 2t8.5 3t6.5 4.5t5.5 6t3 8.5l21 91q2 10 10.5 17t19.5 7q47 0 87.5 7t80.5 24.5t63.5 52.5t23.5 84q0 36 -14.5 61t-41 36.5t-53.5 15.5t-62 4zM1835 798q-26 0 -74 -6 q-38 -6 -48 -16q-7 -8 -11 -19q-8 -24 -8 -39q0 -10 8 -10q1 0 41 12q30 8 58 8q74 0 74 -36q0 -12 -10 -14q-4 -1 -57 -7q-38 -4 -64.5 -10t-56.5 -19.5t-45.5 -39t-15.5 -62.5t26 -58.5t64 -21.5q24 0 45 6t34 13t38 24q-3 -15 -3 -16q0 -5 2 -8.5t6.5 -5.5t8 -3.5 t10.5 -2t9.5 -0.5h9.5h8q42 0 48 25l45 194q3 15 3 31q0 81 -145 81zM2157 889h-55q-25 0 -33 -40q-10 -44 -36.5 -167t-42.5 -190v-5q0 -16 16 -18h1h57q10 0 18.5 6.5t10.5 16.5l83 374h-1l1 5q0 7 -5.5 12.5t-13.5 5.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048 q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf1f4;" horiz-adv-x="2304" d="M745 630q0 -37 -25.5 -61.5t-62.5 -24.5q-29 0 -46.5 16t-17.5 44q0 37 25 62.5t62 25.5q28 0 46.5 -16.5t18.5 -45.5zM1530 779q0 -42 -22 -57t-66 -15l-32 -1l17 107q2 11 13 11h18q22 0 35 -2t25 -12.5t12 -30.5zM1881 630q0 -36 -25.5 -61t-61.5 -25q-29 0 -47 16 t-18 44q0 37 25 62.5t62 25.5q28 0 46.5 -16.5t18.5 -45.5zM513 801q0 59 -38.5 85.5t-100.5 26.5h-160q-19 0 -21 -19l-65 -408q-1 -6 3 -11t10 -5h76q20 0 22 19l18 110q1 8 7 13t15 6.5t17 1.5t19 -1t14 -1q86 0 135 48.5t49 134.5zM822 489l41 261q1 6 -3 11t-10 5h-76 q-14 0 -17 -33q-27 40 -95 40q-72 0 -122.5 -54t-50.5 -127q0 -59 34.5 -94t92.5 -35q28 0 58 12t48 32q-4 -12 -4 -21q0 -16 13 -16h69q19 0 22 19zM1269 752q0 5 -4 9.5t-9 4.5h-77q-11 0 -18 -10l-106 -156l-44 150q-5 16 -22 16h-75q-5 0 -9 -4.5t-4 -9.5q0 -2 19.5 -59 t42 -123t23.5 -70q-82 -112 -82 -120q0 -13 13 -13h77q11 0 18 10l255 368q2 2 2 7zM1649 801q0 59 -38.5 85.5t-100.5 26.5h-159q-20 0 -22 -19l-65 -408q-1 -6 3 -11t10 -5h82q12 0 16 13l18 116q1 8 7 13t15 6.5t17 1.5t19 -1t14 -1q86 0 135 48.5t49 134.5zM1958 489 l41 261q1 6 -3 11t-10 5h-76q-14 0 -17 -33q-26 40 -95 40q-72 0 -122.5 -54t-50.5 -127q0 -59 34.5 -94t92.5 -35q29 0 59 12t47 32q0 -1 -2 -9t-2 -12q0 -16 13 -16h69q19 0 22 19zM2176 898v1q0 14 -13 14h-74q-11 0 -13 -11l-65 -416l-1 -2q0 -5 4 -9.5t10 -4.5h66 q19 0 21 19zM392 764q-5 -35 -26 -46t-60 -11l-33 -1l17 107q2 11 13 11h19q40 0 58 -11.5t12 -48.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
 <glyph unicode="&#xf1f5;" horiz-adv-x="2304" d="M1597 633q0 -69 -21 -106q-19 -35 -52 -35q-23 0 -41 9v224q29 30 57 30q57 0 57 -122zM2035 669h-110q6 98 56 98q51 0 54 -98zM476 534q0 59 -33 91.5t-101 57.5q-36 13 -52 24t-16 25q0 26 38 26q58 0 124 -33l18 112q-67 32 -149 32q-77 0 -123 -38q-48 -39 -48 -109 q0 -58 32.5 -90.5t99.5 -56.5q39 -14 54.5 -25.5t15.5 -27.5q0 -31 -48 -31q-29 0 -70 12.5t-72 30.5l-18 -113q72 -41 168 -41q81 0 129 37q51 41 51 117zM771 749l19 111h-96v135l-129 -21l-18 -114l-46 -8l-17 -103h62v-219q0 -84 44 -120q38 -30 111 -30q32 0 79 11v118 q-32 -7 -44 -7q-42 0 -42 50v197h77zM1087 724v139q-15 3 -28 3q-32 0 -55.5 -16t-33.5 -46l-10 56h-131v-471h150v306q26 31 82 31q16 0 26 -2zM1124 389h150v471h-150v-471zM1746 638q0 122 -45 179q-40 52 -111 52q-64 0 -117 -56l-8 47h-132v-645l150 25v151 q36 -11 68 -11q83 0 134 56q61 65 61 202zM1278 986q0 33 -23 56t-56 23t-56 -23t-23 -56t23 -56.5t56 -23.5t56 23.5t23 56.5zM2176 629q0 113 -48 176q-50 64 -144 64q-96 0 -151.5 -66t-55.5 -180q0 -128 63 -188q55 -55 161 -55q101 0 160 40l-16 103q-57 -31 -128 -31 q-43 0 -63 19q-23 19 -28 66h248q2 14 2 52zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
 <glyph unicode="&#xf1f6;" horiz-adv-x="2048" d="M1558 684q61 -356 298 -556q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5zM1024 -176q16 0 16 16t-16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5zM2026 1424q8 -10 7.5 -23.5t-10.5 -22.5 l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5 l418 363q10 8 23.5 7t21.5 -11z" />
 <glyph unicode="&#xf1f7;" horiz-adv-x="2048" d="M1040 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM503 315l877 760q-42 88 -132.5 146.5t-223.5 58.5q-93 0 -169.5 -31.5t-121.5 -80.5t-69 -103t-24 -105q0 -384 -137 -645zM1856 128 q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5l149 129h757q-166 187 -227 459l111 97q61 -356 298 -556zM1942 1520l84 -96q8 -10 7.5 -23.5t-10.5 -22.5l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161 q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5l418 363q10 8 23.5 7t21.5 -11z" />
 <glyph unicode="&#xf20a;" horiz-adv-x="2048" d="M785 528h207q-14 -158 -98.5 -248.5t-214.5 -90.5q-162 0 -254.5 116t-92.5 316q0 194 93 311.5t233 117.5q148 0 232 -87t97 -247h-203q-5 64 -35.5 99t-81.5 35q-57 0 -88.5 -60.5t-31.5 -177.5q0 -48 5 -84t18 -69.5t40 -51.5t66 -18q95 0 109 139zM1497 528h206 q-14 -158 -98 -248.5t-214 -90.5q-162 0 -254.5 116t-92.5 316q0 194 93 311.5t233 117.5q148 0 232 -87t97 -247h-204q-4 64 -35 99t-81 35q-57 0 -88.5 -60.5t-31.5 -177.5q0 -48 5 -84t18 -69.5t39.5 -51.5t65.5 -18q49 0 76.5 38t33.5 101zM1856 647q0 207 -15.5 307 t-60.5 161q-6 8 -13.5 14t-21.5 15t-16 11q-86 63 -697 63q-625 0 -710 -63q-5 -4 -17.5 -11.5t-21 -14t-14.5 -14.5q-45 -60 -60 -159.5t-15 -308.5q0 -208 15 -307.5t60 -160.5q6 -8 15 -15t20.5 -14t17.5 -12q44 -33 239.5 -49t470.5 -16q610 0 697 65q5 4 17 11t20.5 14 t13.5 16q46 60 61 159t15 309zM2048 1408v-1536h-2048v1536h2048z" />
 <glyph unicode="&#xf20b;" d="M992 912v-496q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v496q0 112 -80 192t-192 80h-272v-1152q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v1344q0 14 9 23t23 9h464q135 0 249 -66.5t180.5 -180.5t66.5 -249zM1376 1376v-880q0 -135 -66.5 -249t-180.5 -180.5 t-249 -66.5h-464q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h160q14 0 23 -9t9 -23v-768h272q112 0 192 80t80 192v880q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" />
 <glyph unicode="&#xf20c;" d="M1311 694v-114q0 -24 -13.5 -38t-37.5 -14h-202q-24 0 -38 14t-14 38v114q0 24 14 38t38 14h202q24 0 37.5 -14t13.5 -38zM821 464v250q0 53 -32.5 85.5t-85.5 32.5h-133q-68 0 -96 -52q-28 52 -96 52h-130q-53 0 -85.5 -32.5t-32.5 -85.5v-250q0 -22 21 -22h55 q22 0 22 22v230q0 24 13.5 38t38.5 14h94q24 0 38 -14t14 -38v-230q0 -22 21 -22h54q22 0 22 22v230q0 24 14 38t38 14h97q24 0 37.5 -14t13.5 -38v-230q0 -22 22 -22h55q21 0 21 22zM1410 560v154q0 53 -33 85.5t-86 32.5h-264q-53 0 -86 -32.5t-33 -85.5v-410 q0 -21 22 -21h55q21 0 21 21v180q31 -42 94 -42h191q53 0 86 32.5t33 85.5zM1536 1176v-1072q0 -96 -68 -164t-164 -68h-1072q-96 0 -164 68t-68 164v1072q0 96 68 164t164 68h1072q96 0 164 -68t68 -164z" />
-<glyph unicode="&#xf20d;" horiz-adv-x="1792" />
-<glyph unicode="&#xf20e;" horiz-adv-x="1792" />
+<glyph unicode="&#xf20d;" d="M915 450h-294l147 551zM1001 128h311l-324 1024h-440l-324 -1024h311l383 314zM1536 1120v-960q0 -118 -85 -203t-203 -85h-960q-118 0 -203 85t-85 203v960q0 118 85 203t203 85h960q118 0 203 -85t85 -203z" />
+<glyph unicode="&#xf20e;" horiz-adv-x="2048" d="M2048 641q0 -21 -13 -36.5t-33 -19.5l-205 -356q3 -9 3 -18q0 -20 -12.5 -35.5t-32.5 -19.5l-193 -337q3 -8 3 -16q0 -23 -16.5 -40t-40.5 -17q-25 0 -41 18h-400q-17 -20 -43 -20t-43 20h-399q-17 -20 -43 -20q-23 0 -40 16.5t-17 40.5q0 8 4 20l-193 335 q-20 4 -32.5 19.5t-12.5 35.5q0 9 3 18l-206 356q-20 5 -32.5 20.5t-12.5 35.5q0 21 13.5 36.5t33.5 19.5l199 344q0 1 -0.5 3t-0.5 3q0 36 34 51l209 363q-4 10 -4 18q0 24 17 40.5t40 16.5q26 0 44 -21h396q16 21 43 21t43 -21h398q18 21 44 21q23 0 40 -16.5t17 -40.5 q0 -6 -4 -18l207 -358q23 -1 39 -17.5t16 -38.5q0 -13 -7 -27l187 -324q19 -4 31.5 -19.5t12.5 -35.5zM1063 -158h389l-342 354h-143l-342 -354h360q18 16 39 16t39 -16zM112 654q1 -4 1 -13q0 -10 -2 -15l208 -360q2 0 4.5 -1t5.5 -2.5l5 -2.5l188 199v347l-187 194 q-13 -8 -29 -10zM986 1438h-388l190 -200l554 200h-280q-16 -16 -38 -16t-38 16zM1689 226q1 6 5 11l-64 68l-17 -79h76zM1583 226l22 105l-252 266l-296 -307l63 -64h463zM1495 -142l16 28l65 310h-427l333 -343q8 4 13 5zM578 -158h5l342 354h-373v-335l4 -6q14 -5 22 -13 zM552 226h402l64 66l-309 321l-157 -166v-221zM359 226h163v189l-168 -177q4 -8 5 -12zM358 1051q0 -1 0.5 -2t0.5 -2q0 -16 -8 -29l171 -177v269zM552 1121v-311l153 -157l297 314l-223 236zM556 1425l-4 -8v-264l205 74l-191 201q-6 -2 -10 -3zM1447 1438h-16l-621 -224 l213 -225zM1023 946l-297 -315l311 -319l296 307zM688 634l-136 141v-284zM1038 270l-42 -44h85zM1374 618l238 -251l132 624l-3 5l-1 1zM1718 1018q-8 13 -8 29v2l-216 376q-5 1 -13 5l-437 -463l310 -327zM522 1142v223l-163 -282zM522 196h-163l163 -283v283zM1607 196 l-48 -227l130 227h-82zM1729 266l207 361q-2 10 -2 14q0 1 3 16l-171 296l-129 -612l77 -82q5 3 15 7z" />
+<glyph unicode="&#xf210;" d="M0 856q0 131 91.5 226.5t222.5 95.5h742l352 358v-1470q0 -132 -91.5 -227t-222.5 -95h-780q-131 0 -222.5 95t-91.5 227v790zM1232 102l-176 180v425q0 46 -32 79t-78 33h-484q-46 0 -78 -33t-32 -79v-492q0 -46 32.5 -79.5t77.5 -33.5h770z" />
+<glyph unicode="&#xf211;" d="M934 1386q-317 -121 -556 -362.5t-358 -560.5q-20 89 -20 176q0 208 102.5 384.5t278.5 279t384 102.5q82 0 169 -19zM1203 1267q93 -65 164 -155q-389 -113 -674.5 -400.5t-396.5 -676.5q-93 72 -155 162q112 386 395 671t667 399zM470 -67q115 356 379.5 622t619.5 384 q40 -92 54 -195q-292 -120 -516 -345t-343 -518q-103 14 -194 52zM1536 -125q-193 50 -367 115q-135 -84 -290 -107q109 205 274 370.5t369 275.5q-21 -152 -101 -284q65 -175 115 -370z" />
+<glyph unicode="&#xf212;" horiz-adv-x="2048" d="M1893 1144l155 -1272q-131 0 -257 57q-200 91 -393 91q-226 0 -374 -148q-148 148 -374 148q-193 0 -393 -91q-128 -57 -252 -57h-5l155 1272q224 127 482 127q233 0 387 -106q154 106 387 106q258 0 482 -127zM1398 157q129 0 232 -28.5t260 -93.5l-124 1021 q-171 78 -368 78q-224 0 -374 -141q-150 141 -374 141q-197 0 -368 -78l-124 -1021q105 43 165.5 65t148.5 39.5t178 17.5q202 0 374 -108q172 108 374 108zM1438 191l-55 907q-211 -4 -359 -155q-152 155 -374 155q-176 0 -336 -66l-114 -941q124 51 228.5 76t221.5 25 q209 0 374 -102q172 107 374 102z" />
+<glyph unicode="&#xf213;" horiz-adv-x="2048" d="M1500 165v733q0 21 -15 36t-35 15h-93q-20 0 -35 -15t-15 -36v-733q0 -20 15 -35t35 -15h93q20 0 35 15t15 35zM1216 165v531q0 20 -15 35t-35 15h-101q-20 0 -35 -15t-15 -35v-531q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM924 165v429q0 20 -15 35t-35 15h-101 q-20 0 -35 -15t-15 -35v-429q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM632 165v362q0 20 -15 35t-35 15h-101q-20 0 -35 -15t-15 -35v-362q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM2048 311q0 -166 -118 -284t-284 -118h-1244q-166 0 -284 118t-118 284 q0 116 63 214.5t168 148.5q-10 34 -10 73q0 113 80.5 193.5t193.5 80.5q102 0 180 -67q45 183 194 300t338 117q149 0 275 -73.5t199.5 -199.5t73.5 -275q0 -66 -14 -122q135 -33 221 -142.5t86 -247.5z" />
+<glyph unicode="&#xf214;" d="M0 1536h1536v-1392l-776 -338l-760 338v1392zM1436 209v926h-1336v-926l661 -294zM1436 1235v201h-1336v-201h1336zM181 937v-115h-37v115h37zM181 789v-115h-37v115h37zM181 641v-115h-37v115h37zM181 493v-115h-37v115h37zM181 345v-115h-37v115h37zM207 202l15 34 l105 -47l-15 -33zM343 142l15 34l105 -46l-15 -34zM478 82l15 34l105 -46l-15 -34zM614 23l15 33l104 -46l-15 -34zM797 10l105 46l15 -33l-105 -47zM932 70l105 46l15 -34l-105 -46zM1068 130l105 46l15 -34l-105 -46zM1203 189l105 47l15 -34l-105 -46zM259 1389v-36h-114 v36h114zM421 1389v-36h-115v36h115zM583 1389v-36h-115v36h115zM744 1389v-36h-114v36h114zM906 1389v-36h-114v36h114zM1068 1389v-36h-115v36h115zM1230 1389v-36h-115v36h115zM1391 1389v-36h-114v36h114zM181 1049v-79h-37v115h115v-36h-78zM421 1085v-36h-115v36h115z M583 1085v-36h-115v36h115zM744 1085v-36h-114v36h114zM906 1085v-36h-114v36h114zM1068 1085v-36h-115v36h115zM1230 1085v-36h-115v36h115zM1355 970v79h-78v36h115v-115h-37zM1355 822v115h37v-115h-37zM1355 674v115h37v-115h-37zM1355 526v115h37v-115h-37zM1355 378 v115h37v-115h-37zM1355 230v115h37v-115h-37zM760 265q-129 0 -221 91.5t-92 221.5q0 129 92 221t221 92q130 0 221.5 -92t91.5 -221q0 -130 -91.5 -221.5t-221.5 -91.5zM595 646q0 -36 19.5 -56.5t49.5 -25t64 -7t64 -2t49.5 -9t19.5 -30.5q0 -49 -112 -49q-97 0 -123 51 h-3l-31 -63q67 -42 162 -42q29 0 56.5 5t55.5 16t45.5 33t17.5 53q0 46 -27.5 69.5t-67.5 27t-79.5 3t-67 5t-27.5 25.5q0 21 20.5 33t40.5 15t41 3q34 0 70.5 -11t51.5 -34h3l30 58q-3 1 -21 8.5t-22.5 9t-19.5 7t-22 7t-20 4.5t-24 4t-23 1q-29 0 -56.5 -5t-54 -16.5 t-43 -34t-16.5 -53.5z" />
+<glyph unicode="&#xf215;" horiz-adv-x="2048" d="M863 504q0 112 -79.5 191.5t-191.5 79.5t-191 -79.5t-79 -191.5t79 -191t191 -79t191.5 79t79.5 191zM1726 505q0 112 -79 191t-191 79t-191.5 -79t-79.5 -191q0 -113 79.5 -192t191.5 -79t191 79.5t79 191.5zM2048 1314v-1348q0 -44 -31.5 -75.5t-76.5 -31.5h-1832 q-45 0 -76.5 31.5t-31.5 75.5v1348q0 44 31.5 75.5t76.5 31.5h431q44 0 76 -31.5t32 -75.5v-161h754v161q0 44 32 75.5t76 31.5h431q45 0 76.5 -31.5t31.5 -75.5z" />
+<glyph unicode="&#xf216;" horiz-adv-x="2048" d="M1430 953zM1690 749q148 0 253 -98.5t105 -244.5q0 -157 -109 -261.5t-267 -104.5q-85 0 -162 27.5t-138 73.5t-118 106t-109 126.5t-103.5 132.5t-108.5 126t-117 106t-136 73.5t-159 27.5q-154 0 -251.5 -91.5t-97.5 -244.5q0 -157 104 -250t263 -93q100 0 208 37.5 t193 98.5q5 4 21 18.5t30 24t22 9.5q14 0 24.5 -10.5t10.5 -24.5q0 -24 -60 -77q-101 -88 -234.5 -142t-260.5 -54q-133 0 -245.5 58t-180 165t-67.5 241q0 205 141.5 341t347.5 136q120 0 226.5 -43.5t185.5 -113t151.5 -153t139 -167.5t133.5 -153.5t149.5 -113 t172.5 -43.5q102 0 168.5 61.5t66.5 162.5q0 95 -64.5 159t-159.5 64q-30 0 -81.5 -18.5t-68.5 -18.5q-20 0 -35.5 15t-15.5 35q0 18 8.5 57t8.5 59q0 159 -107.5 263t-266.5 104q-58 0 -111.5 -18.5t-84 -40.5t-55.5 -40.5t-33 -18.5q-15 0 -25.5 10.5t-10.5 25.5 q0 19 25 46q59 67 147 103.5t182 36.5q191 0 318 -125.5t127 -315.5q0 -37 -4 -66q57 15 115 15z" />
+<glyph unicode="&#xf217;" horiz-adv-x="1664" d="M1216 832q0 26 -19 45t-45 19h-128v128q0 26 -19 45t-45 19t-45 -19t-19 -45v-128h-128q-26 0 -45 -19t-19 -45t19 -45t45 -19h128v-128q0 -26 19 -45t45 -19t45 19t19 45v128h128q26 0 45 19t19 45zM640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920 q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf218;" horiz-adv-x="1664" d="M1280 832q0 26 -19 45t-45 19t-45 -19l-147 -146v293q0 26 -19 45t-45 19t-45 -19t-19 -45v-293l-147 146q-19 19 -45 19t-45 -19t-19 -45t19 -45l256 -256q19 -19 45 -19t45 19l256 256q19 19 19 45zM640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920 q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
+<glyph unicode="&#xf219;" horiz-adv-x="2048" d="M212 768l623 -665l-300 665h-323zM1024 -4l349 772h-698zM538 896l204 384h-262l-288 -384h346zM1213 103l623 665h-323zM683 896h682l-204 384h-274zM1510 896h346l-288 384h-262zM1651 1382l384 -512q14 -18 13 -41.5t-17 -40.5l-960 -1024q-18 -20 -47 -20t-47 20 l-960 1024q-16 17 -17 40.5t13 41.5l384 512q18 26 51 26h1152q33 0 51 -26z" />
+<glyph unicode="&#xf21a;" horiz-adv-x="2048" d="M1811 -19q19 19 45 19t45 -19l128 -128l-90 -90l-83 83l-83 -83q-18 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83 q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-128 128l90 90l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83 q19 19 45 19t45 -19l83 -83zM237 19q-19 -19 -45 -19t-45 19l-128 128l90 90l83 -82l83 82q19 19 45 19t45 -19l83 -82l64 64v293l-210 314q-17 26 -7 56.5t40 40.5l177 58v299h128v128h256v128h256v-128h256v-128h128v-299l177 -58q30 -10 40 -40.5t-7 -56.5l-210 -314 v-293l19 18q19 19 45 19t45 -19l83 -82l83 82q19 19 45 19t45 -19l128 -128l-90 -90l-83 83l-83 -83q-18 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83 q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83zM640 1152v-128l384 128l384 -128v128h-128v128h-512v-128h-128z" />
+<glyph unicode="&#xf21b;" d="M576 0l96 448l-96 128l-128 64zM832 0l128 640l-128 -64l-96 -128zM992 1010q-2 4 -4 6q-10 8 -96 8q-70 0 -167 -19q-7 -2 -21 -2t-21 2q-97 19 -167 19q-86 0 -96 -8q-2 -2 -4 -6q2 -18 4 -27q2 -3 7.5 -6.5t7.5 -10.5q2 -4 7.5 -20.5t7 -20.5t7.5 -17t8.5 -17t9 -14 t12 -13.5t14 -9.5t17.5 -8t20.5 -4t24.5 -2q36 0 59 12.5t32.5 30t14.5 34.5t11.5 29.5t17.5 12.5h12q11 0 17.5 -12.5t11.5 -29.5t14.5 -34.5t32.5 -30t59 -12.5q13 0 24.5 2t20.5 4t17.5 8t14 9.5t12 13.5t9 14t8.5 17t7.5 17t7 20.5t7.5 20.5q2 7 7.5 10.5t7.5 6.5 q2 9 4 27zM1408 131q0 -121 -73 -190t-194 -69h-874q-121 0 -194 69t-73 190q0 61 4.5 118t19 125.5t37.5 123.5t63.5 103.5t93.5 74.5l-90 220h214q-22 64 -22 128q0 12 2 32q-194 40 -194 96q0 57 210 99q17 62 51.5 134t70.5 114q32 37 76 37q30 0 84 -31t84 -31t84 31 t84 31q44 0 76 -37q36 -42 70.5 -114t51.5 -134q210 -42 210 -99q0 -56 -194 -96q7 -81 -20 -160h214l-82 -225q63 -33 107.5 -96.5t65.5 -143.5t29 -151.5t8 -148.5z" />
+<glyph unicode="&#xf21c;" horiz-adv-x="2304" d="M2301 500q12 -103 -22 -198.5t-99 -163.5t-158.5 -106t-196.5 -31q-161 11 -279.5 125t-134.5 274q-12 111 27.5 210.5t118.5 170.5l-71 107q-96 -80 -151 -194t-55 -244q0 -27 -18.5 -46.5t-45.5 -19.5h-256h-69q-23 -164 -149 -274t-294 -110q-185 0 -316.5 131.5 t-131.5 316.5t131.5 316.5t316.5 131.5q76 0 152 -27l24 45q-123 110 -304 110h-64q-26 0 -45 19t-19 45t19 45t45 19h128q78 0 145 -13.5t116.5 -38.5t71.5 -39.5t51 -36.5h512h115l-85 128h-222q-30 0 -49 22.5t-14 52.5q4 23 23 38t43 15h253q33 0 53 -28l70 -105 l114 114q19 19 46 19h101q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-179l115 -172q131 63 275 36q143 -26 244 -134.5t118 -253.5zM448 128q115 0 203 72.5t111 183.5h-314q-35 0 -55 31q-18 32 -1 63l147 277q-47 13 -91 13q-132 0 -226 -94t-94 -226t94 -226 t226 -94zM1856 128q132 0 226 94t94 226t-94 226t-226 94q-60 0 -121 -24l174 -260q15 -23 10 -49t-27 -40q-15 -11 -36 -11q-35 0 -53 29l-174 260q-93 -95 -93 -225q0 -132 94 -226t226 -94z" />
+<glyph unicode="&#xf21d;" d="M1408 0q0 -63 -61.5 -113.5t-164 -81t-225 -46t-253.5 -15.5t-253.5 15.5t-225 46t-164 81t-61.5 113.5q0 49 33 88.5t91 66.5t118 44.5t131 29.5q26 5 48 -10.5t26 -41.5q5 -26 -10.5 -48t-41.5 -26q-58 -10 -106 -23.5t-76.5 -25.5t-48.5 -23.5t-27.5 -19.5t-8.5 -12 q3 -11 27 -26.5t73 -33t114 -32.5t160.5 -25t201.5 -10t201.5 10t160.5 25t114 33t73 33.5t27 27.5q-1 4 -8.5 11t-27.5 19t-48.5 23.5t-76.5 25t-106 23.5q-26 4 -41.5 26t-10.5 48q4 26 26 41.5t48 10.5q71 -12 131 -29.5t118 -44.5t91 -66.5t33 -88.5zM1024 896v-384 q0 -26 -19 -45t-45 -19h-64v-384q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v384h-64q-26 0 -45 19t-19 45v384q0 53 37.5 90.5t90.5 37.5h384q53 0 90.5 -37.5t37.5 -90.5zM928 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5 t158.5 -65.5t65.5 -158.5z" />
+<glyph unicode="&#xf21e;" horiz-adv-x="1792" d="M1280 512h305q-5 -6 -10 -10.5t-9 -7.5l-3 -4l-623 -600q-18 -18 -44 -18t-44 18l-624 602q-5 2 -21 20h369q22 0 39.5 13.5t22.5 34.5l70 281l190 -667q6 -20 23 -33t39 -13q21 0 38 13t23 33l146 485l56 -112q18 -35 57 -35zM1792 940q0 -145 -103 -300h-369l-111 221 q-8 17 -25.5 27t-36.5 8q-45 -5 -56 -46l-129 -430l-196 686q-6 20 -23.5 33t-39.5 13t-39 -13.5t-22 -34.5l-116 -464h-423q-103 155 -103 300q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5q224 0 351 -124 t127 -344z" />
+<glyph unicode="&#xf221;" horiz-adv-x="1280" d="M1152 960q0 -221 -147.5 -384.5t-364.5 -187.5v-260h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v260q-150 16 -271.5 103t-186 224t-52.5 292 q11 134 80.5 249t182 188t245.5 88q170 19 319 -54t236 -212t87 -306zM128 960q0 -185 131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5z" />
+<glyph unicode="&#xf222;" d="M1472 1408q26 0 45 -19t19 -45v-416q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v262l-382 -383q126 -156 126 -359q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123t223.5 45.5 q203 0 359 -126l382 382h-261q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h416zM576 0q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf223;" horiz-adv-x="1280" d="M830 1220q145 -72 233.5 -210.5t88.5 -305.5q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-217 24 -364.5 187.5 t-147.5 384.5q0 167 88.5 305.5t233.5 210.5q-165 96 -228 273q-6 16 3.5 29.5t26.5 13.5h69q21 0 29 -20q44 -106 140 -171t214 -65t214 65t140 171q8 20 37 20h61q17 0 26.5 -13.5t3.5 -29.5q-63 -177 -228 -273zM576 256q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf224;" d="M1024 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q126 -158 126 -359q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64 q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-149 16 -270.5 103t-186.5 223.5t-53 291.5q16 204 160 353.5t347 172.5q118 14 228 -19t198 -103l255 254h-134q-14 0 -23 9t-9 23v64zM576 256q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf225;" horiz-adv-x="1792" d="M1280 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q126 -158 126 -359q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64 q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-217 24 -364.5 187.5t-147.5 384.5q0 201 126 359l-52 53l-101 -111q-9 -10 -22 -10.5t-23 7.5l-48 44q-10 8 -10.5 21.5t8.5 23.5l105 115l-111 112v-134q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9 t-9 23v288q0 26 19 45t45 19h288q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-133l106 -107l86 94q9 10 22 10.5t23 -7.5l48 -44q10 -8 10.5 -21.5t-8.5 -23.5l-90 -99l57 -56q158 126 359 126t359 -126l255 254h-134q-14 0 -23 9t-9 23v64zM832 256q185 0 316.5 131.5 t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf226;" horiz-adv-x="1792" d="M1790 1007q12 -155 -52.5 -292t-186 -224t-271.5 -103v-260h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-512v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23 t23 9h224v260q-150 16 -271.5 103t-186 224t-52.5 292q17 206 164.5 356.5t352.5 169.5q206 21 377 -94q171 115 377 94q205 -19 352.5 -169.5t164.5 -356.5zM896 647q128 131 128 313t-128 313q-128 -131 -128 -313t128 -313zM576 512q115 0 218 57q-154 165 -154 391 q0 224 154 391q-103 57 -218 57q-185 0 -316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5zM1152 128v260q-137 15 -256 94q-119 -79 -256 -94v-260h512zM1216 512q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5q-115 0 -218 -57q154 -167 154 -391 q0 -226 -154 -391q103 -57 218 -57z" />
+<glyph unicode="&#xf227;" horiz-adv-x="1920" d="M1536 1120q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q76 -95 107.5 -214t9.5 -247q-31 -182 -166 -312t-318 -156q-210 -29 -384.5 80t-241.5 300q-117 6 -221 57.5t-177.5 133t-113.5 192.5t-32 230 q9 135 78 252t182 191.5t248 89.5q118 14 227.5 -19t198.5 -103l255 254h-134q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q59 -74 93 -169q182 -9 328 -124l255 254h-134q-14 0 -23 9 t-9 23v64zM1024 704q0 20 -4 58q-162 -25 -271 -150t-109 -292q0 -20 4 -58q162 25 271 150t109 292zM128 704q0 -168 111 -294t276 -149q-3 29 -3 59q0 210 135 369.5t338 196.5q-53 120 -163.5 193t-245.5 73q-185 0 -316.5 -131.5t-131.5 -316.5zM1088 -128 q185 0 316.5 131.5t131.5 316.5q0 168 -111 294t-276 149q3 -29 3 -59q0 -210 -135 -369.5t-338 -196.5q53 -120 163.5 -193t245.5 -73z" />
+<glyph unicode="&#xf228;" horiz-adv-x="2048" d="M1664 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q76 -95 107.5 -214t9.5 -247q-32 -180 -164.5 -310t-313.5 -157q-223 -34 -409 90q-117 -78 -256 -93v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23 t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-155 17 -279.5 109.5t-187 237.5t-39.5 307q25 187 159.5 322.5t320.5 164.5q224 34 410 -90q146 97 320 97q201 0 359 -126l255 254h-134q-14 0 -23 9 t-9 23v64zM896 391q128 131 128 313t-128 313q-128 -131 -128 -313t128 -313zM128 704q0 -185 131.5 -316.5t316.5 -131.5q117 0 218 57q-154 167 -154 391t154 391q-101 57 -218 57q-185 0 -316.5 -131.5t-131.5 -316.5zM1216 256q185 0 316.5 131.5t131.5 316.5 t-131.5 316.5t-316.5 131.5q-117 0 -218 -57q154 -167 154 -391t-154 -391q101 -57 218 -57z" />
+<glyph unicode="&#xf229;" d="M1472 1408q26 0 45 -19t19 -45v-416q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v262l-213 -214l140 -140q9 -10 9 -23t-9 -22l-46 -46q-9 -9 -22 -9t-23 9l-140 141l-78 -79q126 -156 126 -359q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5 t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123t223.5 45.5q203 0 359 -126l78 78l-172 172q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l172 -172l213 213h-261q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h416zM576 0q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf22a;" horiz-adv-x="1280" d="M640 892q217 -24 364.5 -187.5t147.5 -384.5q0 -167 -87 -306t-236 -212t-319 -54q-133 15 -245.5 88t-182 188t-80.5 249q-12 155 52.5 292t186 224t271.5 103v132h-160q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h160v165l-92 -92q-10 -9 -23 -9t-22 9l-46 46q-9 9 -9 22 t9 23l202 201q19 19 45 19t45 -19l202 -201q9 -10 9 -23t-9 -22l-46 -46q-9 -9 -22 -9t-23 9l-92 92v-165h160q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-160v-132zM576 -128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5 t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf22b;" horiz-adv-x="2048" d="M1901 621q19 -19 19 -45t-19 -45l-294 -294q-9 -10 -22.5 -10t-22.5 10l-45 45q-10 9 -10 22.5t10 22.5l185 185h-294v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-132q-24 -217 -187.5 -364.5t-384.5 -147.5q-167 0 -306 87t-212 236t-54 319q15 133 88 245.5 t188 182t249 80.5q155 12 292 -52.5t224 -186t103 -271.5h132v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224h294l-185 185q-10 9 -10 22.5t10 22.5l45 45q9 10 22.5 10t22.5 -10zM576 128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5 t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf22c;" horiz-adv-x="1280" d="M1152 960q0 -221 -147.5 -384.5t-364.5 -187.5v-612q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v612q-217 24 -364.5 187.5t-147.5 384.5q0 117 45.5 223.5t123 184t184 123t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5zM576 512q185 0 316.5 131.5 t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+<glyph unicode="&#xf22d;" horiz-adv-x="1280" d="M1024 576q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1152 576q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123 t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5z" />
+<glyph unicode="&#xf22e;" horiz-adv-x="1792" />
+<glyph unicode="&#xf22f;" horiz-adv-x="1792" />
+<glyph unicode="&#xf230;" d="M1451 1408q35 0 60 -25t25 -60v-1366q0 -35 -25 -60t-60 -25h-391v595h199l30 232h-229v148q0 56 23.5 84t91.5 28l122 1v207q-63 9 -178 9q-136 0 -217.5 -80t-81.5 -226v-171h-200v-232h200v-595h-735q-35 0 -60 25t-25 60v1366q0 35 25 60t60 25h1366z" />
+<glyph unicode="&#xf231;" horiz-adv-x="1280" d="M0 939q0 108 37.5 203.5t103.5 166.5t152 123t185 78t202 26q158 0 294 -66.5t221 -193.5t85 -287q0 -96 -19 -188t-60 -177t-100 -149.5t-145 -103t-189 -38.5q-68 0 -135 32t-96 88q-10 -39 -28 -112.5t-23.5 -95t-20.5 -71t-26 -71t-32 -62.5t-46 -77.5t-62 -86.5 l-14 -5l-9 10q-15 157 -15 188q0 92 21.5 206.5t66.5 287.5t52 203q-32 65 -32 169q0 83 52 156t132 73q61 0 95 -40.5t34 -102.5q0 -66 -44 -191t-44 -187q0 -63 45 -104.5t109 -41.5q55 0 102 25t78.5 68t56 95t38 110.5t20 111t6.5 99.5q0 173 -109.5 269.5t-285.5 96.5 q-200 0 -334 -129.5t-134 -328.5q0 -44 12.5 -85t27 -65t27 -45.5t12.5 -30.5q0 -28 -15 -73t-37 -45q-2 0 -17 3q-51 15 -90.5 56t-61 94.5t-32.5 108t-11 106.5z" />
+<glyph unicode="&#xf232;" d="M985 562q13 0 97.5 -44t89.5 -53q2 -5 2 -15q0 -33 -17 -76q-16 -39 -71 -65.5t-102 -26.5q-57 0 -190 62q-98 45 -170 118t-148 185q-72 107 -71 194v8q3 91 74 158q24 22 52 22q6 0 18 -1.5t19 -1.5q19 0 26.5 -6.5t15.5 -27.5q8 -20 33 -88t25 -75q0 -21 -34.5 -57.5 t-34.5 -46.5q0 -7 5 -15q34 -73 102 -137q56 -53 151 -101q12 -7 22 -7q15 0 54 48.5t52 48.5zM782 32q127 0 243.5 50t200.5 134t134 200.5t50 243.5t-50 243.5t-134 200.5t-200.5 134t-243.5 50t-243.5 -50t-200.5 -134t-134 -200.5t-50 -243.5q0 -203 120 -368l-79 -233 l242 77q158 -104 345 -104zM782 1414q153 0 292.5 -60t240.5 -161t161 -240.5t60 -292.5t-60 -292.5t-161 -240.5t-240.5 -161t-292.5 -60q-195 0 -365 94l-417 -134l136 405q-108 178 -108 389q0 153 60 292.5t161 240.5t240.5 161t292.5 60z" />
+<glyph unicode="&#xf233;" horiz-adv-x="1792" d="M128 128h1024v128h-1024v-128zM128 640h1024v128h-1024v-128zM1696 192q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM128 1152h1024v128h-1024v-128zM1696 704q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1696 1216 q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1792 384v-384h-1792v384h1792zM1792 896v-384h-1792v384h1792zM1792 1408v-384h-1792v384h1792z" />
+<glyph unicode="&#xf234;" horiz-adv-x="2048" d="M704 640q-159 0 -271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5t-112.5 -271.5t-271.5 -112.5zM1664 512h352q13 0 22.5 -9.5t9.5 -22.5v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-352v-352q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5 t-9.5 22.5v352h-352q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h352v352q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5v-352zM928 288q0 -52 38 -90t90 -38h256v-238q-68 -50 -171 -50h-874q-121 0 -194 69t-73 190q0 53 3.5 103.5t14 109t26.5 108.5 t43 97.5t62 81t85.5 53.5t111.5 20q19 0 39 -17q79 -61 154.5 -91.5t164.5 -30.5t164.5 30.5t154.5 91.5q20 17 39 17q132 0 217 -96h-223q-52 0 -90 -38t-38 -90v-192z" />
+<glyph unicode="&#xf235;" horiz-adv-x="2048" d="M704 640q-159 0 -271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5t-112.5 -271.5t-271.5 -112.5zM1781 320l249 -249q9 -9 9 -23q0 -13 -9 -22l-136 -136q-9 -9 -22 -9q-14 0 -23 9l-249 249l-249 -249q-9 -9 -23 -9q-13 0 -22 9l-136 136 q-9 9 -9 22q0 14 9 23l249 249l-249 249q-9 9 -9 23q0 13 9 22l136 136q9 9 22 9q14 0 23 -9l249 -249l249 249q9 9 23 9q13 0 22 -9l136 -136q9 -9 9 -22q0 -14 -9 -23zM1283 320l-181 -181q-37 -37 -37 -91q0 -53 37 -90l83 -83q-21 -3 -44 -3h-874q-121 0 -194 69 t-73 190q0 53 3.5 103.5t14 109t26.5 108.5t43 97.5t62 81t85.5 53.5t111.5 20q19 0 39 -17q154 -122 319 -122t319 122q20 17 39 17q28 0 57 -6q-28 -27 -41 -50t-13 -56q0 -54 37 -91z" />
+<glyph unicode="&#xf236;" horiz-adv-x="2048" d="M256 512h1728q26 0 45 -19t19 -45v-448h-256v256h-1536v-256h-256v1216q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-704zM832 832q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM2048 576v64q0 159 -112.5 271.5t-271.5 112.5h-704 q-26 0 -45 -19t-19 -45v-384h1152z" />
+<glyph unicode="&#xf237;" d="M1536 1536l-192 -448h192v-192h-274l-55 -128h329v-192h-411l-357 -832l-357 832h-411v192h329l-55 128h-274v192h192l-192 448h256l323 -768h378l323 768h256zM768 320l108 256h-216z" />
+<glyph unicode="&#xf238;" d="M1088 1536q185 0 316.5 -93.5t131.5 -226.5v-896q0 -130 -125.5 -222t-305.5 -97l213 -202q16 -15 8 -35t-30 -20h-1056q-22 0 -30 20t8 35l213 202q-180 5 -305.5 97t-125.5 222v896q0 133 131.5 226.5t316.5 93.5h640zM768 192q80 0 136 56t56 136t-56 136t-136 56 t-136 -56t-56 -136t56 -136t136 -56zM1344 768v512h-1152v-512h1152z" />
+<glyph unicode="&#xf239;" d="M1088 1536q185 0 316.5 -93.5t131.5 -226.5v-896q0 -130 -125.5 -222t-305.5 -97l213 -202q16 -15 8 -35t-30 -20h-1056q-22 0 -30 20t8 35l213 202q-180 5 -305.5 97t-125.5 222v896q0 133 131.5 226.5t316.5 93.5h640zM288 224q66 0 113 47t47 113t-47 113t-113 47 t-113 -47t-47 -113t47 -113t113 -47zM704 768v512h-544v-512h544zM1248 224q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47zM1408 768v512h-576v-512h576z" />
+<glyph unicode="&#xf23a;" horiz-adv-x="1792" d="M597 1115v-1173q0 -25 -12.5 -42.5t-36.5 -17.5q-17 0 -33 8l-465 233q-21 10 -35.5 33.5t-14.5 46.5v1140q0 20 10 34t29 14q14 0 44 -15l511 -256q3 -3 3 -5zM661 1014l534 -866l-534 266v600zM1792 996v-1054q0 -25 -14 -40.5t-38 -15.5t-47 13l-441 220zM1789 1116 q0 -3 -256.5 -419.5t-300.5 -487.5l-390 634l324 527q17 28 52 28q14 0 26 -6l541 -270q4 -2 4 -6z" />
+<glyph unicode="&#xf23b;" d="M809 532l266 499h-112l-157 -312q-24 -48 -44 -92l-42 92l-155 312h-120l263 -493v-324h101v318zM1536 1408v-1536h-1536v1536h1536z" />
+<glyph unicode="&#xf23c;" horiz-adv-x="2296" d="M478 -139q-8 -16 -27 -34.5t-37 -25.5q-25 -9 -51.5 3.5t-28.5 31.5q-1 22 40 55t68 38q23 4 34 -21.5t2 -46.5zM1819 -139q7 -16 26 -34.5t38 -25.5q25 -9 51.5 3.5t27.5 31.5q2 22 -39.5 55t-68.5 38q-22 4 -33 -21.5t-2 -46.5zM1867 -30q13 -27 56.5 -59.5t77.5 -41.5 q45 -13 82 4.5t37 50.5q0 46 -67.5 100.5t-115.5 59.5q-40 5 -63.5 -37.5t-6.5 -76.5zM428 -30q-13 -27 -56 -59.5t-77 -41.5q-45 -13 -82 4.5t-37 50.5q0 46 67.5 100.5t115.5 59.5q40 5 63 -37.5t6 -76.5zM1158 1094h1q-41 0 -76 -15q27 -8 44 -30.5t17 -49.5 q0 -35 -27 -60t-65 -25q-52 0 -80 43q-5 -23 -5 -42q0 -74 56 -126.5t135 -52.5q80 0 136 52.5t56 126.5t-56 126.5t-136 52.5zM1462 1312q-99 109 -220.5 131.5t-245.5 -44.5q27 60 82.5 96.5t118 39.5t121.5 -17t99.5 -74.5t44.5 -131.5zM2212 73q8 -11 -11 -42 q7 -23 7 -40q1 -56 -44.5 -112.5t-109.5 -91.5t-118 -37q-48 -2 -92 21.5t-66 65.5q-687 -25 -1259 0q-23 -41 -66.5 -65t-92.5 -22q-86 3 -179.5 80.5t-92.5 160.5q2 22 7 40q-19 31 -11 42q6 10 31 1q14 22 41 51q-7 29 2 38q11 10 39 -4q29 20 59 34q0 29 13 37 q23 12 51 -16q35 5 61 -2q18 -4 38 -19v73q-11 0 -18 2q-53 10 -97 44.5t-55 87.5q-9 38 0 81q15 62 93 95q2 17 19 35.5t36 23.5t33 -7.5t19 -30.5h13q46 -5 60 -23q3 -3 5 -7q10 1 30.5 3.5t30.5 3.5q-15 11 -30 17q-23 40 -91 43q0 6 1 10q-62 2 -118.5 18.5t-84.5 47.5 q-32 36 -42.5 92t-2.5 112q16 126 90 179q23 16 52 4.5t32 -40.5q0 -1 1.5 -14t2.5 -21t3 -20t5.5 -19t8.5 -10q27 -14 76 -12q48 46 98 74q-40 4 -162 -14l47 46q61 58 163 111q145 73 282 86q-20 8 -41 15.5t-47 14t-42.5 10.5t-47.5 11t-43 10q595 126 904 -139 q98 -84 158 -222q85 -10 121 9h1q5 3 8.5 10t5.5 19t3 19.5t3 21.5l1 14q3 28 32 40t52 -5q73 -52 91 -178q7 -57 -3.5 -113t-42.5 -91q-28 -32 -83.5 -48.5t-115.5 -18.5v-10q-71 -2 -95 -43q-14 -5 -31 -17q11 -1 32 -3.5t30 -3.5q1 4 5 8q16 18 60 23h13q5 18 19 30t33 8 t36 -23t19 -36q79 -32 93 -95q9 -40 1 -81q-12 -53 -56 -88t-97 -44q-10 -2 -17 -2q0 -49 -1 -73q20 15 38 19q26 7 61 2q28 28 51 16q14 -9 14 -37q33 -16 59 -34q27 13 38 4q10 -10 2 -38q28 -30 41 -51q23 8 31 -1zM1937 1025q0 -29 -9 -54q82 -32 112 -132 q4 37 -9.5 98.5t-41.5 90.5q-20 19 -36 17t-16 -20zM1859 925q35 -42 47.5 -108.5t-0.5 -124.5q67 13 97 45q13 14 18 28q-3 64 -31 114.5t-79 66.5q-15 -15 -52 -21zM1822 921q-30 0 -44 1q42 -115 53 -239q21 0 43 3q16 68 1 135t-53 100zM258 839q30 100 112 132 q-9 25 -9 54q0 18 -16.5 20t-35.5 -17q-28 -29 -41.5 -90.5t-9.5 -98.5zM294 737q29 -31 97 -45q-13 58 -0.5 124.5t47.5 108.5v0q-37 6 -52 21q-51 -16 -78.5 -66t-31.5 -115q9 -17 18 -28zM471 683q14 124 73 235q-19 -4 -55 -18l-45 -19v1q-46 -89 -20 -196q25 -3 47 -3z M1434 644q8 -38 16.5 -108.5t11.5 -89.5q3 -18 9.5 -21.5t23.5 4.5q40 20 62 85.5t23 125.5q-24 2 -146 4zM1152 1285q-116 0 -199 -82.5t-83 -198.5q0 -117 83 -199.5t199 -82.5t199 82.5t83 199.5q0 116 -83 198.5t-199 82.5zM1380 646q-106 2 -211 0v1q-1 -27 2.5 -86 t13.5 -66q29 -14 93.5 -14.5t95.5 10.5q9 3 11 39t-0.5 69.5t-4.5 46.5zM1112 447q8 4 9.5 48t-0.5 88t-4 63v1q-212 -3 -214 -3q-4 -20 -7 -62t0 -83t14 -46q34 -15 101 -16t101 10zM718 636q-16 -59 4.5 -118.5t77.5 -84.5q15 -8 24 -5t12 21q3 16 8 90t10 103 q-69 -2 -136 -6zM591 510q3 -23 -34 -36q132 -141 271.5 -240t305.5 -154q172 49 310.5 146t293.5 250q-33 13 -30 34l3 9v1v-1q-17 2 -50 5.5t-48 4.5q-26 -90 -82 -132q-51 -38 -82 1q-5 6 -9 14q-7 13 -17 62q-2 -5 -5 -9t-7.5 -7t-8 -5.5t-9.5 -4l-10 -2.5t-12 -2 l-12 -1.5t-13.5 -1t-13.5 -0.5q-106 -9 -163 11q-4 -17 -10 -26.5t-21 -15t-23 -7t-36 -3.5q-2 0 -3 -0.5t-3 -0.5h-3q-179 -17 -203 40q-2 -63 -56 -54q-47 8 -91 54q-12 13 -20 26q-17 29 -26 65q-58 -6 -87 -10q1 -2 4 -10zM507 -118q3 14 3 30q-17 71 -51 130t-73 70 q-41 12 -101.5 -14.5t-104.5 -80t-39 -107.5q35 -53 100 -93t119 -42q51 -2 94 28t53 79zM510 53q23 -63 27 -119q195 113 392 174q-98 52 -180.5 120t-179.5 165q-6 -4 -29 -13q0 -2 -1 -5t-1 -4q31 -18 22 -37q-12 -23 -56 -34q-10 -13 -29 -24h-1q-2 -83 1 -150 q19 -34 35 -73zM579 -113q532 -21 1145 0q-254 147 -428 196q-76 -35 -156 -57q-8 -3 -16 0q-65 21 -129 49q-208 -60 -416 -188h-1v-1q1 0 1 1zM1763 -67q4 54 28 120q14 38 33 71l-1 -1q3 77 3 153q-15 8 -30 25q-42 9 -56 33q-9 20 22 38q-2 4 -2 9q-16 4 -28 12 q-204 -190 -383 -284q198 -59 414 -176zM2155 -90q5 54 -39 107.5t-104 80t-102 14.5q-38 -11 -72.5 -70.5t-51.5 -129.5q0 -16 3 -30q10 -49 53 -79t94 -28q54 2 119 42t100 93z" />
+<glyph unicode="&#xf23d;" horiz-adv-x="2304" d="M1524 -25q0 -68 -48 -116t-116 -48t-116.5 48t-48.5 116t48.5 116.5t116.5 48.5t116 -48.5t48 -116.5zM775 -25q0 -68 -48.5 -116t-116.5 -48t-116 48t-48 116t48 116.5t116 48.5t116.5 -48.5t48.5 -116.5zM0 1469q57 -60 110.5 -104.5t121 -82t136 -63t166 -45.5 t200 -31.5t250 -18.5t304 -9.5t372.5 -2.5q139 0 244.5 -5t181 -16.5t124 -27.5t71 -39.5t24 -51.5t-19.5 -64t-56.5 -76.5t-89.5 -91t-116 -104.5t-139 -119q-185 -157 -286 -247q29 51 76.5 109t94 105.5t94.5 98.5t83 91.5t54 80.5t13 70t-45.5 55.5t-116.5 41t-204 23.5 t-304 5q-168 -2 -314 6t-256 23t-204.5 41t-159.5 51.5t-122.5 62.5t-91.5 66.5t-68 71.5t-50.5 69.5t-40 68t-36.5 59.5z" />
+<glyph unicode="&#xf23e;" horiz-adv-x="1792" d="M896 1472q-169 0 -323 -66t-265.5 -177.5t-177.5 -265.5t-66 -323t66 -323t177.5 -265.5t265.5 -177.5t323 -66t323 66t265.5 177.5t177.5 265.5t66 323t-66 323t-177.5 265.5t-265.5 177.5t-323 66zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348 t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM496 704q16 0 16 -16v-480q0 -16 -16 -16h-32q-16 0 -16 16v480q0 16 16 16h32zM896 640q53 0 90.5 -37.5t37.5 -90.5q0 -35 -17.5 -64t-46.5 -46v-114q0 -14 -9 -23 t-23 -9h-64q-14 0 -23 9t-9 23v114q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5zM896 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM544 928v-96 q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v96q0 93 65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5v-96q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v96q0 146 -103 249t-249 103t-249 -103t-103 -249zM1408 192v512q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-512 q0 -26 19 -45t45 -19h896q26 0 45 19t19 45z" />
+<glyph unicode="&#xf240;" horiz-adv-x="2304" d="M1920 1024v-768h-1664v768h1664zM2048 448h128v384h-128v288q0 14 -9 23t-23 9h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288zM2304 832v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113 v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160q53 0 90.5 -37.5t37.5 -90.5z" />
+<glyph unicode="&#xf241;" horiz-adv-x="2304" d="M256 256v768h1280v-768h-1280zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
+<glyph unicode="&#xf242;" horiz-adv-x="2304" d="M256 256v768h896v-768h-896zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
+<glyph unicode="&#xf243;" horiz-adv-x="2304" d="M256 256v768h512v-768h-512zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
+<glyph unicode="&#xf244;" horiz-adv-x="2304" d="M2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9h-1856q-14 0 -23 -9t-9 -23 v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
+<glyph unicode="&#xf245;" horiz-adv-x="1280" d="M1133 493q31 -30 14 -69q-17 -40 -59 -40h-382l201 -476q10 -25 0 -49t-34 -35l-177 -75q-25 -10 -49 0t-35 34l-191 452l-312 -312q-19 -19 -45 -19q-12 0 -24 5q-40 17 -40 59v1504q0 42 40 59q12 5 24 5q27 0 45 -19z" />
+<glyph unicode="&#xf246;" horiz-adv-x="1024" d="M832 1408q-320 0 -320 -224v-416h128v-128h-128v-544q0 -224 320 -224h64v-128h-64q-272 0 -384 146q-112 -146 -384 -146h-64v128h64q320 0 320 224v544h-128v128h128v416q0 224 -320 224h-64v128h64q272 0 384 -146q112 146 384 146h64v-128h-64z" />
+<glyph unicode="&#xf247;" horiz-adv-x="2048" d="M2048 1152h-128v-1024h128v-384h-384v128h-1280v-128h-384v384h128v1024h-128v384h384v-128h1280v128h384v-384zM1792 1408v-128h128v128h-128zM128 1408v-128h128v128h-128zM256 -128v128h-128v-128h128zM1664 0v128h128v1024h-128v128h-1280v-128h-128v-1024h128v-128 h1280zM1920 -128v128h-128v-128h128zM1280 896h384v-768h-896v256h-384v768h896v-256zM512 512h640v512h-640v-512zM1536 256v512h-256v-384h-384v-128h640z" />
+<glyph unicode="&#xf248;" horiz-adv-x="2304" d="M2304 768h-128v-640h128v-384h-384v128h-896v-128h-384v384h128v128h-384v-128h-384v384h128v640h-128v384h384v-128h896v128h384v-384h-128v-128h384v128h384v-384zM2048 1024v-128h128v128h-128zM1408 1408v-128h128v128h-128zM128 1408v-128h128v128h-128zM256 256 v128h-128v-128h128zM1536 384h-128v-128h128v128zM384 384h896v128h128v640h-128v128h-896v-128h-128v-640h128v-128zM896 -128v128h-128v-128h128zM2176 -128v128h-128v-128h128zM2048 128v640h-128v128h-384v-384h128v-384h-384v128h-384v-128h128v-128h896v128h128z" />
+<glyph unicode="&#xf249;" d="M1024 288v-416h-928q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1344q40 0 68 -28t28 -68v-928h-416q-40 0 -68 -28t-28 -68zM1152 256h381q-15 -82 -65 -132l-184 -184q-50 -50 -132 -65v381z" />
+<glyph unicode="&#xf24a;" d="M1400 256h-248v-248q29 10 41 22l185 185q12 12 22 41zM1120 384h288v896h-1280v-1280h896v288q0 40 28 68t68 28zM1536 1312v-1024q0 -40 -20 -88t-48 -76l-184 -184q-28 -28 -76 -48t-88 -20h-1024q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1344q40 0 68 -28t28 -68 z" />
+<glyph unicode="&#xf24b;" horiz-adv-x="2304" d="M1951 538q0 -26 -15.5 -44.5t-38.5 -23.5q-8 -2 -18 -2h-153v140h153q10 0 18 -2q23 -5 38.5 -23.5t15.5 -44.5zM1933 751q0 -25 -15 -42t-38 -21q-3 -1 -15 -1h-139v129h139q3 0 8.5 -0.5t6.5 -0.5q23 -4 38 -21.5t15 -42.5zM728 587v308h-228v-308q0 -58 -38 -94.5 t-105 -36.5q-108 0 -229 59v-112q53 -15 121 -23t109 -9l42 -1q328 0 328 217zM1442 403v113q-99 -52 -200 -59q-108 -8 -169 41t-61 142t61 142t169 41q101 -7 200 -58v112q-48 12 -100 19.5t-80 9.5l-28 2q-127 6 -218.5 -14t-140.5 -60t-71 -88t-22 -106t22 -106t71 -88 t140.5 -60t218.5 -14q101 4 208 31zM2176 518q0 54 -43 88.5t-109 39.5v3q57 8 89 41.5t32 79.5q0 55 -41 88t-107 36q-3 0 -12 0.5t-14 0.5h-455v-510h491q74 0 121.5 36.5t47.5 96.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90 t90 38h2048q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf24c;" horiz-adv-x="2304" d="M858 295v693q-106 -41 -172 -135.5t-66 -211.5t66 -211.5t172 -134.5zM1362 641q0 117 -66 211.5t-172 135.5v-694q106 41 172 135.5t66 211.5zM1577 641q0 -159 -78.5 -294t-213.5 -213.5t-294 -78.5q-119 0 -227.5 46.5t-187 125t-125 187t-46.5 227.5q0 159 78.5 294 t213.5 213.5t294 78.5t294 -78.5t213.5 -213.5t78.5 -294zM1960 634q0 139 -55.5 261.5t-147.5 205.5t-213.5 131t-252.5 48h-301q-176 0 -323.5 -81t-235 -230t-87.5 -335q0 -171 87 -317.5t236 -231.5t323 -85h301q129 0 251.5 50.5t214.5 135t147.5 202.5t55.5 246z M2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf24d;" horiz-adv-x="1792" d="M1664 -96v1088q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5v-1088q0 -13 9.5 -22.5t22.5 -9.5h1088q13 0 22.5 9.5t9.5 22.5zM1792 992v-1088q0 -66 -47 -113t-113 -47h-1088q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1088q66 0 113 -47t47 -113 zM1408 1376v-160h-128v160q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5v-1088q0 -13 9.5 -22.5t22.5 -9.5h160v-128h-160q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1088q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf24e;" horiz-adv-x="2304" d="M1728 1088l-384 -704h768zM448 1088l-384 -704h768zM1269 1280q-14 -40 -45.5 -71.5t-71.5 -45.5v-1291h608q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1344q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h608v1291q-40 14 -71.5 45.5t-45.5 71.5h-491q-14 0 -23 9t-9 23v64 q0 14 9 23t23 9h491q21 57 70 92.5t111 35.5t111 -35.5t70 -92.5h491q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-491zM1088 1264q33 0 56.5 23.5t23.5 56.5t-23.5 56.5t-56.5 23.5t-56.5 -23.5t-23.5 -56.5t23.5 -56.5t56.5 -23.5zM2176 384q0 -73 -46.5 -131t-117.5 -91 t-144.5 -49.5t-139.5 -16.5t-139.5 16.5t-144.5 49.5t-117.5 91t-46.5 131q0 11 35 81t92 174.5t107 195.5t102 184t56 100q18 33 56 33t56 -33q4 -7 56 -100t102 -184t107 -195.5t92 -174.5t35 -81zM896 384q0 -73 -46.5 -131t-117.5 -91t-144.5 -49.5t-139.5 -16.5 t-139.5 16.5t-144.5 49.5t-117.5 91t-46.5 131q0 11 35 81t92 174.5t107 195.5t102 184t56 100q18 33 56 33t56 -33q4 -7 56 -100t102 -184t107 -195.5t92 -174.5t35 -81z" />
+<glyph unicode="&#xf250;" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM874 700q77 29 149 92.5t129.5 152.5t92.5 210t35 253h-1024q0 -132 35 -253t92.5 -210t129.5 -152.5t149 -92.5q19 -7 30.5 -23.5t11.5 -36.5t-11.5 -36.5t-30.5 -23.5q-77 -29 -149 -92.5 t-129.5 -152.5t-92.5 -210t-35 -253h1024q0 132 -35 253t-92.5 210t-129.5 152.5t-149 92.5q-19 7 -30.5 23.5t-11.5 36.5t11.5 36.5t30.5 23.5z" />
+<glyph unicode="&#xf251;" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM1280 1408h-1024q0 -66 9 -128h1006q9 61 9 128zM1280 -128q0 130 -34 249.5t-90.5 208t-126.5 152t-146 94.5h-230q-76 -31 -146 -94.5t-126.5 -152t-90.5 -208t-34 -249.5h1024z" />
+<glyph unicode="&#xf252;" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM1280 1408h-1024q0 -206 85 -384h854q85 178 85 384zM1223 192q-54 141 -145.5 241.5t-194.5 142.5h-230q-103 -42 -194.5 -142.5t-145.5 -241.5h910z" />
+<glyph unicode="&#xf253;" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM874 700q77 29 149 92.5t129.5 152.5t92.5 210t35 253h-1024q0 -132 35 -253t92.5 -210t129.5 -152.5t149 -92.5q19 -7 30.5 -23.5t11.5 -36.5t-11.5 -36.5t-30.5 -23.5q-137 -51 -244 -196 h700q-107 145 -244 196q-19 7 -30.5 23.5t-11.5 36.5t11.5 36.5t30.5 23.5z" />
+<glyph unicode="&#xf254;" d="M1504 -64q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v128q0 14 9 23t23 9h1472zM130 0q3 55 16 107t30 95t46 87t53.5 76t64.5 69.5t66 60t70.5 55t66.5 47.5t65 43q-43 28 -65 43t-66.5 47.5t-70.5 55t-66 60t-64.5 69.5t-53.5 76t-46 87 t-30 95t-16 107h1276q-3 -55 -16 -107t-30 -95t-46 -87t-53.5 -76t-64.5 -69.5t-66 -60t-70.5 -55t-66.5 -47.5t-65 -43q43 -28 65 -43t66.5 -47.5t70.5 -55t66 -60t64.5 -69.5t53.5 -76t46 -87t30 -95t16 -107h-1276zM1504 1536q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9 h-1472q-14 0 -23 9t-9 23v128q0 14 9 23t23 9h1472z" />
+<glyph unicode="&#xf255;" d="M768 1152q-53 0 -90.5 -37.5t-37.5 -90.5v-128h-32v93q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-429l-32 30v172q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-224q0 -47 35 -82l310 -296q39 -39 39 -102q0 -26 19 -45t45 -19h640q26 0 45 19t19 45v25 q0 41 10 77l108 436q10 36 10 77v246q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-32h-32v125q0 40 -25 72.5t-64 40.5q-14 2 -23 2q-46 0 -79 -33t-33 -79v-128h-32v122q0 51 -32.5 89.5t-82.5 43.5q-5 1 -13 1zM768 1280q84 0 149 -50q57 34 123 34q59 0 111 -27 t86 -76q27 7 59 7q100 0 170 -71.5t70 -171.5v-246q0 -51 -13 -108l-109 -436q-6 -24 -6 -71q0 -80 -56 -136t-136 -56h-640q-84 0 -138 58.5t-54 142.5l-308 296q-76 73 -76 175v224q0 99 70.5 169.5t169.5 70.5q11 0 16 -1q6 95 75.5 160t164.5 65q52 0 98 -21 q72 69 174 69z" />
+<glyph unicode="&#xf256;" horiz-adv-x="1792" d="M880 1408q-46 0 -79 -33t-33 -79v-656h-32v528q0 46 -33 79t-79 33t-79 -33t-33 -79v-528v-256l-154 205q-38 51 -102 51q-53 0 -90.5 -37.5t-37.5 -90.5q0 -43 26 -77l384 -512q38 -51 102 -51h688q34 0 61 22t34 56l76 405q5 32 5 59v498q0 46 -33 79t-79 33t-79 -33 t-33 -79v-272h-32v528q0 46 -33 79t-79 33t-79 -33t-33 -79v-528h-32v656q0 46 -33 79t-79 33zM880 1536q68 0 125.5 -35.5t88.5 -96.5q19 4 42 4q99 0 169.5 -70.5t70.5 -169.5v-17q105 6 180.5 -64t75.5 -175v-498q0 -40 -8 -83l-76 -404q-14 -79 -76.5 -131t-143.5 -52 h-688q-60 0 -114.5 27.5t-90.5 74.5l-384 512q-51 68 -51 154q0 106 75 181t181 75q78 0 128 -34v434q0 99 70.5 169.5t169.5 70.5q23 0 42 -4q31 61 88.5 96.5t125.5 35.5z" />
+<glyph unicode="&#xf257;" horiz-adv-x="1792" d="M1073 -128h-177q-163 0 -226 141q-23 49 -23 102v5q-62 30 -98.5 88.5t-36.5 127.5q0 38 5 48h-261q-106 0 -181 75t-75 181t75 181t181 75h113l-44 17q-74 28 -119.5 93.5t-45.5 145.5q0 106 75 181t181 75q46 0 91 -17l628 -239h401q106 0 181 -75t75 -181v-668 q0 -88 -54 -157.5t-140 -90.5l-339 -85q-92 -23 -186 -23zM1024 583l-155 -71l-163 -74q-30 -14 -48 -41.5t-18 -60.5q0 -46 33 -79t79 -33q26 0 46 10l338 154q-49 10 -80.5 50t-31.5 90v55zM1344 272q0 46 -33 79t-79 33q-26 0 -46 -10l-290 -132q-28 -13 -37 -17 t-30.5 -17t-29.5 -23.5t-16 -29t-8 -40.5q0 -50 31.5 -82t81.5 -32q20 0 38 9l352 160q30 14 48 41.5t18 60.5zM1112 1024l-650 248q-24 8 -46 8q-53 0 -90.5 -37.5t-37.5 -90.5q0 -40 22.5 -73t59.5 -47l526 -200v-64h-640q-53 0 -90.5 -37.5t-37.5 -90.5t37.5 -90.5 t90.5 -37.5h535l233 106v198q0 63 46 106l111 102h-69zM1073 0q82 0 155 19l339 85q43 11 70 45.5t27 78.5v668q0 53 -37.5 90.5t-90.5 37.5h-308l-136 -126q-36 -33 -36 -82v-296q0 -46 33 -77t79 -31t79 35t33 81v208h32v-208q0 -70 -57 -114q52 -8 86.5 -48.5t34.5 -93.5 q0 -42 -23 -78t-61 -53l-310 -141h91z" />
+<glyph unicode="&#xf258;" horiz-adv-x="2048" d="M1151 1536q61 0 116 -28t91 -77l572 -781q118 -159 118 -359v-355q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v177l-286 143h-546q-80 0 -136 56t-56 136v32q0 119 84.5 203.5t203.5 84.5h420l42 128h-686q-100 0 -173.5 67.5t-81.5 166.5q-65 79 -65 182v32 q0 80 56 136t136 56h959zM1920 -64v355q0 157 -93 284l-573 781q-39 52 -103 52h-959q-26 0 -45 -19t-19 -45q0 -32 1.5 -49.5t9.5 -40.5t25 -43q10 31 35.5 50t56.5 19h832v-32h-832q-26 0 -45 -19t-19 -45q0 -44 3 -58q8 -44 44 -73t81 -29h640h91q40 0 68 -28t28 -68 q0 -15 -5 -30l-64 -192q-10 -29 -35 -47.5t-56 -18.5h-443q-66 0 -113 -47t-47 -113v-32q0 -26 19 -45t45 -19h561q16 0 29 -7l317 -158q24 -13 38.5 -36t14.5 -50v-197q0 -26 19 -45t45 -19h384q26 0 45 19t19 45z" />
+<glyph unicode="&#xf259;" horiz-adv-x="2048" d="M816 1408q-48 0 -79.5 -34t-31.5 -82q0 -14 3 -28l150 -624h-26l-116 482q-9 38 -39.5 62t-69.5 24q-47 0 -79 -34t-32 -81q0 -11 4 -29q3 -13 39 -161t68 -282t32 -138v-227l-307 230q-34 26 -77 26q-52 0 -89.5 -36.5t-37.5 -88.5q0 -67 56 -110l507 -379 q34 -26 76 -26h694q33 0 59 20.5t34 52.5l100 401q8 30 10 88t9 86l116 478q3 12 3 26q0 46 -33 79t-80 33q-38 0 -69 -25.5t-40 -62.5l-99 -408h-26l132 547q3 14 3 28q0 47 -32 80t-80 33q-38 0 -68.5 -24t-39.5 -62l-145 -602h-127l-164 682q-9 38 -39.5 62t-68.5 24z M1461 -256h-694q-85 0 -153 51l-507 380q-50 38 -78.5 94t-28.5 118q0 105 75 179t180 74q25 0 49.5 -5.5t41.5 -11t41 -20.5t35 -23t38.5 -29.5t37.5 -28.5l-123 512q-7 35 -7 59q0 93 60 162t152 79q14 87 80.5 144.5t155.5 57.5q83 0 148 -51.5t85 -132.5l103 -428 l83 348q20 81 85 132.5t148 51.5q87 0 152.5 -54t82.5 -139q93 -10 155 -78t62 -161q0 -30 -7 -57l-116 -477q-5 -22 -5 -67q0 -51 -13 -108l-101 -401q-19 -75 -79.5 -122.5t-137.5 -47.5z" />
+<glyph unicode="&#xf25a;" horiz-adv-x="1792" d="M640 1408q-53 0 -90.5 -37.5t-37.5 -90.5v-512v-384l-151 202q-41 54 -107 54q-52 0 -89 -38t-37 -90q0 -43 26 -77l384 -512q38 -51 102 -51h718q22 0 39.5 13.5t22.5 34.5l92 368q24 96 24 194v217q0 41 -28 71t-68 30t-68 -28t-28 -68h-32v61q0 48 -32 81.5t-80 33.5 q-46 0 -79 -33t-33 -79v-64h-32v90q0 55 -37 94.5t-91 39.5q-53 0 -90.5 -37.5t-37.5 -90.5v-96h-32v570q0 55 -37 94.5t-91 39.5zM640 1536q107 0 181.5 -77.5t74.5 -184.5v-220q22 2 32 2q99 0 173 -69q47 21 99 21q113 0 184 -87q27 7 56 7q94 0 159 -67.5t65 -161.5 v-217q0 -116 -28 -225l-92 -368q-16 -64 -68 -104.5t-118 -40.5h-718q-60 0 -114.5 27.5t-90.5 74.5l-384 512q-51 68 -51 154q0 105 74.5 180.5t179.5 75.5q71 0 130 -35v547q0 106 75 181t181 75zM768 128v384h-32v-384h32zM1024 128v384h-32v-384h32zM1280 128v384h-32 v-384h32z" />
+<glyph unicode="&#xf25b;" d="M1288 889q60 0 107 -23q141 -63 141 -226v-177q0 -94 -23 -186l-85 -339q-21 -86 -90.5 -140t-157.5 -54h-668q-106 0 -181 75t-75 181v401l-239 628q-17 45 -17 91q0 106 75 181t181 75q80 0 145.5 -45.5t93.5 -119.5l17 -44v113q0 106 75 181t181 75t181 -75t75 -181 v-261q27 5 48 5q69 0 127.5 -36.5t88.5 -98.5zM1072 896q-33 0 -60.5 -18t-41.5 -48l-74 -163l-71 -155h55q50 0 90 -31.5t50 -80.5l154 338q10 20 10 46q0 46 -33 79t-79 33zM1293 761q-22 0 -40.5 -8t-29 -16t-23.5 -29.5t-17 -30.5t-17 -37l-132 -290q-10 -20 -10 -46 q0 -46 33 -79t79 -33q33 0 60.5 18t41.5 48l160 352q9 18 9 38q0 50 -32 81.5t-82 31.5zM128 1120q0 -22 8 -46l248 -650v-69l102 111q43 46 106 46h198l106 233v535q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5v-640h-64l-200 526q-14 37 -47 59.5t-73 22.5 q-53 0 -90.5 -37.5t-37.5 -90.5zM1180 -128q44 0 78.5 27t45.5 70l85 339q19 73 19 155v91l-141 -310q-17 -38 -53 -61t-78 -23q-53 0 -93.5 34.5t-48.5 86.5q-44 -57 -114 -57h-208v32h208q46 0 81 33t35 79t-31 79t-77 33h-296q-49 0 -82 -36l-126 -136v-308 q0 -53 37.5 -90.5t90.5 -37.5h668z" />
+<glyph unicode="&#xf25c;" horiz-adv-x="1973" d="M857 992v-117q0 -13 -9.5 -22t-22.5 -9h-298v-812q0 -13 -9 -22.5t-22 -9.5h-135q-13 0 -22.5 9t-9.5 23v812h-297q-13 0 -22.5 9t-9.5 22v117q0 14 9 23t23 9h793q13 0 22.5 -9.5t9.5 -22.5zM1895 995l77 -961q1 -13 -8 -24q-10 -10 -23 -10h-134q-12 0 -21 8.5 t-10 20.5l-46 588l-189 -425q-8 -19 -29 -19h-120q-20 0 -29 19l-188 427l-45 -590q-1 -12 -10 -20.5t-21 -8.5h-135q-13 0 -23 10q-9 10 -9 24l78 961q1 12 10 20.5t21 8.5h142q20 0 29 -19l220 -520q10 -24 20 -51q3 7 9.5 24.5t10.5 26.5l221 520q9 19 29 19h141 q13 0 22 -8.5t10 -20.5z" />
+<glyph unicode="&#xf25d;" horiz-adv-x="1792" d="M1042 833q0 88 -60 121q-33 18 -117 18h-123v-281h162q66 0 102 37t36 105zM1094 548l205 -373q8 -17 -1 -31q-8 -16 -27 -16h-152q-20 0 -28 17l-194 365h-155v-350q0 -14 -9 -23t-23 -9h-134q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h294q128 0 190 -24q85 -31 134 -109 t49 -180q0 -92 -42.5 -165.5t-115.5 -109.5q6 -10 9 -16zM896 1376q-150 0 -286 -58.5t-234.5 -157t-157 -234.5t-58.5 -286t58.5 -286t157 -234.5t234.5 -157t286 -58.5t286 58.5t234.5 157t157 234.5t58.5 286t-58.5 286t-157 234.5t-234.5 157t-286 58.5zM1792 640 q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+<glyph unicode="&#xf25e;" horiz-adv-x="1792" d="M605 303q153 0 257 104q14 18 3 36l-45 82q-6 13 -24 17q-16 2 -27 -11l-4 -3q-4 -4 -11.5 -10t-17.5 -13t-23.5 -14.5t-28.5 -13.5t-33.5 -9.5t-37.5 -3.5q-76 0 -125 50t-49 127q0 76 48 125.5t122 49.5q37 0 71.5 -14t50.5 -28l16 -14q11 -11 26 -10q16 2 24 14l53 78 q13 20 -2 39q-3 4 -11 12t-30 23.5t-48.5 28t-67.5 22.5t-86 10q-148 0 -246 -96.5t-98 -240.5q0 -146 97 -241.5t247 -95.5zM1235 303q153 0 257 104q14 18 4 36l-45 82q-8 14 -25 17q-16 2 -27 -11l-4 -3q-4 -4 -11.5 -10t-17.5 -13t-23.5 -14.5t-28.5 -13.5t-33.5 -9.5 t-37.5 -3.5q-76 0 -125 50t-49 127q0 76 48 125.5t122 49.5q37 0 71.5 -14t50.5 -28l16 -14q11 -11 26 -10q16 2 24 14l53 78q13 20 -2 39q-3 4 -11 12t-30 23.5t-48.5 28t-67.5 22.5t-86 10q-147 0 -245.5 -96.5t-98.5 -240.5q0 -146 97 -241.5t247 -95.5zM896 1376 q-150 0 -286 -58.5t-234.5 -157t-157 -234.5t-58.5 -286t58.5 -286t157 -234.5t234.5 -157t286 -58.5t286 58.5t234.5 157t157 234.5t58.5 286t-58.5 286t-157 234.5t-234.5 157t-286 58.5zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191 t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71z" />
+<glyph unicode="&#xf260;" horiz-adv-x="2048" d="M736 736l384 -384l-384 -384l-672 672l672 672l168 -168l-96 -96l-72 72l-480 -480l480 -480l193 193l-289 287zM1312 1312l672 -672l-672 -672l-168 168l96 96l72 -72l480 480l-480 480l-193 -193l289 -287l-96 -96l-384 384z" />
+<glyph unicode="&#xf261;" horiz-adv-x="1792" d="M717 182l271 271l-279 279l-88 -88l192 -191l-96 -96l-279 279l279 279l40 -40l87 87l-127 128l-454 -454zM1075 190l454 454l-454 454l-271 -271l279 -279l88 88l-192 191l96 96l279 -279l-279 -279l-40 40l-87 -88zM1792 640q0 -182 -71 -348t-191 -286t-286 -191 t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+<glyph unicode="&#xf262;" horiz-adv-x="2304" d="M651 539q0 -39 -27.5 -66.5t-65.5 -27.5q-39 0 -66.5 27.5t-27.5 66.5q0 38 27.5 65.5t66.5 27.5q38 0 65.5 -27.5t27.5 -65.5zM1805 540q0 -39 -27.5 -66.5t-66.5 -27.5t-66.5 27.5t-27.5 66.5t27.5 66t66.5 27t66.5 -27t27.5 -66zM765 539q0 79 -56.5 136t-136.5 57 t-136.5 -56.5t-56.5 -136.5t56.5 -136.5t136.5 -56.5t136.5 56.5t56.5 136.5zM1918 540q0 80 -56.5 136.5t-136.5 56.5q-79 0 -136 -56.5t-57 -136.5t56.5 -136.5t136.5 -56.5t136.5 56.5t56.5 136.5zM850 539q0 -116 -81.5 -197.5t-196.5 -81.5q-116 0 -197.5 82t-81.5 197 t82 196.5t197 81.5t196.5 -81.5t81.5 -196.5zM2004 540q0 -115 -81.5 -196.5t-197.5 -81.5q-115 0 -196.5 81.5t-81.5 196.5t81.5 196.5t196.5 81.5q116 0 197.5 -81.5t81.5 -196.5zM1040 537q0 191 -135.5 326.5t-326.5 135.5q-125 0 -231 -62t-168 -168.5t-62 -231.5 t62 -231.5t168 -168.5t231 -62q191 0 326.5 135.5t135.5 326.5zM1708 1110q-254 111 -556 111q-319 0 -573 -110q117 0 223 -45.5t182.5 -122.5t122 -183t45.5 -223q0 115 43.5 219.5t118 180.5t177.5 123t217 50zM2187 537q0 191 -135 326.5t-326 135.5t-326.5 -135.5 t-135.5 -326.5t135.5 -326.5t326.5 -135.5t326 135.5t135 326.5zM1921 1103h383q-44 -51 -75 -114.5t-40 -114.5q110 -151 110 -337q0 -156 -77 -288t-209 -208.5t-287 -76.5q-133 0 -249 56t-196 155q-47 -56 -129 -179q-11 22 -53.5 82.5t-74.5 97.5 q-80 -99 -196.5 -155.5t-249.5 -56.5q-155 0 -287 76.5t-209 208.5t-77 288q0 186 110 337q-9 51 -40 114.5t-75 114.5h365q149 100 355 156.5t432 56.5q224 0 421 -56t348 -157z" />
+<glyph unicode="&#xf263;" horiz-adv-x="1280" d="M640 629q-188 0 -321 133t-133 320q0 188 133 321t321 133t321 -133t133 -321q0 -187 -133 -320t-321 -133zM640 1306q-92 0 -157.5 -65.5t-65.5 -158.5q0 -92 65.5 -157.5t157.5 -65.5t157.5 65.5t65.5 157.5q0 93 -65.5 158.5t-157.5 65.5zM1163 574q13 -27 15 -49.5 t-4.5 -40.5t-26.5 -38.5t-42.5 -37t-61.5 -41.5q-115 -73 -315 -94l73 -72l267 -267q30 -31 30 -74t-30 -73l-12 -13q-31 -30 -74 -30t-74 30q-67 68 -267 268l-267 -268q-31 -30 -74 -30t-73 30l-12 13q-31 30 -31 73t31 74l267 267l72 72q-203 21 -317 94 q-39 25 -61.5 41.5t-42.5 37t-26.5 38.5t-4.5 40.5t15 49.5q10 20 28 35t42 22t56 -2t65 -35q5 -4 15 -11t43 -24.5t69 -30.5t92 -24t113 -11q91 0 174 25.5t120 50.5l38 25q33 26 65 35t56 2t42 -22t28 -35z" />
+<glyph unicode="&#xf264;" d="M927 956q0 -66 -46.5 -112.5t-112.5 -46.5t-112.5 46.5t-46.5 112.5t46.5 112.5t112.5 46.5t112.5 -46.5t46.5 -112.5zM1141 593q-10 20 -28 32t-47.5 9.5t-60.5 -27.5q-10 -8 -29 -20t-81 -32t-127 -20t-124 18t-86 36l-27 18q-31 25 -60.5 27.5t-47.5 -9.5t-28 -32 q-22 -45 -2 -74.5t87 -73.5q83 -53 226 -67l-51 -52q-142 -142 -191 -190q-22 -22 -22 -52.5t22 -52.5l9 -9q22 -22 52.5 -22t52.5 22l191 191q114 -115 191 -191q22 -22 52.5 -22t52.5 22l9 9q22 22 22 52.5t-22 52.5l-191 190l-52 52q141 14 225 67q67 44 87 73.5t-2 74.5 zM1092 956q0 134 -95 229t-229 95t-229 -95t-95 -229t95 -229t229 -95t229 95t95 229zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+<glyph unicode="&#xf265;" horiz-adv-x="1720" d="M1565 1408q65 0 110 -45.5t45 -110.5v-519q0 -176 -68 -336t-182.5 -275t-274 -182.5t-334.5 -67.5q-176 0 -335.5 67.5t-274.5 182.5t-183 275t-68 336v519q0 64 46 110t110 46h1409zM861 344q47 0 82 33l404 388q37 35 37 85q0 49 -34.5 83.5t-83.5 34.5q-47 0 -82 -33 l-323 -310l-323 310q-35 33 -81 33q-49 0 -83.5 -34.5t-34.5 -83.5q0 -51 36 -85l405 -388q33 -33 81 -33z" />
+<glyph unicode="&#xf266;" horiz-adv-x="2304" d="M1494 -103l-295 695q-25 -49 -158.5 -305.5t-198.5 -389.5q-1 -1 -27.5 -0.5t-26.5 1.5q-82 193 -255.5 587t-259.5 596q-21 50 -66.5 107.5t-103.5 100.5t-102 43q0 5 -0.5 24t-0.5 27h583v-50q-39 -2 -79.5 -16t-66.5 -43t-10 -64q26 -59 216.5 -499t235.5 -540 q31 61 140 266.5t131 247.5q-19 39 -126 281t-136 295q-38 69 -201 71v50l513 -1v-47q-60 -2 -93.5 -25t-12.5 -69q33 -70 87 -189.5t86 -187.5q110 214 173 363q24 55 -10 79.5t-129 26.5q1 7 1 25v24q64 0 170.5 0.5t180 1t92.5 0.5v-49q-62 -2 -119 -33t-90 -81 l-213 -442q13 -33 127.5 -290t121.5 -274l441 1017q-14 38 -49.5 62.5t-65 31.5t-55.5 8v50l460 -4l1 -2l-1 -44q-139 -4 -201 -145q-526 -1216 -559 -1291h-49z" />
+<glyph unicode="&#xf267;" horiz-adv-x="1792" d="M949 643q0 -26 -16.5 -45t-41.5 -19q-26 0 -45 16.5t-19 41.5q0 26 17 45t42 19t44 -16.5t19 -41.5zM964 585l350 581q-9 -8 -67.5 -62.5t-125.5 -116.5t-136.5 -127t-117 -110.5t-50.5 -51.5l-349 -580q7 7 67 62t126 116.5t136 127t117 111t50 50.5zM1611 640 q0 -201 -104 -371q-3 2 -17 11t-26.5 16.5t-16.5 7.5q-13 0 -13 -13q0 -10 59 -44q-74 -112 -184.5 -190.5t-241.5 -110.5l-16 67q-1 10 -15 10q-5 0 -8 -5.5t-2 -9.5l16 -68q-72 -15 -146 -15q-199 0 -372 105q1 2 13 20.5t21.5 33.5t9.5 19q0 13 -13 13q-6 0 -17 -14.5 t-22.5 -34.5t-13.5 -23q-113 75 -192 187.5t-110 244.5l69 15q10 3 10 15q0 5 -5.5 8t-10.5 2l-68 -15q-14 72 -14 139q0 206 109 379q2 -1 18.5 -12t30 -19t17.5 -8q13 0 13 12q0 6 -12.5 15.5t-32.5 21.5l-20 12q77 112 189 189t244 107l15 -67q2 -10 15 -10q5 0 8 5.5 t2 10.5l-15 66q71 13 134 13q204 0 379 -109q-39 -56 -39 -65q0 -13 12 -13q11 0 48 64q111 -75 187.5 -186t107.5 -241l-56 -12q-10 -2 -10 -16q0 -5 5.5 -8t9.5 -2l57 13q14 -72 14 -140zM1696 640q0 163 -63.5 311t-170.5 255t-255 170.5t-311 63.5t-311 -63.5 t-255 -170.5t-170.5 -255t-63.5 -311t63.5 -311t170.5 -255t255 -170.5t311 -63.5t311 63.5t255 170.5t170.5 255t63.5 311zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191 t191 -286t71 -348z" />
+<glyph unicode="&#xf268;" horiz-adv-x="1792" d="M893 1536q240 2 451 -120q232 -134 352 -372l-742 39q-160 9 -294 -74.5t-185 -229.5l-276 424q128 159 311 245.5t383 87.5zM146 1131l337 -663q72 -143 211 -217t293 -45l-230 -451q-212 33 -385 157.5t-272.5 316t-99.5 411.5q0 267 146 491zM1732 962 q58 -150 59.5 -310.5t-48.5 -306t-153 -272t-246 -209.5q-230 -133 -498 -119l405 623q88 131 82.5 290.5t-106.5 277.5zM896 942q125 0 213.5 -88.5t88.5 -213.5t-88.5 -213.5t-213.5 -88.5t-213.5 88.5t-88.5 213.5t88.5 213.5t213.5 88.5z" />
+<glyph unicode="&#xf269;" horiz-adv-x="1792" d="M903 -256q-283 0 -504.5 150.5t-329.5 398.5q-58 131 -67 301t26 332.5t111 312t179 242.5l-11 -281q11 14 68 15.5t70 -15.5q42 81 160.5 138t234.5 59q-54 -45 -119.5 -148.5t-58.5 -163.5q25 -8 62.5 -13.5t63 -7.5t68 -4t50.5 -3q15 -5 9.5 -45.5t-30.5 -75.5 q-5 -7 -16.5 -18.5t-56.5 -35.5t-101 -34l15 -189l-139 67q-18 -43 -7.5 -81.5t36 -66.5t65.5 -41.5t81 -6.5q51 9 98 34.5t83.5 45t73.5 17.5q61 -4 89.5 -33t19.5 -65q-1 -2 -2.5 -5.5t-8.5 -12.5t-18 -15.5t-31.5 -10.5t-46.5 -1q-60 -95 -144.5 -135.5t-209.5 -29.5 q74 -61 162.5 -82.5t168.5 -6t154.5 52t128 87.5t80.5 104q43 91 39 192.5t-37.5 188.5t-78.5 125q87 -38 137 -79.5t77 -112.5q15 170 -57.5 343t-209.5 284q265 -77 412 -279.5t151 -517.5q2 -127 -40.5 -255t-123.5 -238t-189 -196t-247.5 -135.5t-288.5 -49.5z" />
+<glyph unicode="&#xf26a;" horiz-adv-x="1792" d="M1493 1308q-165 110 -359 110q-155 0 -293 -73t-240 -200q-75 -93 -119.5 -218t-48.5 -266v-42q4 -141 48.5 -266t119.5 -218q102 -127 240 -200t293 -73q194 0 359 110q-121 -108 -274.5 -168t-322.5 -60q-29 0 -43 1q-175 8 -333 82t-272 193t-181 281t-67 339 q0 182 71 348t191 286t286 191t348 71h3q168 -1 320.5 -60.5t273.5 -167.5zM1792 640q0 -192 -77 -362.5t-213 -296.5q-104 -63 -222 -63q-137 0 -255 84q154 56 253.5 233t99.5 405q0 227 -99 404t-253 234q119 83 254 83q119 0 226 -65q135 -125 210.5 -295t75.5 -361z " />
+<glyph unicode="&#xf26b;" horiz-adv-x="1792" d="M1792 599q0 -56 -7 -104h-1151q0 -146 109.5 -244.5t257.5 -98.5q99 0 185.5 46.5t136.5 130.5h423q-56 -159 -170.5 -281t-267.5 -188.5t-321 -66.5q-187 0 -356 83q-228 -116 -394 -116q-237 0 -237 263q0 115 45 275q17 60 109 229q199 360 475 606 q-184 -79 -427 -354q63 274 283.5 449.5t501.5 175.5q30 0 45 -1q255 117 433 117q64 0 116 -13t94.5 -40.5t66.5 -76.5t24 -115q0 -116 -75 -286q101 -182 101 -390zM1722 1239q0 83 -53 132t-137 49q-108 0 -254 -70q121 -47 222.5 -131.5t170.5 -195.5q51 135 51 216z M128 2q0 -86 48.5 -132.5t134.5 -46.5q115 0 266 83q-122 72 -213.5 183t-137.5 245q-98 -205 -98 -332zM632 715h728q-5 142 -113 237t-251 95q-144 0 -251.5 -95t-112.5 -237z" />
+<glyph unicode="&#xf26c;" horiz-adv-x="2048" d="M1792 288v960q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1248v-960q0 -66 -47 -113t-113 -47h-736v-128h352q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23 v64q0 14 9 23t23 9h352v128h-736q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
+<glyph unicode="&#xf26d;" horiz-adv-x="1792" d="M138 1408h197q-70 -64 -126 -149q-36 -56 -59 -115t-30 -125.5t-8.5 -120t10.5 -132t21 -126t28 -136.5q4 -19 6 -28q51 -238 81 -329q57 -171 152 -275h-272q-48 0 -82 34t-34 82v1304q0 48 34 82t82 34zM1346 1408h308q48 0 82 -34t34 -82v-1304q0 -48 -34 -82t-82 -34 h-178q212 210 196 565l-469 -101q-2 -45 -12 -82t-31 -72t-59.5 -59.5t-93.5 -36.5q-123 -26 -199 40q-32 27 -53 61t-51.5 129t-64.5 258q-35 163 -45.5 263t-5.5 139t23 77q20 41 62.5 73t102.5 45q45 12 83.5 6.5t67 -17t54 -35t43 -48t34.5 -56.5l468 100 q-68 175 -180 287z" />
+<glyph unicode="&#xf26e;" d="M1401 -11l-6 -6q-113 -114 -259 -175q-154 -64 -317 -64q-165 0 -317 64q-148 63 -259 175q-113 112 -175 258q-42 103 -54 189q-4 28 48 36q51 8 56 -20q1 -1 1 -4q18 -90 46 -159q50 -124 152 -226q98 -98 226 -152q132 -56 276 -56q143 0 276 56q128 55 225 152l6 6 q10 10 25 6q12 -3 33 -22q36 -37 17 -58zM929 604l-66 -66l63 -63q21 -21 -7 -49q-17 -17 -32 -17q-10 0 -19 10l-62 61l-66 -66q-5 -5 -15 -5q-15 0 -31 16l-2 2q-18 15 -18 29q0 7 8 17l66 65l-66 66q-16 16 14 45q18 18 31 18q6 0 13 -5l65 -66l65 65q18 17 48 -13 q27 -27 11 -44zM1400 547q0 -118 -46 -228q-45 -105 -126 -186q-80 -80 -187 -126t-228 -46t-228 46t-187 126q-82 82 -125 186q-15 32 -15 40h-1q-9 27 43 44q50 16 60 -12q37 -99 97 -167h1v339v2q3 136 102 232q105 103 253 103q147 0 251 -103t104 -249 q0 -147 -104.5 -251t-250.5 -104q-58 0 -112 16q-28 11 -13 61q16 51 44 43l14 -3q14 -3 32.5 -6t30.5 -3q104 0 176 71.5t72 174.5q0 101 -72 171q-71 71 -175 71q-107 0 -178 -80q-64 -72 -64 -160v-413q110 -67 242 -67q96 0 185 36.5t156 103.5t103.5 155t36.5 183 q0 198 -141 339q-140 140 -339 140q-200 0 -340 -140q-53 -53 -77 -87l-2 -2q-8 -11 -13 -15.5t-21.5 -9.5t-38.5 3q-21 5 -36.5 16.5t-15.5 26.5v680q0 15 10.5 26.5t27.5 11.5h877q30 0 30 -55t-30 -55h-811v-483h1q40 42 102 84t108 61q109 46 231 46q121 0 228 -46 t187 -126q81 -81 126 -186q46 -112 46 -229zM1369 1128q9 -8 9 -18t-5.5 -18t-16.5 -21q-26 -26 -39 -26q-9 0 -16 7q-106 91 -207 133q-128 56 -276 56q-133 0 -262 -49q-27 -10 -45 37q-9 25 -8 38q3 16 16 20q130 57 299 57q164 0 316 -64q137 -58 235 -152z" />
+<glyph unicode="&#xf270;" horiz-adv-x="1792" d="M1551 60q15 6 26 3t11 -17.5t-15 -33.5q-13 -16 -44 -43.5t-95.5 -68t-141 -74t-188 -58t-229.5 -24.5q-119 0 -238 31t-209 76.5t-172.5 104t-132.5 105t-84 87.5q-8 9 -10 16.5t1 12t8 7t11.5 2t11.5 -4.5q192 -117 300 -166q389 -176 799 -90q190 40 391 135z M1758 175q11 -16 2.5 -69.5t-28.5 -102.5q-34 -83 -85 -124q-17 -14 -26 -9t0 24q21 45 44.5 121.5t6.5 98.5q-5 7 -15.5 11.5t-27 6t-29.5 2.5t-35 0t-31.5 -2t-31 -3t-22.5 -2q-6 -1 -13 -1.5t-11 -1t-8.5 -1t-7 -0.5h-5.5h-4.5t-3 0.5t-2 1.5l-1.5 3q-6 16 47 40t103 30 q46 7 108 1t76 -24zM1364 618q0 -31 13.5 -64t32 -58t37.5 -46t33 -32l13 -11l-227 -224q-40 37 -79 75.5t-58 58.5l-19 20q-11 11 -25 33q-38 -59 -97.5 -102.5t-127.5 -63.5t-140 -23t-137.5 21t-117.5 65.5t-83 113t-31 162.5q0 84 28 154t72 116.5t106.5 83t122.5 57 t130 34.5t119.5 18.5t99.5 6.5v127q0 65 -21 97q-34 53 -121 53q-6 0 -16.5 -1t-40.5 -12t-56 -29.5t-56 -59.5t-48 -96l-294 27q0 60 22 119t67 113t108 95t151.5 65.5t190.5 24.5q100 0 181 -25t129.5 -61.5t81 -83t45 -86t12.5 -73.5v-589zM692 597q0 -86 70 -133 q66 -44 139 -22q84 25 114 123q14 45 14 101v162q-59 -2 -111 -12t-106.5 -33.5t-87 -71t-32.5 -114.5z" />
+<glyph unicode="&#xf271;" horiz-adv-x="1792" d="M1536 1280q52 0 90 -38t38 -90v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128zM1152 1376v-288q0 -14 9 -23t23 -9 h64q14 0 23 9t9 23v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM384 1376v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM1536 -128v1024h-1408v-1024h1408zM896 448h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224 v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224z" />
+<glyph unicode="&#xf272;" horiz-adv-x="1792" d="M1152 416v-64q0 -14 -9 -23t-23 -9h-576q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h576q14 0 23 -9t9 -23zM128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23 t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47 t47 -113v-96h128q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf273;" horiz-adv-x="1792" d="M1111 151l-46 -46q-9 -9 -22 -9t-23 9l-188 189l-188 -189q-10 -9 -23 -9t-22 9l-46 46q-9 9 -9 22t9 23l189 188l-189 188q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l188 -188l188 188q10 9 23 9t22 -9l46 -46q9 -9 9 -22t-9 -23l-188 -188l188 -188q9 -10 9 -23t-9 -22z M128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280 q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf274;" horiz-adv-x="1792" d="M1303 572l-512 -512q-10 -9 -23 -9t-23 9l-288 288q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l220 -220l444 444q10 9 23 9t22 -9l46 -46q9 -9 9 -22t-9 -23zM128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23 t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47 t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
+<glyph unicode="&#xf275;" horiz-adv-x="1792" d="M448 1536q26 0 45 -19t19 -45v-891l536 429q17 14 40 14q26 0 45 -19t19 -45v-379l536 429q17 14 40 14q26 0 45 -19t19 -45v-1152q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h384z" />
+<glyph unicode="&#xf276;" horiz-adv-x="1024" d="M512 448q66 0 128 15v-655q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v655q61 -15 128 -15zM512 1536q212 0 362 -150t150 -362t-150 -362t-362 -150t-362 150t-150 362t150 362t362 150zM512 1312q14 0 23 9t9 23t-9 23t-23 9q-146 0 -249 -103t-103 -249 q0 -14 9 -23t23 -9t23 9t9 23q0 119 84.5 203.5t203.5 84.5z" />
+<glyph unicode="&#xf277;" horiz-adv-x="1792" d="M1745 1239q10 -10 10 -23t-10 -23l-141 -141q-28 -28 -68 -28h-1344q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h576v64q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-64h512q40 0 68 -28zM768 320h256v-512q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v512zM1600 768 q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-1344q-40 0 -68 28l-141 141q-10 10 -10 23t10 23l141 141q28 28 68 28h512v192h256v-192h576z" />
+<glyph unicode="&#xf278;" horiz-adv-x="2048" d="M2020 1525q28 -20 28 -53v-1408q0 -20 -11 -36t-29 -23l-640 -256q-24 -11 -48 0l-616 246l-616 -246q-10 -5 -24 -5q-19 0 -36 11q-28 20 -28 53v1408q0 20 11 36t29 23l640 256q24 11 48 0l616 -246l616 246q32 13 60 -6zM736 1390v-1270l576 -230v1270zM128 1173 v-1270l544 217v1270zM1920 107v1270l-544 -217v-1270z" />
+<glyph unicode="&#xf279;" horiz-adv-x="1792" d="M512 1536q13 0 22.5 -9.5t9.5 -22.5v-1472q0 -20 -17 -28l-480 -256q-7 -4 -15 -4q-13 0 -22.5 9.5t-9.5 22.5v1472q0 20 17 28l480 256q7 4 15 4zM1760 1536q13 0 22.5 -9.5t9.5 -22.5v-1472q0 -20 -17 -28l-480 -256q-7 -4 -15 -4q-13 0 -22.5 9.5t-9.5 22.5v1472 q0 20 17 28l480 256q7 4 15 4zM640 1536q8 0 14 -3l512 -256q18 -10 18 -29v-1472q0 -13 -9.5 -22.5t-22.5 -9.5q-8 0 -14 3l-512 256q-18 10 -18 29v1472q0 13 9.5 22.5t22.5 9.5z" />
+<glyph unicode="&#xf27a;" horiz-adv-x="1792" d="M640 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1408 640q0 53 -37.5 90.5t-90.5 37.5 t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-110 0 -211 18q-173 -173 -435 -229q-52 -10 -86 -13q-12 -1 -22 6t-13 18q-4 15 20 37q5 5 23.5 21.5t25.5 23.5t23.5 25.5t24 31.5t20.5 37 t20 48t14.5 57.5t12.5 72.5q-146 90 -229.5 216.5t-83.5 269.5q0 174 120 321.5t326 233t450 85.5t450 -85.5t326 -233t120 -321.5z" />
+<glyph unicode="&#xf27b;" horiz-adv-x="1792" d="M640 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1024 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 -53 -37.5 -90.5t-90.5 -37.5 t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM896 1152q-204 0 -381.5 -69.5t-282 -187.5t-104.5 -255q0 -112 71.5 -213.5t201.5 -175.5l87 -50l-27 -96q-24 -91 -70 -172q152 63 275 171l43 38l57 -6q69 -8 130 -8q204 0 381.5 69.5t282 187.5 t104.5 255t-104.5 255t-282 187.5t-381.5 69.5zM1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22h-5q-15 0 -27 10.5t-16 27.5v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51 t27 59t26 76q-157 89 -247.5 220t-90.5 281q0 130 71 248.5t191 204.5t286 136.5t348 50.5t348 -50.5t286 -136.5t191 -204.5t71 -248.5z" />
+<glyph unicode="&#xf27c;" horiz-adv-x="1024" d="M512 345l512 295v-591l-512 -296v592zM0 640v-591l512 296zM512 1527v-591l-512 -296v591zM512 936l512 295v-591z" />
+<glyph unicode="&#xf27d;" horiz-adv-x="1792" d="M1709 1018q-10 -236 -332 -651q-333 -431 -562 -431q-142 0 -240 263q-44 160 -132 482q-72 262 -157 262q-18 0 -127 -76l-77 98q24 21 108 96.5t130 115.5q156 138 241 146q95 9 153 -55.5t81 -203.5q44 -287 66 -373q55 -249 120 -249q51 0 154 161q101 161 109 246 q13 139 -109 139q-57 0 -121 -26q120 393 459 382q251 -8 236 -326z" />
+<glyph unicode="&#xf27e;" d="M0 1408h1536v-1536h-1536v1536zM1085 293l-221 631l221 297h-634l221 -297l-221 -631l317 -304z" />
+<glyph unicode="&#xf280;" d="M0 1408h1536v-1536h-1536v1536zM908 1088l-12 -33l75 -83l-31 -114l25 -25l107 57l107 -57l25 25l-31 114l75 83l-12 33h-95l-53 96h-32l-53 -96h-95zM641 925q32 0 44.5 -16t11.5 -63l174 21q0 55 -17.5 92.5t-50.5 56t-69 25.5t-85 7q-133 0 -199 -57.5t-66 -182.5v-72 h-96v-128h76q20 0 20 -8v-382q0 -14 -5 -20t-18 -7l-73 -7v-88h448v86l-149 14q-6 1 -8.5 1.5t-3.5 2.5t-0.5 4t1 7t0.5 10v387h191l38 128h-231q-6 0 -2 6t4 9v80q0 27 1.5 40.5t7.5 28t19.5 20t36.5 5.5zM1248 96v86l-54 9q-7 1 -9.5 2.5t-2.5 3t1 7.5t1 12v520h-275 l-23 -101l83 -22q23 -7 23 -27v-370q0 -14 -6 -18.5t-20 -6.5l-70 -9v-86h352z" />
+<glyph unicode="&#xf281;" horiz-adv-x="1792" d="M1792 690q0 -58 -29.5 -105.5t-79.5 -72.5q12 -46 12 -96q0 -155 -106.5 -287t-290.5 -208.5t-400 -76.5t-399.5 76.5t-290 208.5t-106.5 287q0 47 11 94q-51 25 -82 73.5t-31 106.5q0 82 58 140.5t141 58.5q85 0 145 -63q218 152 515 162l116 521q3 13 15 21t26 5 l369 -81q18 37 54 59.5t79 22.5q62 0 106 -43.5t44 -105.5t-44 -106t-106 -44t-105.5 43.5t-43.5 105.5l-334 74l-104 -472q300 -9 519 -160q58 61 143 61q83 0 141 -58.5t58 -140.5zM418 491q0 -62 43.5 -106t105.5 -44t106 44t44 106t-44 105.5t-106 43.5q-61 0 -105 -44 t-44 -105zM1228 136q11 11 11 26t-11 26q-10 10 -25 10t-26 -10q-41 -42 -121 -62t-160 -20t-160 20t-121 62q-11 10 -26 10t-25 -10q-11 -10 -11 -25.5t11 -26.5q43 -43 118.5 -68t122.5 -29.5t91 -4.5t91 4.5t122.5 29.5t118.5 68zM1225 341q62 0 105.5 44t43.5 106 q0 61 -44 105t-105 44q-62 0 -106 -43.5t-44 -105.5t44 -106t106 -44z" />
+<glyph unicode="&#xf282;" horiz-adv-x="1792" d="M69 741h1q16 126 58.5 241.5t115 217t167.5 176t223.5 117.5t276.5 43q231 0 414 -105.5t294 -303.5q104 -187 104 -442v-188h-1125q1 -111 53.5 -192.5t136.5 -122.5t189.5 -57t213 -3t208 46.5t173.5 84.5v-377q-92 -55 -229.5 -92t-312.5 -38t-316 53 q-189 73 -311.5 249t-124.5 372q-3 242 111 412t325 268q-48 -60 -78 -125.5t-46 -159.5h635q8 77 -8 140t-47 101.5t-70.5 66.5t-80.5 41t-75 20.5t-56 8.5l-22 1q-135 -5 -259.5 -44.5t-223.5 -104.5t-176 -140.5t-138 -163.5z" />
+<glyph unicode="&#xf283;" horiz-adv-x="2304" d="M0 32v608h2304v-608q0 -66 -47 -113t-113 -47h-1984q-66 0 -113 47t-47 113zM640 256v-128h384v128h-384zM256 256v-128h256v128h-256zM2144 1408q66 0 113 -47t47 -113v-224h-2304v224q0 66 47 113t113 47h1984z" />
+<glyph unicode="&#xf284;" horiz-adv-x="1792" d="M1549 857q55 0 85.5 -28.5t30.5 -83.5t-34 -82t-91 -27h-136v-177h-25v398h170zM1710 267l-4 -11l-5 -10q-113 -230 -330.5 -366t-474.5 -136q-182 0 -348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71q244 0 454.5 -124t329.5 -338l2 -4l8 -16 q-30 -15 -136.5 -68.5t-163.5 -84.5q-6 -3 -479 -268q384 -183 799 -366zM896 -234q250 0 462.5 132.5t322.5 357.5l-287 129q-72 -140 -206 -222t-292 -82q-151 0 -280 75t-204 204t-75 280t75 280t204 204t280 75t280 -73.5t204 -204.5l280 143q-116 208 -321 329 t-443 121q-119 0 -232.5 -31.5t-209 -87.5t-176.5 -137t-137 -176.5t-87.5 -209t-31.5 -232.5t31.5 -232.5t87.5 -209t137 -176.5t176.5 -137t209 -87.5t232.5 -31.5z" />
+<glyph unicode="&#xf285;" horiz-adv-x="1792" d="M1427 827l-614 386l92 151h855zM405 562l-184 116v858l1183 -743zM1424 697l147 -95v-858l-532 335zM1387 718l-500 -802h-855l356 571z" />
+<glyph unicode="&#xf286;" horiz-adv-x="1792" d="M640 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1152 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1664 496v-752h-640v320q0 80 -56 136t-136 56t-136 -56t-56 -136v-320h-640v752q0 16 16 16h96 q16 0 16 -16v-112h128v624q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h16v393q-32 19 -32 55q0 26 19 45t45 19t45 -19t19 -45q0 -36 -32 -55v-9h272q16 0 16 -16v-224q0 -16 -16 -16h-272v-128h16q16 0 16 -16v-112h128 v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-624h128v112q0 16 16 16h96q16 0 16 -16z" />
+<glyph unicode="&#xf287;" horiz-adv-x="2304" d="M2288 731q16 -8 16 -27t-16 -27l-320 -192q-8 -5 -16 -5q-9 0 -16 4q-16 10 -16 28v128h-858q37 -58 83 -165q16 -37 24.5 -55t24 -49t27 -47t27 -34t31.5 -26t33 -8h96v96q0 14 9 23t23 9h320q14 0 23 -9t9 -23v-320q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v96h-96 q-32 0 -61 10t-51 23.5t-45 40.5t-37 46t-33.5 57t-28.5 57.5t-28 60.5q-23 53 -37 81.5t-36 65t-44.5 53.5t-46.5 17h-360q-22 -84 -91 -138t-157 -54q-106 0 -181 75t-75 181t75 181t181 75q88 0 157 -54t91 -138h104q24 0 46.5 17t44.5 53.5t36 65t37 81.5q19 41 28 60.5 t28.5 57.5t33.5 57t37 46t45 40.5t51 23.5t61 10h107q21 57 70 92.5t111 35.5q80 0 136 -56t56 -136t-56 -136t-136 -56q-62 0 -111 35.5t-70 92.5h-107q-17 0 -33 -8t-31.5 -26t-27 -34t-27 -47t-24 -49t-24.5 -55q-46 -107 -83 -165h1114v128q0 18 16 28t32 -1z" />
+<glyph unicode="&#xf288;" horiz-adv-x="1792" d="M1150 774q0 -56 -39.5 -95t-95.5 -39h-253v269h253q56 0 95.5 -39.5t39.5 -95.5zM1329 774q0 130 -91.5 222t-222.5 92h-433v-896h180v269h253q130 0 222 91.5t92 221.5zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348 t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+<glyph unicode="&#xf289;" horiz-adv-x="2304" d="M1645 438q0 59 -34 106.5t-87 68.5q-7 -45 -23 -92q-7 -24 -27.5 -38t-44.5 -14q-12 0 -24 3q-31 10 -45 38.5t-4 58.5q23 71 23 143q0 123 -61 227.5t-166 165.5t-228 61q-134 0 -247 -73t-167 -194q108 -28 188 -106q22 -23 22 -55t-22 -54t-54 -22t-55 22 q-75 75 -180 75q-106 0 -181 -74.5t-75 -180.5t75 -180.5t181 -74.5h1046q79 0 134.5 55.5t55.5 133.5zM1798 438q0 -142 -100.5 -242t-242.5 -100h-1046q-169 0 -289 119.5t-120 288.5q0 153 100 267t249 136q62 184 221 298t354 114q235 0 408.5 -158.5t196.5 -389.5 q116 -25 192.5 -118.5t76.5 -214.5zM2048 438q0 -175 -97 -319q-23 -33 -64 -33q-24 0 -43 13q-26 17 -32 48.5t12 57.5q71 104 71 233t-71 233q-18 26 -12 57t32 49t57.5 11.5t49.5 -32.5q97 -142 97 -318zM2304 438q0 -244 -134 -443q-23 -34 -64 -34q-23 0 -42 13 q-26 18 -32.5 49t11.5 57q108 164 108 358q0 195 -108 357q-18 26 -11.5 57.5t32.5 48.5q26 18 57 12t49 -33q134 -198 134 -442z" />
+<glyph unicode="&#xf28a;" d="M1500 -13q0 -89 -63 -152.5t-153 -63.5t-153.5 63.5t-63.5 152.5q0 90 63.5 153.5t153.5 63.5t153 -63.5t63 -153.5zM1267 268q-115 -15 -192.5 -102.5t-77.5 -205.5q0 -74 33 -138q-146 -78 -379 -78q-109 0 -201 21t-153.5 54.5t-110.5 76.5t-76 85t-44.5 83 t-23.5 66.5t-6 39.5q0 19 4.5 42.5t18.5 56t36.5 58t64 43.5t94.5 18t94 -17.5t63 -41t35.5 -53t17.5 -49t4 -33.5q0 -34 -23 -81q28 -27 82 -42t93 -17l40 -1q115 0 190 51t75 133q0 26 -9 48.5t-31.5 44.5t-49.5 41t-74 44t-93.5 47.5t-119.5 56.5q-28 13 -43 20 q-116 55 -187 100t-122.5 102t-72 125.5t-20.5 162.5q0 78 20.5 150t66 137.5t112.5 114t166.5 77t221.5 28.5q120 0 220 -26t164.5 -67t109.5 -94t64 -105.5t19 -103.5q0 -46 -15 -82.5t-36.5 -58t-48.5 -36t-49 -19.5t-39 -5h-8h-32t-39 5t-44 14t-41 28t-37 46t-24 70.5 t-10 97.5q-15 16 -59 25.5t-81 10.5l-37 1q-68 0 -117.5 -31t-70.5 -70t-21 -76q0 -24 5 -43t24 -46t53 -51t97 -53.5t150 -58.5q76 -25 138.5 -53.5t109 -55.5t83 -59t60.5 -59.5t41 -62.5t26.5 -62t14.5 -63.5t6 -62t1 -62.5z" />
+<glyph unicode="&#xf28b;" d="M704 352v576q0 14 -9 23t-23 9h-256q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h256q14 0 23 9t9 23zM1152 352v576q0 14 -9 23t-23 9h-256q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h256q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103 t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+<glyph unicode="&#xf28c;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM768 96q148 0 273 73t198 198t73 273t-73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273 t73 -273t198 -198t273 -73zM864 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-192zM480 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-192z" />
+<glyph unicode="&#xf28d;" d="M1088 352v576q0 14 -9 23t-23 9h-576q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h576q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 t103 -385.5z" />
+<glyph unicode="&#xf28e;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM768 96q148 0 273 73t198 198t73 273t-73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273 t73 -273t198 -198t273 -73zM480 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h576q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-576z" />
+<glyph unicode="&#xf290;" horiz-adv-x="1792" d="M1757 128l35 -313q3 -28 -16 -50q-19 -21 -48 -21h-1664q-29 0 -48 21q-19 22 -16 50l35 313h1722zM1664 967l86 -775h-1708l86 775q3 24 21 40.5t43 16.5h256v-128q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5v128h384v-128q0 -53 37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5v128h256q25 0 43 -16.5t21 -40.5zM1280 1152v-256q0 -26 -19 -45t-45 -19t-45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-256q0 -26 -19 -45t-45 -19t-45 19t-19 45v256q0 159 112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5z" />
+<glyph unicode="&#xf291;" horiz-adv-x="2048" d="M1920 768q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5h-15l-115 -662q-8 -46 -44 -76t-82 -30h-1280q-46 0 -82 30t-44 76l-115 662h-15q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5h1792zM485 -32q26 2 43.5 22.5t15.5 46.5l-32 416q-2 26 -22.5 43.5 t-46.5 15.5t-43.5 -22.5t-15.5 -46.5l32 -416q2 -25 20.5 -42t43.5 -17h5zM896 32v416q0 26 -19 45t-45 19t-45 -19t-19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45zM1280 32v416q0 26 -19 45t-45 19t-45 -19t-19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45zM1632 27l32 416 q2 26 -15.5 46.5t-43.5 22.5t-46.5 -15.5t-22.5 -43.5l-32 -416q-2 -26 15.5 -46.5t43.5 -22.5h5q25 0 43.5 17t20.5 42zM476 1244l-93 -412h-132l101 441q19 88 89 143.5t160 55.5h167q0 26 19 45t45 19h384q26 0 45 -19t19 -45h167q90 0 160 -55.5t89 -143.5l101 -441 h-132l-93 412q-11 44 -45.5 72t-79.5 28h-167q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45h-167q-45 0 -79.5 -28t-45.5 -72z" />
+<glyph unicode="&#xf292;" horiz-adv-x="1792" d="M991 512l64 256h-254l-64 -256h254zM1759 1016l-56 -224q-7 -24 -31 -24h-327l-64 -256h311q15 0 25 -12q10 -14 6 -28l-56 -224q-5 -24 -31 -24h-327l-81 -328q-7 -24 -31 -24h-224q-16 0 -26 12q-9 12 -6 28l78 312h-254l-81 -328q-7 -24 -31 -24h-225q-15 0 -25 12 q-9 12 -6 28l78 312h-311q-15 0 -25 12q-9 12 -6 28l56 224q7 24 31 24h327l64 256h-311q-15 0 -25 12q-10 14 -6 28l56 224q5 24 31 24h327l81 328q7 24 32 24h224q15 0 25 -12q9 -12 6 -28l-78 -312h254l81 328q7 24 32 24h224q15 0 25 -12q9 -12 6 -28l-78 -312h311 q15 0 25 -12q9 -12 6 -28z" />
+<glyph unicode="&#xf293;" d="M841 483l148 -148l-149 -149zM840 1094l149 -149l-148 -148zM710 -130l464 464l-306 306l306 306l-464 464v-611l-255 255l-93 -93l320 -321l-320 -321l93 -93l255 255v-611zM1429 640q0 -209 -32 -365.5t-87.5 -257t-140.5 -162.5t-181.5 -86.5t-219.5 -24.5 t-219.5 24.5t-181.5 86.5t-140.5 162.5t-87.5 257t-32 365.5t32 365.5t87.5 257t140.5 162.5t181.5 86.5t219.5 24.5t219.5 -24.5t181.5 -86.5t140.5 -162.5t87.5 -257t32 -365.5z" />
+<glyph unicode="&#xf294;" horiz-adv-x="1024" d="M596 113l173 172l-173 172v-344zM596 823l173 172l-173 172v-344zM628 640l356 -356l-539 -540v711l-297 -296l-108 108l372 373l-372 373l108 108l297 -296v711l539 -540z" />
+<glyph unicode="&#xf295;" d="M1280 256q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM512 1024q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5 t112.5 -271.5zM1440 1344q0 -20 -13 -38l-1056 -1408q-19 -26 -51 -26h-160q-26 0 -45 19t-19 45q0 20 13 38l1056 1408q19 26 51 26h160q26 0 45 -19t19 -45zM768 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5 t271.5 -112.5t112.5 -271.5z" />
+<glyph unicode="&#xf296;" horiz-adv-x="1792" />
+<glyph unicode="&#xf297;" horiz-adv-x="1792" />
+<glyph unicode="&#xf298;" horiz-adv-x="1792" />
+<glyph unicode="&#xf299;" horiz-adv-x="1792" />
+<glyph unicode="&#xf29a;" horiz-adv-x="1792" />
+<glyph unicode="&#xf29b;" horiz-adv-x="1792" />
+<glyph unicode="&#xf29c;" horiz-adv-x="1792" />
+<glyph unicode="&#xf29d;" horiz-adv-x="1792" />
+<glyph unicode="&#xf29e;" horiz-adv-x="1792" />
 <glyph unicode="&#xf500;" horiz-adv-x="1792" />
 </font>
 </defs></svg> 
\ No newline at end of file
old mode 100755 (executable)
new mode 100644 (file)
index 96a3639..26dea79
Binary files a/view/theme/vier/font/fontawesome-webfont.ttf and b/view/theme/vier/font/fontawesome-webfont.ttf differ
old mode 100755 (executable)
new mode 100644 (file)
index 628b6a5..dc35ce3
Binary files a/view/theme/vier/font/fontawesome-webfont.woff and b/view/theme/vier/font/fontawesome-webfont.woff differ
diff --git a/view/theme/vier/font/fontawesome-webfont.woff2 b/view/theme/vier/font/fontawesome-webfont.woff2
new file mode 100644 (file)
index 0000000..500e517
Binary files /dev/null and b/view/theme/vier/font/fontawesome-webfont.woff2 differ
index 2b78d25d7fa76836c46880013bb100aa76477c2a..e08e103b8a919be0f4ca424082d15fc5adddddc9 100644 (file)
@@ -24,72 +24,72 @@ img {
 }
 
 #pending-update {
-        float:right;
-        color: #ffffff;
-        font-weight: bold;
-        background-color: #FF0000;
-        padding: 0em 0.3em;
+  float:right;
+  color: #ffffff;
+  font-weight: bold;
+  background-color: #FF0000;
+  padding: 0em 0.3em;
 }
 
 .admin.linklist {
-        border: 0px;
-       padding: 0px;
-       list-style: none;
-       margin-top: 0px;
+  border: 0px;
+  padding: 0px;
+  list-style: none;
+  margin-top: 0px;
 }
 
 .admin.link {
-        list-style-position: inside;
-        font-size: 1em;
-/*        padding-left: 5px;
-        margin: 5px; */
+  list-style-position: inside;
+  font-size: 1em;
+/*  padding-left: 5px;
+    margin: 5px; */
 }
 
 #adminpage dl {
-        clear: left;
-        margin-bottom: 2px;
-        padding-bottom: 2px;
-        border-bottom: 1px solid black;
+  clear: left;
+  margin-bottom: 2px;
+  padding-bottom: 2px;
+  border-bottom: 1px solid black;
 }
 
 #adminpage dt {
-        width: 200px;
-        float: left;
-        font-weight: bold;
+  width: 200px;
+  float: left;
+  font-weight: bold;
 }
 
 #adminpage dd {
-        margin-left: 200px;
+  margin-left: 200px;
 }
 #adminpage h3 {
-        border-bottom: 1px solid #898989;
-        margin-bottom: 5px;
-        margin-top: 10px;
+  border-bottom: 1px solid #898989;
+  margin-bottom: 5px;
+  margin-top: 10px;
 }
 
 #adminpage .submit {
-        clear:left;
+  clear:left;
 }
 
 #adminpage #pluginslist {
-        margin: 0px; padding: 0px;
+  margin: 0px; padding: 0px;
 }
 
 #adminpage .plugin {
-        list-style: none;
-        display: block;
-       /* border: 1px solid #888888; */
-        padding: 1em;
-        margin-bottom: 5px;
-        clear: left;
+  list-style: none;
+  display: block;
+  /* border: 1px solid #888888; */
+  padding: 1em;
+  margin-bottom: 5px;
+  clear: left;
 }
 
 #adminpage .toggleplugin {
-        float:left;
-        margin-right: 1em;
+  float:left;
+  margin-right: 1em;
 }
 
-#adminpage table {width:100%; border-bottom: 1p solid #000000; margin: 5px 0px;}
+#adminpage table {width:100%; border-bottom: 1px solid #000000; margin: 5px 0px;}
 #adminpage table th { text-align: left;}
 #adminpage td .icon { float: left;}
 #adminpage table#users img { width: 16px; height: 16px; }
@@ -247,15 +247,6 @@ div.pager {
   float: left;
 }
 
-#contact-edit-drop-link-end {
-  /* clear: both; */
-}
-
-#contact-edit-links ul {
-  list-style: none;
-  list-style-type: none;
-}
-
 .hide-comments-outer {
   margin-left: 80px;
   margin-bottom: 5px;
@@ -385,6 +376,14 @@ code {
   overflow: auto;
   padding: 0px;
 }
+.menu-popup .divider {
+  width: 90%;
+  height: 1px;
+  margin: 3px auto;
+  overflow: hidden;
+  background-color: #737373;
+  opacity: 0.4;
+}
 #saved-search-ul .tool:hover,
 #nets-sidebar .tool:hover,
 #sidebar-group-list .tool:hover {
@@ -793,7 +792,8 @@ nav #nav-user-linklabel:hover #nav-user-menu,
 nav #nav-user-linkmenu:hover #nav-user-menu,
 nav #nav-apps-link:hover #nav-apps-menu,
 nav #nav-site-linkmenu:hover #nav-site-menu,
-nav #nav-notifications-linkmenu:hover #nav-notifications-menu {
+nav #nav-notifications-linkmenu:hover #nav-notifications-menu,
+#contact-edit-actions:hover #contact-actions-menu {
   display:block;
   visibility:visible;
   opacity:1;
@@ -1141,6 +1141,10 @@ aside h4, right_aside h4 {
   font-size: 1.17em;
 }
 
+aside img {
+  max-width: 175px;
+}
+
 .nets-ul {
   margin-top: 0px;
 }
@@ -1428,8 +1432,8 @@ section.minimal {
 }
 .children .wall-item-container .wall-item-item .wall-item-content img {
   /* max-width: 650px; */
-  /* max-width: 580px; */
-  max-width: 100%;
+  max-width: 520px;
+  /* max-width: 100%; */
 }
 .wall-item-container .wall-item-links, .wall-item-container .wall-item-actions {
   display: table-cell;
@@ -2931,6 +2935,48 @@ a.mail-list-link {
   color: #999999;
 }
 
+/* contact edit page */
+#contact-edit-nav-wrapper {
+  margin-top: 24px;
+}
+#contact-edit-status-wrapper {
+  border-color: #c9d8f6;
+  background-color: #e0e8fa;
+  border-radius: 3px;
+}
+
+#contact-edit-contact-status {
+  font-weight: bold;
+}
+
+#contact-edit-drop-link-end {
+  /* clear: both; */
+}
+
+#contact-edit-links ul {
+  list-style: none;
+  list-style-type: none;
+}
+
+#contact-edit-settings {
+  margin-top: 10px;
+}
+
+a.btn#contact-edit-actions-button {
+  cursor: pointer;
+  border-radius: 3px;
+  font-size: inherit;
+  font-weight: normal;
+  height: auto;
+  line-height: inherit;
+  padding: 5px 10px;
+}
+
+#lost-contact-message, #insecure-message,
+#block-message, #ignore-message, #archive-message {
+  color: #CB4437;
+}
+
 /* photo album page */
 .photo-top-image-wrapper {
   position: relative;
diff --git a/view/theme/vier/templates/contact_edit.tpl b/view/theme/vier/templates/contact_edit.tpl
new file mode 100644 (file)
index 0000000..ce3cfbf
--- /dev/null
@@ -0,0 +1,98 @@
+
+{{if $header}}<h2>{{$header}}</h2>{{/if}}
+
+<div id="contact-edit-wrapper" >
+
+       {{* Insert Tab-Nav *}}
+       {{$tab_str}}
+
+
+       <div id="contact-edit-nav-wrapper" >
+               <div id="contact-edit-links">
+                       <div id="contact-edit-status-wrapper">
+                               <span id="contact-edit-contact-status">{{$contact_status}}</span>
+
+                               {{* This is the Action menu where contact related actions like 'ignore', 'hide' can be performed *}}
+                               <div id="contact-edit-actions">
+                                       <a class="btn" id="contact-edit-actions-button">{{$contact_action_button}}</a>
+
+                                       <ul role="menu" aria-haspopup="true" id="contact-actions-menu" class="menu-popup" >
+                                               {{if $lblsuggest}}<li role="menuitem"><a  href="#" title="{{$contact_actions.suggest.title}}" onclick="window.location.href='{{$contact_actions.suggest.url}}'; return false;">{{$contact_actions.suggest.label}}</a></li>{{/if}}
+                                               {{if $poll_enabled}}<li role="menuitem"><a  href="#" title="{{$contact_actions.update.title}}" onclick="window.location.href='{{$contact_actions.update.url}}'; return false;">{{$contact_actions.update.label}}</a></li>{{/if}}
+                                               <li class="divider"></li>
+                                               <li role="menuitem"><a  href="#" title="{{$contact_actions.block.title}}" onclick="window.location.href='{{$contact_actions.block.url}}'; return false;">{{$contact_actions.block.label}}</a></li>
+                                               <li role="menuitem"><a  href="#" title="{{$contact_actions.ignore.title}}" onclick="window.location.href='{{$contact_actions.ignore.url}}'; return false;">{{$contact_actions.ignore.label}}</a></li>
+                                               <li role="menuitem"><a  href="#" title="{{$contact_actions.archive.title}}" onclick="window.location.href='{{$contact_actions.archive.url}}'; return false;">{{$contact_actions.archive.label}}</a></li>
+                                               <li role="menuitem"><a  href="#" title="{{$contact_actions.delete.title}}" onclick="return confirmDelete();">{{$contact_actions.delete.label}}</a></li>
+                                       </ul>
+                               </div>
+
+                               {{* Block with status information about the contact *}}
+                               <ul>
+                                       {{if $relation_text}}<li><div id="contact-edit-rel">{{$relation_text}}</div></li>{{/if}}
+
+                                       {{if $poll_enabled}}
+                                               <li><div id="contact-edit-last-update-text">{{$lastupdtext}} <span id="contact-edit-last-updated">{{$last_update}}</span></div>
+                                               {{if $poll_interval}}
+                                                       <span id="contact-edit-poll-text">{{$updpub}}</span> {{$poll_interval}}
+                                               {{/if}}
+                                               </li>
+                                       {{/if}}
+
+                                       {{if $lost_contact}}<li><div id="lost-contact-message">{{$lost_contact}}</div></li>{{/if}}
+                                       {{if $insecure}}<li><div id="insecure-message">{{$insecure}}</div></li> {{/if}}
+                                       {{if $blocked}}<li><div id="block-message">{{$blocked}}</div></li>{{/if}}
+                                       {{if $ignored}}<li><div id="ignore-message">{{$ignored}}</div></li>{{/if}}
+                                       {{if $archived}}<li><div id="archive-message">{{$archived}}</div></li>{{/if}}
+                               </ul>
+
+                               <ul>
+                                       <!-- <li><a href="network/0?nets=all&cid={{$contact_id}}" id="contact-edit-view-recent">{{$lblrecent}}</a></li> -->
+                                       {{if $follow}}<li><div id="contact-edit-follow"><a href="{{$follow}}">{{$follow_text}}</a></div></li>{{/if}}
+                               </ul>
+                       </div> {{* End of contact-edit-status-wrapper *}}
+
+                       {{* Some information about the contact from the profile *}}
+                       <dl><dt>{{$profileurllabel}}</dt><dd><a target="blank" href="{{$url}}">{{$profileurl}}</a></dd></dl>
+                       {{if $location}}<dl><dt>{{$location_label}}</dt><dd>{{$location}}</dd></dl>{{/if}}
+                       {{if $keywords}}<dl><dt>{{$keywords_label}}</dt><dd>{{$keywords}}</dd></dl>{{/if}}
+                       {{if $about}}<dl><dt>{{$about_label}}</dt><dd>{{$about}}</dd></dl>{{/if}}
+               </div>{{* End of contact-edit-links *}}
+
+               <div id="contact-edit-links-end"></div>
+
+               <hr />
+
+               <h4 id="contact-edit-settings-label" class="fakelink" onclick="openClose('contact-edit-settings')">{{$contact_settings_label}}</h4>
+               <div id="contact-edit-settings">
+                       <form action="contacts/{{$contact_id}}" method="post" >
+                       <input type="hidden" name="contact_id" value="{{$contact_id}}">
+
+                               <div id="contact-edit-end" ></div>
+                               {{include file="field_checkbox.tpl" field=$notify}}
+                               {{if $fetch_further_information}}
+                                       {{include file="field_select.tpl" field=$fetch_further_information}}
+                                       {{if $fetch_further_information.2 == 2 }} {{include file="field_textarea.tpl" field=$ffi_keyword_blacklist}} {{/if}}
+                               {{/if}}
+                               {{include file="field_checkbox.tpl" field=$hidden}}
+
+                       <div id="contact-edit-info-wrapper">
+                               <h4>{{$lbl_info1}}</h4>
+                               <textarea id="contact-edit-info" rows="8" cols="60" name="info">{{$info}}</textarea>
+                               <input class="contact-edit-submit" type="submit" name="submit" value="{{$submit|escape:'html'}}" />
+                       </div>
+                       <div id="contact-edit-info-end"></div>
+
+                       {{if $profile_select}}
+                               <div id="contact-edit-profile-select-text">
+                               <h4>{{$lbl_vis1}}</h4>
+                               <p>{{$lbl_vis2}}</p> 
+                               </div>
+                               {{$profile_select}}
+                               <div id="contact-edit-profile-select-end"></div>
+                               <input class="contact-edit-submit" type="submit" name="submit" value="{{$submit|escape:'html'}}" />
+                       {{/if}}
+                       </form>
+               </div>
+       </div>{{* End of contact-edit-nav-wrapper *}}
+</div>
index 267b35df77f3b8f3d7fac41d10351a5d85a1528e..c9ee770819c8e5e33070bb03a6923a485650de69 100644 (file)
@@ -91,7 +91,7 @@
                        {{if $item.threaded}}
                        {{/if}}
                        {{if $item.comment}}
-                               <a role="button" id="comment-{{$item.id}}" class="fakelink togglecomment" onclick="openClose('item-comments-{{$item.id}}'); commentExpand({{$item.id}});" title="{{$item.switchcomment}}"><i class="icon-reply"><span class="sr-only">{{$item.switchcomment}}</span></i></a>
+                               <a role="button" id="comment-{{$item.id}}" class="fakelink togglecomment" onclick="openClose('item-comments-{{$item.id}}'); commentExpand({{$item.id}});" title="{{$item.switchcomment}}"><i class="icon-commenting"><span class="sr-only">{{$item.switchcomment}}</span></i></a>
                        {{/if}}
 
                        {{if $item.isevent}}
index c2669f5a931ae8030d88ba603d596b243ec894b9..925ac76a1fe2284b4798a34268ceb95e7189b314 100644 (file)
@@ -19,10 +19,6 @@ function vier_init(&$a) {
 
        set_template_engine($a, 'smarty3');
 
-       $baseurl = $a->get_baseurl();
-
-       $a->theme_info = array();
-
        if ($a->argv[0].$a->argv[1] === "profile".$a->user['nickname'] or $a->argv[0] === "network" && local_user()) {
                vier_community_info();
 
@@ -160,7 +156,7 @@ function vier_community_info() {
                                $entry = replace_macros($tpl,array(
                                        '$id' => $rr['id'],
                                        //'$profile_link' => zrl($rr['url']),
-                                       '$profile_link' => $a->get_baseurl().'/follow/?url='.urlencode($rr['url']),
+                                       '$profile_link' => 'follow/?url='.urlencode($rr['url']),
                                        '$photo' => proxy_url($rr['photo'], false, PROXY_SIZE_MICRO),
                                        '$alt_text' => $rr['name'],
                                ));
@@ -186,11 +182,11 @@ function vier_community_info() {
                        $aside['$lastusers_items'] = array();
 
                        foreach($r as $rr) {
-                               $profile_link = $a->get_baseurl() . '/profile/' . ((strlen($rr['nickname'])) ? $rr['nickname'] : $rr['profile_uid']);
+                               $profile_link = 'profile/' . ((strlen($rr['nickname'])) ? $rr['nickname'] : $rr['profile_uid']);
                                $entry = replace_macros($tpl,array(
                                        '$id' => $rr['id'],
                                        '$profile_link' => $profile_link,
-                                       '$photo' => $a->get_cached_avatar_image($rr['thumb']),
+                                       '$photo' => $a->remove_baseurl($rr['thumb']),
                                        '$alt_text' => $rr['name']));
                                $aside['$lastusers_items'][] = $entry;
                        }
@@ -207,7 +203,7 @@ function vier_community_info() {
                $nv['suggest'] = Array('suggest', t('Friend Suggestions'), "", "");
                $nv['invite'] = Array('invite', t('Invite Friends'), "", "");
 
-               $nv['search'] = '<form name="simple_bar" method="get" action="'.$a->get_baseurl().'/dirfind">
+               $nv['search'] = '<form name="simple_bar" method="get" action="dirfind">
                                                <span class="sbox_l"></span>
                                                <span class="sbox">
                                                <input type="text" name="search" size="13" maxlength="50">
@@ -241,12 +237,12 @@ function vier_community_info() {
                                $selected = (($cid == $contact['id']) ? ' forum-selected' : '');
 
                                $entry = array(
-                                       'url' => z_root() . '/network?f=&cid=' . $contact['id'],
-                                       'external_url' => z_root() . '/redir/' . $contact['id'],
+                                       'url' => 'network?f=&cid=' . $contact['id'],
+                                       'external_url' => 'redir/' . $contact['id'],
                                        'name' => $contact['name'],
                                        'cid' => $contact['id'],
                                        'selected'      => $selected,
-                                       'micro' => proxy_url($contact['micro'], false, PROXY_SIZE_MICRO),
+                                       'micro' => App::remove_baseurl(proxy_url($contact['micro'], false, PROXY_SIZE_MICRO)),
                                        'id' => ++$id,
                                );
                                $entries[] = $entry;