summaryrefslogtreecommitdiff
path: root/vendor/miniflux/picofeed/lib/PicoFeed
diff options
context:
space:
mode:
authorFrederic Guillot <fred@kanboard.net>2017-10-25 16:22:10 -0700
committerFrederic Guillot <fred@kanboard.net>2017-10-25 16:22:10 -0700
commit9e2b2a32fd0e967ad3184e9a5d091a29953acb91 (patch)
tree00822e24aa1110c73ca455a8d096ef296c008cbc /vendor/miniflux/picofeed/lib/PicoFeed
parentc507c5416251c505cb3e088a03c6664bed73c812 (diff)
Include composer dependencies in repo
Diffstat (limited to 'vendor/miniflux/picofeed/lib/PicoFeed')
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Base.php38
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Client/Client.php719
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Client/Curl.php402
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Client/HttpHeaders.php79
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidUrlException.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxRedirectException.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxSizeException.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Client/Stream.php205
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Client/UnauthorizedException.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Client/Url.php290
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php33
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Filter/Attribute.php700
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php155
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Filter/Html.php243
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php218
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php23
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Generator/FileContentGenerator.php36
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php67
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php114
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php382
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php128
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php315
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Parser/Item.php534
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Parser/Parser.php404
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserInterface.php182
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss10.php306
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php319
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss92.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlEntityException.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlParser.php246
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentFilterProcessor.php37
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php49
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php106
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php25
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Processor/ScraperProcessor.php96
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php186
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php190
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Reader/SubscriptionNotFoundException.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Reader/UnsupportedFeedFormatException.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blog.lemonde.fr.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blogs.nytimes.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/.igen.fr.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/.nytimes.com.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/.over-blog.com.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/.phoronix.com.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/.slate.com.php20
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/.theguardian.com.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wikipedia.org.php29
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wired.com.php44
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wsj.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/01net.com.php18
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/abstrusegoose.com.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/adventuregamers.com.php23
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/alainonline.net.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/aljazeera.com.php25
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/allafrica.com.php20
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/allgemeine-zeitung.de.php23
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/amazingsuperpowers.com.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/anythingcomic.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/ap.org.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/areadvd.de.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/arstechnica.com.php25
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/awkwardzombie.com.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/backchannel.com.php18
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/bangkokpost.com.php19
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/bgr.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigfootjustice.com.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigpicture.ru.php31
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/bizjournals.com.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/biztimes.com.php22
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/bleepingcomputer.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.fefe.de.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.mapillary.com.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/brewers.mlb.com.php22
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/buenosairesherald.com.php17
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/bunicomic.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/buttersafe.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/cad-comic.com.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/chaoslife.findchaos.com.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/chinafile.com.php18
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/cliquerefresh.com.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/cnet.com.php37
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/consomac.fr.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/cowbirdsinlove.com.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/crash.net.php28
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/csmonitor.com.php19
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyjs.com.php19
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyreporter.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailytech.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/degroupnews.com.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/derstandard.at.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/dilbert.com.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/discovermagazine.com.php26
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/distrowatch.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/dozodomo.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/drawingboardcomic.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/e-w-e.ru.php22
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/economist.com.php25
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/encyclopedie.naheulbeuk.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/endlessorigami.com.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/engadget.com.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/escapistmagazine.com.php45
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/espn.go.com.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/exocomics.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/explosm.net.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/extrafabulouscomics.com.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/factroom.ru.php27
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcodesign.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcoexist.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcompany.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/ffworld.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/foreignpolicy.com.php21
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/fossbytes.com.php18
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/fototelegraf.ru.php19
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/fowllanguagecomics.com.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/geek.com.php17
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/geektimes.ru.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/gerbilwithajetpack.com.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/giantitp.com.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/github.com.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/gocomics.com.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/golem.de.php20
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/gorabbit.ru.php19
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/habrahabr.ru.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/happletea.com.php18
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/hardware.fr.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/heise.de.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/hotshowlife.com.php23
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/huffingtonpost.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/imogenquest.net.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/indiehaven.com.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/ing.dk.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/invisiblebread.com.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/ir.amd.com.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantimes.co.jp.php21
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantoday.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/journaldugeek.com.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/jsonline.com.php37
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/justcoolidea.ru.php19
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/kanpai.fr.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/karriere.jobfinder.dk.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/kodi.tv.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreaherald.com.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreatimes.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/lastplacecomics.com.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/legorafi.fr.php22
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/lejapon.fr.php17
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/lesjoiesducode.fr.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/lfg.co.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.com.php18
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.ru.php22
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.ru.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/linuxinsider.com.php20
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/lists.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/loadingartist.com.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/loldwell.com.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/lukesurl.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/macg.co.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/marc.info.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/marriedtothesea.com.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/marycagle.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/maximumble.thebookofbiff.com.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/medium.com.php19
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/mercworks.net.php17
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/metronieuws.nl.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/milwaukeenns.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/mokepon.smackjeeves.com.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/monandroid.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/monwindows.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/moya-planeta.ru.php21
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/mrlovenstein.com.php9
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/muckrock.com.php20
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/mynorthshorenow.com.php27
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/nakedCapitalism.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/nasa.gov.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/nat-geo.ru.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/nationaljournal.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/nature.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/nba.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/nedroid.com.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/networkworld.com.php20
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/neustadt-ticker.de.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/nextinpact.com.php18
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/niceteethcomic.com.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/nichtlustig.de.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/oglaf.com.php19
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/onhax.net.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/onmilwaukee.php24
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/openculture.com.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/opennet.ru.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/openrightsgroup.org.php20
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/opensource.com.php22
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/optipess.com.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/osnews.com.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/pastebin.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/peebleslab.com.php9
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/penny-arcade.com.php21
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/pixelbeat.org.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/plus.google.com.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/popstrip.com.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/publicpolicyforum.org.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/publy.ru.php24
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/putaindecode.fr.php16
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/recode.net.php20
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/retractionwatch.com.php18
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/rockpapershotgun.com.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/rue89.nouvelobs.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/rugbyrama.fr.php20
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/satwcomic.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/scrumalliance.org.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/securityfocus.com.php17
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/sentfromthemoon.com.php18
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/sitepoint.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/slashdot.org.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/smallhousebliss.com.php19
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/smarthomewelt.de.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/smashingmagazine.com.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/smbc-comics.com.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/snopes.com.php22
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/soundandvision.com.php21
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/spiegel.de.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/stereophile.com.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/stupidfox.net.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/subtraction.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/sz.de.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/takprosto.cc.php21
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/techcrunch.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/the-ebook-reader.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/theatlantic.com.php23
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/theawkwardyeti.com.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/thecodinglove.com.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/thedoghousediaries.com.php18
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/thegamercat.com.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/thehindu.com.php19
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/thelocal.se.php17
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/themerepublic.net.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/themoscowtimes.com.php18
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/thenewslens.com.php21
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/theodd1sout.com.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/theonion.com.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/thestandard.com.hk.php22
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/threepanelsoul.com.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/timesofindia.indiatimes.com.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/travel-dealz.de.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/treehugger.com.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/treelobsters.com.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/twogag.com.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/twokinds.keenspot.com.php10
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/undeadly.org.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/upi.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/usatoday.com.php27
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/version2.dk.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/vgcats.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/vuxml.org.php17
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/wausaudailyherald.com.php27
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bbc.co.uk.php33
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bdgest.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bgr.in.php23
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.businessweek.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.cnn.com.php24
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.developpez.com.php21
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.egscomics.com.php12
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.fakingnews.firstpost.com.php17
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.forbes.com.php20
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.franceculture.fr.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.futura-sciences.com.php19
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.geekculture.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.howtogeek.com.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lepoint.fr.php18
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lesnumeriques.com.php25
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.mac4ever.com.php13
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.makeuseof.com.php18
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.monsieur-le-chien.fr.php11
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.npr.org.php28
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.numerama.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.oneindia.com.php14
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.pseudo-sciences.org.php16
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.sciencemag.org.php16
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.slate.fr.php19
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.universfreebox.com.php15
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.zeit.de.php41
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/xkcd.com.php8
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/ymatuhin.ru.php21
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Rules/zdnet.com.php23
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php283
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php20
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleLoader.php107
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php102
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php279
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php175
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php75
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php204
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php100
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php142
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php65
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php139
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php63
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php185
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php209
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php76
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php115
-rw-r--r--vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php67
312 files changed, 13320 insertions, 0 deletions
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Base.php b/vendor/miniflux/picofeed/lib/PicoFeed/Base.php
new file mode 100644
index 00000000..41a6f8f0
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Base.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace PicoFeed;
+
+use PicoFeed\Config\Config;
+use PicoFeed\Logging\Logger;
+
+/**
+ * Base class
+ *
+ * @package PicoFeed
+ * @author Frederic Guillot
+ */
+abstract class Base
+{
+ /**
+ * Config class instance
+ *
+ * @access protected
+ * @var \PicoFeed\Config\Config
+ */
+ protected $config;
+
+ /**
+ * Constructor.
+ *
+ * @param \PicoFeed\Config\Config $config Config class instance
+ */
+ public function __construct(Config $config = null)
+ {
+ $this->config = $config ?: new Config();
+ Logger::setTimezone($this->config->getTimezone());
+ }
+
+ public function setConfig(Config $config) {
+ $this->config = $config;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/Client.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Client.php
new file mode 100644
index 00000000..0548d5c6
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Client.php
@@ -0,0 +1,719 @@
+<?php
+
+namespace PicoFeed\Client;
+
+use DateTime;
+use Exception;
+use LogicException;
+use PicoFeed\Logging\Logger;
+use PicoFeed\Config\Config;
+
+/**
+ * Client class.
+ *
+ * @author Frederic Guillot
+ */
+abstract class Client
+{
+ /**
+ * Flag that say if the resource have been modified.
+ *
+ * @var bool
+ */
+ private $is_modified = true;
+
+ /**
+ * HTTP Content-Type.
+ *
+ * @var string
+ */
+ private $content_type = '';
+
+ /**
+ * HTTP encoding.
+ *
+ * @var string
+ */
+ private $encoding = '';
+
+ /**
+ * HTTP request headers.
+ *
+ * @var array
+ */
+ protected $request_headers = array();
+
+ /**
+ * HTTP Etag header.
+ *
+ * @var string
+ */
+ protected $etag = '';
+
+ /**
+ * HTTP Last-Modified header.
+ *
+ * @var string
+ */
+ protected $last_modified = '';
+
+ /**
+ * Expiration DateTime
+ *
+ * @var DateTime
+ */
+ protected $expiration = null;
+
+ /**
+ * Proxy hostname.
+ *
+ * @var string
+ */
+ protected $proxy_hostname = '';
+
+ /**
+ * Proxy port.
+ *
+ * @var int
+ */
+ protected $proxy_port = 3128;
+
+ /**
+ * Proxy username.
+ *
+ * @var string
+ */
+ protected $proxy_username = '';
+
+ /**
+ * Proxy password.
+ *
+ * @var string
+ */
+ protected $proxy_password = '';
+
+ /**
+ * Basic auth username.
+ *
+ * @var string
+ */
+ protected $username = '';
+
+ /**
+ * Basic auth password.
+ *
+ * @var string
+ */
+ protected $password = '';
+
+ /**
+ * Client connection timeout.
+ *
+ * @var int
+ */
+ protected $timeout = 10;
+
+ /**
+ * User-agent.
+ *
+ * @var string
+ */
+ protected $user_agent = 'PicoFeed (https://github.com/miniflux/picoFeed)';
+
+ /**
+ * Real URL used (can be changed after a HTTP redirect).
+ *
+ * @var string
+ */
+ protected $url = '';
+
+ /**
+ * Page/Feed content.
+ *
+ * @var string
+ */
+ protected $content = '';
+
+ /**
+ * Number maximum of HTTP redirections to avoid infinite loops.
+ *
+ * @var int
+ */
+ protected $max_redirects = 5;
+
+ /**
+ * Maximum size of the HTTP body response.
+ *
+ * @var int
+ */
+ protected $max_body_size = 2097152; // 2MB
+
+ /**
+ * HTTP response status code.
+ *
+ * @var int
+ */
+ protected $status_code = 0;
+
+ /**
+ * Enables direct passthrough to requesting client.
+ *
+ * @var bool
+ */
+ protected $passthrough = false;
+
+ /**
+ * Do the HTTP request.
+ *
+ * @abstract
+ *
+ * @return array
+ */
+ abstract public function doRequest();
+
+ /**
+ * Get client instance: curl or stream driver.
+ *
+ * @static
+ *
+ * @return \PicoFeed\Client\Client
+ */
+ public static function getInstance()
+ {
+ if (function_exists('curl_init')) {
+ return new Curl();
+ } elseif (ini_get('allow_url_fopen')) {
+ return new Stream();
+ }
+
+ throw new LogicException('You must have "allow_url_fopen=1" or curl extension installed');
+ }
+
+ /**
+ * Add HTTP Header to the request.
+ *
+ * @param array $headers
+ */
+ public function setHeaders($headers)
+ {
+ $this->request_headers = $headers;
+ }
+
+ /**
+ * Perform the HTTP request.
+ *
+ * @param string $url URL
+ *
+ * @return Client
+ */
+ public function execute($url = '')
+ {
+ if ($url !== '') {
+ $this->url = $url;
+ }
+
+ Logger::setMessage(get_called_class().' Fetch URL: '.$this->url);
+ Logger::setMessage(get_called_class().' Etag provided: '.$this->etag);
+ Logger::setMessage(get_called_class().' Last-Modified provided: '.$this->last_modified);
+
+ $response = $this->doRequest();
+
+ $this->status_code = $response['status'];
+ $this->handleNotModifiedResponse($response);
+ $this->handleErrorResponse($response);
+ $this->handleNormalResponse($response);
+
+ $this->expiration = $this->parseExpiration($response['headers']);
+ Logger::setMessage(get_called_class().' Expiration: '.$this->expiration->format(DATE_ISO8601));
+
+ return $this;
+ }
+
+ /**
+ * Handle not modified response.
+ *
+ * @param array $response Client response
+ */
+ protected function handleNotModifiedResponse(array $response)
+ {
+ if ($response['status'] == 304) {
+ $this->is_modified = false;
+ } elseif ($response['status'] == 200) {
+ $this->is_modified = $this->hasBeenModified($response, $this->etag, $this->last_modified);
+ $this->etag = $this->getHeader($response, 'ETag');
+ $this->last_modified = $this->getHeader($response, 'Last-Modified');
+ }
+
+ if ($this->is_modified === false) {
+ Logger::setMessage(get_called_class().' Resource not modified');
+ }
+ }
+
+ /**
+ * Handle Http Error codes
+ *
+ * @param array $response Client response
+ * @throws ForbiddenException
+ * @throws InvalidUrlException
+ * @throws UnauthorizedException
+ */
+ protected function handleErrorResponse(array $response)
+ {
+ $status = $response['status'];
+ if ($status == 401) {
+ throw new UnauthorizedException('Wrong or missing credentials');
+ } else if ($status == 403) {
+ throw new ForbiddenException('Not allowed to access resource');
+ } else if ($status == 404) {
+ throw new InvalidUrlException('Resource not found');
+ }
+ }
+
+ /**
+ * Handle normal response.
+ *
+ * @param array $response Client response
+ */
+ protected function handleNormalResponse(array $response)
+ {
+ if ($response['status'] == 200) {
+ $this->content = $response['body'];
+ $this->content_type = $this->findContentType($response);
+ $this->encoding = $this->findCharset();
+ }
+ }
+
+ /**
+ * Check if a request has been modified according to the parameters.
+ *
+ * @param array $response
+ * @param string $etag
+ * @param string $lastModified
+ *
+ * @return bool
+ */
+ private function hasBeenModified($response, $etag, $lastModified)
+ {
+ $headers = array(
+ 'Etag' => $etag,
+ 'Last-Modified' => $lastModified,
+ );
+
+ // Compare the values for each header that is present
+ $presentCacheHeaderCount = 0;
+ foreach ($headers as $key => $value) {
+ if (isset($response['headers'][$key])) {
+ if ($response['headers'][$key] !== $value) {
+ return true;
+ }
+ ++$presentCacheHeaderCount;
+ }
+ }
+
+ // If at least one header is present and the values match, the response
+ // was not modified
+ if ($presentCacheHeaderCount > 0) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Find content type from response headers.
+ *
+ * @param array $response Client response
+ *
+ * @return string
+ */
+ public function findContentType(array $response)
+ {
+ return strtolower($this->getHeader($response, 'Content-Type'));
+ }
+
+ /**
+ * Find charset from response headers.
+ *
+ * @return string
+ */
+ public function findCharset()
+ {
+ $result = explode('charset=', $this->content_type);
+
+ return isset($result[1]) ? $result[1] : '';
+ }
+
+ /**
+ * Get header value from a client response.
+ *
+ * @param array $response Client response
+ * @param string $header Header name
+ *
+ * @return string
+ */
+ public function getHeader(array $response, $header)
+ {
+ return isset($response['headers'][$header]) ? $response['headers'][$header] : '';
+ }
+
+ /**
+ * Set the Last-Modified HTTP header.
+ *
+ * @param string $last_modified Header value
+ *
+ * @return \PicoFeed\Client\Client
+ */
+ public function setLastModified($last_modified)
+ {
+ $this->last_modified = $last_modified;
+
+ return $this;
+ }
+
+ /**
+ * Get the value of the Last-Modified HTTP header.
+ *
+ * @return string
+ */
+ public function getLastModified()
+ {
+ return $this->last_modified;
+ }
+
+ /**
+ * Set the value of the Etag HTTP header.
+ *
+ * @param string $etag Etag HTTP header value
+ *
+ * @return \PicoFeed\Client\Client
+ */
+ public function setEtag($etag)
+ {
+ $this->etag = $etag;
+
+ return $this;
+ }
+
+ /**
+ * Get the Etag HTTP header value.
+ *
+ * @return string
+ */
+ public function getEtag()
+ {
+ return $this->etag;
+ }
+
+ /**
+ * Get the final url value.
+ *
+ * @return string
+ */
+ public function getUrl()
+ {
+ return $this->url;
+ }
+
+ /**
+ * Set the url.
+ *
+ * @param $url
+ * @return string
+ */
+ public function setUrl($url)
+ {
+ $this->url = $url;
+ return $this;
+ }
+
+ /**
+ * Get the HTTP response status code.
+ *
+ * @return int
+ */
+ public function getStatusCode()
+ {
+ return $this->status_code;
+ }
+
+ /**
+ * Get the body of the HTTP response.
+ *
+ * @return string
+ */
+ public function getContent()
+ {
+ return $this->content;
+ }
+
+ /**
+ * Get the content type value from HTTP headers.
+ *
+ * @return string
+ */
+ public function getContentType()
+ {
+ return $this->content_type;
+ }
+
+ /**
+ * Get the encoding value from HTTP headers.
+ *
+ * @return string
+ */
+ public function getEncoding()
+ {
+ return $this->encoding;
+ }
+
+ /**
+ * Return true if the remote resource has changed.
+ *
+ * @return bool
+ */
+ public function isModified()
+ {
+ return $this->is_modified;
+ }
+
+ /**
+ * return true if passthrough mode is enabled.
+ *
+ * @return bool
+ */
+ public function isPassthroughEnabled()
+ {
+ return $this->passthrough;
+ }
+
+ /**
+ * Set connection timeout.
+ *
+ * @param int $timeout Connection timeout
+ *
+ * @return \PicoFeed\Client\Client
+ */
+ public function setTimeout($timeout)
+ {
+ $this->timeout = $timeout ?: $this->timeout;
+
+ return $this;
+ }
+
+ /**
+ * Set a custom user agent.
+ *
+ * @param string $user_agent User Agent
+ *
+ * @return \PicoFeed\Client\Client
+ */
+ public function setUserAgent($user_agent)
+ {
+ $this->user_agent = $user_agent ?: $this->user_agent;
+
+ return $this;
+ }
+
+ /**
+ * Set the maximum number of HTTP redirections.
+ *
+ * @param int $max Maximum
+ *
+ * @return \PicoFeed\Client\Client
+ */
+ public function setMaxRedirections($max)
+ {
+ $this->max_redirects = $max ?: $this->max_redirects;
+
+ return $this;
+ }
+
+ /**
+ * Set the maximum size of the HTTP body.
+ *
+ * @param int $max Maximum
+ *
+ * @return \PicoFeed\Client\Client
+ */
+ public function setMaxBodySize($max)
+ {
+ $this->max_body_size = $max ?: $this->max_body_size;
+
+ return $this;
+ }
+
+ /**
+ * Set the proxy hostname.
+ *
+ * @param string $hostname Proxy hostname
+ *
+ * @return \PicoFeed\Client\Client
+ */
+ public function setProxyHostname($hostname)
+ {
+ $this->proxy_hostname = $hostname ?: $this->proxy_hostname;
+
+ return $this;
+ }
+
+ /**
+ * Set the proxy port.
+ *
+ * @param int $port Proxy port
+ *
+ * @return \PicoFeed\Client\Client
+ */
+ public function setProxyPort($port)
+ {
+ $this->proxy_port = $port ?: $this->proxy_port;
+
+ return $this;
+ }
+
+ /**
+ * Set the proxy username.
+ *
+ * @param string $username Proxy username
+ *
+ * @return \PicoFeed\Client\Client
+ */
+ public function setProxyUsername($username)
+ {
+ $this->proxy_username = $username ?: $this->proxy_username;
+
+ return $this;
+ }
+
+ /**
+ * Set the proxy password.
+ *
+ * @param string $password Password
+ *
+ * @return \PicoFeed\Client\Client
+ */
+ public function setProxyPassword($password)
+ {
+ $this->proxy_password = $password ?: $this->proxy_password;
+
+ return $this;
+ }
+
+ /**
+ * Set the username.
+ *
+ * @param string $username Basic Auth username
+ *
+ * @return \PicoFeed\Client\Client
+ */
+ public function setUsername($username)
+ {
+ $this->username = $username ?: $this->username;
+
+ return $this;
+ }
+
+ /**
+ * Set the password.
+ *
+ * @param string $password Basic Auth Password
+ *
+ * @return \PicoFeed\Client\Client
+ */
+ public function setPassword($password)
+ {
+ $this->password = $password ?: $this->password;
+
+ return $this;
+ }
+
+ /**
+ * Enable the passthrough mode.
+ *
+ * @return \PicoFeed\Client\Client
+ */
+ public function enablePassthroughMode()
+ {
+ $this->passthrough = true;
+
+ return $this;
+ }
+
+ /**
+ * Disable the passthrough mode.
+ *
+ * @return \PicoFeed\Client\Client
+ */
+ public function disablePassthroughMode()
+ {
+ $this->passthrough = false;
+
+ return $this;
+ }
+
+ /**
+ * Set config object.
+ *
+ * @param \PicoFeed\Config\Config $config Config instance
+ *
+ * @return \PicoFeed\Client\Client
+ */
+ public function setConfig(Config $config)
+ {
+ if ($config !== null) {
+ $this->setTimeout($config->getClientTimeout());
+ $this->setUserAgent($config->getClientUserAgent());
+ $this->setMaxRedirections($config->getMaxRedirections());
+ $this->setMaxBodySize($config->getMaxBodySize());
+ $this->setProxyHostname($config->getProxyHostname());
+ $this->setProxyPort($config->getProxyPort());
+ $this->setProxyUsername($config->getProxyUsername());
+ $this->setProxyPassword($config->getProxyPassword());
+ }
+
+ return $this;
+ }
+
+ /**
+ * Return true if the HTTP status code is a redirection
+ *
+ * @access protected
+ * @param integer $code
+ * @return boolean
+ */
+ public function isRedirection($code)
+ {
+ return $code == 301 || $code == 302 || $code == 303 || $code == 307;
+ }
+
+ public function parseExpiration(HttpHeaders $headers)
+ {
+ try {
+
+ if (isset($headers['Cache-Control'])) {
+ if (preg_match('/s-maxage=(\d+)/', $headers['Cache-Control'], $matches)) {
+ return new DateTime('+' . $matches[1] . ' seconds');
+ } else if (preg_match('/max-age=(\d+)/', $headers['Cache-Control'], $matches)) {
+ return new DateTime('+' . $matches[1] . ' seconds');
+ }
+ }
+
+ if (! empty($headers['Expires'])) {
+ return new DateTime($headers['Expires']);
+ }
+ } catch (Exception $e) {
+ Logger::setMessage('Unable to parse expiration date: '.$e->getMessage());
+ }
+
+ return new DateTime();
+ }
+
+ /**
+ * Get expiration date time from "Expires" or "Cache-Control" headers
+ *
+ * @return DateTime
+ */
+ public function getExpiration()
+ {
+ return $this->expiration ?: new DateTime();
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php
new file mode 100644
index 00000000..b3a95c9f
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace PicoFeed\Client;
+
+use PicoFeed\PicoFeedException;
+
+/**
+ * ClientException Exception.
+ *
+ * @author Frederic Guillot
+ */
+abstract class ClientException extends PicoFeedException
+{
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/Curl.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Curl.php
new file mode 100644
index 00000000..f4a65782
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Curl.php
@@ -0,0 +1,402 @@
+<?php
+
+namespace PicoFeed\Client;
+
+use PicoFeed\Logging\Logger;
+
+/**
+ * cURL HTTP client.
+ *
+ * @author Frederic Guillot
+ */
+class Curl extends Client
+{
+ protected $nbRedirects = 0;
+
+ /**
+ * HTTP response body.
+ *
+ * @var string
+ */
+ private $body = '';
+
+ /**
+ * Body size.
+ *
+ * @var int
+ */
+ private $body_length = 0;
+
+ /**
+ * HTTP response headers.
+ *
+ * @var array
+ */
+ private $response_headers = array();
+
+ /**
+ * Counter on the number of header received.
+ *
+ * @var int
+ */
+ private $response_headers_count = 0;
+
+ /**
+ * cURL callback to read the HTTP body.
+ *
+ * If the function return -1, curl stop to read the HTTP response
+ *
+ * @param resource $ch cURL handler
+ * @param string $buffer Chunk of data
+ *
+ * @return int Length of the buffer
+ */
+ public function readBody($ch, $buffer)
+ {
+ $length = strlen($buffer);
+ $this->body_length += $length;
+
+ if ($this->body_length > $this->max_body_size) {
+ return -1;
+ }
+
+ $this->body .= $buffer;
+
+ return $length;
+ }
+
+ /**
+ * cURL callback to read HTTP headers.
+ *
+ * @param resource $ch cURL handler
+ * @param string $buffer Header line
+ *
+ * @return int Length of the buffer
+ */
+ public function readHeaders($ch, $buffer)
+ {
+ $length = strlen($buffer);
+
+ if ($buffer === "\r\n" || $buffer === "\n") {
+ ++$this->response_headers_count;
+ } else {
+ if (!isset($this->response_headers[$this->response_headers_count])) {
+ $this->response_headers[$this->response_headers_count] = '';
+ }
+
+ $this->response_headers[$this->response_headers_count] .= $buffer;
+ }
+
+ return $length;
+ }
+
+ /**
+ * cURL callback to passthrough the HTTP body to the client.
+ *
+ * If the function return -1, curl stop to read the HTTP response
+ *
+ * @param resource $ch cURL handler
+ * @param string $buffer Chunk of data
+ *
+ * @return int Length of the buffer
+ */
+ public function passthroughBody($ch, $buffer)
+ {
+ // do it only at the beginning of a transmission
+ if ($this->body_length === 0) {
+ list($status, $headers) = HttpHeaders::parse(explode("\n", $this->response_headers[$this->response_headers_count - 1]));
+
+ if ($this->isRedirection($status)) {
+ return $this->handleRedirection($headers['Location']);
+ }
+
+ // Do not work with PHP-FPM
+ if (strpos(PHP_SAPI, 'cgi') !== false) {
+ header(':', true, $status);
+ }
+
+ if (isset($headers['Content-Type'])) {
+ header('Content-Type:' .$headers['Content-Type']);
+ }
+ }
+
+ $length = strlen($buffer);
+ $this->body_length += $length;
+
+ echo $buffer;
+
+ return $length;
+ }
+
+ /**
+ * Prepare HTTP headers.
+ *
+ * @return string[]
+ */
+ private function prepareHeaders()
+ {
+ $headers = array(
+ 'Connection: close',
+ );
+
+ if ($this->etag) {
+ $headers[] = 'If-None-Match: '.$this->etag;
+ $headers[] = 'A-IM: feed';
+ }
+
+ if ($this->last_modified) {
+ $headers[] = 'If-Modified-Since: '.$this->last_modified;
+ }
+
+ $headers = array_merge($headers, $this->request_headers);
+
+ return $headers;
+ }
+
+ /**
+ * Prepare curl proxy context.
+ *
+ * @param resource $ch
+ *
+ * @return resource $ch
+ */
+ private function prepareProxyContext($ch)
+ {
+ if ($this->proxy_hostname) {
+ Logger::setMessage(get_called_class().' Proxy: '.$this->proxy_hostname.':'.$this->proxy_port);
+
+ curl_setopt($ch, CURLOPT_PROXYPORT, $this->proxy_port);
+ curl_setopt($ch, CURLOPT_PROXYTYPE, 'HTTP');
+ curl_setopt($ch, CURLOPT_PROXY, $this->proxy_hostname);
+
+ if ($this->proxy_username) {
+ Logger::setMessage(get_called_class().' Proxy credentials: Yes');
+ curl_setopt($ch, CURLOPT_PROXYUSERPWD, $this->proxy_username.':'.$this->proxy_password);
+ } else {
+ Logger::setMessage(get_called_class().' Proxy credentials: No');
+ }
+ }
+
+ return $ch;
+ }
+
+ /**
+ * Prepare curl auth context.
+ *
+ * @param resource $ch
+ *
+ * @return resource $ch
+ */
+ private function prepareAuthContext($ch)
+ {
+ if ($this->username && $this->password) {
+ curl_setopt($ch, CURLOPT_USERPWD, $this->username.':'.$this->password);
+ }
+
+ return $ch;
+ }
+
+ /**
+ * Set write/header functions.
+ *
+ * @param resource $ch
+ *
+ * @return resource $ch
+ */
+ private function prepareDownloadMode($ch)
+ {
+ $this->body = '';
+ $this->response_headers = array();
+ $this->response_headers_count = 0;
+ $write_function = 'readBody';
+ $header_function = 'readHeaders';
+
+ if ($this->isPassthroughEnabled()) {
+ $write_function = 'passthroughBody';
+ }
+
+ curl_setopt($ch, CURLOPT_WRITEFUNCTION, array($this, $write_function));
+ curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, $header_function));
+
+ return $ch;
+ }
+
+ /**
+ * Prepare curl context.
+ *
+ * @return resource
+ */
+ private function prepareContext()
+ {
+ $ch = curl_init();
+
+ curl_setopt($ch, CURLOPT_URL, $this->url);
+ curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+ curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->timeout);
+ curl_setopt($ch, CURLOPT_USERAGENT, $this->user_agent);
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $this->prepareHeaders());
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
+ curl_setopt($ch, CURLOPT_ENCODING, '');
+ curl_setopt($ch, CURLOPT_COOKIEJAR, 'php://memory');
+ curl_setopt($ch, CURLOPT_COOKIEFILE, 'php://memory');
+
+ // Disable SSLv3 by enforcing TLSv1.x for curl >= 7.34.0 and < 7.39.0.
+ // Versions prior to 7.34 and at least when compiled against openssl
+ // interpret this parameter as "limit to TLSv1.0" which fails for sites
+ // which enforce TLS 1.1+.
+ // Starting with curl 7.39.0 SSLv3 is disabled by default.
+ $version = curl_version();
+ if ($version['version_number'] >= 467456 && $version['version_number'] < 468736) {
+ curl_setopt($ch, CURLOPT_SSLVERSION, 1);
+ }
+
+ $ch = $this->prepareDownloadMode($ch);
+ $ch = $this->prepareProxyContext($ch);
+ $ch = $this->prepareAuthContext($ch);
+
+ return $ch;
+ }
+
+ /**
+ * Execute curl context.
+ */
+ private function executeContext()
+ {
+ $ch = $this->prepareContext();
+ curl_exec($ch);
+
+ Logger::setMessage(get_called_class().' cURL total time: '.curl_getinfo($ch, CURLINFO_TOTAL_TIME));
+ Logger::setMessage(get_called_class().' cURL dns lookup time: '.curl_getinfo($ch, CURLINFO_NAMELOOKUP_TIME));
+ Logger::setMessage(get_called_class().' cURL connect time: '.curl_getinfo($ch, CURLINFO_CONNECT_TIME));
+ Logger::setMessage(get_called_class().' cURL speed download: '.curl_getinfo($ch, CURLINFO_SPEED_DOWNLOAD));
+ Logger::setMessage(get_called_class().' cURL effective url: '.curl_getinfo($ch, CURLINFO_EFFECTIVE_URL));
+
+ $curl_errno = curl_errno($ch);
+
+ if ($curl_errno) {
+ Logger::setMessage(get_called_class().' cURL error: '.curl_error($ch));
+ curl_close($ch);
+
+ $this->handleError($curl_errno);
+ }
+
+ // Update the url if there where redirects
+ $this->url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
+
+ curl_close($ch);
+ }
+
+ /**
+ * Do the HTTP request.
+ *
+ * @return array HTTP response ['body' => ..., 'status' => ..., 'headers' => ...]
+ */
+ public function doRequest()
+ {
+ $this->executeContext();
+
+ list($status, $headers) = HttpHeaders::parse(explode("\n", $this->response_headers[$this->response_headers_count - 1]));
+
+ if ($this->isRedirection($status)) {
+ if (empty($headers['Location'])) {
+ $status = 200;
+ } else {
+ return $this->handleRedirection($headers['Location']);
+ }
+ }
+
+ return array(
+ 'status' => $status,
+ 'body' => $this->body,
+ 'headers' => $headers,
+ );
+ }
+
+ /**
+ * Handle HTTP redirects
+ *
+ * @param string $location Redirected URL
+ * @return array
+ * @throws MaxRedirectException
+ */
+ private function handleRedirection($location)
+ {
+ $result = array();
+ $this->url = Url::resolve($location, $this->url);
+ $this->body = '';
+ $this->body_length = 0;
+ $this->response_headers = array();
+ $this->response_headers_count = 0;
+
+ while (true) {
+ $this->nbRedirects++;
+
+ if ($this->nbRedirects >= $this->max_redirects) {
+ throw new MaxRedirectException('Maximum number of redirections reached');
+ }
+
+ $result = $this->doRequest();
+
+ if ($this->isRedirection($result['status'])) {
+ $this->url = Url::resolve($result['headers']['Location'], $this->url);
+ $this->body = '';
+ $this->body_length = 0;
+ $this->response_headers = array();
+ $this->response_headers_count = 0;
+ } else {
+ break;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Handle cURL errors (throw individual exceptions).
+ *
+ * We don't use constants because they are not necessary always available
+ * (depends of the version of libcurl linked to php)
+ *
+ * @see http://curl.haxx.se/libcurl/c/libcurl-errors.html
+ *
+ * @param int $errno cURL error code
+ * @throws InvalidCertificateException
+ * @throws InvalidUrlException
+ * @throws MaxRedirectException
+ * @throws MaxSizeException
+ * @throws TimeoutException
+ */
+ private function handleError($errno)
+ {
+ switch ($errno) {
+ case 78: // CURLE_REMOTE_FILE_NOT_FOUND
+ throw new InvalidUrlException('Resource not found', $errno);
+ case 6: // CURLE_COULDNT_RESOLVE_HOST
+ throw new InvalidUrlException('Unable to resolve hostname', $errno);
+ case 7: // CURLE_COULDNT_CONNECT
+ throw new InvalidUrlException('Unable to connect to the remote host', $errno);
+ case 23: // CURLE_WRITE_ERROR
+ throw new MaxSizeException('Maximum response size exceeded', $errno);
+ case 28: // CURLE_OPERATION_TIMEDOUT
+ throw new TimeoutException('Operation timeout', $errno);
+ case 35: // CURLE_SSL_CONNECT_ERROR
+ case 51: // CURLE_PEER_FAILED_VERIFICATION
+ case 58: // CURLE_SSL_CERTPROBLEM
+ case 60: // CURLE_SSL_CACERT
+ case 59: // CURLE_SSL_CIPHER
+ case 64: // CURLE_USE_SSL_FAILED
+ case 66: // CURLE_SSL_ENGINE_INITFAILED
+ case 77: // CURLE_SSL_CACERT_BADFILE
+ case 83: // CURLE_SSL_ISSUER_ERROR
+ $msg = 'Invalid SSL certificate caused by CURL error number ' . $errno;
+ throw new InvalidCertificateException($msg, $errno);
+ case 47: // CURLE_TOO_MANY_REDIRECTS
+ throw new MaxRedirectException('Maximum number of redirections reached', $errno);
+ case 63: // CURLE_FILESIZE_EXCEEDED
+ throw new MaxSizeException('Maximum response size exceeded', $errno);
+ default:
+ throw new InvalidUrlException('Unable to fetch the URL', $errno);
+ }
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php
new file mode 100644
index 00000000..c226e95a
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace PicoFeed\Client;
+
+/**
+ * @author Bernhard Posselt
+ */
+class ForbiddenException extends ClientException
+{
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/HttpHeaders.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/HttpHeaders.php
new file mode 100644
index 00000000..34b81399
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/HttpHeaders.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace PicoFeed\Client;
+
+use ArrayAccess;
+use PicoFeed\Logging\Logger;
+
+/**
+ * Class to handle HTTP headers case insensitivity.
+ *
+ * @author Bernhard Posselt
+ * @author Frederic Guillot
+ */
+class HttpHeaders implements ArrayAccess
+{
+ private $headers = array();
+
+ public function __construct(array $headers)
+ {
+ foreach ($headers as $key => $value) {
+ $this->headers[strtolower($key)] = $value;
+ }
+ }
+
+ public function offsetGet($offset)
+ {
+ return $this->offsetExists($offset) ? $this->headers[strtolower($offset)] : '';
+ }
+
+ public function offsetSet($offset, $value)
+ {
+ $this->headers[strtolower($offset)] = $value;
+ }
+
+ public function offsetExists($offset)
+ {
+ return isset($this->headers[strtolower($offset)]);
+ }
+
+ public function offsetUnset($offset)
+ {
+ unset($this->headers[strtolower($offset)]);
+ }
+
+ /**
+ * Parse HTTP headers.
+ *
+ * @static
+ *
+ * @param array $lines List of headers
+ *
+ * @return array
+ */
+ public static function parse(array $lines)
+ {
+ $status = 0;
+ $headers = array();
+
+ foreach ($lines as $line) {
+ if (strpos($line, 'HTTP/1') === 0) {
+ $headers = array();
+ $status = (int) substr($line, 9, 3);
+ } elseif (strpos($line, ': ') !== false) {
+ list($name, $value) = explode(': ', $line);
+ if ($value) {
+ $headers[trim($name)] = trim($value);
+ }
+ }
+ }
+
+ Logger::setMessage(get_called_class().' HTTP status code: '.$status);
+
+ foreach ($headers as $name => $value) {
+ Logger::setMessage(get_called_class().' HTTP header: '.$name.' => '.$value);
+ }
+
+ return array($status, new self($headers));
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php
new file mode 100644
index 00000000..8d25d7e4
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace PicoFeed\Client;
+
+/**
+ * InvalidCertificateException Exception.
+ *
+ * @author Frederic Guillot
+ */
+class InvalidCertificateException extends ClientException
+{
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidUrlException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidUrlException.php
new file mode 100644
index 00000000..15534d98
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidUrlException.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace PicoFeed\Client;
+
+/**
+ * InvalidUrlException Exception.
+ *
+ * @author Frederic Guillot
+ */
+class InvalidUrlException extends ClientException
+{
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxRedirectException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxRedirectException.php
new file mode 100644
index 00000000..0a221af6
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxRedirectException.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace PicoFeed\Client;
+
+/**
+ * MaxRedirectException Exception.
+ *
+ * @author Frederic Guillot
+ */
+class MaxRedirectException extends ClientException
+{
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxSizeException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxSizeException.php
new file mode 100644
index 00000000..201b22a6
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxSizeException.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace PicoFeed\Client;
+
+/**
+ * MaxSizeException Exception.
+ *
+ * @author Frederic Guillot
+ */
+class MaxSizeException extends ClientException
+{
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/Stream.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Stream.php
new file mode 100644
index 00000000..2e91d472
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Stream.php
@@ -0,0 +1,205 @@
+<?php
+
+namespace PicoFeed\Client;
+
+use PicoFeed\Logging\Logger;
+
+/**
+ * Stream context HTTP client.
+ *
+ * @author Frederic Guillot
+ */
+class Stream extends Client
+{
+ /**
+ * Prepare HTTP headers.
+ *
+ * @return string[]
+ */
+ private function prepareHeaders()
+ {
+ $headers = array(
+ 'Connection: close',
+ 'User-Agent: '.$this->user_agent,
+ );
+
+ // disable compression in passthrough mode. It could result in double
+ // compressed content which isn't decodeable by browsers
+ if (function_exists('gzdecode') && !$this->isPassthroughEnabled()) {
+ $headers[] = 'Accept-Encoding: gzip';
+ }
+
+ if ($this->etag) {
+ $headers[] = 'If-None-Match: '.$this->etag;
+ $headers[] = 'A-IM: feed';
+ }
+
+ if ($this->last_modified) {
+ $headers[] = 'If-Modified-Since: '.$this->last_modified;
+ }
+
+ if ($this->proxy_username) {
+ $headers[] = 'Proxy-Authorization: Basic '.base64_encode($this->proxy_username.':'.$this->proxy_password);
+ }
+
+ if ($this->username && $this->password) {
+ $headers[] = 'Authorization: Basic '.base64_encode($this->username.':'.$this->password);
+ }
+
+ $headers = array_merge($headers, $this->request_headers);
+
+ return $headers;
+ }
+
+ /**
+ * Construct the final URL from location headers.
+ *
+ * @param array $headers List of HTTP response header
+ */
+ private function setEffectiveUrl($headers)
+ {
+ foreach ($headers as $header) {
+ if (stripos($header, 'Location') === 0) {
+ list(, $value) = explode(': ', $header);
+
+ $this->url = Url::resolve($value, $this->url);
+ }
+ }
+ }
+
+ /**
+ * Prepare stream context.
+ *
+ * @return array
+ */
+ private function prepareContext()
+ {
+ $context = array(
+ 'http' => array(
+ 'method' => 'GET',
+ 'protocol_version' => 1.1,
+ 'timeout' => $this->timeout,
+ 'max_redirects' => $this->max_redirects,
+ ),
+ );
+
+ if ($this->proxy_hostname) {
+ Logger::setMessage(get_called_class().' Proxy: '.$this->proxy_hostname.':'.$this->proxy_port);
+
+ $context['http']['proxy'] = 'tcp://'.$this->proxy_hostname.':'.$this->proxy_port;
+ $context['http']['request_fulluri'] = true;
+
+ if ($this->proxy_username) {
+ Logger::setMessage(get_called_class().' Proxy credentials: Yes');
+ } else {
+ Logger::setMessage(get_called_class().' Proxy credentials: No');
+ }
+ }
+
+ $context['http']['header'] = implode("\r\n", $this->prepareHeaders());
+
+ return $context;
+ }
+
+ /**
+ * Do the HTTP request.
+ *
+ * @return array HTTP response ['body' => ..., 'status' => ..., 'headers' => ...]
+ * @throws InvalidUrlException
+ * @throws MaxSizeException
+ * @throws TimeoutException
+ */
+ public function doRequest()
+ {
+ $body = '';
+
+ // Create context
+ $context = stream_context_create($this->prepareContext());
+
+ // Make HTTP request
+ $stream = @fopen($this->url, 'r', false, $context);
+ if (!is_resource($stream)) {
+ throw new InvalidUrlException('Unable to establish a connection');
+ }
+
+ // Get HTTP headers response
+ $metadata = stream_get_meta_data($stream);
+ list($status, $headers) = HttpHeaders::parse($metadata['wrapper_data']);
+
+ if ($this->isPassthroughEnabled()) {
+ header(':', true, $status);
+
+ if (isset($headers['Content-Type'])) {
+ header('Content-Type: '.$headers['Content-Type']);
+ }
+
+ fpassthru($stream);
+ } else {
+ // Get the entire body until the max size
+ $body = stream_get_contents($stream, $this->max_body_size + 1);
+
+ // If the body size is too large abort everything
+ if (strlen($body) > $this->max_body_size) {
+ throw new MaxSizeException('Content size too large');
+ }
+
+ if ($metadata['timed_out']) {
+ throw new TimeoutException('Operation timeout');
+ }
+ }
+
+ fclose($stream);
+
+ $this->setEffectiveUrl($metadata['wrapper_data']);
+
+ return array(
+ 'status' => $status,
+ 'body' => $this->decodeBody($body, $headers),
+ 'headers' => $headers,
+ );
+ }
+
+ /**
+ * Decode body response according to the HTTP headers.
+ *
+ * @param string $body Raw body
+ * @param HttpHeaders $headers HTTP headers
+ *
+ * @return string
+ */
+ public function decodeBody($body, HttpHeaders $headers)
+ {
+ if (isset($headers['Transfer-Encoding']) && $headers['Transfer-Encoding'] === 'chunked') {
+ $body = $this->decodeChunked($body);
+ }
+
+ if (isset($headers['Content-Encoding']) && $headers['Content-Encoding'] === 'gzip') {
+ $body = gzdecode($body);
+ }
+
+ return $body;
+ }
+
+ /**
+ * Decode a chunked body.
+ *
+ * @param string $str Raw body
+ *
+ * @return string Decoded body
+ */
+ public function decodeChunked($str)
+ {
+ for ($result = ''; !empty($str); $str = trim($str)) {
+
+ // Get the chunk length
+ $pos = strpos($str, "\r\n");
+ $len = hexdec(substr($str, 0, $pos));
+
+ // Append the chunk to the result
+ $result .= substr($str, $pos + 2, $len);
+ $str = substr($str, $pos + 2 + $len);
+ }
+
+ return $result;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php
new file mode 100644
index 00000000..da98da12
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace PicoFeed\Client;
+
+/**
+ * TimeoutException Exception.
+ *
+ * @author Frederic Guillot
+ */
+class TimeoutException extends ClientException
+{
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/UnauthorizedException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/UnauthorizedException.php
new file mode 100644
index 00000000..81898b99
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/UnauthorizedException.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace PicoFeed\Client;
+
+/**
+ * @author Bernhard Posselt
+ */
+class UnauthorizedException extends ClientException
+{
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/Url.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Url.php
new file mode 100644
index 00000000..a9337988
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Url.php
@@ -0,0 +1,290 @@
+<?php
+
+namespace PicoFeed\Client;
+
+/**
+ * URL class.
+ *
+ * @author Frederic Guillot
+ */
+class Url
+{
+ /**
+ * URL.
+ *
+ * @var string
+ */
+ private $url = '';
+
+ /**
+ * URL components.
+ *
+ * @var array
+ */
+ private $components = array();
+
+ /**
+ * Constructor.
+ *
+ * @param string $url URL
+ */
+ public function __construct($url)
+ {
+ $this->url = $url;
+ $this->components = parse_url($url) ?: array();
+
+ // Issue with PHP < 5.4.7 and protocol relative url
+ if (version_compare(PHP_VERSION, '5.4.7', '<') && $this->isProtocolRelative()) {
+ $pos = strpos($this->components['path'], '/', 2);
+
+ if ($pos === false) {
+ $pos = strlen($this->components['path']);
+ }
+
+ $this->components['host'] = substr($this->components['path'], 2, $pos - 2);
+ $this->components['path'] = substr($this->components['path'], $pos);
+ }
+ }
+
+ /**
+ * Shortcut method to get an absolute url from relative url.
+ *
+ * @static
+ *
+ * @param mixed $item_url Unknown url (can be relative or not)
+ * @param mixed $website_url Website url
+ *
+ * @return string
+ */
+ public static function resolve($item_url, $website_url)
+ {
+ $link = is_string($item_url) ? new self($item_url) : $item_url;
+ $website = is_string($website_url) ? new self($website_url) : $website_url;
+
+ if ($link->isRelativeUrl()) {
+ if ($link->isRelativePath()) {
+ return $link->getAbsoluteUrl($website->getBaseUrl($website->getBasePath()));
+ }
+
+ return $link->getAbsoluteUrl($website->getBaseUrl());
+ } elseif ($link->isProtocolRelative()) {
+ $link->setScheme($website->getScheme());
+ }
+
+ return $link->getAbsoluteUrl();
+ }
+
+ /**
+ * Shortcut method to get a base url.
+ *
+ * @static
+ *
+ * @param string $url
+ *
+ * @return string
+ */
+ public static function base($url)
+ {
+ $link = new self($url);
+
+ return $link->getBaseUrl();
+ }
+
+ /**
+ * Get the base URL.
+ *
+ * @param string $suffix Add a suffix to the url
+ *
+ * @return string
+ */
+ public function getBaseUrl($suffix = '')
+ {
+ return $this->hasHost() ? $this->getScheme('://').$this->getHost().$this->getPort(':').$suffix : '';
+ }
+
+ /**
+ * Get the absolute URL.
+ *
+ * @param string $base_url Use this url as base url
+ *
+ * @return string
+ */
+ public function getAbsoluteUrl($base_url = '')
+ {
+ if ($base_url) {
+ $base = new self($base_url);
+ $url = $base->getAbsoluteUrl().substr($this->getFullPath(), 1);
+ } else {
+ $url = $this->hasHost() ? $this->getBaseUrl().$this->getFullPath() : '';
+ }
+
+ return $url;
+ }
+
+ /**
+ * Return true if the url is relative.
+ *
+ * @return bool
+ */
+ public function isRelativeUrl()
+ {
+ return !$this->hasScheme() && !$this->isProtocolRelative();
+ }
+
+ /**
+ * Return true if the path is relative.
+ *
+ * @return bool
+ */
+ public function isRelativePath()
+ {
+ $path = $this->getPath();
+
+ return empty($path) || $path{0}
+ !== '/';
+ }
+
+ /**
+ * Filters the path of a URI.
+ *
+ * Imported from Guzzle library: https://github.com/guzzle/psr7/blob/master/src/Uri.php#L568-L582
+ *
+ * @param $path
+ *
+ * @return string
+ */
+ public function filterPath($path, $charUnreserved = 'a-zA-Z0-9_\-\.~', $charSubDelims = '!\$&\'\(\)\*\+,;=')
+ {
+ return preg_replace_callback(
+ '/(?:[^'.$charUnreserved.$charSubDelims.':@\/%]+|%(?![A-Fa-f0-9]{2}))/',
+ function (array $matches) { return rawurlencode($matches[0]); },
+ $path
+ );
+ }
+
+ /**
+ * Get the path.
+ *
+ * @return string
+ */
+ public function getPath()
+ {
+ return $this->filterPath(empty($this->components['path']) ? '' : $this->components['path']);
+ }
+
+ /**
+ * Get the base path.
+ *
+ * @return string
+ */
+ public function getBasePath()
+ {
+ $current_path = $this->getPath();
+
+ $path = $this->isRelativePath() ? '/' : '';
+ $path .= substr($current_path, -1) === '/' ? $current_path : dirname($current_path);
+
+ return preg_replace('/\\\\\/|\/\//', '/', $path.'/');
+ }
+
+ /**
+ * Get the full path (path + querystring + fragment).
+ *
+ * @return string
+ */
+ public function getFullPath()
+ {
+ $path = $this->isRelativePath() ? '/' : '';
+ $path .= $this->getPath();
+ $path .= empty($this->components['query']) ? '' : '?'.$this->components['query'];
+ $path .= empty($this->components['fragment']) ? '' : '#'.$this->components['fragment'];
+
+ return $path;
+ }
+
+ /**
+ * Get the hostname.
+ *
+ * @return string
+ */
+ public function getHost()
+ {
+ return empty($this->components['host']) ? '' : $this->components['host'];
+ }
+
+ /**
+ * Return true if the url has a hostname.
+ *
+ * @return bool
+ */
+ public function hasHost()
+ {
+ return !empty($this->components['host']);
+ }
+
+ /**
+ * Get the scheme.
+ *
+ * @param string $suffix Suffix to add when there is a scheme
+ *
+ * @return string
+ */
+ public function getScheme($suffix = '')
+ {
+ return ($this->hasScheme() ? $this->components['scheme'] : 'http').$suffix;
+ }
+
+ /**
+ * Set the scheme.
+ *
+ * @param string $scheme Set a scheme
+ *
+ * @return string
+ */
+ public function setScheme($scheme)
+ {
+ $this->components['scheme'] = $scheme;
+ }
+
+ /**
+ * Return true if the url has a scheme.
+ *
+ * @return bool
+ */
+ public function hasScheme()
+ {
+ return !empty($this->components['scheme']);
+ }
+
+ /**
+ * Get the port.
+ *
+ * @param string $prefix Prefix to add when there is a port
+ *
+ * @return string
+ */
+ public function getPort($prefix = '')
+ {
+ return $this->hasPort() ? $prefix.$this->components['port'] : '';
+ }
+
+ /**
+ * Return true if the url has a port.
+ *
+ * @return bool
+ */
+ public function hasPort()
+ {
+ return !empty($this->components['port']);
+ }
+
+ /**
+ * Return true if the url is protocol relative (start with //).
+ *
+ * @return bool
+ */
+ public function isProtocolRelative()
+ {
+ return strpos($this->url, '//') === 0;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php b/vendor/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php
new file mode 100644
index 00000000..fa0917e8
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace PicoFeed\Encoding;
+
+/**
+ * Encoding class.
+ */
+class Encoding
+{
+ public static function convert($input, $encoding)
+ {
+ if ($encoding === 'utf-8' || $encoding === '') {
+ return $input;
+ }
+
+ // suppress all notices since it isn't possible to silence only the
+ // notice "Wrong charset, conversion from $in_encoding to $out_encoding is not allowed"
+ set_error_handler(function () {}, E_NOTICE);
+
+ // convert input to utf-8 and strip invalid characters
+ $value = iconv($encoding, 'UTF-8//IGNORE', $input);
+
+ // stop silencing of notices
+ restore_error_handler();
+
+ // return input if something went wrong, maybe it's usable anyway
+ if ($value === false) {
+ return $input;
+ }
+
+ return $value;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Attribute.php b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Attribute.php
new file mode 100644
index 00000000..f0021532
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Attribute.php
@@ -0,0 +1,700 @@
+<?php
+
+namespace PicoFeed\Filter;
+
+use PicoFeed\Client\Url;
+
+/**
+ * Attribute Filter class.
+ *
+ * @author Frederic Guillot
+ */
+class Attribute
+{
+ /**
+ * Image proxy url.
+ *
+ * @var string
+ */
+ private $image_proxy_url = '';
+
+ /**
+ * Image proxy callback.
+ *
+ * @var \Closure|null
+ */
+ private $image_proxy_callback = null;
+
+ /**
+ * limits the image proxy usage to this protocol.
+ *
+ * @var string
+ */
+ private $image_proxy_limit_protocol = '';
+
+ /**
+ * Tags and attribute whitelist.
+ *
+ * @var array
+ */
+ private $attribute_whitelist = array(
+ 'audio' => array('controls', 'src'),
+ 'video' => array('poster', 'controls', 'height', 'width', 'src'),
+ 'source' => array('src', 'type'),
+ 'dt' => array(),
+ 'dd' => array(),
+ 'dl' => array(),
+ 'table' => array(),
+ 'caption' => array(),
+ 'tr' => array(),
+ 'th' => array(),
+ 'td' => array(),
+ 'tbody' => array(),
+ 'thead' => array(),
+ 'h1' => array(),
+ 'h2' => array(),
+ 'h3' => array(),
+ 'h4' => array(),
+ 'h5' => array(),
+ 'h6' => array(),
+ 'strong' => array(),
+ 'em' => array(),
+ 'code' => array(),
+ 'pre' => array(),
+ 'blockquote' => array(),
+ 'p' => array(),
+ 'ul' => array(),
+ 'li' => array(),
+ 'ol' => array(),
+ 'br' => array(),
+ 'del' => array(),
+ 'a' => array('href'),
+ 'img' => array('src', 'title', 'alt'),
+ 'figure' => array(),
+ 'figcaption' => array(),
+ 'cite' => array(),
+ 'time' => array('datetime'),
+ 'abbr' => array('title'),
+ 'iframe' => array('width', 'height', 'frameborder', 'src', 'allowfullscreen'),
+ 'q' => array('cite'),
+ );
+
+ /**
+ * Scheme whitelist.
+ *
+ * For a complete list go to http://en.wikipedia.org/wiki/URI_scheme
+ *
+ * @var array
+ */
+ private $scheme_whitelist = array(
+ 'bitcoin:',
+ 'callto:',
+ 'ed2k://',
+ 'facetime://',
+ 'feed:',
+ 'ftp://',
+ 'geo:',
+ 'git://',
+ 'http://',
+ 'https://',
+ 'irc://',
+ 'irc6://',
+ 'ircs://',
+ 'jabber:',
+ 'magnet:',
+ 'mailto:',
+ 'nntp://',
+ 'rtmp://',
+ 'sftp://',
+ 'sip:',
+ 'sips:',
+ 'skype:',
+ 'smb://',
+ 'sms:',
+ 'spotify:',
+ 'ssh:',
+ 'steam:',
+ 'svn://',
+ 'tel:',
+ );
+
+ /**
+ * Iframe source whitelist, everything else is ignored.
+ *
+ * @var array
+ */
+ private $iframe_whitelist = array(
+ 'http://www.youtube.com',
+ 'https://www.youtube.com',
+ 'http://player.vimeo.com',
+ 'https://player.vimeo.com',
+ 'http://www.dailymotion.com',
+ 'https://www.dailymotion.com',
+ 'http://vk.com',
+ 'https://vk.com',
+ );
+
+ /**
+ * Blacklisted resources.
+ *
+ * @var array
+ */
+ private $media_blacklist = array(
+ 'api.flattr.com',
+ 'feeds.feedburner.com',
+ 'share.feedsportal.com',
+ 'da.feedsportal.com',
+ 'rc.feedsportal.com',
+ 'rss.feedsportal.com',
+ 'res.feedsportal.com',
+ 'res1.feedsportal.com',
+ 'res2.feedsportal.com',
+ 'res3.feedsportal.com',
+ 'pi.feedsportal.com',
+ 'rss.nytimes.com',
+ 'feeds.wordpress.com',
+ 'stats.wordpress.com',
+ 'rss.cnn.com',
+ 'twitter.com/home?status=',
+ 'twitter.com/share',
+ 'twitter_icon_large.png',
+ 'www.facebook.com/sharer.php',
+ 'facebook_icon_large.png',
+ 'plus.google.com/share',
+ 'www.gstatic.com/images/icons/gplus-16.png',
+ 'www.gstatic.com/images/icons/gplus-32.png',
+ 'www.gstatic.com/images/icons/gplus-64.png',
+ );
+
+ /**
+ * Attributes used for external resources.
+ *
+ * @var array
+ */
+ private $media_attributes = array(
+ 'src',
+ 'href',
+ 'poster',
+ );
+
+ /**
+ * Attributes that must be integer.
+ *
+ * @var array
+ */
+ private $integer_attributes = array(
+ 'width',
+ 'height',
+ 'frameborder',
+ );
+
+ /**
+ * Mandatory attributes for specified tags.
+ *
+ * @var array
+ */
+ private $required_attributes = array(
+ 'a' => array('href'),
+ 'img' => array('src'),
+ 'iframe' => array('src'),
+ 'audio' => array('src'),
+ 'source' => array('src'),
+ );
+
+ /**
+ * Add attributes to specified tags.
+ *
+ * @var array
+ */
+ private $add_attributes = array(
+ 'a' => array('rel' => 'noreferrer', 'target' => '_blank'),
+ 'video' => array('controls' => 'true'),
+ );
+
+ /**
+ * List of filters to apply.
+ *
+ * @var array
+ */
+ private $filters = array(
+ 'filterAllowedAttribute',
+ 'filterIntegerAttribute',
+ 'rewriteAbsoluteUrl',
+ 'filterIframeAttribute',
+ 'filterBlacklistResourceAttribute',
+ 'filterProtocolUrlAttribute',
+ 'rewriteImageProxyUrl',
+ 'secureIframeSrc',
+ 'removeYouTubeAutoplay',
+ );
+
+ /**
+ * Add attributes to specified tags.
+ *
+ * @var \PicoFeed\Client\Url
+ */
+ private $website;
+
+ /**
+ * Constructor.
+ *
+ * @param \PicoFeed\Client\Url $website Website url instance
+ */
+ public function __construct(Url $website)
+ {
+ $this->website = $website;
+ }
+
+ /**
+ * Apply filters to the attributes list.
+ *
+ * @param string $tag Tag name
+ * @param array $attributes Attributes dictionary
+ *
+ * @return array Filtered attributes
+ */
+ public function filter($tag, array $attributes)
+ {
+ foreach ($attributes as $attribute => &$value) {
+ foreach ($this->filters as $filter) {
+ if (!$this->$filter($tag, $attribute, $value)) {
+ unset($attributes[$attribute]);
+ break;
+ }
+ }
+ }
+
+ return $attributes;
+ }
+
+ /**
+ * Return true if the value is allowed (remove not allowed attributes).
+ *
+ * @param string $tag Tag name
+ * @param string $attribute Attribute name
+ * @param string $value Attribute value
+ *
+ * @return bool
+ */
+ public function filterAllowedAttribute($tag, $attribute, $value)
+ {
+ return isset($this->attribute_whitelist[$tag]) && in_array($attribute, $this->attribute_whitelist[$tag]);
+ }
+
+ /**
+ * Return true if the value is not integer (remove attributes that should have an integer value).
+ *
+ * @param string $tag Tag name
+ * @param string $attribute Attribute name
+ * @param string $value Attribute value
+ *
+ * @return bool
+ */
+ public function filterIntegerAttribute($tag, $attribute, $value)
+ {
+ if (in_array($attribute, $this->integer_attributes)) {
+ return ctype_digit($value);
+ }
+
+ return true;
+ }
+
+ /**
+ * Return true if the iframe source is allowed (remove not allowed iframe).
+ *
+ * @param string $tag Tag name
+ * @param string $attribute Attribute name
+ * @param string $value Attribute value
+ *
+ * @return bool
+ */
+ public function filterIframeAttribute($tag, $attribute, $value)
+ {
+ if ($tag === 'iframe' && $attribute === 'src') {
+ foreach ($this->iframe_whitelist as $url) {
+ if (strpos($value, $url) === 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Return true if the resource is not blacklisted (remove blacklisted resource attributes).
+ *
+ * @param string $tag Tag name
+ * @param string $attribute Attribute name
+ * @param string $value Attribute value
+ *
+ * @return bool
+ */
+ public function filterBlacklistResourceAttribute($tag, $attribute, $value)
+ {
+ if ($this->isResource($attribute) && $this->isBlacklistedMedia($value)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Convert all relative links to absolute url.
+ *
+ * @param string $tag Tag name
+ * @param string $attribute Attribute name
+ * @param string $value Attribute value
+ *
+ * @return bool
+ */
+ public function rewriteAbsoluteUrl($tag, $attribute, &$value)
+ {
+ if ($this->isResource($attribute)) {
+ $value = Url::resolve($value, $this->website);
+ }
+
+ return true;
+ }
+
+ /**
+ * Turns iframes' src attribute from http to https to prevent
+ * mixed active content.
+ *
+ * @param string $tag Tag name
+ * @param array $attribute Atttributes name
+ * @param string $value Attribute value
+ *
+ * @return bool
+ */
+ public function secureIframeSrc($tag, $attribute, &$value)
+ {
+ if ($tag === 'iframe' && $attribute === 'src' && strpos($value, 'http://') === 0) {
+ $value = substr_replace($value, 's', 4, 0);
+ }
+
+ return true;
+ }
+
+ /**
+ * Removes YouTube autoplay from iframes.
+ *
+ * @param string $tag Tag name
+ * @param array $attribute Atttributes name
+ * @param string $value Attribute value
+ *
+ * @return bool
+ */
+ public function removeYouTubeAutoplay($tag, $attribute, &$value)
+ {
+ $regex = '%^(https://(?:www\.)?youtube.com/.*\?.*autoplay=)(1)(.*)%i';
+ if ($tag === 'iframe' && $attribute === 'src' && preg_match($regex, $value)) {
+ $value = preg_replace($regex, '${1}0$3', $value);
+ }
+
+ return true;
+ }
+
+ /**
+ * Rewrite image url to use with a proxy.
+ *
+ * @param string $tag Tag name
+ * @param string $attribute Attribute name
+ * @param string $value Attribute value
+ *
+ * @return bool
+ */
+ public function rewriteImageProxyUrl($tag, $attribute, &$value)
+ {
+ if ($tag === 'img' && $attribute === 'src'
+ && !($this->image_proxy_limit_protocol !== '' && stripos($value, $this->image_proxy_limit_protocol.':') !== 0)) {
+ if ($this->image_proxy_url) {
+ $value = sprintf($this->image_proxy_url, rawurlencode($value));
+ } elseif (is_callable($this->image_proxy_callback)) {
+ $value = call_user_func($this->image_proxy_callback, $value);
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Return true if the scheme is authorized.
+ *
+ * @param string $tag Tag name
+ * @param string $attribute Attribute name
+ * @param string $value Attribute value
+ *
+ * @return bool
+ */
+ public function filterProtocolUrlAttribute($tag, $attribute, $value)
+ {
+ if ($this->isResource($attribute) && !$this->isAllowedProtocol($value)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Automatically add/override some attributes for specific tags.
+ *
+ * @param string $tag Tag name
+ * @param array $attributes Attributes list
+ *
+ * @return array
+ */
+ public function addAttributes($tag, array $attributes)
+ {
+ if (isset($this->add_attributes[$tag])) {
+ $attributes += $this->add_attributes[$tag];
+ }
+
+ return $attributes;
+ }
+
+ /**
+ * Return true if all required attributes are present.
+ *
+ * @param string $tag Tag name
+ * @param array $attributes Attributes list
+ *
+ * @return bool
+ */
+ public function hasRequiredAttributes($tag, array $attributes)
+ {
+ if (isset($this->required_attributes[$tag])) {
+ foreach ($this->required_attributes[$tag] as $attribute) {
+ if (!isset($attributes[$attribute])) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if an attribute name is an external resource.
+ *
+ * @param string $attribute Attribute name
+ *
+ * @return bool
+ */
+ public function isResource($attribute)
+ {
+ return in_array($attribute, $this->media_attributes);
+ }
+
+ /**
+ * Detect if the protocol is allowed or not.
+ *
+ * @param string $value Attribute value
+ *
+ * @return bool
+ */
+ public function isAllowedProtocol($value)
+ {
+ foreach ($this->scheme_whitelist as $protocol) {
+ if (strpos($value, $protocol) === 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Detect if an url is blacklisted.
+ *
+ * @param string $resource Attribute value (URL)
+ *
+ * @return bool
+ */
+ public function isBlacklistedMedia($resource)
+ {
+ foreach ($this->media_blacklist as $name) {
+ if (strpos($resource, $name) !== false) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Convert the attribute list to html.
+ *
+ * @param array $attributes Attributes
+ *
+ * @return string
+ */
+ public function toHtml(array $attributes)
+ {
+ $html = array();
+
+ foreach ($attributes as $attribute => $value) {
+ $html[] = sprintf('%s="%s"', $attribute, Filter::escape($value));
+ }
+
+ return implode(' ', $html);
+ }
+
+ /**
+ * Set whitelisted tags and attributes for each tag.
+ *
+ * @param array $values List of tags: ['video' => ['src', 'cover'], 'img' => ['src']]
+ *
+ * @return Attribute
+ */
+ public function setWhitelistedAttributes(array $values)
+ {
+ $this->attribute_whitelist = $values ?: $this->attribute_whitelist;
+
+ return $this;
+ }
+
+ /**
+ * Set scheme whitelist.
+ *
+ * @param array $values List of scheme: ['http://', 'ftp://']
+ *
+ * @return Attribute
+ */
+ public function setSchemeWhitelist(array $values)
+ {
+ $this->scheme_whitelist = $values ?: $this->scheme_whitelist;
+
+ return $this;
+ }
+
+ /**
+ * Set media attributes (used to load external resources).
+ *
+ * @param array $values List of values: ['src', 'href']
+ *
+ * @return Attribute
+ */
+ public function setMediaAttributes(array $values)
+ {
+ $this->media_attributes = $values ?: $this->media_attributes;
+
+ return $this;
+ }
+
+ /**
+ * Set blacklisted external resources.
+ *
+ * @param array $values List of tags: ['http://google.com/', '...']
+ *
+ * @return Attribute
+ */
+ public function setMediaBlacklist(array $values)
+ {
+ $this->media_blacklist = $values ?: $this->media_blacklist;
+
+ return $this;
+ }
+
+ /**
+ * Set mandatory attributes for whitelisted tags.
+ *
+ * @param array $values List of tags: ['img' => 'src']
+ *
+ * @return Attribute
+ */
+ public function setRequiredAttributes(array $values)
+ {
+ $this->required_attributes = $values ?: $this->required_attributes;
+
+ return $this;
+ }
+
+ /**
+ * Set attributes to automatically to specific tags.
+ *
+ * @param array $values List of tags: ['a' => 'target="_blank"']
+ *
+ * @return Attribute
+ */
+ public function setAttributeOverrides(array $values)
+ {
+ $this->add_attributes = $values ?: $this->add_attributes;
+
+ return $this;
+ }
+
+ /**
+ * Set attributes that must be an integer.
+ *
+ * @param array $values List of tags: ['width', 'height']
+ *
+ * @return Attribute
+ */
+ public function setIntegerAttributes(array $values)
+ {
+ $this->integer_attributes = $values ?: $this->integer_attributes;
+
+ return $this;
+ }
+
+ /**
+ * Set allowed iframe resources.
+ *
+ * @param array $values List of tags: ['http://www.youtube.com']
+ *
+ * @return Attribute
+ */
+ public function setIframeWhitelist(array $values)
+ {
+ $this->iframe_whitelist = $values ?: $this->iframe_whitelist;
+
+ return $this;
+ }
+
+ /**
+ * Set image proxy URL.
+ *
+ * The original image url will be urlencoded
+ *
+ * @param string $url Proxy URL
+ *
+ * @return Attribute
+ */
+ public function setImageProxyUrl($url)
+ {
+ $this->image_proxy_url = $url ?: $this->image_proxy_url;
+
+ return $this;
+ }
+
+ /**
+ * Set image proxy callback.
+ *
+ * @param \Closure $callback
+ *
+ * @return Attribute
+ */
+ public function setImageProxyCallback($callback)
+ {
+ $this->image_proxy_callback = $callback ?: $this->image_proxy_callback;
+
+ return $this;
+ }
+
+ /**
+ * Set image proxy protocol restriction.
+ *
+ * @param string $value
+ *
+ * @return Attribute
+ */
+ public function setImageProxyProtocol($value)
+ {
+ $this->image_proxy_limit_protocol = $value ?: $this->image_proxy_limit_protocol;
+
+ return $this;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php
new file mode 100644
index 00000000..bae2aff0
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php
@@ -0,0 +1,155 @@
+<?php
+
+namespace PicoFeed\Filter;
+
+/**
+ * Filter class.
+ *
+ * @author Frederic Guillot
+ */
+class Filter
+{
+ /**
+ * Get the Html filter instance.
+ *
+ * @static
+ *
+ * @param string $html HTML content
+ * @param string $website Site URL (used to build absolute URL)
+ *
+ * @return Html
+ */
+ public static function html($html, $website)
+ {
+ $filter = new Html($html, $website);
+
+ return $filter;
+ }
+
+ /**
+ * Escape HTML content.
+ *
+ * @static
+ *
+ * @return string
+ */
+ public static function escape($content)
+ {
+ return htmlspecialchars($content, ENT_QUOTES, 'UTF-8', false);
+ }
+
+ /**
+ * Remove HTML tags.
+ *
+ * @param string $data Input data
+ *
+ * @return string
+ */
+ public function removeHTMLTags($data)
+ {
+ return preg_replace('~<(?:!DOCTYPE|/?(?:html|head|body))[^>]*>\s*~i', '', $data);
+ }
+
+ /**
+ * Remove the XML tag from a document.
+ *
+ * @static
+ *
+ * @param string $data Input data
+ *
+ * @return string
+ */
+ public static function stripXmlTag($data)
+ {
+ if (strpos($data, '<?xml') !== false) {
+ $data = ltrim(substr($data, strpos($data, '?>') + 2));
+ }
+
+ do {
+ $pos = strpos($data, '<?xml-stylesheet ');
+
+ if ($pos !== false) {
+ $data = ltrim(substr($data, strpos($data, '?>') + 2));
+ }
+ } while ($pos !== false && $pos < 200);
+
+ return $data;
+ }
+
+ /**
+ * Strip head tag from the HTML content.
+ *
+ * @static
+ *
+ * @param string $data Input data
+ *
+ * @return string
+ */
+ public static function stripHeadTags($data)
+ {
+ return preg_replace('@<head[^>]*?>.*?</head>@siu', '', $data);
+ }
+
+ /**
+ * Trim whitespace from the begining, the end and inside a string and don't break utf-8 string.
+ *
+ * @static
+ *
+ * @param string $value Raw data
+ *
+ * @return string Normalized data
+ */
+ public static function stripWhiteSpace($value)
+ {
+ $value = str_replace("\r", ' ', $value);
+ $value = str_replace("\t", ' ', $value);
+ $value = str_replace("\n", ' ', $value);
+ // $value = preg_replace('/\s+/', ' ', $value); <= break utf-8
+ return trim($value);
+ }
+
+ /**
+ * Fixes before XML parsing.
+ *
+ * @static
+ *
+ * @param string $data Raw data
+ *
+ * @return string Normalized data
+ */
+ public static function normalizeData($data)
+ {
+ $entities = array(
+ '/(&#)(\d+);/m', // decimal encoded
+ '/(&#x)([a-f0-9]+);/mi', // hex encoded
+ );
+
+ // strip invalid XML 1.0 characters which are encoded as entities
+ $data = preg_replace_callback($entities, function ($matches) {
+ $code_point = $matches[2];
+
+ // convert hex entity to decimal
+ if (strtolower($matches[1]) === '&#x') {
+ $code_point = hexdec($code_point);
+ }
+
+ $code_point = (int) $code_point;
+
+ // replace invalid characters
+ if ($code_point < 9
+ || ($code_point > 10 && $code_point < 13)
+ || ($code_point > 13 && $code_point < 32)
+ || ($code_point > 55295 && $code_point < 57344)
+ || ($code_point > 65533 && $code_point < 65536)
+ || $code_point > 1114111
+ ) {
+ return '';
+ };
+
+ return $matches[0];
+ }, $data);
+
+ // strip every utf-8 character than isn't in the range of valid XML 1.0 characters
+ return (string) preg_replace('/[^\x{0009}\x{000A}\x{000D}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]/u', '', $data);
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Html.php b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Html.php
new file mode 100644
index 00000000..0ccc192f
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Html.php
@@ -0,0 +1,243 @@
+<?php
+
+namespace PicoFeed\Filter;
+
+use PicoFeed\Config\Config;
+use PicoFeed\Client\Url;
+use PicoFeed\Scraper\RuleLoader;
+use PicoFeed\Parser\XmlParser;
+
+/**
+ * HTML Filter class.
+ *
+ * @author Frederic Guillot
+ */
+class Html
+{
+ /**
+ * Config object.
+ *
+ * @var \PicoFeed\Config\Config
+ */
+ private $config;
+
+ /**
+ * Unfiltered XML data.
+ *
+ * @var string
+ */
+ private $input = '';
+
+ /**
+ * Filtered XML data.
+ *
+ * @var string
+ */
+ private $output = '';
+
+ /**
+ * List of empty tags.
+ *
+ * @var array
+ */
+ private $empty_tags = array();
+
+ /**
+ * Empty flag.
+ *
+ * @var bool
+ */
+ private $empty = true;
+
+ /**
+ * Tag instance.
+ *
+ * @var \PicoFeed\Filter\Tag
+ */
+ public $tag = '';
+
+ /**
+ * Attribute instance.
+ *
+ * @var \PicoFeed\Filter\Attribute
+ */
+ public $attribute = '';
+
+ /**
+ * The website to filter.
+ *
+ * @var string
+ */
+ private $website;
+
+ /**
+ * Initialize the filter, all inputs data must be encoded in UTF-8 before.
+ *
+ * @param string $html HTML content
+ * @param string $website Site URL (used to build absolute URL)
+ */
+ public function __construct($html, $website)
+ {
+ $this->config = new Config();
+ $this->input = XmlParser::htmlToXml($html);
+ $this->output = '';
+ $this->tag = new Tag($this->config);
+ $this->website = $website;
+ $this->attribute = new Attribute(new Url($website));
+ }
+
+ /**
+ * Set config object.
+ *
+ * @param \PicoFeed\Config\Config $config Config instance
+ *
+ * @return \PicoFeed\Filter\Html
+ */
+ public function setConfig($config)
+ {
+ $this->config = $config;
+
+ if ($this->config !== null) {
+ $this->attribute->setImageProxyCallback($this->config->getFilterImageProxyCallback());
+ $this->attribute->setImageProxyUrl($this->config->getFilterImageProxyUrl());
+ $this->attribute->setImageProxyProtocol($this->config->getFilterImageProxyProtocol());
+ $this->attribute->setIframeWhitelist($this->config->getFilterIframeWhitelist(array()));
+ $this->attribute->setIntegerAttributes($this->config->getFilterIntegerAttributes(array()));
+ $this->attribute->setAttributeOverrides($this->config->getFilterAttributeOverrides(array()));
+ $this->attribute->setRequiredAttributes($this->config->getFilterRequiredAttributes(array()));
+ $this->attribute->setMediaBlacklist($this->config->getFilterMediaBlacklist(array()));
+ $this->attribute->setMediaAttributes($this->config->getFilterMediaAttributes(array()));
+ $this->attribute->setSchemeWhitelist($this->config->getFilterSchemeWhitelist(array()));
+ $this->attribute->setWhitelistedAttributes($this->config->getFilterWhitelistedTags(array()));
+ $this->tag->setWhitelistedTags(array_keys($this->config->getFilterWhitelistedTags(array())));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Run tags/attributes filtering.
+ *
+ * @return string
+ */
+ public function execute()
+ {
+ $this->preFilter();
+
+ $parser = xml_parser_create();
+
+ xml_set_object($parser, $this);
+ xml_set_element_handler($parser, 'startTag', 'endTag');
+ xml_set_character_data_handler($parser, 'dataTag');
+ xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
+ xml_parse($parser, $this->input, true);
+ xml_parser_free($parser);
+
+ $this->postFilter();
+
+ return $this->output;
+ }
+
+ /**
+ * Called before XML parsing.
+ */
+ public function preFilter()
+ {
+ $this->input = $this->tag->removeBlacklistedTags($this->input);
+ }
+
+ /**
+ * Called after XML parsing.
+ */
+ public function postFilter()
+ {
+ $this->output = $this->tag->removeEmptyTags($this->output);
+ $this->output = $this->filterRules($this->output);
+ $this->output = $this->tag->removeMultipleBreakTags($this->output);
+ $this->output = trim($this->output);
+ }
+
+ /**
+ * Called after XML parsing.
+ *
+ * @param string $content the content that should be filtered
+ */
+ public function filterRules($content)
+ {
+ // the constructor should require a config, then this if can be removed
+ if ($this->config === null) {
+ $config = new Config();
+ } else {
+ $config = $this->config;
+ }
+
+ $loader = new RuleLoader($config);
+ $rules = $loader->getRules($this->website);
+
+ $url = new Url($this->website);
+ $sub_url = $url->getFullPath();
+
+ if (isset($rules['filter'])) {
+ foreach ($rules['filter'] as $pattern => $rule) {
+ if (preg_match($pattern, $sub_url)) {
+ foreach ($rule as $search => $replace) {
+ $content = preg_replace($search, $replace, $content);
+ }
+ }
+ }
+ }
+
+ return $content;
+ }
+
+ /**
+ * Parse opening tag.
+ *
+ * @param resource $parser XML parser
+ * @param string $tag Tag name
+ * @param array $attributes Tag attributes
+ */
+ public function startTag($parser, $tag, array $attributes)
+ {
+ $this->empty = true;
+
+ if ($this->tag->isAllowed($tag, $attributes)) {
+ $attributes = $this->attribute->filter($tag, $attributes);
+
+ if ($this->attribute->hasRequiredAttributes($tag, $attributes)) {
+ $attributes = $this->attribute->addAttributes($tag, $attributes);
+
+ $this->output .= $this->tag->openHtmlTag($tag, $this->attribute->toHtml($attributes));
+ $this->empty = false;
+ }
+ }
+
+ $this->empty_tags[] = $this->empty;
+ }
+
+ /**
+ * Parse closing tag.
+ *
+ * @param resource $parser XML parser
+ * @param string $tag Tag name
+ */
+ public function endTag($parser, $tag)
+ {
+ if (!array_pop($this->empty_tags) && $this->tag->isAllowedTag($tag)) {
+ $this->output .= $this->tag->closeHtmlTag($tag);
+ }
+ }
+
+ /**
+ * Parse tag content.
+ *
+ * @param resource $parser XML parser
+ * @param string $content Tag content
+ */
+ public function dataTag($parser, $content)
+ {
+ // Replace &nbsp; with normal space
+ $content = str_replace("\xc2\xa0", ' ', $content);
+ $this->output .= Filter::escape($content);
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php
new file mode 100644
index 00000000..84a298a7
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php
@@ -0,0 +1,218 @@
+<?php
+
+namespace PicoFeed\Filter;
+
+use DOMXPath;
+use PicoFeed\Base;
+use PicoFeed\Parser\XmlParser;
+
+/**
+ * Tag Filter class.
+ *
+ * @author Frederic Guillot
+ */
+class Tag extends Base
+{
+ /**
+ * Tags blacklist (Xpath expressions).
+ *
+ * @var array
+ */
+ private $tag_blacklist = array(
+ '//script',
+ '//style',
+ );
+
+ /**
+ * Tags whitelist.
+ *
+ * @var array
+ */
+ private $tag_whitelist = array(
+ 'audio',
+ 'video',
+ 'source',
+ 'dt',
+ 'dd',
+ 'dl',
+ 'table',
+ 'caption',
+ 'tr',
+ 'th',
+ 'td',
+ 'tbody',
+ 'thead',
+ 'h1',
+ 'h2',
+ 'h3',
+ 'h4',
+ 'h5',
+ 'h6',
+ 'strong',
+ 'em',
+ 'code',
+ 'pre',
+ 'blockquote',
+ 'p',
+ 'ul',
+ 'li',
+ 'ol',
+ 'br',
+ 'del',
+ 'a',
+ 'img',
+ 'figure',
+ 'figcaption',
+ 'cite',
+ 'time',
+ 'abbr',
+ 'iframe',
+ 'q',
+ 'sup',
+ 'sub',
+ );
+
+ /**
+ * Check if the tag is allowed and is not a pixel tracker.
+ *
+ * @param string $tag Tag name
+ * @param array $attributes Attributes dictionary
+ *
+ * @return bool
+ */
+ public function isAllowed($tag, array $attributes)
+ {
+ return $this->isAllowedTag($tag) && !$this->isPixelTracker($tag, $attributes);
+ }
+
+ /**
+ * Return the HTML opening tag.
+ *
+ * @param string $tag Tag name
+ * @param string $attributes Attributes converted in html
+ *
+ * @return string
+ */
+ public function openHtmlTag($tag, $attributes = '')
+ {
+ return '<'.$tag.(empty($attributes) ? '' : ' '.$attributes).($this->isSelfClosingTag($tag) ? '/>' : '>');
+ }
+
+ /**
+ * Return the HTML closing tag.
+ *
+ * @param string $tag Tag name
+ *
+ * @return string
+ */
+ public function closeHtmlTag($tag)
+ {
+ return $this->isSelfClosingTag($tag) ? '' : '</'.$tag.'>';
+ }
+
+ /**
+ * Return true is the tag is self-closing.
+ *
+ * @param string $tag Tag name
+ *
+ * @return bool
+ */
+ public function isSelfClosingTag($tag)
+ {
+ return $tag === 'br' || $tag === 'img';
+ }
+
+ /**
+ * Check if a tag is on the whitelist.
+ *
+ * @param string $tag Tag name
+ *
+ * @return bool
+ */
+ public function isAllowedTag($tag)
+ {
+ return in_array($tag, array_merge(
+ $this->tag_whitelist,
+ array_keys($this->config->getFilterWhitelistedTags(array()))
+ ));
+ }
+
+ /**
+ * Detect if an image tag is a pixel tracker.
+ *
+ * @param string $tag Tag name
+ * @param array $attributes Tag attributes
+ *
+ * @return bool
+ */
+ public function isPixelTracker($tag, array $attributes)
+ {
+ return $tag === 'img' &&
+ isset($attributes['height']) && isset($attributes['width']) &&
+ $attributes['height'] == 1 && $attributes['width'] == 1;
+ }
+
+ /**
+ * Remove script tags.
+ *
+ * @param string $data Input data
+ *
+ * @return string
+ */
+ public function removeBlacklistedTags($data)
+ {
+ $dom = XmlParser::getDomDocument($data);
+
+ if ($dom === false) {
+ return '';
+ }
+
+ $xpath = new DOMXpath($dom);
+
+ $nodes = $xpath->query(implode(' | ', $this->tag_blacklist));
+
+ foreach ($nodes as $node) {
+ $node->parentNode->removeChild($node);
+ }
+
+ return $dom->saveXML();
+ }
+
+ /**
+ * Remove empty tags.
+ *
+ * @param string $data Input data
+ *
+ * @return string
+ */
+ public function removeEmptyTags($data)
+ {
+ return preg_replace('/<([^<\/>]*)>([\s]*?|(?R))<\/\1>/imsU', '', $data);
+ }
+
+ /**
+ * Replace <br/><br/> by only one.
+ *
+ * @param string $data Input data
+ *
+ * @return string
+ */
+ public function removeMultipleBreakTags($data)
+ {
+ return preg_replace("/(<br\s*\/?>\s*)+/", '<br/>', $data);
+ }
+
+ /**
+ * Set whitelisted tags adn attributes for each tag.
+ *
+ * @param array $values List of tags: ['video' => ['src', 'cover'], 'img' => ['src']]
+ *
+ * @return Tag
+ */
+ public function setWhitelistedTags(array $values)
+ {
+ $this->tag_whitelist = $values ?: $this->tag_whitelist;
+
+ return $this;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php
new file mode 100644
index 00000000..5c2f205c
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace PicoFeed\Generator;
+
+use PicoFeed\Parser\Item;
+
+/**
+ * Content Generator Interface
+ *
+ * @package PicoFeed\Generator
+ * @author Frederic Guillot
+ */
+interface ContentGeneratorInterface
+{
+ /**
+ * Execute Content Generator
+ *
+ * @access public
+ * @param Item $item
+ * @return boolean
+ */
+ public function execute(Item $item);
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Generator/FileContentGenerator.php b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/FileContentGenerator.php
new file mode 100644
index 00000000..03f37e16
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/FileContentGenerator.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace PicoFeed\Generator;
+
+use PicoFeed\Base;
+use PicoFeed\Parser\Item;
+
+/**
+ * File Content Generator
+ *
+ * @package PicoFeed\Generator
+ * @author Frederic Guillot
+ */
+class FileContentGenerator extends Base implements ContentGeneratorInterface
+{
+ private $extensions = array('pdf');
+
+ /**
+ * Execute Content Generator
+ *
+ * @access public
+ * @param Item $item
+ * @return boolean
+ */
+ public function execute(Item $item)
+ {
+ foreach ($this->extensions as $extension) {
+ if (substr($item->getUrl(), - strlen($extension)) === $extension) {
+ $item->setContent('<a href="'.$item->getUrl().'" target="_blank">'.$item->getUrl().'</a>');
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php
new file mode 100644
index 00000000..198090d4
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace PicoFeed\Generator;
+
+use PicoFeed\Base;
+use PicoFeed\Parser\Item;
+
+/**
+ * Youtube Content Generator
+ *
+ * @package PicoFeed\Generator
+ * @author Frederic Guillot
+ */
+class YoutubeContentGenerator extends Base implements ContentGeneratorInterface
+{
+ /**
+ * Execute Content Generator
+ *
+ * @access public
+ * @param Item $item
+ * @return boolean
+ */
+ public function execute(Item $item)
+ {
+ if ($item->hasNamespace('yt')) {
+ return $this->generateHtmlFromXml($item);
+ }
+
+ return $this->generateHtmlFromUrl($item);
+ }
+
+ /**
+ * Generate HTML
+ *
+ * @access public
+ * @param Item $item
+ * @return boolean
+ */
+ private function generateHtmlFromXml(Item $item)
+ {
+ $videoId = $item->getTag('yt:videoId');
+
+ if (! empty($videoId)) {
+ $item->setContent('<iframe width="560" height="315" src="//www.youtube.com/embed/'.$videoId[0].'" frameborder="0"></iframe>');
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Generate HTML from item URL
+ *
+ * @access public
+ * @param Item $item
+ * @return bool
+ */
+ public function generateHtmlFromUrl(Item $item)
+ {
+ if (preg_match('/youtube\.com\/watch\?v=(.*)/', $item->getUrl(), $matches)) {
+ $item->setContent('<iframe width="560" height="315" src="//www.youtube.com/embed/'.$matches[1].'" frameborder="0"></iframe>');
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php b/vendor/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php
new file mode 100644
index 00000000..caec463f
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace PicoFeed\Logging;
+
+use DateTime;
+use DateTimeZone;
+
+/**
+ * Logging class.
+ *
+ * @author Frederic Guillot
+ */
+class Logger
+{
+ /**
+ * List of messages.
+ *
+ * @static
+ *
+ * @var array
+ */
+ private static $messages = array();
+
+ /**
+ * Default timezone.
+ *
+ * @static
+ *
+ * @var string
+ */
+ private static $timezone = 'UTC';
+
+ /**
+ * Enable or disable logging.
+ *
+ * @static
+ *
+ * @var bool
+ */
+ public static $enable = false;
+
+ /**
+ * Enable logging.
+ *
+ * @static
+ */
+ public static function enable()
+ {
+ self::$enable = true;
+ }
+
+ /**
+ * Add a new message.
+ *
+ * @static
+ *
+ * @param string $message Message
+ */
+ public static function setMessage($message)
+ {
+ if (self::$enable) {
+ $date = new DateTime('now', new DateTimeZone(self::$timezone));
+ self::$messages[] = '['.$date->format('Y-m-d H:i:s').'] '.$message;
+ }
+ }
+
+ /**
+ * Get all logged messages.
+ *
+ * @static
+ *
+ * @return array
+ */
+ public static function getMessages()
+ {
+ return self::$messages;
+ }
+
+ /**
+ * Remove all logged messages.
+ *
+ * @static
+ */
+ public static function deleteMessages()
+ {
+ self::$messages = array();
+ }
+
+ /**
+ * Set a different timezone.
+ *
+ * @static
+ *
+ * @see http://php.net/manual/en/timezones.php
+ *
+ * @param string $timezone Timezone
+ */
+ public static function setTimeZone($timezone)
+ {
+ self::$timezone = $timezone ?: self::$timezone;
+ }
+
+ /**
+ * Get all messages serialized into a string.
+ *
+ * @static
+ *
+ * @return string
+ */
+ public static function toString()
+ {
+ return implode(PHP_EOL, self::$messages).PHP_EOL;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php
new file mode 100644
index 00000000..1c570a08
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php
@@ -0,0 +1,382 @@
+<?php
+
+namespace PicoFeed\Parser;
+
+use SimpleXMLElement;
+use PicoFeed\Filter\Filter;
+use PicoFeed\Client\Url;
+
+/**
+ * Atom parser.
+ *
+ * @package PicoFeed\Parser
+ * @author Frederic Guillot
+ */
+class Atom extends Parser
+{
+ /**
+ * Supported namespaces.
+ */
+ protected $namespaces = array(
+ 'atom' => 'http://www.w3.org/2005/Atom',
+ );
+
+ /**
+ * Get the path to the items XML tree.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ *
+ * @return SimpleXMLElement
+ */
+ public function getItemsTree(SimpleXMLElement $xml)
+ {
+ return XmlParser::getXPathResult($xml, 'atom:entry', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'entry');
+ }
+
+ /**
+ * Find the feed url.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedUrl(SimpleXMLElement $xml, Feed $feed)
+ {
+ $feed->setFeedUrl($this->getUrl($xml, 'self'));
+ }
+
+ /**
+ * Find the site url.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findSiteUrl(SimpleXMLElement $xml, Feed $feed)
+ {
+ $feed->setSiteUrl($this->getUrl($xml, 'alternate', true));
+ }
+
+ /**
+ * Find the feed description.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedDescription(SimpleXMLElement $xml, Feed $feed)
+ {
+ $description = XmlParser::getXPathResult($xml, 'atom:subtitle', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'subtitle');
+
+ $feed->setDescription(XmlParser::getValue($description));
+ }
+
+ /**
+ * Find the feed logo url.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedLogo(SimpleXMLElement $xml, Feed $feed)
+ {
+ $logo = XmlParser::getXPathResult($xml, 'atom:logo', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'logo');
+
+ $feed->setLogo(XmlParser::getValue($logo));
+ }
+
+ /**
+ * Find the feed icon.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedIcon(SimpleXMLElement $xml, Feed $feed)
+ {
+ $icon = XmlParser::getXPathResult($xml, 'atom:icon', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'icon');
+
+ $feed->setIcon(XmlParser::getValue($icon));
+ }
+
+ /**
+ * Find the feed title.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedTitle(SimpleXMLElement $xml, Feed $feed)
+ {
+ $title = XmlParser::getXPathResult($xml, 'atom:title', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'title');
+
+ $feed->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $feed->getSiteUrl());
+ }
+
+ /**
+ * Find the feed language.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed)
+ {
+ $language = XmlParser::getXPathResult($xml, '*[not(self::atom:entry)]/@xml:lang', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, '@xml:lang');
+
+ $feed->setLanguage(XmlParser::getValue($language));
+ }
+
+ /**
+ * Find the feed id.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedId(SimpleXMLElement $xml, Feed $feed)
+ {
+ $id = XmlParser::getXPathResult($xml, 'atom:id', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'id');
+
+ $feed->setId(XmlParser::getValue($id));
+ }
+
+ /**
+ * Find the feed date.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedDate(SimpleXMLElement $xml, Feed $feed)
+ {
+ $updated = XmlParser::getXPathResult($xml, 'atom:updated', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'updated');
+
+ $feed->setDate($this->getDateParser()->getDateTime(XmlParser::getValue($updated)));
+ }
+
+ /**
+ * Find the item published date.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findItemPublishedDate(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ $date = XmlParser::getXPathResult($entry, 'atom:published', $this->namespaces)
+ ?: XmlParser::getXPathResult($entry, 'published');
+
+ $item->setPublishedDate(!empty($date) ? $this->getDateParser()->getDateTime((string) current($date)) : null);
+ }
+
+ /**
+ * Find the item updated date.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findItemUpdatedDate(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ $date = XmlParser::getXPathResult($entry, 'atom:updated', $this->namespaces)
+ ?: XmlParser::getXPathResult($entry, 'updated');
+
+ $item->setUpdatedDate(!empty($date) ? $this->getDateParser()->getDateTime((string) current($date)) : null);
+ }
+
+ /**
+ * Find the item title.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ */
+ public function findItemTitle(SimpleXMLElement $entry, Item $item)
+ {
+ $title = XmlParser::getXPathResult($entry, 'atom:title', $this->namespaces)
+ ?: XmlParser::getXPathResult($entry, 'title');
+
+ $item->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $item->getUrl());
+ }
+
+ /**
+ * Find the item author.
+ *
+ * @param SimpleXMLElement $xml Feed
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ */
+ public function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item)
+ {
+ $author = XmlParser::getXPathResult($entry, 'atom:author/atom:name', $this->namespaces)
+ ?: XmlParser::getXPathResult($entry, 'author/name')
+ ?: XmlParser::getXPathResult($xml, 'atom:author/atom:name', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'author/name');
+
+ $item->setAuthor(XmlParser::getValue($author));
+ }
+
+ /**
+ * Find the item content.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ */
+ public function findItemContent(SimpleXMLElement $entry, Item $item)
+ {
+ $item->setContent($this->getContent($entry));
+ }
+
+ /**
+ * Find the item URL.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ */
+ public function findItemUrl(SimpleXMLElement $entry, Item $item)
+ {
+ $item->setUrl($this->getUrl($entry, 'alternate', true));
+ }
+
+ /**
+ * Genereate the item id.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ $id = XmlParser::getXPathResult($entry, 'atom:id', $this->namespaces)
+ ?: XmlParser::getXPathResult($entry, 'id');
+
+ if (!empty($id)) {
+ $item->setId($this->generateId(XmlParser::getValue($id)));
+ } else {
+ $item->setId($this->generateId(
+ $item->getTitle(), $item->getUrl(), $item->getContent()
+ ));
+ }
+ }
+
+ /**
+ * Find the item enclosure.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ $enclosure = $this->findLink($entry, 'enclosure');
+
+ if ($enclosure) {
+ $item->setEnclosureUrl(Url::resolve((string) $enclosure['href'], $feed->getSiteUrl()));
+ $item->setEnclosureType((string) $enclosure['type']);
+ }
+ }
+
+ /**
+ * Find the item language.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ $language = XmlParser::getXPathResult($entry, './/@xml:lang');
+ $item->setLanguage(XmlParser::getValue($language) ?: $feed->getLanguage());
+ }
+
+ /**
+ * Find the item categories.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ * @param Feed $feed Feed object
+ */
+ public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ $categories = XmlParser::getXPathResult($entry, 'atom:category/@term', $this->namespaces)
+ ?: XmlParser::getXPathResult($entry, 'category/@term');
+ $item->setCategoriesFromXml($categories);
+ }
+
+ /**
+ * Get the URL from a link tag.
+ *
+ * @param SimpleXMLElement $xml XML tag
+ * @param string $rel Link relationship: alternate, enclosure, related, self, via
+ *
+ * @return string
+ */
+ private function getUrl(SimpleXMLElement $xml, $rel, $fallback = false)
+ {
+ $link = $this->findLink($xml, $rel);
+
+ if ($link) {
+ return (string) $link['href'];
+ }
+
+ if ($fallback) {
+ $link = $this->findLink($xml, '');
+ return $link ? (string) $link['href'] : '';
+ }
+
+ return '';
+ }
+
+ /**
+ * Get a link tag that match a relationship.
+ *
+ * @param SimpleXMLElement $xml XML tag
+ * @param string $rel Link relationship: alternate, enclosure, related, self, via
+ *
+ * @return SimpleXMLElement|null
+ */
+ private function findLink(SimpleXMLElement $xml, $rel)
+ {
+ $links = XmlParser::getXPathResult($xml, 'atom:link', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'link');
+
+ foreach ($links as $link) {
+ if ($rel === (string) $link['rel']) {
+ return $link;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the entry content.
+ *
+ * @param SimpleXMLElement $entry XML Entry
+ *
+ * @return string
+ */
+ private function getContent(SimpleXMLElement $entry)
+ {
+ $content = current(
+ XmlParser::getXPathResult($entry, 'atom:content', $this->namespaces)
+ ?: XmlParser::getXPathResult($entry, 'content')
+ );
+
+ if (!empty($content) && count($content->children())) {
+ $xml_string = '';
+
+ foreach ($content->children() as $child) {
+ $xml_string .= $child->asXML();
+ }
+
+ return $xml_string;
+ } elseif (trim((string) $content) !== '') {
+ return (string) $content;
+ }
+
+ $summary = XmlParser::getXPathResult($entry, 'atom:summary', $this->namespaces)
+ ?: XmlParser::getXPathResult($entry, 'summary');
+
+ return (string) current($summary);
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php
new file mode 100644
index 00000000..0e5b80e3
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace PicoFeed\Parser;
+
+use DateTime;
+use DateTimeZone;
+use PicoFeed\Base;
+
+/**
+ * Date Parser.
+ *
+ * @package PicoFeed\Parser
+ * @author Frederic Guillot
+ */
+class DateParser extends Base
+{
+ /**
+ * Timezone used to parse feed dates.
+ *
+ * @access private
+ * @var string
+ */
+ private $timezone = 'UTC';
+
+ /**
+ * Supported formats [ 'format' => length ].
+ *
+ * @var array
+ */
+ public $formats = array(
+ DATE_ATOM => null,
+ DATE_RSS => null,
+ DATE_COOKIE => null,
+ DATE_ISO8601 => null,
+ DATE_RFC822 => null,
+ DATE_RFC850 => null,
+ DATE_RFC1036 => null,
+ DATE_RFC1123 => null,
+ DATE_RFC2822 => null,
+ DATE_RFC3339 => null,
+ 'l, d M Y H:i:s' => null,
+ 'D, d M Y H:i:s' => 25,
+ 'D, d M Y h:i:s' => 25,
+ 'D M d Y H:i:s' => 24,
+ 'j M Y H:i:s' => 20,
+ 'Y-m-d H:i:s' => 19,
+ 'Y-m-d\TH:i:s' => 19,
+ 'd/m/Y H:i:s' => 19,
+ 'D, d M Y' => 16,
+ 'Y-m-d' => 10,
+ 'd-m-Y' => 10,
+ 'm-d-Y' => 10,
+ 'd.m.Y' => 10,
+ 'm.d.Y' => 10,
+ 'd/m/Y' => 10,
+ 'm/d/Y' => 10,
+ );
+
+ /**
+ * Try to parse all date format for broken feeds.
+ *
+ * @param string $value Original date format
+ *
+ * @return DateTime
+ */
+ public function getDateTime($value)
+ {
+ $value = trim($value);
+
+ foreach ($this->formats as $format => $length) {
+ $truncated_value = $value;
+ if ($length !== null) {
+ $truncated_value = substr($truncated_value, 0, $length);
+ }
+
+ $date = $this->getValidDate($format, $truncated_value);
+ if ($date !== false) {
+ return $date;
+ }
+ }
+
+ return $this->getCurrentDateTime();
+ }
+
+ /**
+ * Get a valid date from a given format.
+ *
+ * @param string $format Date format
+ * @param string $value Original date value
+ *
+ * @return DateTime|bool
+ */
+ public function getValidDate($format, $value)
+ {
+ $date = DateTime::createFromFormat($format, $value, $this->getTimeZone());
+
+ if ($date !== false) {
+ $errors = DateTime::getLastErrors();
+
+ if ($errors['error_count'] === 0 && $errors['warning_count'] === 0) {
+ return $date;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the current datetime.
+ *
+ * @return DateTime
+ */
+ public function getCurrentDateTime()
+ {
+ return new DateTime('now', $this->getTimeZone());
+ }
+
+ /**
+ * Get DateTimeZone instance
+ *
+ * @access public
+ * @return DateTimeZone
+ */
+ public function getTimeZone()
+ {
+ return new DateTimeZone($this->config->getTimezone() ?: $this->timezone);
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php
new file mode 100644
index 00000000..a56e71c3
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php
@@ -0,0 +1,315 @@
+<?php
+
+namespace PicoFeed\Parser;
+
+/**
+ * Feed.
+ *
+ * @package PicoFeed\Parser
+ * @author Frederic Guillot
+ */
+class Feed
+{
+ /**
+ * Feed items.
+ *
+ * @var Item[]
+ */
+ public $items = array();
+
+ /**
+ * Feed id.
+ *
+ * @var string
+ */
+ public $id = '';
+
+ /**
+ * Feed title.
+ *
+ * @var string
+ */
+ public $title = '';
+
+ /**
+ * Feed description.
+ *
+ * @var string
+ */
+ public $description = '';
+
+ /**
+ * Feed url.
+ *
+ * @var string
+ */
+ public $feedUrl = '';
+
+ /**
+ * Site url.
+ *
+ * @var string
+ */
+ public $siteUrl = '';
+
+ /**
+ * Feed date.
+ *
+ * @var \DateTime
+ */
+ public $date = null;
+
+ /**
+ * Feed language.
+ *
+ * @var string
+ */
+ public $language = '';
+
+ /**
+ * Feed logo URL.
+ *
+ * @var string
+ */
+ public $logo = '';
+
+ /**
+ * Feed icon URL.
+ *
+ * @var string
+ */
+ public $icon = '';
+
+ /**
+ * Return feed information.
+ */
+ public function __toString()
+ {
+ $output = '';
+
+ foreach (array('id', 'title', 'feedUrl', 'siteUrl', 'language', 'description', 'logo') as $property) {
+ $output .= 'Feed::'.$property.' = '.$this->$property.PHP_EOL;
+ }
+
+ $output .= 'Feed::date = '.$this->date->format(DATE_RFC822).PHP_EOL;
+ $output .= 'Feed::isRTL() = '.($this->isRTL() ? 'true' : 'false').PHP_EOL;
+ $output .= 'Feed::items = '.count($this->items).' items'.PHP_EOL;
+
+ foreach ($this->items as $item) {
+ $output .= '----'.PHP_EOL;
+ $output .= $item;
+ }
+
+ return $output;
+ }
+
+ /**
+ * Get title.
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * Get description.
+ */
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * Get the logo url.
+ */
+ public function getLogo()
+ {
+ return $this->logo;
+ }
+
+ /**
+ * Get the icon url.
+ */
+ public function getIcon()
+ {
+ return $this->icon;
+ }
+
+ /**
+ * Get feed url.
+ */
+ public function getFeedUrl()
+ {
+ return $this->feedUrl;
+ }
+
+ /**
+ * Get site url.
+ */
+ public function getSiteUrl()
+ {
+ return $this->siteUrl;
+ }
+
+ /**
+ * Get date.
+ */
+ public function getDate()
+ {
+ return $this->date;
+ }
+
+ /**
+ * Get language.
+ */
+ public function getLanguage()
+ {
+ return $this->language;
+ }
+
+ /**
+ * Get id.
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Get feed items.
+ */
+ public function getItems()
+ {
+ return $this->items;
+ }
+
+ /**
+ * Return true if the feed is "Right to Left".
+ *
+ * @return bool
+ */
+ public function isRTL()
+ {
+ return Parser::isLanguageRTL($this->language);
+ }
+
+ /**
+ * Set feed items.
+ *
+ * @param Item[] $items
+ * @return Feed
+ */
+ public function setItems(array $items)
+ {
+ $this->items = $items;
+ return $this;
+ }
+
+ /**
+ * Set feed id.
+ *
+ * @param string $id
+ * @return Feed
+ */
+ public function setId($id)
+ {
+ $this->id = $id;
+ return $this;
+ }
+
+ /**
+ * Set feed title.
+ *
+ * @param string $title
+ * @return Feed
+ */
+ public function setTitle($title)
+ {
+ $this->title = $title;
+ return $this;
+ }
+
+ /**
+ * Set feed description.
+ *
+ * @param string $description
+ * @return Feed
+ */
+ public function setDescription($description)
+ {
+ $this->description = $description;
+ return $this;
+ }
+
+ /**
+ * Set feed url.
+ *
+ * @param string $feedUrl
+ * @return Feed
+ */
+ public function setFeedUrl($feedUrl)
+ {
+ $this->feedUrl = $feedUrl;
+ return $this;
+ }
+
+ /**
+ * Set feed website url.
+ *
+ * @param string $siteUrl
+ * @return Feed
+ */
+ public function setSiteUrl($siteUrl)
+ {
+ $this->siteUrl = $siteUrl;
+ return $this;
+ }
+
+ /**
+ * Set feed date.
+ *
+ * @param \DateTime $date
+ * @return Feed
+ */
+ public function setDate($date)
+ {
+ $this->date = $date;
+ return $this;
+ }
+
+ /**
+ * Set feed language.
+ *
+ * @param string $language
+ * @return Feed
+ */
+ public function setLanguage($language)
+ {
+ $this->language = $language;
+ return $this;
+ }
+
+ /**
+ * Set feed logo.
+ *
+ * @param string $logo
+ * @return Feed
+ */
+ public function setLogo($logo)
+ {
+ $this->logo = $logo;
+ return $this;
+ }
+
+ /**
+ * Set feed icon.
+ *
+ * @param string $icon
+ * @return Feed
+ */
+ public function setIcon($icon)
+ {
+ $this->icon = $icon;
+ return $this;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Item.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Item.php
new file mode 100644
index 00000000..f9581941
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Item.php
@@ -0,0 +1,534 @@
+<?php
+
+namespace PicoFeed\Parser;
+
+/**
+ * Feed Item.
+ *
+ * @package PicoFeed\Parser
+ * @author Frederic Guillot
+ */
+class Item
+{
+ /**
+ * List of known RTL languages.
+ *
+ * @var string[]
+ */
+ public $rtl = array(
+ 'ar', // Arabic (ar-**)
+ 'fa', // Farsi (fa-**)
+ 'ur', // Urdu (ur-**)
+ 'ps', // Pashtu (ps-**)
+ 'syr', // Syriac (syr-**)
+ 'dv', // Divehi (dv-**)
+ 'he', // Hebrew (he-**)
+ 'yi', // Yiddish (yi-**)
+ );
+
+ /**
+ * Item id.
+ *
+ * @var string
+ */
+ public $id = '';
+
+ /**
+ * Item title.
+ *
+ * @var string
+ */
+ public $title = '';
+
+ /**
+ * Item url.
+ *
+ * @var string
+ */
+ public $url = '';
+
+ /**
+ * Item author.
+ *
+ * @var string
+ */
+ public $author = '';
+
+ /**
+ * Item date.
+ *
+ * @var \DateTime
+ */
+ public $date = null;
+
+ /**
+ * Item published date.
+ *
+ * @var \DateTime
+ */
+ public $publishedDate = null;
+
+ /**
+ * Item updated date.
+ *
+ * @var \DateTime
+ */
+ public $updatedDate = null;
+
+ /**
+ * Item content.
+ *
+ * @var string
+ */
+ public $content = '';
+
+ /**
+ * Item enclosure url.
+ *
+ * @var string
+ */
+ public $enclosureUrl = '';
+
+ /**
+ * Item enclusure type.
+ *
+ * @var string
+ */
+ public $enclosureType = '';
+
+ /**
+ * Item language.
+ *
+ * @var string
+ */
+ public $language = '';
+
+ /**
+ * Item categories.
+ *
+ * @var array
+ */
+ public $categories = array();
+
+ /**
+ * Raw XML.
+ *
+ * @var \SimpleXMLElement
+ */
+ public $xml;
+
+ /**
+ * List of namespaces.
+ *
+ * @var array
+ */
+ public $namespaces = array();
+
+ /**
+ * Check if a XML namespace exists
+ *
+ * @access public
+ * @param string $namespace
+ * @return bool
+ */
+ public function hasNamespace($namespace)
+ {
+ return array_key_exists($namespace, $this->namespaces);
+ }
+
+ /**
+ * Get specific XML tag or attribute value.
+ *
+ * @param string $tag Tag name (examples: guid, media:content)
+ * @param string $attribute Tag attribute
+ *
+ * @return array|false Tag values or error
+ */
+ public function getTag($tag, $attribute = '')
+ {
+ if ($attribute !== '') {
+ $attribute = '/@'.$attribute;
+ }
+
+ $query = './/'.$tag.$attribute;
+ $elements = XmlParser::getXPathResult($this->xml, $query, $this->namespaces);
+
+ if ($elements === false) { // xPath error
+ return false;
+ }
+
+ return array_map(function ($element) { return (string) $element;}, $elements);
+ }
+
+ /**
+ * Return item information.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ $output = '';
+
+ foreach (array('id', 'title', 'url', 'language', 'author', 'enclosureUrl', 'enclosureType') as $property) {
+ $output .= 'Item::'.$property.' = '.$this->$property.PHP_EOL;
+ }
+
+ $publishedDate = $this->publishedDate != null ? $this->publishedDate->format(DATE_RFC822) : null;
+ $updatedDate = $this->updatedDate != null ? $this->updatedDate->format(DATE_RFC822) : null;
+
+ $categoryString = $this->categories != null ? implode(',', $this->categories) : null;
+
+ $output .= 'Item::date = '.$this->date->format(DATE_RFC822).PHP_EOL;
+ $output .= 'Item::publishedDate = '.$publishedDate.PHP_EOL;
+ $output .= 'Item::updatedDate = '.$updatedDate.PHP_EOL;
+ $output .= 'Item::isRTL() = '.($this->isRTL() ? 'true' : 'false').PHP_EOL;
+ $output .= 'Item::categories = ['.$categoryString.']'.PHP_EOL;
+ $output .= 'Item::content = '.strlen($this->content).' bytes'.PHP_EOL;
+
+ return $output;
+ }
+
+ /**
+ * Get title.
+ *
+ * @return string
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * Get URL
+ *
+ * @access public
+ * @return string
+ */
+ public function getUrl()
+ {
+ return $this->url;
+ }
+
+ /**
+ * Set URL
+ *
+ * @access public
+ * @param string $url
+ * @return Item
+ */
+ public function setUrl($url)
+ {
+ $this->url = $url;
+ return $this;
+ }
+
+ /**
+ * Get id.
+ *
+ * @return string
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Get date.
+ *
+ * @return \DateTime
+ */
+ public function getDate()
+ {
+ return $this->date;
+ }
+
+ /**
+ * Get published date.
+ *
+ * @return \DateTime
+ */
+ public function getPublishedDate()
+ {
+ return $this->publishedDate;
+ }
+
+ /**
+ * Get updated date.
+ *
+ * @return \DateTime
+ */
+ public function getUpdatedDate()
+ {
+ return $this->updatedDate;
+ }
+
+ /**
+ * Get content.
+ *
+ * @return string
+ */
+ public function getContent()
+ {
+ return $this->content;
+ }
+
+ /**
+ * Set content
+ *
+ * @access public
+ * @param string $value
+ * @return Item
+ */
+ public function setContent($value)
+ {
+ $this->content = $value;
+ return $this;
+ }
+
+ /**
+ * Get enclosure url.
+ *
+ * @return string
+ */
+ public function getEnclosureUrl()
+ {
+ return $this->enclosureUrl;
+ }
+
+ /**
+ * Get enclosure type.
+ *
+ * @return string
+ */
+ public function getEnclosureType()
+ {
+ return $this->enclosureType;
+ }
+
+ /**
+ * Get language.
+ *
+ * @return string
+ */
+ public function getLanguage()
+ {
+ return $this->language;
+ }
+
+ /**
+ * Get categories.
+ *
+ * @return string
+ */
+ public function getCategories()
+ {
+ return $this->categories;
+ }
+
+ /**
+ * Get author.
+ *
+ * @return string
+ */
+ public function getAuthor()
+ {
+ return $this->author;
+ }
+
+ /**
+ * Return true if the item is "Right to Left".
+ *
+ * @return bool
+ */
+ public function isRTL()
+ {
+ return Parser::isLanguageRTL($this->language);
+ }
+
+ /**
+ * Set item id.
+ *
+ * @param string $id
+ * @return Item
+ */
+ public function setId($id)
+ {
+ $this->id = $id;
+ return $this;
+ }
+
+ /**
+ * Set item title.
+ *
+ * @param string $title
+ * @return Item
+ */
+ public function setTitle($title)
+ {
+ $this->title = $title;
+ return $this;
+ }
+
+ /**
+ * Set author.
+ *
+ * @param string $author
+ * @return Item
+ */
+ public function setAuthor($author)
+ {
+ $this->author = $author;
+ return $this;
+ }
+
+ /**
+ * Set item date.
+ *
+ * @param \DateTime $date
+ * @return Item
+ */
+ public function setDate($date)
+ {
+ $this->date = $date;
+ return $this;
+ }
+
+ /**
+ * Set item published date.
+ *
+ * @param \DateTime $publishedDate
+ * @return Item
+ */
+ public function setPublishedDate($publishedDate)
+ {
+ $this->publishedDate = $publishedDate;
+ return $this;
+ }
+
+ /**
+ * Set item updated date.
+ *
+ * @param \DateTime $updatedDate
+ * @return Item
+ */
+ public function setUpdatedDate($updatedDate)
+ {
+ $this->updatedDate = $updatedDate;
+ return $this;
+ }
+
+ /**
+ * Set enclosure url.
+ *
+ * @param string $enclosureUrl
+ * @return Item
+ */
+ public function setEnclosureUrl($enclosureUrl)
+ {
+ $this->enclosureUrl = $enclosureUrl;
+ return $this;
+ }
+
+ /**
+ * Set enclosure type.
+ *
+ * @param string $enclosureType
+ * @return Item
+ */
+ public function setEnclosureType($enclosureType)
+ {
+ $this->enclosureType = $enclosureType;
+ return $this;
+ }
+
+ /**
+ * Set item language.
+ *
+ * @param string $language
+ * @return Item
+ */
+ public function setLanguage($language)
+ {
+ $this->language = $language;
+ return $this;
+ }
+
+ /**
+ * Set item categories.
+ *
+ * @param array $categories
+ * @return Item
+ */
+ public function setCategories($categories)
+ {
+ $this->categories = $categories;
+ return $this;
+ }
+
+ /**
+ * Set item categories from xml.
+ *
+ * @param SimpleXMLElement[] $categories
+ * @return Item
+ */
+ public function setCategoriesFromXml($categories)
+ {
+ if ($categories !== false) {
+ $this->setCategories(
+ array_map(
+ function ($element) {
+ return trim((string) $element);
+ },
+ $categories
+ )
+ );
+ } else {
+ $categories = array();
+ }
+ return $this;
+ }
+
+ /**
+ * Set raw XML.
+ *
+ * @param \SimpleXMLElement $xml
+ * @return Item
+ */
+ public function setXml($xml)
+ {
+ $this->xml = $xml;
+ return $this;
+ }
+
+ /**
+ * Get raw XML.
+ *
+ * @return \SimpleXMLElement
+ */
+ public function getXml()
+ {
+ return $this->xml;
+ }
+
+ /**
+ * Set XML namespaces.
+ *
+ * @param array $namespaces
+ * @return Item
+ */
+ public function setNamespaces($namespaces)
+ {
+ $this->namespaces = $namespaces;
+ return $this;
+ }
+
+ /**
+ * Get XML namespaces.
+ *
+ * @return array
+ */
+ public function getNamespaces()
+ {
+ return $this->namespaces;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php
new file mode 100644
index 00000000..efaf0ff1
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace PicoFeed\Parser;
+
+/**
+ * MalformedXmlException Exception.
+ *
+ * @package PicoFeed\Parser
+ * @author Frederic Guillot
+ */
+class MalformedXmlException extends ParserException
+{
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Parser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Parser.php
new file mode 100644
index 00000000..3ee99f59
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Parser.php
@@ -0,0 +1,404 @@
+<?php
+
+namespace PicoFeed\Parser;
+
+use PicoFeed\Processor\ContentFilterProcessor;
+use PicoFeed\Processor\ContentGeneratorProcessor;
+use PicoFeed\Processor\ItemPostProcessor;
+use PicoFeed\Processor\ScraperProcessor;
+use SimpleXMLElement;
+use PicoFeed\Client\Url;
+use PicoFeed\Encoding\Encoding;
+use PicoFeed\Filter\Filter;
+use PicoFeed\Logging\Logger;
+
+/**
+ * Base parser class.
+ *
+ * @package PicoFeed\Parser
+ * @author Frederic Guillot
+ */
+abstract class Parser implements ParserInterface
+{
+ /**
+ * Config object.
+ *
+ * @var \PicoFeed\Config\Config
+ */
+ private $config;
+
+ /**
+ * DateParser object.
+ *
+ * @var \PicoFeed\Parser\DateParser
+ */
+ private $dateParser;
+
+ /**
+ * Hash algorithm used to generate item id, any value supported by PHP, see hash_algos().
+ *
+ * @var string
+ */
+ private $hash_algo = 'sha256';
+
+ /**
+ * Feed content (XML data).
+ *
+ * @var string
+ */
+ protected $content = '';
+
+ /**
+ * Fallback url.
+ *
+ * @var string
+ */
+ protected $fallback_url = '';
+
+ /**
+ * XML namespaces supported by parser.
+ *
+ * @var array
+ */
+ protected $namespaces = array();
+
+ /**
+ * XML namespaces used in document.
+ *
+ * @var array
+ */
+ protected $used_namespaces = array();
+
+ /**
+ * Item Post Processor instance
+ *
+ * @access private
+ * @var ItemPostProcessor
+ */
+ private $itemPostProcessor;
+
+ /**
+ * Constructor.
+ *
+ * @param string $content Feed content
+ * @param string $http_encoding HTTP encoding (headers)
+ * @param string $fallback_url Fallback url when the feed provide relative or broken url
+ */
+ public function __construct($content, $http_encoding = '', $fallback_url = '')
+ {
+ $this->fallback_url = $fallback_url;
+ $xml_encoding = XmlParser::getEncodingFromXmlTag($content);
+
+ // Strip XML tag to avoid multiple encoding/decoding in the next XML processing
+ $this->content = Filter::stripXmlTag($content);
+
+ // Encode everything in UTF-8
+ Logger::setMessage(get_called_class().': HTTP Encoding "'.$http_encoding.'" ; XML Encoding "'.$xml_encoding.'"');
+ $this->content = Encoding::convert($this->content, $xml_encoding ?: $http_encoding);
+
+ $this->itemPostProcessor = new ItemPostProcessor($this->config);
+ $this->itemPostProcessor->register(new ContentGeneratorProcessor($this->config));
+ $this->itemPostProcessor->register(new ContentFilterProcessor($this->config));
+ }
+
+ /**
+ * Parse the document.
+ *
+ * @return \PicoFeed\Parser\Feed
+ */
+ public function execute()
+ {
+ Logger::setMessage(get_called_class().': begin parsing');
+
+ $xml = XmlParser::getSimpleXml($this->content);
+
+ if ($xml === false) {
+ Logger::setMessage(get_called_class().': Applying XML workarounds');
+ $this->content = Filter::normalizeData($this->content);
+ $xml = XmlParser::getSimpleXml($this->content);
+
+ if ($xml === false) {
+ Logger::setMessage(get_called_class().': XML parsing error');
+ Logger::setMessage(XmlParser::getErrors());
+ throw new MalformedXmlException('XML parsing error');
+ }
+ }
+
+ $this->used_namespaces = $xml->getNamespaces(true);
+ $xml = $this->registerSupportedNamespaces($xml);
+
+ $feed = new Feed();
+
+ $this->findFeedUrl($xml, $feed);
+ $this->checkFeedUrl($feed);
+
+ $this->findSiteUrl($xml, $feed);
+ $this->checkSiteUrl($feed);
+
+ $this->findFeedTitle($xml, $feed);
+ $this->findFeedDescription($xml, $feed);
+ $this->findFeedLanguage($xml, $feed);
+ $this->findFeedId($xml, $feed);
+ $this->findFeedDate($xml, $feed);
+ $this->findFeedLogo($xml, $feed);
+ $this->findFeedIcon($xml, $feed);
+
+ foreach ($this->getItemsTree($xml) as $entry) {
+ $entry = $this->registerSupportedNamespaces($entry);
+
+ $item = new Item();
+ $item->xml = $entry;
+ $item->namespaces = $this->used_namespaces;
+
+ $this->findItemAuthor($xml, $entry, $item);
+
+ $this->findItemUrl($entry, $item);
+ $this->checkItemUrl($feed, $item);
+
+ $this->findItemTitle($entry, $item);
+ $this->findItemContent($entry, $item);
+
+ // Id generation can use the item url/title/content (order is important)
+ $this->findItemId($entry, $item, $feed);
+ $this->findItemDate($entry, $item, $feed);
+ $this->findItemEnclosure($entry, $item, $feed);
+ $this->findItemLanguage($entry, $item, $feed);
+ $this->findItemCategories($entry, $item, $feed);
+
+ $this->itemPostProcessor->execute($feed, $item);
+ $feed->items[] = $item;
+ }
+
+ Logger::setMessage(get_called_class().PHP_EOL.$feed);
+
+ return $feed;
+ }
+
+ /**
+ * Check if the feed url is correct.
+ *
+ * @param Feed $feed Feed object
+ */
+ public function checkFeedUrl(Feed $feed)
+ {
+ if ($feed->getFeedUrl() === '') {
+ $feed->feedUrl = $this->fallback_url;
+ } else {
+ $feed->feedUrl = Url::resolve($feed->getFeedUrl(), $this->fallback_url);
+ }
+ }
+
+ /**
+ * Check if the site url is correct.
+ *
+ * @param Feed $feed Feed object
+ */
+ public function checkSiteUrl(Feed $feed)
+ {
+ if ($feed->getSiteUrl() === '') {
+ $feed->siteUrl = Url::base($feed->getFeedUrl());
+ } else {
+ $feed->siteUrl = Url::resolve($feed->getSiteUrl(), $this->fallback_url);
+ }
+ }
+
+ /**
+ * Check if the item url is correct.
+ *
+ * @param Feed $feed Feed object
+ * @param Item $item Item object
+ */
+ public function checkItemUrl(Feed $feed, Item $item)
+ {
+ $item->url = Url::resolve($item->getUrl(), $feed->getSiteUrl());
+ }
+
+ /**
+ * Find the item date.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findItemDate(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ $this->findItemPublishedDate($entry, $item, $feed);
+ $this->findItemUpdatedDate($entry, $item, $feed);
+
+ if ($item->getPublishedDate() === null) {
+ // Use the updated date if available, otherwise use the feed date
+ $item->setPublishedDate($item->getUpdatedDate() ?: $feed->getDate());
+ }
+
+ if ($item->getUpdatedDate() === null) {
+ // Use the published date as fallback
+ $item->setUpdatedDate($item->getPublishedDate());
+ }
+
+ // Use the most recent of published and updated dates
+ $item->setDate(max($item->getPublishedDate(), $item->getUpdatedDate()));
+ }
+
+ /**
+ * Get Item Post Processor instance
+ *
+ * @access public
+ * @return ItemPostProcessor
+ */
+ public function getItemPostProcessor()
+ {
+ return $this->itemPostProcessor;
+ }
+
+ /**
+ * Get DateParser instance
+ *
+ * @access public
+ * @return DateParser
+ */
+ public function getDateParser()
+ {
+ if ($this->dateParser === null) {
+ $this->dateParser = new DateParser($this->config);
+ }
+
+ return $this->dateParser;
+ }
+
+ /**
+ * Generate a unique id for an entry (hash all arguments).
+ *
+ * @return string
+ */
+ public function generateId()
+ {
+ return hash($this->hash_algo, implode(func_get_args()));
+ }
+
+ /**
+ * Return true if the given language is "Right to Left".
+ *
+ * @static
+ *
+ * @param string $language Language: fr-FR, en-US
+ *
+ * @return bool
+ */
+ public static function isLanguageRTL($language)
+ {
+ $language = strtolower($language);
+
+ $rtl_languages = array(
+ 'ar', // Arabic (ar-**)
+ 'fa', // Farsi (fa-**)
+ 'ur', // Urdu (ur-**)
+ 'ps', // Pashtu (ps-**)
+ 'syr', // Syriac (syr-**)
+ 'dv', // Divehi (dv-**)
+ 'he', // Hebrew (he-**)
+ 'yi', // Yiddish (yi-**)
+ );
+
+ foreach ($rtl_languages as $prefix) {
+ if (strpos($language, $prefix) === 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Set Hash algorithm used for id generation.
+ *
+ * @param string $algo Algorithm name
+ * @return \PicoFeed\Parser\Parser
+ */
+ public function setHashAlgo($algo)
+ {
+ $this->hash_algo = $algo ?: $this->hash_algo;
+ return $this;
+ }
+
+ /**
+ * Set config object.
+ *
+ * @param \PicoFeed\Config\Config $config Config instance
+ *
+ * @return \PicoFeed\Parser\Parser
+ */
+ public function setConfig($config)
+ {
+ $this->config = $config;
+ $this->itemPostProcessor->setConfig($config);
+ return $this;
+ }
+
+ /**
+ * Enable the content grabber.
+ *
+ * @return \PicoFeed\Parser\Parser
+ */
+ public function disableContentFiltering()
+ {
+ $this->itemPostProcessor->unregister('PicoFeed\Processor\ContentFilterProcessor');
+ return $this;
+ }
+
+ /**
+ * Enable the content grabber.
+ *
+ * @param bool $needsRuleFile true if only pages with rule files should be
+ * scraped
+ * @param null|\Closure $scraperCallback Callback function that gets called for each
+ * scraper execution
+ *
+ * @return \PicoFeed\Parser\Parser
+ */
+ public function enableContentGrabber($needsRuleFile = false, $scraperCallback = null)
+ {
+ $processor = new ScraperProcessor($this->config);
+
+ if ($needsRuleFile) {
+ $processor->getScraper()->disableCandidateParser();
+ }
+
+ if ($scraperCallback !== null) {
+ $processor->setExecutionCallback($scraperCallback);
+ }
+
+ $this->itemPostProcessor->register($processor);
+ return $this;
+ }
+
+ /**
+ * Set ignored URLs for the content grabber.
+ *
+ * @param array $urls URLs
+ *
+ * @return \PicoFeed\Parser\Parser
+ */
+ public function setGrabberIgnoreUrls(array $urls)
+ {
+ $this->itemPostProcessor->getProcessor('PicoFeed\Processor\ScraperProcessor')->ignoreUrls($urls);
+ return $this;
+ }
+
+ /**
+ * Register all supported namespaces to be used within an xpath query.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ *
+ * @return SimpleXMLElement
+ */
+ public function registerSupportedNamespaces(SimpleXMLElement $xml)
+ {
+ foreach ($this->namespaces as $prefix => $ns) {
+ $xml->registerXPathNamespace($prefix, $ns);
+ }
+
+ return $xml;
+ }
+
+
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php
new file mode 100644
index 00000000..b5fbb699
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace PicoFeed\Parser;
+
+use PicoFeed\PicoFeedException;
+
+/**
+ * ParserException Exception.
+ *
+ * @package PicoFeed\Parser
+ * @author Frederic Guillot
+ */
+abstract class ParserException extends PicoFeedException
+{
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserInterface.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserInterface.php
new file mode 100644
index 00000000..8d6be085
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserInterface.php
@@ -0,0 +1,182 @@
+<?php
+
+namespace PicoFeed\Parser;
+
+use SimpleXMLElement;
+
+/**
+ * Interface ParserInterface
+ *
+ * @package PicoFeed\Parser
+ * @author Frederic Guillot
+ */
+interface ParserInterface
+{
+ /**
+ * Find the feed url.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param Feed $feed Feed object
+ */
+ public function findFeedUrl(SimpleXMLElement $xml, Feed $feed);
+
+ /**
+ * Find the site url.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param Feed $feed Feed object
+ */
+ public function findSiteUrl(SimpleXMLElement $xml, Feed $feed);
+
+ /**
+ * Find the feed title.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param Feed $feed Feed object
+ */
+ public function findFeedTitle(SimpleXMLElement $xml, Feed $feed);
+
+ /**
+ * Find the feed description.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param Feed $feed Feed object
+ */
+ public function findFeedDescription(SimpleXMLElement $xml, Feed $feed);
+
+ /**
+ * Find the feed language.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param Feed $feed Feed object
+ */
+ public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed);
+
+ /**
+ * Find the feed id.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param Feed $feed Feed object
+ */
+ public function findFeedId(SimpleXMLElement $xml, Feed $feed);
+
+ /**
+ * Find the feed date.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param Feed $feed Feed object
+ */
+ public function findFeedDate(SimpleXMLElement $xml, Feed $feed);
+
+ /**
+ * Find the feed logo url.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param Feed $feed Feed object
+ */
+ public function findFeedLogo(SimpleXMLElement $xml, Feed $feed);
+
+ /**
+ * Find the feed icon.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param Feed $feed Feed object
+ */
+ public function findFeedIcon(SimpleXMLElement $xml, Feed $feed);
+
+ /**
+ * Get the path to the items XML tree.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ *
+ * @return SimpleXMLElement
+ */
+ public function getItemsTree(SimpleXMLElement $xml);
+
+ /**
+ * Find the item author.
+ *
+ * @param SimpleXMLElement $xml Feed
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ */
+ public function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item);
+
+ /**
+ * Find the item URL.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ */
+ public function findItemUrl(SimpleXMLElement $entry, Item $item);
+
+ /**
+ * Find the item title.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ */
+ public function findItemTitle(SimpleXMLElement $entry, Item $item);
+
+ /**
+ * Genereate the item id.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ * @param Feed $feed Feed object
+ */
+ public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed);
+
+ /**
+ * Find the item published date.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ * @param Feed $feed Feed object
+ */
+ public function findItemPublishedDate(SimpleXMLElement $entry, Item $item, Feed $feed);
+
+ /**
+ * Find the item updated date.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ * @param Feed $feed Feed object
+ */
+ public function findItemUpdatedDate(SimpleXMLElement $entry, Item $item, Feed $feed);
+
+ /**
+ * Find the item content.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ */
+ public function findItemContent(SimpleXMLElement $entry, Item $item);
+
+ /**
+ * Find the item enclosure.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ * @param Feed $feed Feed object
+ */
+ public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed);
+
+ /**
+ * Find the item language.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ * @param Feed $feed Feed object
+ */
+ public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed);
+
+ /**
+ * Find the item categories.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ * @param Feed $feed Feed object
+ */
+ public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed);
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss10.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss10.php
new file mode 100644
index 00000000..bb9bf424
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss10.php
@@ -0,0 +1,306 @@
+<?php
+
+namespace PicoFeed\Parser;
+
+use SimpleXMLElement;
+use PicoFeed\Filter\Filter;
+
+/**
+ * RSS 1.0 parser.
+ *
+ * @package PicoFeed\Parser
+ * @author Frederic Guillot
+ */
+class Rss10 extends Parser
+{
+ /**
+ * Supported namespaces.
+ */
+ protected $namespaces = array(
+ 'rss' => 'http://purl.org/rss/1.0/',
+ 'dc' => 'http://purl.org/dc/elements/1.1/',
+ 'content' => 'http://purl.org/rss/1.0/modules/content/',
+ 'feedburner' => 'http://rssnamespace.org/feedburner/ext/1.0',
+ );
+
+ /**
+ * Get the path to the items XML tree.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ *
+ * @return SimpleXMLElement
+ */
+ public function getItemsTree(SimpleXMLElement $xml)
+ {
+ return XmlParser::getXPathResult($xml, 'rss:item', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'item')
+ ?: $xml->item;
+ }
+
+ /**
+ * Find the feed url.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedUrl(SimpleXMLElement $xml, Feed $feed)
+ {
+ $feed->setFeedUrl('');
+ }
+
+ /**
+ * Find the site url.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findSiteUrl(SimpleXMLElement $xml, Feed $feed)
+ {
+ $value = XmlParser::getXPathResult($xml, 'rss:channel/rss:link', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'channel/link')
+ ?: $xml->channel->link;
+
+ $feed->setSiteUrl(XmlParser::getValue($value));
+ }
+
+ /**
+ * Find the feed description.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedDescription(SimpleXMLElement $xml, Feed $feed)
+ {
+ $description = XmlParser::getXPathResult($xml, 'rss:channel/rss:description', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'channel/description')
+ ?: $xml->channel->description;
+
+ $feed->setDescription(XmlParser::getValue($description));
+ }
+
+ /**
+ * Find the feed logo url.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedLogo(SimpleXMLElement $xml, Feed $feed)
+ {
+ $logo = XmlParser::getXPathResult($xml, 'rss:image/rss:url', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'image/url');
+
+ $feed->setLogo(XmlParser::getValue($logo));
+ }
+
+ /**
+ * Find the feed icon.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedIcon(SimpleXMLElement $xml, Feed $feed)
+ {
+ $feed->setIcon('');
+ }
+
+ /**
+ * Find the feed title.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedTitle(SimpleXMLElement $xml, Feed $feed)
+ {
+ $title = XmlParser::getXPathResult($xml, 'rss:channel/rss:title', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'channel/title')
+ ?: $xml->channel->title;
+
+ $feed->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $feed->getSiteUrl());
+ }
+
+ /**
+ * Find the feed language.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed)
+ {
+ $language = XmlParser::getXPathResult($xml, 'rss:channel/dc:language', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'channel/dc:language', $this->namespaces);
+
+ $feed->setLanguage(XmlParser::getValue($language));
+ }
+
+ /**
+ * Find the feed id.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedId(SimpleXMLElement $xml, Feed $feed)
+ {
+ $feed->setId($feed->getFeedUrl() ?: $feed->getSiteUrl());
+ }
+
+ /**
+ * Find the feed date.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedDate(SimpleXMLElement $xml, Feed $feed)
+ {
+ $date = XmlParser::getXPathResult($xml, 'rss:channel/dc:date', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'channel/dc:date', $this->namespaces);
+
+ $feed->setDate($this->getDateParser()->getDateTime(XmlParser::getValue($date)));
+ }
+
+ /**
+ * Find the item published date.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findItemPublishedDate(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ $date = XmlParser::getXPathResult($entry, 'dc:date', $this->namespaces);
+
+ $item->setPublishedDate(!empty($date) ? $this->getDateParser()->getDateTime(XmlParser::getValue($date)) : null);
+ }
+
+ /**
+ * Find the item updated date.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findItemUpdatedDate(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ if ($item->publishedDate === null) {
+ $this->findItemPublishedDate($entry, $item, $feed);
+ }
+ $item->setUpdatedDate($item->getPublishedDate()); // No updated date in RSS 1.0 specifications
+ }
+
+ /**
+ * Find the item title.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ */
+ public function findItemTitle(SimpleXMLElement $entry, Item $item)
+ {
+ $title = XmlParser::getXPathResult($entry, 'rss:title', $this->namespaces)
+ ?: XmlParser::getXPathResult($entry, 'title')
+ ?: $entry->title;
+
+ $item->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $item->getUrl());
+ }
+
+ /**
+ * Find the item author.
+ *
+ * @param SimpleXMLElement $xml Feed
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ */
+ public function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item)
+ {
+ $author = XmlParser::getXPathResult($entry, 'dc:creator', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'rss:channel/dc:creator', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'channel/dc:creator', $this->namespaces);
+
+ $item->setAuthor(XmlParser::getValue($author));
+ }
+
+ /**
+ * Find the item content.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ */
+ public function findItemContent(SimpleXMLElement $entry, Item $item)
+ {
+ $content = XmlParser::getXPathResult($entry, 'content:encoded', $this->namespaces);
+
+ if (XmlParser::getValue($content) === '') {
+ $content = XmlParser::getXPathResult($entry, 'rss:description', $this->namespaces)
+ ?: XmlParser::getXPathResult($entry, 'description')
+ ?: $entry->description;
+ }
+
+ $item->setContent(XmlParser::getValue($content));
+ }
+
+ /**
+ * Find the item URL.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ */
+ public function findItemUrl(SimpleXMLElement $entry, Item $item)
+ {
+ $link = XmlParser::getXPathResult($entry, 'feedburner:origLink', $this->namespaces)
+ ?: XmlParser::getXPathResult($entry, 'rss:link', $this->namespaces)
+ ?: XmlParser::getXPathResult($entry, 'link')
+ ?: $entry->link;
+
+ $item->setUrl(XmlParser::getValue($link));
+ }
+
+ /**
+ * Genereate the item id.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ $item->setId($this->generateId(
+ $item->getTitle(), $item->getUrl(), $item->getContent()
+ ));
+ }
+
+ /**
+ * Find the item enclosure.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ }
+
+ /**
+ * Find the item language.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ $language = XmlParser::getXPathResult($entry, 'dc:language', $this->namespaces);
+
+ $item->setLanguage(XmlParser::getValue($language) ?: $feed->getLanguage());
+ }
+
+ /**
+ * Find the item categories.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ * @param Feed $feed Feed object
+ */
+ public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ $categories = XmlParser::getXPathResult($entry, 'dc:subject', $this->namespaces);
+ $item->setCategoriesFromXml($categories);
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php
new file mode 100644
index 00000000..1dd3bf8c
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php
@@ -0,0 +1,319 @@
+<?php
+
+namespace PicoFeed\Parser;
+
+use SimpleXMLElement;
+use PicoFeed\Filter\Filter;
+use PicoFeed\Client\Url;
+
+/**
+ * RSS 2.0 Parser.
+ *
+ * @package PicoFeed\Parser
+ * @author Frederic Guillot
+ */
+class Rss20 extends Parser
+{
+ /**
+ * Supported namespaces.
+ */
+ protected $namespaces = array(
+ 'dc' => 'http://purl.org/dc/elements/1.1/',
+ 'content' => 'http://purl.org/rss/1.0/modules/content/',
+ 'feedburner' => 'http://rssnamespace.org/feedburner/ext/1.0',
+ 'atom' => 'http://www.w3.org/2005/Atom',
+ );
+
+ /**
+ * Get the path to the items XML tree.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ *
+ * @return SimpleXMLElement
+ */
+ public function getItemsTree(SimpleXMLElement $xml)
+ {
+ return XmlParser::getXPathResult($xml, 'channel/item');
+ }
+
+ /**
+ * Find the feed url.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedUrl(SimpleXMLElement $xml, Feed $feed)
+ {
+ $feed->setFeedUrl('');
+ }
+
+ /**
+ * Find the site url.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findSiteUrl(SimpleXMLElement $xml, Feed $feed)
+ {
+ $value = XmlParser::getXPathResult($xml, 'channel/link');
+ $feed->setSiteUrl(XmlParser::getValue($value));
+ }
+
+ /**
+ * Find the feed description.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedDescription(SimpleXMLElement $xml, Feed $feed)
+ {
+ $value = XmlParser::getXPathResult($xml, 'channel/description');
+ $feed->setDescription(XmlParser::getValue($value));
+ }
+
+ /**
+ * Find the feed logo url.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedLogo(SimpleXMLElement $xml, Feed $feed)
+ {
+ $value = XmlParser::getXPathResult($xml, 'channel/image/url');
+ $feed->setLogo(XmlParser::getValue($value));
+ }
+
+ /**
+ * Find the feed icon.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedIcon(SimpleXMLElement $xml, Feed $feed)
+ {
+ $feed->setIcon('');
+ }
+
+ /**
+ * Find the feed title.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedTitle(SimpleXMLElement $xml, Feed $feed)
+ {
+ $title = XmlParser::getXPathResult($xml, 'channel/title');
+ $feed->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $feed->getSiteUrl());
+ }
+
+ /**
+ * Find the feed language.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed)
+ {
+ $value = XmlParser::getXPathResult($xml, 'channel/language');
+ $feed->setLanguage(XmlParser::getValue($value));
+ }
+
+ /**
+ * Find the feed id.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedId(SimpleXMLElement $xml, Feed $feed)
+ {
+ $feed->setId($feed->getFeedUrl() ?: $feed->getSiteUrl());
+ }
+
+ /**
+ * Find the feed date.
+ *
+ * @param SimpleXMLElement $xml Feed xml
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findFeedDate(SimpleXMLElement $xml, Feed $feed)
+ {
+ $publish_date = XmlParser::getXPathResult($xml, 'channel/pubDate');
+ $update_date = XmlParser::getXPathResult($xml, 'channel/lastBuildDate');
+
+ $published = !empty($publish_date) ? $this->getDateParser()->getDateTime(XmlParser::getValue($publish_date)) : null;
+ $updated = !empty($update_date) ? $this->getDateParser()->getDateTime(XmlParser::getValue($update_date)) : null;
+
+ if ($published === null && $updated === null) {
+ $feed->setDate($this->getDateParser()->getCurrentDateTime()); // We use the current date if there is no date for the feed
+ } elseif ($published !== null && $updated !== null) {
+ $feed->setDate(max($published, $updated)); // We use the most recent date between published and updated
+ } else {
+ $feed->setDate($updated ?: $published);
+ }
+ }
+
+ /**
+ * Find the item published date.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findItemPublishedDate(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ $date = XmlParser::getXPathResult($entry, 'pubDate');
+
+ $item->setPublishedDate(!empty($date) ? $this->getDateParser()->getDateTime(XmlParser::getValue($date)) : null);
+ }
+
+ /**
+ * Find the item updated date.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findItemUpdatedDate(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ if ($item->publishedDate === null) {
+ $this->findItemPublishedDate($entry, $item, $feed);
+ }
+ $item->setUpdatedDate($item->getPublishedDate()); // No updated date in RSS 2.0 specifications
+ }
+
+ /**
+ * Find the item title.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ */
+ public function findItemTitle(SimpleXMLElement $entry, Item $item)
+ {
+ $value = XmlParser::getXPathResult($entry, 'title');
+ $item->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($value)) ?: $item->getUrl());
+ }
+
+ /**
+ * Find the item author.
+ *
+ * @param SimpleXMLElement $xml Feed
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ */
+ public function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item)
+ {
+ $value = XmlParser::getXPathResult($entry, 'dc:creator', $this->namespaces)
+ ?: XmlParser::getXPathResult($entry, 'author')
+ ?: XmlParser::getXPathResult($xml, 'channel/dc:creator', $this->namespaces)
+ ?: XmlParser::getXPathResult($xml, 'channel/managingEditor');
+
+ $item->setAuthor(XmlParser::getValue($value));
+ }
+
+ /**
+ * Find the item content.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ */
+ public function findItemContent(SimpleXMLElement $entry, Item $item)
+ {
+ $content = XmlParser::getXPathResult($entry, 'content:encoded', $this->namespaces);
+
+ if (XmlParser::getValue($content) === '') {
+ $content = XmlParser::getXPathResult($entry, 'description');
+ }
+
+ $item->setContent(XmlParser::getValue($content));
+ }
+
+ /**
+ * Find the item URL.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ */
+ public function findItemUrl(SimpleXMLElement $entry, Item $item)
+ {
+ $link = XmlParser::getXPathResult($entry, 'feedburner:origLink', $this->namespaces)
+ ?: XmlParser::getXPathResult($entry, 'link')
+ ?: XmlParser::getXPathResult($entry, 'atom:link/@href', $this->namespaces);
+
+ if (!empty($link)) {
+ $item->setUrl(XmlParser::getValue($link));
+ } else {
+ $link = XmlParser::getXPathResult($entry, 'guid');
+ $link = XmlParser::getValue($link);
+
+ if (filter_var($link, FILTER_VALIDATE_URL) !== false) {
+ $item->setUrl($link);
+ }
+ }
+ }
+
+ /**
+ * Genereate the item id.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ $id = XmlParser::getValue(XmlParser::getXPathResult($entry, 'guid'));
+
+ if ($id) {
+ $item->setId($this->generateId($id));
+ } else {
+ $item->setId($this->generateId(
+ $item->getTitle(), $item->getUrl(), $item->getContent()
+ ));
+ }
+ }
+
+ /**
+ * Find the item enclosure.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ if (isset($entry->enclosure)) {
+ $type = XmlParser::getXPathResult($entry, 'enclosure/@type');
+ $url = XmlParser::getXPathResult($entry, 'feedburner:origEnclosureLink', $this->namespaces)
+ ?: XmlParser::getXPathResult($entry, 'enclosure/@url');
+
+ $item->setEnclosureUrl(Url::resolve(XmlParser::getValue($url), $feed->getSiteUrl()));
+ $item->setEnclosureType(XmlParser::getValue($type));
+ }
+ }
+
+ /**
+ * Find the item language.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param \PicoFeed\Parser\Item $item Item object
+ * @param \PicoFeed\Parser\Feed $feed Feed object
+ */
+ public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ $language = XmlParser::getXPathResult($entry, 'dc:language', $this->namespaces);
+ $item->setLanguage(XmlParser::getValue($language) ?: $feed->getLanguage());
+ }
+
+ /**
+ * Find the item categories.
+ *
+ * @param SimpleXMLElement $entry Feed item
+ * @param Item $item Item object
+ * @param Feed $feed Feed object
+ */
+ public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed)
+ {
+ $categories = XmlParser::getXPathResult($entry, 'category');
+ $item->setCategoriesFromXml($categories);
+ }
+
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php
new file mode 100644
index 00000000..058fca1b
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace PicoFeed\Parser;
+
+/**
+ * RSS 0.91 Parser.
+ *
+ * @package PicoFeed\Parser
+ * @author Frederic Guillot
+ */
+class Rss91 extends Rss20
+{
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss92.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss92.php
new file mode 100644
index 00000000..e3df3c8b
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss92.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace PicoFeed\Parser;
+
+/**
+ * RSS 0.92 Parser.
+ *
+ * @package PicoFeed\Parser
+ * @author Frederic Guillot
+ */
+class Rss92 extends Rss20
+{
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlEntityException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlEntityException.php
new file mode 100644
index 00000000..1188217d
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlEntityException.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace PicoFeed\Parser;
+
+/**
+ * XmlEntityException Exception.
+ *
+ * @package PicoFeed\Parser
+ * @author Bernhard Posselt
+ */
+class XmlEntityException extends MalformedXmlException
+{
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlParser.php
new file mode 100644
index 00000000..ea42949f
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlParser.php
@@ -0,0 +1,246 @@
+<?php
+
+namespace PicoFeed\Parser;
+
+use DOMDocument;
+use SimpleXMLElement;
+use ZendXml\Exception\RuntimeException;
+use ZendXml\Security;
+
+/**
+ * XML parser class.
+ *
+ * Checks for XML eXternal Entity (XXE) and XML Entity Expansion (XEE) attacks on XML documents
+ *
+ * @package PicoFeed\Parser
+ * @author Frederic Guillot
+ */
+class XmlParser
+{
+ /**
+ * Get a SimpleXmlElement instance or return false.
+ *
+ * @static
+ * @param string $input XML content
+ * @return mixed
+ */
+ public static function getSimpleXml($input)
+ {
+ return self::scan($input);
+ }
+
+ /**
+ * Get a DomDocument instance or return false.
+ *
+ * @static
+ * @param string $input XML content
+ * @return DOMDocument
+ */
+ public static function getDomDocument($input)
+ {
+ if (empty($input)) {
+ return false;
+ }
+
+ $dom = self::scan($input, new DOMDocument());
+
+ // The document is empty, there is probably some parsing errors
+ if ($dom && $dom->childNodes->length === 0) {
+ return false;
+ }
+
+ return $dom;
+ }
+
+ /**
+ * Small wrapper around ZendXml to turn their exceptions into PicoFeed exceptions
+ *
+ * @static
+ * @access private
+ * @param string $input
+ * @param DOMDocument $dom
+ * @throws XmlEntityException
+ * @return SimpleXMLElement|DomDocument|boolean
+ */
+ private static function scan($input, $dom = null)
+ {
+ try {
+ return Security::scan($input, $dom);
+ } catch(RuntimeException $e) {
+ throw new XmlEntityException($e->getMessage());
+ }
+ }
+
+ /**
+ * Load HTML document by using a DomDocument instance or return false on failure.
+ *
+ * @static
+ * @access public
+ * @param string $input XML content
+ * @return DOMDocument
+ */
+ public static function getHtmlDocument($input)
+ {
+ $dom = new DomDocument();
+
+ if (empty($input)) {
+ return $dom;
+ }
+
+ libxml_use_internal_errors(true);
+
+ if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
+ $dom->loadHTML($input, LIBXML_NONET);
+ } else {
+ $dom->loadHTML($input);
+ }
+
+ return $dom;
+ }
+
+ /**
+ * Convert a HTML document to XML.
+ *
+ * @static
+ * @access public
+ * @param string $html HTML document
+ * @return string
+ */
+ public static function htmlToXml($html)
+ {
+ $dom = self::getHtmlDocument('<?xml version="1.0" encoding="UTF-8">'.$html);
+ return $dom->saveXML($dom->getElementsByTagName('body')->item(0));
+ }
+
+ /**
+ * Get XML parser errors.
+ *
+ * @static
+ * @access public
+ * @return string
+ */
+ public static function getErrors()
+ {
+ $errors = array();
+
+ foreach (libxml_get_errors() as $error) {
+ $errors[] = sprintf('XML error: %s (Line: %d - Column: %d - Code: %d)',
+ $error->message,
+ $error->line,
+ $error->column,
+ $error->code
+ );
+ }
+
+ return implode(', ', $errors);
+ }
+
+ /**
+ * Get the encoding from a xml tag.
+ *
+ * @static
+ * @access public
+ * @param string $data Input data
+ * @return string
+ */
+ public static function getEncodingFromXmlTag($data)
+ {
+ $encoding = '';
+
+ if (strpos($data, '<?xml') !== false) {
+ $data = substr($data, 0, strrpos($data, '?>'));
+ $data = str_replace("'", '"', $data);
+
+ $p1 = strpos($data, 'encoding=');
+ $p2 = strpos($data, '"', $p1 + 10);
+
+ if ($p1 !== false && $p2 !== false) {
+ $encoding = substr($data, $p1 + 10, $p2 - $p1 - 10);
+ $encoding = strtolower($encoding);
+ }
+ }
+
+ return $encoding;
+ }
+
+ /**
+ * Get the charset from a meta tag.
+ *
+ * @static
+ * @access public
+ * @param string $data Input data
+ * @return string
+ */
+ public static function getEncodingFromMetaTag($data)
+ {
+ $encoding = '';
+
+ if (preg_match('/<meta.*?charset\s*=\s*["\']?\s*([^"\'\s\/>;]+)/i', $data, $match) === 1) {
+ $encoding = strtolower($match[1]);
+ }
+
+ return $encoding;
+ }
+
+ /**
+ * Rewrite XPath query to use namespace-uri and local-name derived from prefix.
+ *
+ * @static
+ * @access public
+ * @param string $query XPath query
+ * @param array $ns Prefix to namespace URI mapping
+ * @return string
+ */
+ public static function replaceXPathPrefixWithNamespaceURI($query, array $ns)
+ {
+ return preg_replace_callback('/([A-Z0-9]+):([A-Z0-9]+)/iu', function ($matches) use ($ns) {
+ // don't try to map the special prefix XML
+ if (strtolower($matches[1]) === 'xml') {
+ return $matches[0];
+ }
+
+ return '*[namespace-uri()="'.$ns[$matches[1]].'" and local-name()="'.$matches[2].'"]';
+ },
+ $query);
+ }
+
+ /**
+ * Get the result elements of a XPath query.
+ *
+ * @static
+ * @access public
+ * @param SimpleXMLElement $xml XML element
+ * @param string $query XPath query
+ * @param array $ns Prefix to namespace URI mapping
+ * @return SimpleXMLElement[]
+ */
+ public static function getXPathResult(SimpleXMLElement $xml, $query, array $ns = array())
+ {
+ if (!empty($ns)) {
+ $query = static::replaceXPathPrefixWithNamespaceURI($query, $ns);
+ }
+
+ return $xml->xpath($query);
+ }
+
+ /**
+ * Get the first Xpath result or SimpleXMLElement value
+ *
+ * @static
+ * @access public
+ * @param mixed $value
+ * @return string
+ */
+ public static function getValue($value)
+ {
+ $result = '';
+
+ if (is_array($value) && count($value) > 0) {
+ $result = (string) $value[0];
+ } elseif (is_a($value, 'SimpleXMLElement')) {
+ return $result = (string) $value;
+ }
+
+ return trim($result);
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php b/vendor/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php
new file mode 100644
index 00000000..2de9e4b7
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace PicoFeed;
+
+use Exception;
+
+/**
+ * PicoFeedException Exception.
+ *
+ * @author Frederic Guillot
+ */
+abstract class PicoFeedException extends Exception
+{
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentFilterProcessor.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentFilterProcessor.php
new file mode 100644
index 00000000..9b7ddcce
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentFilterProcessor.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace PicoFeed\Processor;
+
+use PicoFeed\Base;
+use PicoFeed\Filter\Filter;
+use PicoFeed\Logging\Logger;
+use PicoFeed\Parser\Feed;
+use PicoFeed\Parser\Item;
+
+/**
+ * Item Content Filter
+ *
+ * @package PicoFeed\Processor
+ * @author Frederic Guillot
+ */
+class ContentFilterProcessor extends Base implements ItemProcessorInterface
+{
+ /**
+ * Execute Item Processor
+ *
+ * @access public
+ * @param Feed $feed
+ * @param Item $item
+ * @return bool
+ */
+ public function execute(Feed $feed, Item $item)
+ {
+ if ($this->config->getContentFiltering(true)) {
+ $filter = Filter::html($item->getContent(), $feed->getSiteUrl());
+ $filter->setConfig($this->config);
+ $item->setContent($filter->execute());
+ } else {
+ Logger::setMessage(get_called_class().': Content filtering disabled');
+ }
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php
new file mode 100644
index 00000000..49adf9cc
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace PicoFeed\Processor;
+
+use PicoFeed\Base;
+use PicoFeed\Parser\Feed;
+use PicoFeed\Parser\Item;
+
+/**
+ * Item Content Generator
+ *
+ * @package PicoFeed\Processor
+ * @author Frederic Guillot
+ */
+class ContentGeneratorProcessor extends Base implements ItemProcessorInterface
+{
+ /**
+ * List of generators
+ *
+ * @access protected
+ * @var array
+ */
+ protected $generators = array(
+ 'youtube',
+ 'file',
+ );
+
+ /**
+ * Execute Item Processor
+ *
+ * @access public
+ * @param Feed $feed
+ * @param Item $item
+ * @return bool
+ */
+ public function execute(Feed $feed, Item $item)
+ {
+ foreach ($this->generators as $generator) {
+ $className = '\PicoFeed\Generator\\'.ucfirst($generator).'ContentGenerator';
+ $object = new $className($this->config);
+
+ if ($object->execute($item)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php
new file mode 100644
index 00000000..d2787677
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace PicoFeed\Processor;
+
+use PicoFeed\Base;
+use PicoFeed\Parser\Feed;
+use PicoFeed\Parser\Item;
+use PicoFeed\Config\Config;
+
+/**
+ * Item Post Processor
+ *
+ * @package PicoFeed\Processor
+ * @author Frederic Guillot
+ */
+class ItemPostProcessor extends Base
+{
+ /**
+ * List of processors
+ *
+ * @access private
+ * @var array
+ */
+ private $processors = array();
+
+ /**
+ * Execute all processors
+ *
+ * @access public
+ * @param Feed $feed
+ * @param Item $item
+ * @return bool
+ */
+ public function execute(Feed $feed, Item $item)
+ {
+ foreach ($this->processors as $processor) {
+ if ($processor->execute($feed, $item)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Register a new Item post-processor
+ *
+ * @access public
+ * @param ItemProcessorInterface $processor
+ * @return ItemPostProcessor
+ */
+ public function register(ItemProcessorInterface $processor)
+ {
+ $this->processors[get_class($processor)] = $processor;
+ return $this;
+ }
+
+ /**
+ * Remove Processor instance
+ *
+ * @access public
+ * @param string $class
+ * @return ItemPostProcessor
+ */
+ public function unregister($class)
+ {
+ if (isset($this->processors[$class])) {
+ unset($this->processors[$class]);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Checks wheather a specific processor is registered or not
+ *
+ * @access public
+ * @param string $class
+ * @return bool
+ */
+ public function hasProcessor($class)
+ {
+ return isset($this->processors[$class]);
+ }
+
+ /**
+ * Get Processor instance
+ *
+ * @access public
+ * @param string $class
+ * @return ItemProcessorInterface|null
+ */
+ public function getProcessor($class)
+ {
+ return isset($this->processors[$class]) ? $this->processors[$class] : null;
+ }
+
+ public function setConfig(Config $config)
+ {
+ foreach ($this->processors as $processor) {
+ $processor->setConfig($config);
+ }
+
+ return false;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php
new file mode 100644
index 00000000..5d532262
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace PicoFeed\Processor;
+
+use PicoFeed\Parser\Feed;
+use PicoFeed\Parser\Item;
+
+/**
+ * Item Processor Interface
+ *
+ * @package PicoFeed\Processor
+ * @author Frederic Guillot
+ */
+interface ItemProcessorInterface
+{
+ /**
+ * Execute Item Processor
+ *
+ * @access public
+ * @param Feed $feed
+ * @param Item $item
+ * @return bool
+ */
+ public function execute(Feed $feed, Item $item);
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ScraperProcessor.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ScraperProcessor.php
new file mode 100644
index 00000000..0c467af0
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ScraperProcessor.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace PicoFeed\Processor;
+
+use Closure;
+use PicoFeed\Base;
+use PicoFeed\Parser\Feed;
+use PicoFeed\Parser\Item;
+use PicoFeed\Scraper\Scraper;
+
+/**
+ * Scraper Processor
+ *
+ * @package PicoFeed\Processor
+ * @author Frederic Guillot
+ */
+class ScraperProcessor extends Base implements ItemProcessorInterface
+{
+ private $ignoredUrls = array();
+ private $scraper;
+
+ /**
+ * Callback function for each scraper execution
+ *
+ * @var Closure
+ */
+ private $executionCallback;
+
+ /**
+ * Add a new execution callback
+ *
+ * @access public
+ * @param Closure $executionCallback
+ * @return $this
+ */
+ public function setExecutionCallback(Closure $executionCallback)
+ {
+ $this->executionCallback = $executionCallback;
+ return $this;
+ }
+
+ /**
+ * Execute Item Processor
+ *
+ * @access public
+ * @param Feed $feed
+ * @param Item $item
+ * @return bool
+ */
+ public function execute(Feed $feed, Item $item)
+ {
+ if (!in_array($item->getUrl(), $this->ignoredUrls)) {
+ $scraper = $this->getScraper();
+ $scraper->setUrl($item->getUrl());
+ $scraper->execute();
+
+ if ($this->executionCallback && is_callable($this->executionCallback)) {
+ call_user_func($this->executionCallback, $feed, $item, $scraper);
+ }
+
+ if ($scraper->hasRelevantContent()) {
+ $item->setContent($scraper->getFilteredContent());
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Ignore list of URLs
+ *
+ * @access public
+ * @param array $urls
+ * @return $this
+ */
+ public function ignoreUrls(array $urls)
+ {
+ $this->ignoredUrls = $urls;
+ return $this;
+ }
+
+ /**
+ * Returns Scraper instance
+ *
+ * @access public
+ * @return Scraper
+ */
+ public function getScraper()
+ {
+ if ($this->scraper === null) {
+ $this->scraper = new Scraper($this->config);
+ }
+
+ return $this->scraper;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php
new file mode 100644
index 00000000..d4ca07db
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php
@@ -0,0 +1,186 @@
+<?php
+
+namespace PicoFeed\Reader;
+
+use DOMXPath;
+use PicoFeed\Base;
+use PicoFeed\Client\Client;
+use PicoFeed\Client\ClientException;
+use PicoFeed\Client\Url;
+use PicoFeed\Logging\Logger;
+use PicoFeed\Parser\XmlParser;
+
+/**
+ * Favicon class.
+ *
+ * https://en.wikipedia.org/wiki/Favicon
+ *
+ * @author Frederic Guillot
+ */
+class Favicon extends Base
+{
+ /**
+ * Valid types for favicon (supported by browsers).
+ *
+ * @var array
+ */
+ private $types = array(
+ 'image/png',
+ 'image/gif',
+ 'image/x-icon',
+ 'image/jpeg',
+ 'image/jpg',
+ 'image/svg+xml',
+ );
+
+ /**
+ * Icon binary content.
+ *
+ * @var string
+ */
+ private $content = '';
+
+ /**
+ * Icon content type.
+ *
+ * @var string
+ */
+ private $content_type = '';
+
+ /**
+ * Get the icon file content (available only after the download).
+ *
+ * @return string
+ */
+ public function getContent()
+ {
+ return $this->content;
+ }
+
+ /**
+ * Get the icon file type (available only after the download).
+ *
+ * @return string
+ */
+ public function getType()
+ {
+ foreach ($this->types as $type) {
+ if (strpos($this->content_type, $type) === 0) {
+ return $type;
+ }
+ }
+
+ return 'image/x-icon';
+ }
+
+ /**
+ * Get data URI (http://en.wikipedia.org/wiki/Data_URI_scheme).
+ *
+ * @return string
+ */
+ public function getDataUri()
+ {
+ if (empty($this->content)) {
+ return '';
+ }
+
+ return sprintf(
+ 'data:%s;base64,%s',
+ $this->getType(),
+ base64_encode($this->content)
+ );
+ }
+
+ /**
+ * Download and check if a resource exists.
+ *
+ * @param string $url URL
+ * @return \PicoFeed\Client\Client Client instance
+ */
+ public function download($url)
+ {
+ $client = Client::getInstance();
+ $client->setConfig($this->config);
+
+ Logger::setMessage(get_called_class().' Download => '.$url);
+
+ try {
+ $client->execute($url);
+ } catch (ClientException $e) {
+ Logger::setMessage(get_called_class().' Download Failed => '.$e->getMessage());
+ }
+
+ return $client;
+ }
+
+ /**
+ * Check if a remote file exists.
+ *
+ * @param string $url URL
+ * @return bool
+ */
+ public function exists($url)
+ {
+ return $this->download($url)->getContent() !== '';
+ }
+
+ /**
+ * Get the icon link for a website.
+ *
+ * @param string $website_link URL
+ * @param string $favicon_link optional URL
+ * @return string
+ */
+ public function find($website_link, $favicon_link = '')
+ {
+ $website = new Url($website_link);
+
+ if ($favicon_link !== '') {
+ $icons = array($favicon_link);
+ } else {
+ $icons = $this->extract($this->download($website->getBaseUrl('/'))->getContent());
+ $icons[] = $website->getBaseUrl('/favicon.ico');
+ }
+
+ foreach ($icons as $icon_link) {
+ $icon_link = Url::resolve($icon_link, $website);
+ $resource = $this->download($icon_link);
+ $this->content = $resource->getContent();
+ $this->content_type = $resource->getContentType();
+
+ if ($this->content !== '') {
+ return $icon_link;
+ } elseif ($favicon_link !== '') {
+ return $this->find($website_link);
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * Extract the icon links from the HTML.
+ *
+ * @param string $html HTML
+ * @return array
+ */
+ public function extract($html)
+ {
+ $icons = array();
+
+ if (empty($html)) {
+ return $icons;
+ }
+
+ $dom = XmlParser::getHtmlDocument($html);
+
+ $xpath = new DOMXpath($dom);
+ $elements = $xpath->query('//link[@rel="icon" or @rel="shortcut icon" or @rel="Shortcut Icon" or @rel="icon shortcut"]');
+
+ for ($i = 0; $i < $elements->length; ++$i) {
+ $icons[] = $elements->item($i)->getAttribute('href');
+ }
+
+ return $icons;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php
new file mode 100644
index 00000000..769ffe93
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace PicoFeed\Reader;
+
+use DOMXPath;
+use PicoFeed\Base;
+use PicoFeed\Client\Client;
+use PicoFeed\Client\Url;
+use PicoFeed\Logging\Logger;
+use PicoFeed\Parser\XmlParser;
+
+/**
+ * Reader class.
+ *
+ * @author Frederic Guillot
+ */
+class Reader extends Base
+{
+ /**
+ * Feed formats for detection.
+ *
+ * @var array
+ */
+ private $formats = array(
+ 'Atom' => '//feed',
+ 'Rss20' => '//rss[@version="2.0"]',
+ 'Rss92' => '//rss[@version="0.92"]',
+ 'Rss91' => '//rss[@version="0.91"]',
+ 'Rss10' => '//rdf',
+ );
+
+ /**
+ * Download a feed (no discovery).
+ *
+ * @param string $url Feed url
+ * @param string $last_modified Last modified HTTP header
+ * @param string $etag Etag HTTP header
+ * @param string $username HTTP basic auth username
+ * @param string $password HTTP basic auth password
+ *
+ * @return \PicoFeed\Client\Client
+ */
+ public function download($url, $last_modified = '', $etag = '', $username = '', $password = '')
+ {
+ $url = $this->prependScheme($url);
+
+ return Client::getInstance()
+ ->setConfig($this->config)
+ ->setLastModified($last_modified)
+ ->setEtag($etag)
+ ->setUsername($username)
+ ->setPassword($password)
+ ->execute($url);
+ }
+
+ /**
+ * Discover and download a feed.
+ *
+ * @param string $url Feed or website url
+ * @param string $last_modified Last modified HTTP header
+ * @param string $etag Etag HTTP header
+ * @param string $username HTTP basic auth username
+ * @param string $password HTTP basic auth password
+ * @return Client
+ * @throws SubscriptionNotFoundException
+ */
+ public function discover($url, $last_modified = '', $etag = '', $username = '', $password = '')
+ {
+ $client = $this->download($url, $last_modified, $etag, $username, $password);
+
+ // It's already a feed or the feed was not modified
+ if (!$client->isModified() || $this->detectFormat($client->getContent())) {
+ return $client;
+ }
+
+ // Try to find a subscription
+ $links = $this->find($client->getUrl(), $client->getContent());
+
+ if (empty($links)) {
+ throw new SubscriptionNotFoundException('Unable to find a subscription');
+ }
+
+ return $this->download($links[0], $last_modified, $etag, $username, $password);
+ }
+
+ /**
+ * Find feed urls inside a HTML document.
+ *
+ * @param string $url Website url
+ * @param string $html HTML content
+ *
+ * @return array List of feed links
+ */
+ public function find($url, $html)
+ {
+ Logger::setMessage(get_called_class().': Try to discover subscriptions');
+
+ $dom = XmlParser::getHtmlDocument($html);
+ $xpath = new DOMXPath($dom);
+ $links = array();
+
+ $queries = array(
+ '//link[@type="application/rss+xml"]',
+ '//link[@type="application/atom+xml"]',
+ );
+
+ foreach ($queries as $query) {
+ $nodes = $xpath->query($query);
+
+ foreach ($nodes as $node) {
+ $link = $node->getAttribute('href');
+
+ if (!empty($link)) {
+ $feedUrl = new Url($link);
+ $siteUrl = new Url($url);
+
+ $links[] = $feedUrl->getAbsoluteUrl($feedUrl->isRelativeUrl() ? $siteUrl->getBaseUrl() : '');
+ }
+ }
+ }
+
+ Logger::setMessage(get_called_class().': '.implode(', ', $links));
+
+ return $links;
+ }
+
+ /**
+ * Get a parser instance.
+ *
+ * @param string $url Site url
+ * @param string $content Feed content
+ * @param string $encoding HTTP encoding
+ *
+ * @return \PicoFeed\Parser\Parser
+ */
+ public function getParser($url, $content, $encoding)
+ {
+ $format = $this->detectFormat($content);
+
+ if (empty($format)) {
+ throw new UnsupportedFeedFormatException('Unable to detect feed format');
+ }
+
+ $className = '\PicoFeed\Parser\\'.$format;
+
+ $parser = new $className($content, $encoding, $url);
+ $parser->setHashAlgo($this->config->getParserHashAlgo());
+ $parser->setConfig($this->config);
+
+ return $parser;
+ }
+
+ /**
+ * Detect the feed format.
+ *
+ * @param string $content Feed content
+ *
+ * @return string
+ */
+ public function detectFormat($content)
+ {
+ $dom = XmlParser::getHtmlDocument($content);
+ $xpath = new DOMXPath($dom);
+
+ foreach ($this->formats as $parser_name => $query) {
+ $nodes = $xpath->query($query);
+
+ if ($nodes->length === 1) {
+ return $parser_name;
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * Add the prefix "http://" if the end-user just enter a domain name.
+ *
+ * @param string $url Url
+ * @retunr string
+ */
+ public function prependScheme($url)
+ {
+ if (!preg_match('%^https?://%', $url)) {
+ $url = 'http://'.$url;
+ }
+
+ return $url;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php
new file mode 100644
index 00000000..4f03dbe0
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace PicoFeed\Reader;
+
+use PicoFeed\PicoFeedException;
+
+/**
+ * ReaderException Exception.
+ *
+ * @author Frederic Guillot
+ */
+abstract class ReaderException extends PicoFeedException
+{
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/SubscriptionNotFoundException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/SubscriptionNotFoundException.php
new file mode 100644
index 00000000..dd77847e
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/SubscriptionNotFoundException.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace PicoFeed\Reader;
+
+/**
+ * SubscriptionNotFoundException Exception.
+ *
+ * @author Frederic Guillot
+ */
+class SubscriptionNotFoundException extends ReaderException
+{
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/UnsupportedFeedFormatException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/UnsupportedFeedFormatException.php
new file mode 100644
index 00000000..4aa8d87f
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/UnsupportedFeedFormatException.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace PicoFeed\Reader;
+
+/**
+ * UnsupportedFeedFormatException Exception.
+ *
+ * @author Frederic Guillot
+ */
+class UnsupportedFeedFormatException extends ReaderException
+{
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blog.lemonde.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blog.lemonde.fr.php
new file mode 100644
index 00000000..773616c4
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blog.lemonde.fr.php
@@ -0,0 +1,14 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://combat.blog.lemonde.fr/2013/08/31/teddy-riner-le-rookie-devenu-rambo/#xtor=RSS-3208',
+ 'body' => array(
+ '//div[@class="entry-content"]',
+ ),
+ 'strip' => array(
+ '//*[contains(@class, "fb-like") or contains(@class, "social")]'
+ ),
+ )
+ )
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blogs.nytimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blogs.nytimes.com.php
new file mode 100644
index 00000000..ee641b09
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blogs.nytimes.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'title' => '//header/h1',
+ 'test_url' => 'http://bits.blogs.nytimes.com/2012/01/16/wikipedia-plans-to-go-dark-on-wednesday-to-protest-sopa/',
+ 'body' => array(
+ '//div[@class="postContent"]',
+ ),
+ 'strip' => array(
+ '//*[@class="shareToolsBox"]',
+ ),
+ )
+ )
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.igen.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.igen.fr.php
new file mode 100644
index 00000000..f2028f4e
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.igen.fr.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.igen.fr/ailleurs/2014/05/nvidia-va-delaisser-les-smartphones-grand-public-86031',
+ 'body' => array(
+ '//div[contains(@class, "field-name-body")]'
+ ),
+ 'strip' => array(
+ ),
+ )
+ )
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.nytimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.nytimes.com.php
new file mode 100644
index 00000000..ed27bb5c
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.nytimes.com.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.nytimes.com/2011/05/15/world/middleeast/15prince.html',
+ 'body' => array(
+ '//div[@class="articleBody"]',
+ ),
+ )
+ )
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.over-blog.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.over-blog.com.php
new file mode 100644
index 00000000..cc5d83c7
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.over-blog.com.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://eliascarpe.over-blog.com/2015/12/re-upload-projets-d-avenir.html',
+ 'body' => array(
+ '//div[contains(concat(" ", normalize-space(@class), " "), " ob-section ")]',
+ ),
+ )
+ )
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.phoronix.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.phoronix.com.php
new file mode 100644
index 00000000..66713f71
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.phoronix.com.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.phoronix.com/scan.php?page=article&item=amazon_ec2_bare&num=1',
+ 'body' => array(
+ '//div[@class="content"]',
+ ),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.slate.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.slate.com.php
new file mode 100644
index 00000000..a795bca3
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.slate.com.php
@@ -0,0 +1,20 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.slate.com/articles/business/moneybox/2013/08/microsoft_ceo_steve_ballmer_retires_a_firsthand_account_of_the_company_s.html',
+ 'body' => array(
+ '//div[@class="sl-art-body"]',
+ ),
+ 'strip' => array(
+ '//*[contains(@class, "social") or contains(@class, "comments") or contains(@class, "sl-article-floatin-tools") or contains(@class, "sl-art-pag")]',
+ '//*[@id="mys_slate_logged_in"]',
+ '//*[@id="sl_article_tools_myslate_bottom"]',
+ '//*[@id="mys_myslate"]',
+ '//*[@class="sl-viral-container"]',
+ '//*[@class="sl-art-creds-cntr"]',
+ '//*[@class="sl-art-ad-midflex"]',
+ )
+ )
+ )
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.theguardian.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.theguardian.com.php
new file mode 100644
index 00000000..e0d6f3fd
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.theguardian.com.php
@@ -0,0 +1,14 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.theguardian.com/sustainable-business/2015/feb/02/2015-hyper-transparency-global-business',
+ 'body' => array(
+ '//div[contains(@class, "content__main-column--article")]',
+ ),
+ 'strip' => array(
+ '//div[contains(@class, "meta-container")]',
+ ),
+ )
+ )
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wikipedia.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wikipedia.org.php
new file mode 100644
index 00000000..7b8f76e5
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wikipedia.org.php
@@ -0,0 +1,29 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://en.wikipedia.org/wiki/Grace_Hopper',
+ 'body' => array(
+ '//div[@id="bodyContent"]',
+ ),
+ 'strip' => array(
+ "//div[@id='toc']",
+ "//div[@id='catlinks']",
+ "//div[@id='jump-to-nav']",
+ "//div[@class='thumbcaption']//div[@class='magnify']",
+ "//table[@class='navbox']",
+ "//table[contains(@class, 'infobox')]",
+ "//div[@class='dablink']",
+ "//div[@id='contentSub']",
+ "//div[@id='siteSub']",
+ "//table[@id='persondata']",
+ "//table[contains(@class, 'metadata')]",
+ "//*[contains(@class, 'noprint')]",
+ "//*[contains(@class, 'printfooter')]",
+ "//*[contains(@class, 'editsection')]",
+ "//*[contains(@class, 'error')]",
+ "//span[@title='pronunciation:']",
+ ),
+ )
+ )
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wired.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wired.com.php
new file mode 100644
index 00000000..952b09ac
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wired.com.php
@@ -0,0 +1,44 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.wired.com/gamelife/2013/09/ouya-free-the-games/',
+ 'body' => array(
+ '//div[@data-js="gallerySlides"]',
+ '//div[starts-with(@class,"post")]',
+ ),
+ 'strip' => array(
+ '//h1',
+ '//nav',
+ '//button',
+ '//figure[starts-with(@class,"rad-slide")]',
+ '//figure[starts-with(@class,"end-slate")]',
+ '//div[contains(@class,"mobile-")]',
+ '//div[starts-with(@class,"mob-gallery-launcher")]',
+ '//div[contains(@id,"mobile-")]',
+ '//span[contains(@class,"slide-count")]',
+ '//div[contains(@class,"show-ipad")]',
+ '//img[contains(@id,"-hero-bg")]',
+ '//div[@data-js="overlayWrap"]',
+ '//ul[contains(@class,"metadata")]',
+ '//div[@class="opening center"]',
+ '//p[contains(@class="byline-mob"]',
+ '//div[@id="o-gallery"]',
+ '//div[starts-with(@class,"sm-col")]',
+ '//div[contains(@class,"pad-b-huge")]',
+ '//a[contains(@class,"visually-hidden")]',
+ '//*[@class="social"]',
+ '//i',
+ '//div[@data-js="mobGalleryAd"]',
+ '//div[contains(@class,"footer")]',
+ '//div[contains(@data-js,"fader")]',
+ '//div[@id="sharing"]',
+ '//div[contains(@id,"related")]',
+ '//div[@id="most-pop"]',
+ '//ul[@id="article-tags"]',
+ '//style',
+ '//section[contains(@class,"footer")]'
+ ),
+ )
+ )
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wsj.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wsj.com.php
new file mode 100644
index 00000000..f6e6cc12
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wsj.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://online.wsj.com/article/SB10001424127887324108204579023143974408428.html',
+ 'body' => array(
+ '//div[@class="articlePage"]',
+ ),
+ 'strip' => array(
+ '//*[@id="articleThumbnail_2"]',
+ '//*[@class="socialByline"]',
+ )
+ )
+ )
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/01net.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/01net.com.php
new file mode 100644
index 00000000..6d144f05
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/01net.com.php
@@ -0,0 +1,18 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.01net.com/editorial/624550/twitter-rachete-madbits-un-specialiste-francais-de-lanalyse-dimages/',
+ 'body' => array(
+ '//div[@class="article_ventre_box"]',
+ ),
+ 'strip' => array(
+ '//link',
+ '//*[contains(@class, "article_navigation")]',
+ '//h1',
+ '//*[contains(@class, "article_toolbarMain")]',
+ '//*[contains(@class, "article_imagehaute_box")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/abstrusegoose.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/abstrusegoose.com.php
new file mode 100644
index 00000000..752d0413
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/abstrusegoose.com.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%alt="(.+)" title="(.+)" */>%' => '/><br/>$1<br/>$2',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/adventuregamers.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/adventuregamers.com.php
new file mode 100644
index 00000000..98d384e6
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/adventuregamers.com.php
@@ -0,0 +1,23 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%^/news.*%' => array(
+ 'test_url' => 'http://www.adventuregamers.com/news/view/31079',
+ 'body' => array(
+ '//div[@class="bodytext"]',
+ )
+ ),
+ '%^/videos.*%' => array(
+ 'test_url' => 'http://www.adventuregamers.com/videos/view/31056',
+ 'body' => array(
+ '//iframe',
+ )
+ ),
+ '%^/articles.*%' => array(
+ 'test_url' => 'http://www.adventuregamers.com/articles/view/31049',
+ 'body' => array(
+ '//div[@class="cleft"]',
+ )
+ )
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/alainonline.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/alainonline.net.php
new file mode 100644
index 00000000..f440b234
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/alainonline.net.php
@@ -0,0 +1,14 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.alainonline.net/news_details.php?lang=arabic&sid=18907',
+ 'body' => array(
+ '//div[@class="news_details"]',
+ ),
+ 'strip' => array(
+ '//div[@class="news_details"]/div/div[last()]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/aljazeera.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/aljazeera.com.php
new file mode 100644
index 00000000..c02eb219
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/aljazeera.com.php
@@ -0,0 +1,25 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.aljazeera.com/news/2015/09/xi-jinping-seattle-china-150922230118373.html',
+ 'body' => array(
+ '//article[@id="main-story"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//header',
+ '//ul',
+ '//section[contains(@class,"profile")]',
+ '//a[@target="_self"]',
+ '//div[contains(@id,"_2")]',
+ '//div[contains(@id,"_3")]',
+ '//img[@class="viewMode"]',
+ '//table[contains(@class,"in-article-item")]',
+ '//div[@data-embed-type="Brightcove"]',
+ '//div[@class="QuoteContainer"]',
+ '//div[@class="BottomByLine"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allafrica.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allafrica.com.php
new file mode 100644
index 00000000..e8a506d4
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allafrica.com.php
@@ -0,0 +1,20 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.aljazeera.com/news/2015/09/xi-jinping-seattle-china-150922230118373.html',
+ 'body' => array(
+ '//div[@class="story-body"]',
+ ),
+ 'strip' => array(
+ '//p[@class="kindofstory"]',
+ '//cite[@class="byline"]',
+ '//div[@class="useful-top"]',
+ '//div[contains(@class,"related-topics")]',
+ '//links',
+ '//sharebar',
+ '//related-topics',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allgemeine-zeitung.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allgemeine-zeitung.de.php
new file mode 100644
index 00000000..8ede99b1
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allgemeine-zeitung.de.php
@@ -0,0 +1,23 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.allgemeine-zeitung.de/lokales/polizei/mainz-gonsenheim-unbekannte-rauben-esso-tankstelle-in-kurt-schumacher-strasse-aus_14913147.htm',
+ 'body' => array(
+ '//div[contains(@class, "article")][1]',
+ ),
+ 'strip' => array(
+ '//read/h1',
+ '//*[@id="t-map"]',
+ '//*[contains(@class, "modules")]',
+ '//*[contains(@class, "adsense")]',
+ '//*[contains(@class, "linkbox")]',
+ '//*[contains(@class, "info")]',
+ '//*[@class="skip"]',
+ '//*[@class="funcs"]',
+ '//span[@class="nd address"]',
+ '//a[contains(@href, "abo-und-services")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/amazingsuperpowers.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/amazingsuperpowers.com.php
new file mode 100644
index 00000000..3214c62a
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/amazingsuperpowers.com.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%title="(.+)" */>%' => '/><br/>$1',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/anythingcomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/anythingcomic.com.php
new file mode 100644
index 00000000..51247f76
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/anythingcomic.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'body' => array(
+ '//img[@id="comic_image"]',
+ '//div[@class="comment-wrapper"][position()=1]',
+ ),
+ 'strip' => array(),
+ 'test_url' => 'http://www.anythingcomic.com/comics/2108929/stress-free/',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ap.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ap.org.php
new file mode 100644
index 00000000..5bb2bb6c
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ap.org.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://hosted.ap.org/dynamic/stories/A/AS_CHINA_GAO_ZHISHENG?SITE=AP&SECTION=HOME&TEMPLATE=DEFAULT',
+ 'body' => array(
+ '//img[@class="ap-smallphoto-img"]',
+ '//span[@class="entry-content"]',
+ ),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/areadvd.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/areadvd.de.php
new file mode 100644
index 00000000..fc569220
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/areadvd.de.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.areadvd.de/news/daily-deals-angebote-bei-lautsprecher-teufel-3/',
+ 'body' => array('//div[contains(@class,"entry")]'),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/arstechnica.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/arstechnica.com.php
new file mode 100644
index 00000000..55e01ce3
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/arstechnica.com.php
@@ -0,0 +1,25 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://arstechnica.com/tech-policy/2015/09/judge-warners-2m-happy-birthday-copyright-is-bogus/',
+ 'body' => array(
+ '//article',
+ ),
+ 'strip' => array(
+ '//h4[@class="post-upperdek"]',
+ '//h1',
+ '//ul[@class="lSPager lSGallery"]',
+ '//div[@class="lSAction"]',
+ '//section[@class="post-meta"]',
+ '//figcaption',
+ '//aside',
+ '//div[@class="gallery-image-credit"]',
+ '//section[@class="article-author"]',
+ '//*[contains(@id,"social-")]',
+ '//div[contains(@id,"footer")]',
+ ),
+ ),
+ ),
+);
+
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/awkwardzombie.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/awkwardzombie.com.php
new file mode 100644
index 00000000..5ab70514
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/awkwardzombie.com.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%/index.php.*comic=.*%' => array(
+ 'test_url' => 'http://www.awkwardzombie.com/index.php?comic=041315',
+ 'body' => array('//*[@id="comic"]/img'),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/backchannel.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/backchannel.com.php
new file mode 100644
index 00000000..bc5932a2
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/backchannel.com.php
@@ -0,0 +1,18 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://medium.com/lessons-learned/917b8b63ae3e',
+ 'body' => array(
+ '//div[contains(@class,"section-inner")]',
+ ),
+ 'strip' => array(
+ '//div[contains(@class,"metabar")]',
+ '//img[contains(@class,"thumbnail")]',
+ '//h1',
+ '//blockquote',
+ '//p[contains(@class,"graf-after--h4")]'
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bangkokpost.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bangkokpost.com.php
new file mode 100644
index 00000000..165515bb
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bangkokpost.com.php
@@ -0,0 +1,19 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.bangkokpost.com/news/politics/704204/new-us-ambassador-arrives-in-bangkok',
+ 'body' => array(
+ '//article/div[@class="articleContents"]',
+ ),
+ 'strip' => array(
+ '//h2',
+ '//h4',
+ '//div[@class="text-size"]',
+ '//div[@class="relate-story"]',
+ '//div[@class="text-ads"]',
+ '//ul',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bgr.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bgr.com.php
new file mode 100644
index 00000000..7507a2f1
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bgr.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://bgr.com/2015/09/27/iphone-6s-waterproof-testing/',
+ 'body' => array(
+ '//img[contains(@class,"img")]',
+ '//div[@class="text-column"]',
+ ),
+ 'strip' => array(
+ '//strong',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigfootjustice.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigfootjustice.com.php
new file mode 100644
index 00000000..d06ed124
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigfootjustice.com.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%-150x150%' => '',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigpicture.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigpicture.ru.php
new file mode 100644
index 00000000..55c40894
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigpicture.ru.php
@@ -0,0 +1,31 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://bigpicture.ru/?p=556658',
+ 'body' => array(
+ '//div[@class="article container"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//form',
+ '//style',
+ '//h1',
+ '//*[@class="wp-smiley"]',
+ '//div[@class="ipmd"]',
+ '//div[@class="tags"]',
+ '//div[@class="social-button"]',
+ '//div[@class="bottom-share"]',
+ '//div[@class="raccoonbox"]',
+ '//div[@class="yndadvert"]',
+ '//div[@class="we-recommend"]',
+ '//div[@class="relap-bigpicture_ru-wrapper"]',
+ '//div[@id="mmail"]',
+ '//div[@id="mobile-ads-cut"]',
+ '//div[@id="liquidstorm-alt-html"]',
+ '//div[contains(@class, "post-tags")]',
+ '//*[contains(text(),"Смотрите также")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bizjournals.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bizjournals.com.php
new file mode 100644
index 00000000..d1cc3da9
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bizjournals.com.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.bizjournals.com/milwaukee/news/2015/09/30/bucks-will-hike-prices-on-best-seats-at-new-arena.html',
+ 'body' => array(
+ '//figure/div/a/img',
+ '//p[@class="content__segment"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/biztimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/biztimes.com.php
new file mode 100644
index 00000000..d21aa98c
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/biztimes.com.php
@@ -0,0 +1,22 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://www.biztimes.com/2017/02/10/settlement-would-revive-fowler-lake-condo-project-in-oconomowoc/',
+ 'body' => array(
+ '//h2/span[@class="subhead"]',
+ '//div[contains(@class,"article-content")]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//div[contains(@class,"mobile-article-content")]',
+ '//div[contains(@class,"sharedaddy")]',
+ '//div[contains(@class,"author-details")]',
+ '//div[@class="row ad"]',
+ '//div[contains(@class,"relatedposts")]',
+ '//div[@class="col-lg-12"]',
+ '//div[contains(@class,"widget")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bleepingcomputer.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bleepingcomputer.com.php
new file mode 100644
index 00000000..7b740600
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bleepingcomputer.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://www.bleepingcomputer.com/news/google/chromes-sandbox-feature-infringes-on-three-patents-so-google-must-now-pay-20m/',
+ 'body' => array(
+ '//div[@class="article_section"]',
+ ),
+ 'strip' => array(
+ '//*[@itemprop="headline"]',
+ '//div[@class="cz-news-story-title-section"]'
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.fefe.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.fefe.de.php
new file mode 100644
index 00000000..39c88ae4
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.fefe.de.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://blog.fefe.de/?ts=ad706a73',
+ 'body' => array(
+ '/html/body/ul',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.mapillary.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.mapillary.com.php
new file mode 100644
index 00000000..ce016510
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.mapillary.com.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://blog.mapillary.com/update/2015/08/26/traffic-sign-updates.html',
+ 'body' => array(
+ '//div[contains(@class, "blog-post__content")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/brewers.mlb.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/brewers.mlb.com.php
new file mode 100644
index 00000000..be406faf
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/brewers.mlb.com.php
@@ -0,0 +1,22 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://m.brewers.mlb.com/news/article/161364798',
+ 'body' => array(
+ '//article[contains(@class,"article")]',
+ ),
+ 'strip' => array(
+ '//div[contains(@class,"ad-slot")]',
+ '//h1',
+ '//span[@class="timestamp"]',
+ '//div[contains(@class,"contributor-bottom")]',
+ '//div[contains(@class,"video")]',
+ '//ul[contains(@class,"social")]',
+ '//p[@class="tagline"]',
+ '//div[contains(@class,"social")]',
+ '//div[@class="button-wrap"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buenosairesherald.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buenosairesherald.com.php
new file mode 100644
index 00000000..4e73e79f
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buenosairesherald.com.php
@@ -0,0 +1,17 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.buenosairesherald.com/article/199344/manzur-named-next-governor-of-tucum%C3%A1n',
+ 'body' => array(
+ '//div[@style="float:none"]',
+ ),
+ 'strip' => array(
+ '//div[contains(@class, "bz_alias_short_desc_container"]',
+ '//td[@id="bz_show_bug_column_1"]',
+ '//table[@id="attachment_table"]',
+ '//table[@class="bz_comment_table"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bunicomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bunicomic.com.php
new file mode 100644
index 00000000..ad83e436
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bunicomic.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.bunicomic.com/comic/buni-623/',
+ 'body' => array(
+ '//div[@class="comic-table"]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buttersafe.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buttersafe.com.php
new file mode 100644
index 00000000..1f313cd0
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buttersafe.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://buttersafe.com/2015/04/21/the-incredible-flexible-man/',
+ 'body' => array(
+ '//div[@id="comic"]',
+ '//div[@class="post-comic"]',
+ ),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cad-comic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cad-comic.com.php
new file mode 100644
index 00000000..a631c97f
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cad-comic.com.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%/cad/.+%' => array(
+ 'test_url' => 'http://www.cad-comic.com/cad/20150417',
+ 'body' => array(
+ '//*[@id="content"]/img',
+ ),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chaoslife.findchaos.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chaoslife.findchaos.com.php
new file mode 100644
index 00000000..ea6191e8
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chaoslife.findchaos.com.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://chaoslife.findchaos.com/pets-in-the-wild',
+ 'body' => array('//div[@id="comic"]'),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chinafile.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chinafile.com.php
new file mode 100644
index 00000000..450117b3
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chinafile.com.php
@@ -0,0 +1,18 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.chinafile.com/books/shanghai-faithful?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+chinafile%2FAll+%28ChinaFile%29',
+ 'body' => array(
+ '//div[contains(@class,"pane-featured-photo-panel-pane-1")]',
+ '//div[contains(@class,"video-above-fold")]',
+ '//div[@class="sc-media"]',
+ '//div[contains(@class,"field-name-body")]',
+ ),
+ 'strip' => array(
+ '//div[contains(@class,"cboxes")]',
+ '//div[contains(@class,"l-middle")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cliquerefresh.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cliquerefresh.com.php
new file mode 100644
index 00000000..9dcc7e54
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cliquerefresh.com.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%/comic.*%' => array(
+ 'test_url' => 'http://cliquerefresh.com/comic/078-stating-the-obvious/',
+ 'body' => array('//div[@class="comicImg"]/img | //div[@class="comicImg"]/a/img'),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cnet.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cnet.com.php
new file mode 100644
index 00000000..60767a53
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cnet.com.php
@@ -0,0 +1,37 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%^/products.*%' => array(
+ 'test_url' => 'http://www.cnet.com/products/fibaro-flood-sensor/#ftag=CADf328eec',
+ 'body' => array(
+ '//li[contains(@class,"slide first"] || //figure[contains(@class,(promoFigure))]',
+ '//div[@class="quickInfo"]',
+ '//div[@class="col-6 ratings"]',
+ '//div[@id="editorReview"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//a[@class="clickToEnlarge"]',
+ '//div[@section="topSharebar"]',
+ '//div[contains(@class,"related")]',
+ '//div[contains(@class,"ad-")]',
+ '//div[@section="shortcodeGallery"]',
+ ),
+ ),
+ '%.*%' => array(
+ 'test_url' => 'http://cnet.com.feedsportal.com/c/34938/f/645093/s/4a340866/sc/28/l/0L0Scnet0N0Cnews0Cman0Eclaims0Eonline0Epsychic0Emade0Ehim0Ebuy0E10Emillion0Epowerball0Ewinning0Eticket0C0Tftag0FCAD590Aa51e/story01.htm',
+ 'body' => array(
+ '//p[@itemprop="description"]',
+ '//div[@itemprop="articleBody"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//a[@class="clickToEnlarge"]',
+ '//div[@section="topSharebar"]',
+ '//div[contains(@class,"related")]',
+ '//div[contains(@class,"ad-")]',
+ '//div[@section="shortcodeGallery"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/consomac.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/consomac.fr.php
new file mode 100644
index 00000000..9209f9cb
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/consomac.fr.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://consomac.fr/news-2430-l-iphone-6-toujours-un-secret-bien-garde.html',
+ 'body' => array(
+ '//div[contains(@id, "newscontent")]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cowbirdsinlove.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cowbirdsinlove.com.php
new file mode 100644
index 00000000..3214c62a
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cowbirdsinlove.com.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%title="(.+)" */>%' => '/><br/>$1',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/crash.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/crash.net.php
new file mode 100644
index 00000000..9d9b35a6
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/crash.net.php
@@ -0,0 +1,28 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.crash.net/motogp/interview/247550/1/exclusive-andrea-dovizioso-interview.html',
+ 'body' => array(
+ '//div[@id="content"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//style',
+ '//*[@title="Social Networking"]',
+ '//*[@class="crash-ad2"]',
+ '//*[@class="clearfix"]',
+ '//*[@class="crash-ad2"]',
+ '//*[contains(@id, "divCB"]',
+ '//*[@class="pnlComment"]',
+ '//*[@class="comments-tabs"]',
+ '//*[contains(@class, "ad-twocol"]',
+ '//*[@class="stories-list"]',
+ '//*[contains(@class, "btn")]',
+ '//*[@class="content"]',
+ '//h3',
+ ),
+ ),
+ ),
+);
+
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/csmonitor.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/csmonitor.com.php
new file mode 100644
index 00000000..481e4b09
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/csmonitor.com.php
@@ -0,0 +1,19 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.csmonitor.com/USA/Politics/2015/0925/John-Boehner-steps-down-Self-sacrificing-but-will-it-lead-to-better-government',
+ 'body' => array(
+ '//h2[@id="summary"]',
+ '//div[@class="flex-video youtube"]',
+ '//div[contains(@class,"eza-body")]',
+ ),
+ 'strip' => array(
+ '//span[@id="breadcrumb"]',
+ '//div[@id="byline-wrapper"]',
+ '//div[@class="injection"]',
+ '//*[contains(@class,"promo_link")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyjs.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyjs.com.php
new file mode 100644
index 00000000..20eb1d75
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyjs.com.php
@@ -0,0 +1,19 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://dailyjs.com/2014/08/07/p5js/',
+ 'body' => array(
+ '//div[@id="post"]',
+ ),
+ 'strip' => array(
+ '//h2[@class="post"]',
+ '//div[@class="meta"]',
+ '//*[contains(@class, "addthis_toolbox")]',
+ '//*[contains(@class, "addthis_default_style")]',
+ '//*[@class="navigation small"]',
+ '//*[@id="related"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyreporter.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyreporter.com.php
new file mode 100644
index 00000000..db3fc0e1
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyreporter.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://dailyreporter.com/2016/01/09/us-supreme-court-case-could-weaken-government-workers-unions/',
+ 'body' => array(
+ '//div[contains(@class, "entry-content")]',
+ ),
+ 'strip' => array(
+ '//div[@class="dmcss_login_form"]',
+ '//*[contains(@class, "sharedaddy")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailytech.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailytech.com.php
new file mode 100644
index 00000000..5d1df4a9
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailytech.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.dailytech.com/Apples+First+Fixes+to+iOS+9+Land+w+iOS++901+Release/article37495.htm',
+ 'body' => array(
+ '//div[@class="NewsBodyImage"]',
+ '//span[@id="lblSummary"]',
+ '//span[@id="lblBody"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/degroupnews.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/degroupnews.com.php
new file mode 100644
index 00000000..91f5c56e
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/degroupnews.com.php
@@ -0,0 +1,14 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.degroupnews.com/medias/vodsvod/amazon-concurrence-la-chromecast-de-google-avec-fire-tv-stick',
+ 'body' => array(
+ '//div[@class="contenu"]',
+ ),
+ 'strip' => array(
+ '//div[contains(@class, "a2a")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/derstandard.at.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/derstandard.at.php
new file mode 100644
index 00000000..7e95a51f
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/derstandard.at.php
@@ -0,0 +1,14 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://derstandard.at/2000010267354/The-Witcher-3-Hohe-Hardware-Anforderungen-fuer-PC-Spieler?ref=rss',
+ 'body' => array(
+ '//div[@class="copytext"]',
+ '//ul[@id="media-list"]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dilbert.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dilbert.com.php
new file mode 100644
index 00000000..b8e9b3d6
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dilbert.com.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'body' => array(
+ '//img[@class="img-responsive img-comic"]',
+ ),
+ 'test_url' => 'http://dilbert.com/strip/2016-01-28',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/discovermagazine.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/discovermagazine.com.php
new file mode 100644
index 00000000..ae0dfe71
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/discovermagazine.com.php
@@ -0,0 +1,26 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://blogs.discovermagazine.com/neuroskeptic/2017/01/25/publishers-jeffrey-beall/',
+ 'body' => array(
+ '//div[@class="contentWell"]',
+ ),
+ 'strip' => array(
+ '//h1',
+ '//div[@class="breadcrumbs"]',
+ '//div[@class="mobile"]',
+ '//div[@class="fromIssue"]',
+ '//div[contains(@class,"belowDeck")]',
+ '//div[@class="meta"]',
+ '//div[@class="shareIcons"]',
+ '//div[@class="categories"]',
+ '//div[@class="navigation"]',
+ '//div[@class="heading"]',
+ '//div[contains(@id,"-ad")]',
+ '//div[@class="relatedArticles"]',
+ '//div[@id="disqus_thread"]'
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/distrowatch.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/distrowatch.com.php
new file mode 100644
index 00000000..aefc8f81
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/distrowatch.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://distrowatch.com/?newsid=08355',
+ 'body' => array(
+ '//td[@class="NewsText"][1]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dozodomo.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dozodomo.com.php
new file mode 100644
index 00000000..e1166957
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dozodomo.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://dozodomo.com/bento/2014/03/04/lart-des-maki-de-takayo-kiyota/',
+ 'body' => array(
+ '//div[@class="joke"]',
+ '//div[@class="story-cover"]',
+ '//div[@class="story-content"]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/drawingboardcomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/drawingboardcomic.com.php
new file mode 100644
index 00000000..cd30f2e0
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/drawingboardcomic.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'body' => array('//img[@id="comicimage"]'),
+ 'strip' => array(),
+ 'test_url' => 'http://drawingboardcomic.com/index.php?comic=208',
+ ),
+ ),
+ 'filter' => array(
+ '%.*%' => array(
+ '%title="(.+)" */>%' => '/><br/>$1',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/e-w-e.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/e-w-e.ru.php
new file mode 100644
index 00000000..8139cc9a
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/e-w-e.ru.php
@@ -0,0 +1,22 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://e-w-e.ru/16-prekrasnyx-izobretenij-zhenshhin/',
+ 'body' => array(
+ '//div[contains(@class, "post_text")]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//form',
+ '//style',
+ '//*[@class="views_post"]',
+ '//*[@class="adman_mobile"]',
+ '//*[@class="adman_desctop"]',
+ '//*[contains(@rel, "nofollow")]',
+ '//*[contains(@class, "wp-smiley")]',
+ '//*[contains(text(),"Источник:")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/economist.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/economist.com.php
new file mode 100644
index 00000000..522032fd
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/economist.com.php
@@ -0,0 +1,25 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.economist.com/blogs/buttonwood/2017/02/mixed-signals?fsrc=rss',
+ 'body' => array(
+ '//article',
+ ),
+ 'strip' => array(
+ '//span[@class="blog-post__siblings-list-header "]',
+ '//h1',
+ '//aside',
+ '//div[@class="blog-post__asideable-wrapper"]',
+ '//div[@class="share_inline_header"]',
+ '//div[@id="column-right"]',
+ '//div[contains(@class,"blog-post__siblings-list-aside")]',
+ '//div[@class="video-player__wrapper"]',
+ '//div[@class="blog-post__bottom-panel"]',
+ '//div[contains(@class,"latest-updates-panel__container")]',
+ '//div[contains(@class,"blog-post__asideable-content")]',
+ '//div[@aria-label="Advertisement"]'
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/encyclopedie.naheulbeuk.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/encyclopedie.naheulbeuk.com.php
new file mode 100644
index 00000000..19bcbdef
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/encyclopedie.naheulbeuk.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://encyclopedie.naheulbeuk.com/article.php3?id_article=352',
+ 'body' => array(
+ '//td//h1[@class="titre-texte"]',
+ '//td//div[@class="surtitre"]',
+ '//td//div[@class="texte"]',
+ ),
+ )
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/endlessorigami.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/endlessorigami.com.php
new file mode 100644
index 00000000..d06ed124
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/endlessorigami.com.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%-150x150%' => '',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/engadget.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/engadget.com.php
new file mode 100644
index 00000000..cf9e4485
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/engadget.com.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.engadget.com/2015/04/20/dark-matter-discovery/?ncid=rss_truncated',
+ 'body' => array('//div[@id="page_body"]/div[@class="container@m-"]'),
+ 'strip' => array('//aside[@role="banner"]'),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/escapistmagazine.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/escapistmagazine.com.php
new file mode 100644
index 00000000..e86b59cb
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/escapistmagazine.com.php
@@ -0,0 +1,45 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%/articles/view/comicsandcosplay/comics/critical-miss.*%' => array(
+ 'body' => array('//*[@class="body"]/span/img | //div[@class="folder_nav_links"]/following::p'),
+ 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/critical-miss/13776-Critical-Miss-on-Framerates?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=articles',
+ 'strip' => array(),
+ ),
+ '%/articles/view/comicsandcosplay/comics/namegame.*%' => array(
+ 'body' => array('//*[@class="body"]/span/p/img[@height != "120"]'),
+ 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/namegame/9759-Leaving-the-Nest?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=articles',
+ 'strip' => array(),
+ ),
+ '%/articles/view/comicsandcosplay/comics/stolen-pixels.*%' => array(
+ 'body' => array('//*[@class="body"]/span/p[2]/img'),
+ 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/stolen-pixels/8866-Stolen-Pixels-258-Where-the-Boys-Are?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=articles',
+ 'strip' => array(),
+ ),
+ '%/articles/view/comicsandcosplay/comics/bumhugparade.*%' => array(
+ 'body' => array('//*[@class="body"]/span/p[2]/img'),
+ 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/bumhugparade/8262-Bumhug-Parade-13?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=articles',
+ 'strip' => array(),
+ ),
+ '%/articles/view/comicsandcosplay.*/comics/escapistradiotheater%' => array(
+ 'body' => array('//*[@class="body"]/span/p[2]/img'),
+ 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/escapistradiotheater/8265-The-Escapist-Radio-Theater-13?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=articles',
+ 'strip' => array(),
+ ),
+ '%/articles/view/comicsandcosplay/comics/paused.*%' => array(
+ 'body' => array('//*[@class="body"]/span/p[2]/img | //*[@class="body"]/span/div/img'),
+ 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/paused/8263-Paused-16?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=articles',
+ 'strip' => array(),
+ ),
+ '%/articles/view/comicsandcosplay/comics/fraughtwithperil.*%' => array(
+ 'body' => array('//*[@class="body"]'),
+ 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/fraughtwithperil/12166-The-Escapist-Presents-Escapist-Comics-Critical-Miss-B-lyeh-Fhlop?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=articles',
+ 'strip' => array(),
+ ),
+ '%/articles/view/video-games/columns/.*%' => array(
+ 'body' => array('//*[@id="article_content"]'),
+ 'test_url' => 'http://www.escapistmagazine.com/articles/view/video-games/columns/experienced-points/13971-What-50-Shades-and-Batman-Have-in-Common.2',
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/espn.go.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/espn.go.com.php
new file mode 100644
index 00000000..76a20f74
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/espn.go.com.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://espn.go.com/nfl/story/_/id/13388208/jason-whitlock-chip-kelly-controversy',
+ 'body' => array(
+ '//p',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/exocomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/exocomics.com.php
new file mode 100644
index 00000000..5adc59f8
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/exocomics.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'body' => array('//a[@class="comic"]/img'),
+ 'strip' => array(),
+ 'test_url' => 'http://www.exocomics.com/379',
+ ),
+ ),
+ 'filter' => array(
+ '%.*%' => array(
+ '%title="(.+)" */>%' => '/><br/>$1',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/explosm.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/explosm.net.php
new file mode 100644
index 00000000..3fdf02c0
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/explosm.net.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://explosm.net/comics/3803/',
+ 'body' => array(
+ '//div[@id="comic-container"]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/extrafabulouscomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/extrafabulouscomics.com.php
new file mode 100644
index 00000000..12697ccb
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/extrafabulouscomics.com.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%-150x150%' => '',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/factroom.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/factroom.ru.php
new file mode 100644
index 00000000..a572061d
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/factroom.ru.php
@@ -0,0 +1,27 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.factroom.ru/life/20-facts-about-oil',
+ 'body' => array(
+ '//div[@class="post"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//form',
+ '//style',
+ '//h1',
+ '//div[@id="yandex_ad2"]',
+ '//*[@class="jp-relatedposts"]',
+ '//div[contains(@class, "likely-desktop")]',
+ '//div[contains(@class, "likely-mobile")]',
+ '//p[last()]',
+ '//div[contains(@class, "facebook")]',
+ '//div[contains(@class, "desktop-underpost-direct")]',
+ '//div[contains(@class, "source-box")]',
+ '//div[contains(@class, "under-likely-desktop")]',
+ '//div[contains(@class, "mobile-down-post")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcodesign.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcodesign.com.php
new file mode 100644
index 00000000..74e70a86
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcodesign.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.fastcodesign.com/3026548/exposure/peek-inside-the-worlds-forbidden-subway-tunnels',
+ 'body' => array(
+ '//article[contains(@class, "body prose")]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcoexist.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcoexist.com.php
new file mode 100644
index 00000000..6916f280
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcoexist.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.fastcoexist.com/3026114/take-a-seat-on-this-gates-funded-future-toilet-that-will-change-how-we-think-about-poop',
+ 'body' => array(
+ '//article[contains(@class, "body prose")]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcompany.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcompany.com.php
new file mode 100644
index 00000000..e0869a29
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcompany.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.fastcompany.com/3026712/fast-feed/elon-musk-an-apple-tesla-merger-is-very-unlikely',
+ 'body' => array(
+ '//article[contains(@class, "body prose")]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ffworld.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ffworld.com.php
new file mode 100644
index 00000000..20a47b2d
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ffworld.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.ffworld.com/?rub=news&page=voir&id=2709',
+ 'body' => array(
+ '//div[@class="news_body"]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/foreignpolicy.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/foreignpolicy.com.php
new file mode 100644
index 00000000..3cbcddc4
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/foreignpolicy.com.php
@@ -0,0 +1,21 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://foreignpolicy.com/2016/01/09/networking-giant-pulls-nsa-linked-code-exploited-by-hackers/',
+ 'body' => array(
+ '//article',
+ ),
+ 'strip' => array(
+ '//div[@id="post-category"]',
+ '//div[@id="desktop-right"]',
+ '//h1',
+ '//section[@class="article-meta"]',
+ '//div[@class="side-panel-wrapper"]',
+ '//*[contains(@class, "share-")]',
+ '//*[contains(@id, "taboola-")]',
+ '//div[@class="comments"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fossbytes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fossbytes.com.php
new file mode 100644
index 00000000..6ce47256
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fossbytes.com.php
@@ -0,0 +1,18 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://fossbytes.com/fbi-hacked-1000-computers-to-shut-down-largest-child-pornography-site-on-the-dark-web/',
+ 'body' => array(
+ '//div[@class="entry-inner"]',
+ ),
+ 'strip' => array(
+ '//*[@class="at-above-post addthis_default_style addthis_toolbox at-wordpress-hide"]',
+ '//*[@class="at-below-post addthis_default_style addthis_toolbox at-wordpress-hide"]',
+ '//*[@class="at-below-post-recommended addthis_default_style addthis_toolbox at-wordpress-hide"]',
+ '//*[@class="code-block code-block-12 ai-desktop"]',
+ '//*[@class="code-block code-block-13 ai-tablet-phone"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fototelegraf.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fototelegraf.ru.php
new file mode 100644
index 00000000..ca2f85aa
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fototelegraf.ru.php
@@ -0,0 +1,19 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://fototelegraf.ru/?p=348232',
+ 'body' => array(
+ '//div[@class="post-content"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//form',
+ '//style',
+ '//div[@class="imageButtonsBlock"]',
+ '//div[@class="adOnPostBtwImg"]',
+ '//div[contains(@class, "post-tags")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fowllanguagecomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fowllanguagecomics.com.php
new file mode 100644
index 00000000..3f62f071
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fowllanguagecomics.com.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'body' => array('//*[@id="comic"] | //*[@class="post-image"]'),
+ 'strip' => array(),
+ 'test_url' => 'http://www.fowllanguagecomics.com/comic/working-out/',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geek.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geek.com.php
new file mode 100644
index 00000000..d9ccecc2
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geek.com.php
@@ -0,0 +1,17 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.geek.com/news/the-11-best-ways-to-eat-eggs-1634076/',
+ 'body' => array(
+ '//div[@class="articleinfo"]/figure',
+ '//div[@class="articleinfo"]/article',
+ '//span[@class="by"]',
+ ),
+ 'strip' => array(
+ '//span[@class="red"]',
+ '//div[@class="on-target"]'
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geektimes.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geektimes.ru.php
new file mode 100644
index 00000000..19541386
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geektimes.ru.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://geektimes.ru/post/289151/',
+ 'body' => array(
+ "//div[contains(concat(' ',normalize-space(@class),' '),' content ')]"
+ ),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gerbilwithajetpack.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gerbilwithajetpack.com.php
new file mode 100644
index 00000000..44013b3b
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gerbilwithajetpack.com.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'body' => array(
+ '//div[@id="comic-1"]',
+ '//div[@class="entry"]',
+ ),
+ 'test_url' => 'http://gerbilwithajetpack.com/passing-the-digital-buck/',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/giantitp.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/giantitp.com.php
new file mode 100644
index 00000000..d9c3ae5d
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/giantitp.com.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%/comics/oots.*%' => array(
+ 'test_url' => 'http://www.giantitp.com/comics/oots0989.html',
+ 'body' => array(
+ '//td[@align="center"]/img',
+ ),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/github.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/github.com.php
new file mode 100644
index 00000000..726634f9
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/github.com.php
@@ -0,0 +1,14 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://github.com/audreyr/favicon-cheat-sheet',
+ 'body' => array(
+ '//article[contains(@class, "entry-content")]',
+ ),
+ 'strip' => array(
+ '//h1',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gocomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gocomics.com.php
new file mode 100644
index 00000000..32960f0e
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gocomics.com.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.gocomics.com/pearlsbeforeswine/2015/05/30',
+ 'body' => array(
+ '//div[1]/p[1]/a[1]/img',
+ ),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/golem.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/golem.de.php
new file mode 100644
index 00000000..84224830
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/golem.de.php
@@ -0,0 +1,20 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.golem.de/news/breko-telekom-verzoegert-gezielt-den-vectoring-ausbau-1311-102974.html',
+ 'body' => array(
+ '//header[@class="cluster-header"]',
+ '//header[@class="paged-cluster-header"]',
+ '//div[@class="formatted"]',
+ ),
+ 'next_page' => array(
+ '//a[@id="atoc_next"]'
+ ),
+ 'strip' => array(
+ '//header[@class="cluster-header"]/a',
+ '//div[@id="iqadtile4"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gorabbit.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gorabbit.ru.php
new file mode 100644
index 00000000..4e432481
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gorabbit.ru.php
@@ -0,0 +1,19 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://gorabbit.ru/article/10-oshchushcheniy-za-rulem-kogda-tolko-poluchil-voditelskie-prava',
+ 'body' => array(
+ '//div[@class="detail_text"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//form',
+ '//style',
+ '//div[@class="socials"]',
+ '//div[@id="cr_1"]',
+ '//div[@class="related_items"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/habrahabr.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/habrahabr.ru.php
new file mode 100644
index 00000000..3f1ec165
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/habrahabr.ru.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://habrahabr.ru/company/pentestit/blog/328606/',
+ 'body' => array(
+ "//div[contains(concat(' ',normalize-space(@class),' '),' content ')]"
+ ),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/happletea.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/happletea.com.php
new file mode 100644
index 00000000..75b0b83d
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/happletea.com.php
@@ -0,0 +1,18 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'body' => array(
+ '//div[@id="comic"]',
+ '//div[@class="entry"]',
+ ),
+ 'strip' => array('//div[@class="ssba"]'),
+ 'test_url' => 'http://www.happletea.com/comic/mans-best-friend/',
+ ),
+ ),
+ 'filter' => array(
+ '%.*%' => array(
+ '%title="(.+)" */>%' => '/><br/>$1',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hardware.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hardware.fr.php
new file mode 100644
index 00000000..56aec4f4
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hardware.fr.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%^/news.*%' => array(
+ 'test_url' => 'http://www.hardware.fr/news/14760/intel-lance-nouveaux-ssd-nand-3d.html',
+ 'body' => array(
+ '//div[@class="content_actualite"]/div[@class="md"]',
+ )
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/heise.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/heise.de.php
new file mode 100644
index 00000000..2055b3bc
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/heise.de.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.heise.de/security/meldung/BND-300-Millionen-Euro-fuer-Fruehwarnsystem-gegen-Cyber-Attacken-2192237.html',
+ 'body' => array(
+ '//div[@class="meldung_wrapper"]',
+ '//div[@class="artikel_content"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hotshowlife.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hotshowlife.com.php
new file mode 100644
index 00000000..faf01f3d
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hotshowlife.com.php
@@ -0,0 +1,23 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://hotshowlife.com/top-10-chempionov-produktov-po-szhiganiyu-kalorij/',
+ 'body' => array(
+ '//div[@class="entry-content"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//form',
+ '//style',
+ '//div[@class="ads2"]',
+ '//div[@class="mistape_caption"]',
+ '//div[contains(@class, "et_social_media_hidden")]',
+ '//div[contains(@class, "et_social_inline_bottom")]',
+ '//div[contains(@class, "avatar")]',
+ '//ul[contains(@class, "entry-tags")]',
+ '//div[contains(@class, "entry-meta")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/huffingtonpost.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/huffingtonpost.com.php
new file mode 100644
index 00000000..b52b07b5
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/huffingtonpost.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.huffingtonpost.com/2014/02/20/centscere-social-media-syracuse_n_4823848.html',
+ 'body' => array(
+ '//article[@class="content")]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/imogenquest.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/imogenquest.net.php
new file mode 100644
index 00000000..3214c62a
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/imogenquest.net.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%title="(.+)" */>%' => '/><br/>$1',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/indiehaven.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/indiehaven.com.php
new file mode 100644
index 00000000..a40ce694
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/indiehaven.com.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://indiehaven.com/no-mans-sky-is-a-solo-space-adventure-and-im-ok-with-that/',
+ 'body' => array(
+ '//section[contains(@class, "entry-content")]',
+ )
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ing.dk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ing.dk.php
new file mode 100644
index 00000000..5a021a08
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ing.dk.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://ing.dk/artikel/smart-husisolering-og-styring-skal-mindske-japans-energikrise-164517',
+ 'body' => array(
+ '//section[contains(@class, "teaser")]',
+ '//section[contains(@class, "body")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/invisiblebread.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/invisiblebread.com.php
new file mode 100644
index 00000000..90f87597
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/invisiblebread.com.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%(<img.+(\\d{4}-\\d{2}-\\d{2}).+/>)%' => '$1<img src="http://invisiblebread.com/eps/$2-extrapanel.png"/>',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ir.amd.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ir.amd.com.php
new file mode 100644
index 00000000..af99fe99
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ir.amd.com.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'body' => array('//span[@class="ccbnTxt"]'),
+ 'strip' => array(),
+ 'test_url' => 'http://ir.amd.com/phoenix.zhtml?c=74093&p=RssLanding&cat=news&id=2055819',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantimes.co.jp.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantimes.co.jp.php
new file mode 100644
index 00000000..9959441d
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantimes.co.jp.php
@@ -0,0 +1,21 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.japantimes.co.jp/news/2015/09/27/world/social-issues-world/pope-meets-sex-abuse-victims-philadelphia-promises-accountability/',
+ 'body' => array(
+ '//article[@role="main"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//header',
+ '//div[contains(@class, "meta")]',
+ '//div[@class="clearfix"]',
+ '//div[@class="OUTBRAIN"]',
+ '//ul[@id="content_footer_menu"]',
+ '//div[@class="article_footer_ad"]',
+ '//div[@id="disqus_thread"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantoday.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantoday.com.php
new file mode 100644
index 00000000..22485d69
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantoday.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.japantoday.com/category/politics/view/japan-u-s-to-sign-new-base-environment-pact',
+ 'body' => array(
+ '//div[@id="article_container"]',
+ ),
+ 'strip' => array(
+ '//h2',
+ '//div[@id="article_info"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/journaldugeek.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/journaldugeek.com.php
new file mode 100644
index 00000000..876b2698
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/journaldugeek.com.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www./2014/05/20/le-playstation-now-arrive-en-beta-fermee-aux-etats-unis/',
+ 'body' => array(
+ '//div[@class="post-content"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/jsonline.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/jsonline.com.php
new file mode 100644
index 00000000..5895256e
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/jsonline.com.php
@@ -0,0 +1,37 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.%/picture-gallery/%' => array(
+ 'test_url' => 'http://www.jsonline.com/picture-gallery/news/local/milwaukee/2017/02/22/photos-aclu-sues-milwaukee-police-over-profiling-stop-and-frisk/98250836/',
+ 'body' => array(
+ '//div[@class="priority-asset-gallery galleries standalone hasendslate"]',
+ ),
+ 'strip' => array(
+ '//div[@class="buy-photo-btn"]',
+ '//div[@class="gallery-thumbs thumbs pag-thumbs")]',
+ ),
+ ),
+ '%.*%' => array(
+ 'test_url' => 'http://www.jsonline.com/news/usandworld/as-many-as-a-million-expected-for-popes-last-mass-in-us-b99585180z1-329688131.html',
+ 'body' => array(
+ '//div[@itemprop="articleBody"]',
+ ),
+ 'strip' => array(
+ '//h1',
+ '//iframe',
+ '//span[@class="mycapture-small-btn mycapture-btn-with-text mycapture-expandable-photo-btn-small js-mycapture-btn-small"]',
+ '//div[@class="close-wrap"]',
+ '//div[contains(@class,"ui-video-wrapper")]',
+ '//div[contains(@class,"media-mob")]',
+ '//div[contains(@class,"left-mob")]',
+ '//div[contains(@class,"nerdbox")]',
+ '//p/span',
+ '//div[contains(@class,"oembed-asset")]',
+ '//*[contains(@class,"share")]',
+ '//div[contains(@class,"gallery-asset")]',
+ '//div[contains(@class,"oembed-asset")]',
+ '//div[@class="article-print-url"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/justcoolidea.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/justcoolidea.ru.php
new file mode 100644
index 00000000..089ff29c
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/justcoolidea.ru.php
@@ -0,0 +1,19 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://justcoolidea.ru/idealnyj-sad-samodelnye-proekty-dlya-berezhlivogo-domovladeltsa/',
+ 'body' => array(
+ '//section[@class="entry-content"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//form',
+ '//style',
+ '//*[contains(@class, "essb_links")]',
+ '//*[contains(@rel, "nofollow")]',
+ '//*[contains(@class, "ads")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kanpai.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kanpai.fr.php
new file mode 100644
index 00000000..c3a1abc4
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kanpai.fr.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.kanpai.fr/japon/comment-donner-lheure-en-japonais.html',
+ 'body' => array(
+ '//div[@class="single-left"]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/karriere.jobfinder.dk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/karriere.jobfinder.dk.php
new file mode 100644
index 00000000..25d6dfa3
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/karriere.jobfinder.dk.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://karriere.jobfinder.dk/artikel/dansk-professor-skal-lede-smart-grid-forskning-20-millioner-dollars-763',
+ 'body' => array(
+ '//section[contains(@class, "teaser")]',
+ '//section[contains(@class, "body")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kodi.tv.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kodi.tv.php
new file mode 100644
index 00000000..439fc907
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kodi.tv.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://kodi.tv/article/andwere-baaaaack',
+ 'body' => array(
+ '//div[@class="l-region--content"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreaherald.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreaherald.com.php
new file mode 100644
index 00000000..96510560
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreaherald.com.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.koreaherald.com/view.php?ud=20150926000018',
+ 'body' => array(
+ '//div[@id="articleText"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreatimes.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreatimes.php
new file mode 100644
index 00000000..f274b4a9
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreatimes.php
@@ -0,0 +1,14 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.koreatimes.co.kr/www/news/nation/2015/12/116_192409.html',
+ 'body' => array(
+ '//div[@id="p"]',
+ ),
+ 'strip' => array(
+ '//div[@id="webtalks_btn_listenDiv"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lastplacecomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lastplacecomics.com.php
new file mode 100644
index 00000000..12697ccb
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lastplacecomics.com.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%-150x150%' => '',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/legorafi.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/legorafi.fr.php
new file mode 100644
index 00000000..e6aae46b
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/legorafi.fr.php
@@ -0,0 +1,22 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => array(
+ 'http://www.legorafi.fr/2016/12/16/gorafi-magazine-bravo-vous-avez-bientot-presque-survecu-a-2016/',
+ 'http://www.legorafi.fr/2016/12/15/manuel-valls-promet-quune-fois-elu-il-debarrassera-la-france-de-manuel-valls/',
+ ),
+ 'body' => array(
+ '//section[@id="banner_magazine"]',
+ '//figure[@class="main_picture"]',
+ '//div[@class="content"]',
+ ),
+ 'strip' => array(
+ '//figcaption',
+ '//div[@class="sharebox"]',
+ '//div[@class="tags"]',
+ '//section[@class="taboola_article"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lejapon.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lejapon.fr.php
new file mode 100644
index 00000000..8f2b2932
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lejapon.fr.php
@@ -0,0 +1,17 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://lejapon.fr/guide-voyage-japon/5223/tokyo-sous-la-neige.htm',
+ 'body' => array(
+ '//div[@class="entry"]',
+ ),
+ 'strip' => array(
+ '//*[contains(@class, "addthis_toolbox")]',
+ '//*[contains(@class, "addthis_default_style")]',
+ '//*[@class="navigation small"]',
+ '//*[@id="related"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lesjoiesducode.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lesjoiesducode.fr.php
new file mode 100644
index 00000000..369206ab
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lesjoiesducode.fr.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://lesjoiesducode.fr/post/75576211207/quand-lappli-ne-fonctionne-plus-sans-aucune-raison',
+ 'body' => array(
+ '//div[@class="blog-post-content"]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lfg.co.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lfg.co.php
new file mode 100644
index 00000000..d978a5fc
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lfg.co.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.lfg.co/page/871/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+LookingForGroup+%28Looking+For+Group%29&utm_content=FeedBurner',
+ 'body' => array(
+ '//*[@id="comic"]/img | //*[@class="content"]',
+ ),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.com.php
new file mode 100644
index 00000000..b9a69338
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.com.php
@@ -0,0 +1,18 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://lifehacker.com/bring-water-bottle-caps-into-concerts-to-protect-your-d-1269334973',
+ 'body' => array(
+ '//div[contains(@class, "row")/img',
+ '//div[contains(@class, "content-column")]',
+ ),
+ 'strip' => array(
+ '//*[contains(@class, "meta")]',
+ '//span[contains(@class, "icon")]',
+ '//h1',
+ '//aside',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.ru.php
new file mode 100644
index 00000000..bc140f67
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.ru.php
@@ -0,0 +1,22 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://lifehacker.ru/2016/03/03/polymail/',
+ 'body' => array(
+ '//div[@class="post-content"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//form',
+ '//style',
+ '//*[@class="wp-thumbnail-caption"]',
+ '//*[contains(@class, "social-likes")]',
+ '//*[@class="jp-relatedposts"]',
+ '//*[contains(@class, "wpappbox")]',
+ '//*[contains(@class, "icon__image")]',
+ '//div[@id="hypercomments_widget"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.php
new file mode 100644
index 00000000..2520d0d0
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.php
@@ -0,0 +1,14 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.linux.org/threads/lua-the-scripting-interpreter.8352/',
+ 'body' => array(
+ '//div[@class="messageContent"]',
+ ),
+ 'strip' => array(
+ '//aside',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.ru.php
new file mode 100644
index 00000000..7fa02497
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.ru.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.linux.org/threads/lua-the-scripting-interpreter.8352/',
+ 'body' => array(
+ '//div[@itemprop="articleBody"]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linuxinsider.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linuxinsider.com.php
new file mode 100644
index 00000000..4e0a4cc1
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linuxinsider.com.php
@@ -0,0 +1,20 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.linuxinsider.com/story/82526.html?rss=1',
+ 'body' => array(
+ '//div[@id="story"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//h1',
+ '//div[@id="story-toolbox1"]',
+ '//div[@id="story-byline"]',
+ '//div[@id="story"]/p',
+ '//div[@class="story-advertisement"]',
+ '//iframe',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lists.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lists.php
new file mode 100644
index 00000000..c7051a20
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lists.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://lists.freebsd.org/pipermail/freebsd-announce/2013-September/001504.html',
+ 'body' => array(
+ '//pre',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loadingartist.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loadingartist.com.php
new file mode 100644
index 00000000..d06ed124
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loadingartist.com.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%-150x150%' => '',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loldwell.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loldwell.com.php
new file mode 100644
index 00000000..d358e156
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loldwell.com.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://loldwell.com/?comic=food-math-101',
+ 'body' => array('//*[@id="comic"]'),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lukesurl.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lukesurl.com.php
new file mode 100644
index 00000000..816233dd
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lukesurl.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'body' => array('//div[@id="comic"]//img'),
+ 'strip' => array(),
+ 'test_url' => 'http://www.lukesurl.com/archives/comic/665-3-of-clubs',
+ ),
+ ),
+ 'filter' => array(
+ '%.*%' => array(
+ '%title="(.+)" */>%' => '/><br/>$1',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/macg.co.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/macg.co.php
new file mode 100644
index 00000000..bbe6dbcd
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/macg.co.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.macg.co//logiciels/2014/05/feedly-sameliore-un-petit-peu-sur-mac-82205',
+ 'body' => array(
+ '//div[contains(@class, "field-name-body")]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marc.info.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marc.info.php
new file mode 100644
index 00000000..5f582a6d
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marc.info.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://marc.info/?l=openbsd-misc&m=141987113202061&w=2',
+ 'body' => array(
+ '//pre',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marriedtothesea.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marriedtothesea.com.php
new file mode 100644
index 00000000..469640df
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marriedtothesea.com.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.marriedtothesea.com/index.php?date=052915',
+ 'body' => array(
+ '//div[@align]/a/img',
+ ),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marycagle.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marycagle.com.php
new file mode 100644
index 00000000..b8665e35
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marycagle.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'body' => array(
+ '//img[@id="cc-comic"]',
+ '//div[@class="cc-newsbody"]',
+ ),
+ 'strip' => array(),
+ 'test_url' => 'http://www.marycagle.com/letsspeakenglish/74-grim-reality/',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/maximumble.thebookofbiff.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/maximumble.thebookofbiff.com.php
new file mode 100644
index 00000000..88800546
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/maximumble.thebookofbiff.com.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://maximumble.thebookofbiff.com/2015/04/20/1084-change/',
+ 'body' => array('//div[@id="comic"]/div/a/img'),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/medium.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/medium.com.php
new file mode 100644
index 00000000..e20860e0
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/medium.com.php
@@ -0,0 +1,19 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://medium.com/lessons-learned/917b8b63ae3e',
+ 'body' => array(
+ '//div[@class="section-content"]',
+ ),
+ 'strip' => array(
+ '//div[contains(@class,"metabar")]',
+ '//img[contains(@class,"thumbnail")]',
+ '//h1',
+ '//blockquote',
+ '//div[@class="aspectRatioPlaceholder-fill"]',
+ '//footer'
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mercworks.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mercworks.net.php
new file mode 100644
index 00000000..c7a27dea
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mercworks.net.php
@@ -0,0 +1,17 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'body' => array('//div[@id="comic"]',
+ '//div[contains(@class,"entry-content")]',
+ ),
+ 'strip' => array(),
+ 'test_url' => 'http://mercworks.net/comicland/healthy-choice/',
+ ),
+ ),
+ 'filter' => array(
+ '%.*%' => array(
+ '%title="(.+)" */>%' => '/><br/>$1',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/metronieuws.nl.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/metronieuws.nl.php
new file mode 100644
index 00000000..5011169f
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/metronieuws.nl.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.metronieuws.nl/sport/2015/04/broer-fellaini-zorgde-bijna-voor-paniek-bij-mourinho',
+ 'body' => array('//div[contains(@class,"article-top")]/div[contains(@class,"image-component")] | //div[@class="article-full-width"]/div[1]'),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/milwaukeenns.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/milwaukeenns.php
new file mode 100644
index 00000000..ddb29a56
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/milwaukeenns.php
@@ -0,0 +1,14 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://milwaukeenns.org/2016/01/08/united-way-grant-enables-sdc-to-restore-free-tax-assistance-program/',
+ 'body' => array(
+ '//div[@class="pf-content"]',
+ ),
+ 'strip' => array(
+ '//div[@class="printfriendly"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mokepon.smackjeeves.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mokepon.smackjeeves.com.php
new file mode 100644
index 00000000..1ddcd407
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mokepon.smackjeeves.com.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://mokepon.smackjeeves.com/comics/2120096/chapter-9-page-68/',
+ 'body' => array('//*[@id="comic_area_inner"]/img | //*[@id="comic_area_inner"]/a/img'),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monandroid.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monandroid.com.php
new file mode 100644
index 00000000..f87560e2
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monandroid.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.monandroid.com/blog/tutoriel-avance-activer-le-stockage-fusionne-sur-android-6-marshamallow-t12.html',
+ 'body' => array(
+ '//div[@class="blog-post-body"]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monwindows.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monwindows.com.php
new file mode 100644
index 00000000..b2b24d74
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monwindows.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.monwindows.com/tout-savoir-sur-le-centre-d-action-de-windows-phone-8-1-t40574.html',
+ 'body' => array(
+ '//div[@class="blog-post-body"]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/moya-planeta.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/moya-planeta.ru.php
new file mode 100644
index 00000000..dd842844
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/moya-planeta.ru.php
@@ -0,0 +1,21 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.moya-planeta.ru/travel/view/chto_yaponcu_horosho_russkomu_ne_ponyat_20432/',
+ 'body' => array(
+ '//div[@class="full_object"]',
+ ),
+ 'strip' => array(
+ '//div[@class="full_object_panel object_panel"]',
+ '//div[@class="full_object_panel_geo object_panel"]',
+ '//div[@class="full_object_title"]',
+ '//div[@class="full_object_social_likes"]',
+ '//div[@class="full_object_planeta_likes"]',
+ '//div[@class="full_object_go2comments"]',
+ '//div[@id="yandex_ad_R-163191-3"]',
+ '//div[@class="full_object_shop_article_recommend"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mrlovenstein.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mrlovenstein.com.php
new file mode 100644
index 00000000..b971091f
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mrlovenstein.com.php
@@ -0,0 +1,9 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%alt="(.+)" */>%' => '/><br/>$1',
+ '%\.png%' => '_rollover.png',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/muckrock.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/muckrock.com.php
new file mode 100644
index 00000000..9e354a36
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/muckrock.com.php
@@ -0,0 +1,20 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://www.muckrock.com/news/archives/2016/jan/13/5-concerns-private-prisons/',
+ 'body' => array(
+ '//div[@class="content"]',
+ ),
+ 'strip' => array(
+ '//div[@class="newsletter-widget"]',
+ '//div[@class="contributors"]',
+ '//time',
+ '//h1',
+ '//div[@class="secondary"]',
+ '//aside',
+ '//div[@class="articles__related"]'
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mynorthshorenow.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mynorthshorenow.com.php
new file mode 100644
index 00000000..b6309157
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mynorthshorenow.com.php
@@ -0,0 +1,27 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.mynorthshorenow.com/story/news/local/fox-point/2017/04/04/fox-point-building-board-approves-dunwood-commons-project/99875570/',
+ 'body' => array(
+ '//div[@itemprop="articleBody"]',
+ ),
+ 'strip' => array(
+ '//h1',
+ '//iframe',
+ '//span[@class="mycapture-small-btn mycapture-btn-with-text mycapture-expandable-photo-btn-small js-mycapture-btn-small"]',
+ '//div[@class="close-wrap"]',
+ '//div[contains(@class,"ui-video-wrapper")]',
+ '//div[contains(@class,"media-mob")]',
+ '//div[contains(@class,"left-mob")]',
+ '//div[contains(@class,"nerdbox")]',
+ '//p/span',
+ '//div[contains(@class,"oembed-asset")]',
+ '//*[contains(@class,"share")]',
+ '//div[contains(@class,"gallery-asset")]',
+ '//div[contains(@class,"oembed-asset")]',
+ '//div[@class="article-print-url"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nakedCapitalism.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nakedCapitalism.php
new file mode 100644
index 00000000..ec2d5fd5
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nakedCapitalism.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://feedproxy.google.com/~r/NakedCapitalism/~3/JOBxEHxN8ZI/mark-blyth-liberalism-undermined-democracy-failure-democratic-party.html',
+ 'body' => array(
+ '//div[@class="pf-content"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nasa.gov.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nasa.gov.php
new file mode 100644
index 00000000..c6692d07
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nasa.gov.php
@@ -0,0 +1,14 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://www.nasa.gov/image-feature/jpl/pia20514/coy-dione',
+ 'body' => array(
+ '//div[@class="article-body"]',
+ ),
+ 'strip' => array(
+ '//div[@class="title-bar"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nat-geo.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nat-geo.ru.php
new file mode 100644
index 00000000..1a42d997
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nat-geo.ru.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.nat-geo.ru/fact/868093-knidos-antichnyy-naukograd/',
+ 'body' => array(
+ '//div[@class="article-inner-text"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nationaljournal.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nationaljournal.com.php
new file mode 100644
index 00000000..5e612bef
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nationaljournal.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.nationaljournal.com/s/354962/south-carolina-evangelicals-outstrip-establishment?mref=home_top_main',
+ 'body' => array(
+ '//div[@class="section-body"]',
+ ),
+ 'strip' => array(
+ '//*[contains(@class, "-related")]',
+ '//*[contains(@class, "social")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nature.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nature.com.php
new file mode 100644
index 00000000..6b9e87f4
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nature.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.nature.com/doifinder/10.1038/nature.2015.18340',
+ 'body' => array(
+ '//div[contains(@class,"main-content")]',
+ ),
+ 'strip' => array(),
+ ),
+ ),
+);
+
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nba.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nba.com.php
new file mode 100644
index 00000000..c8ea926f
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nba.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.nba.com/2015/news/09/25/knicks-jackson-to-spend-more-time-around-coaching-staff.ap/index.html?rss=true',
+ 'body' => array(
+ '//div[@class="paragraphs"]',
+ ),
+ 'strip' => array(
+ '//div[@id="nbaArticleSocialWrapper_bot"]',
+ '//h5',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nedroid.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nedroid.com.php
new file mode 100644
index 00000000..3214c62a
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nedroid.com.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%title="(.+)" */>%' => '/><br/>$1',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/networkworld.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/networkworld.com.php
new file mode 100644
index 00000000..18524354
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/networkworld.com.php
@@ -0,0 +1,20 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.networkworld.com/article/3020585/security/the-incident-response-fab-five.html',
+ 'body' => array(
+ '//figure/img[@class="hero-img"]',
+ '//section[@class="deck"]',
+ '//div[@itemprop="articleBody"] | //div[@itemprop="reviewBody"]',
+ '//div[@class="carousel-inside-crop"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//aside',
+ '//div[@class="credit"]',
+ '//div[@class="view-large"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/neustadt-ticker.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/neustadt-ticker.de.php
new file mode 100644
index 00000000..e0c0d19d
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/neustadt-ticker.de.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.neustadt-ticker.de/41302/alltag/kultur/demo-auf-der-boehmischen',
+ 'body' => array(
+ '//div[@class="entry-content"]',
+ ),
+ 'strip' => array(
+ '//*[contains(@class, "sharedaddy")]',
+ '//*[contains(@class, "yarpp-related")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nextinpact.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nextinpact.com.php
new file mode 100644
index 00000000..29dd9d6b
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nextinpact.com.php
@@ -0,0 +1,18 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.nextinpact.com/news/101122-3d-nand-intel-lance-six-nouvelles-gammes-ssd-pour-tous-usages.htm',
+ 'body' => array(
+ '//div[@class="container_article"]',
+ ),
+ 'strip' => array(
+ '//div[@class="infos_article"]',
+ '//div[@id="actu_auteur"]',
+ '//div[@id="soutenir_journaliste"]',
+ '//section[@id="bandeau_abonnez_vous"]',
+ '//br'
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/niceteethcomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/niceteethcomic.com.php
new file mode 100644
index 00000000..f41e4438
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/niceteethcomic.com.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%/archives.*%' => array(
+ 'test_url' => 'http://niceteethcomic.com/archives/page119/',
+ 'body' => array('//*[@class="comicpane"]/a/img'),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nichtlustig.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nichtlustig.de.php
new file mode 100644
index 00000000..4d083f98
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nichtlustig.de.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%.*static.nichtlustig.de/comics/full/(\\d+).*%s' => '<img src="http://static.nichtlustig.de/comics/full/$1.jpg" />',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/oglaf.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/oglaf.com.php
new file mode 100644
index 00000000..8b2b5b65
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/oglaf.com.php
@@ -0,0 +1,19 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'body' => array(
+ '//img[@id="strip"]',
+ '//a/div[@id="nx"]/..',
+ ),
+ 'strip' => array(),
+ 'test_url' => 'http://oglaf.com/slodging/',
+ ),
+ ),
+ 'filter' => array(
+ '%.*%' => array(
+ '%alt="(.+)" title="(.+)" */>%' => '/><br/>$1<br/>$2<br/>',
+ '%</a>%' => 'Next page</a>',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onhax.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onhax.net.php
new file mode 100644
index 00000000..213849d8
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onhax.net.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://onhax.net/process-lasso-8-9-1-4-pro-key-portable-is-here-latest',
+ 'body' => array(
+ '//div[@class="postcontent"]',
+ ),
+ 'strip' => array(
+ '//*[@class="sharedaddy sd-sharing-enabled"]',
+ '//*[@class="yarpp-related"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onmilwaukee.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onmilwaukee.php
new file mode 100644
index 00000000..f66ac4b8
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onmilwaukee.php
@@ -0,0 +1,24 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://onmilwaukee.com/movies/articles/downerspelunking.html',
+ 'body' => array(
+ '//article[contains(@class, "show")]',
+ ),
+ 'strip' => array(
+ '//h1',
+ '//div[contains(@class,"-ad")]',
+ '//div[contains(@class,"_ad")]',
+ '//div[@id="pub_wrapper"]',
+ '//div[contains(@class,"share_tools")]',
+ '//div[@class="clearfix"]',
+ '//div[contains(@class,"image_control")]',
+ '//section[@class="ribboned"]',
+ '//div[contains(@class,"sidebar")]',
+ '//aside[@class="article_tag_list"]',
+ '//section[contains(@id,"more_posts")]'
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openculture.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openculture.com.php
new file mode 100644
index 00000000..84f2beed
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openculture.com.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.openculture.com/2017/03/are-we-living-inside-a-computer-simulation-watch-the-simulation-argument.html',
+ 'body' => array(
+ '//div[@class="entry"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opennet.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opennet.ru.php
new file mode 100644
index 00000000..1fb7722b
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opennet.ru.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://www.opennet.ru/opennews/art.shtml?num=46549',
+ 'body' => array(
+ '//*[@id="r_memo"]',
+ ),
+ 'strip' => array(
+ ),
+ )
+ )
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openrightsgroup.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openrightsgroup.org.php
new file mode 100644
index 00000000..94139a72
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openrightsgroup.org.php
@@ -0,0 +1,20 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://www.openrightsgroup.org/blog/2014/3-days-to-go-till-orgcon2014',
+ 'body' => array(
+ '//div[contains(@class, "content")]/div',
+ ),
+ 'strip' => array(
+ '//h2[1]',
+ '//div[@class="info"]',
+ '//div[@class="tags"]',
+ '//div[@class="comments"]',
+ '//div[@class="breadcrumbs"]',
+ '//h1[@class="pageTitle"]',
+ '//p[@class="bookmarkThis"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opensource.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opensource.com.php
new file mode 100644
index 00000000..60f3577b
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opensource.com.php
@@ -0,0 +1,22 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://opensource.com/life/15/10/how-internet-things-will-change-way-we-think',
+ 'body' => array(
+ '//div[@id="article-template"]',
+ ),
+ 'strip' => array(
+ '//div[contains(@class,"os-article__sidebar")]',
+ '//div[@class="panel-pane pane-node-title"]',
+ '//div[@class="panel-pane pane-os-article-byline"]',
+ '//ul',
+ '//div[contains(@class,"-license")]',
+ '//div[contains(@class,"-tags")]',
+ '//div[@class="panel-pane pane-os-article-byline"]',
+ '//div[@class="os-article__content-below"]',
+ '//div[@id="comments"]'
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/optipess.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/optipess.com.php
new file mode 100644
index 00000000..3214c62a
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/optipess.com.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%title="(.+)" */>%' => '/><br/>$1',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/osnews.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/osnews.com.php
new file mode 100644
index 00000000..1d1396c8
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/osnews.com.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://osnews.com/story/28863/Google_said_to_be_under_US_antitrust_scrutiny_over_Android',
+ 'body' => array(
+ '//div[@class="newscontent1"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pastebin.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pastebin.com.php
new file mode 100644
index 00000000..b20bf41c
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pastebin.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://pastebin.com/ed1pP9Ak',
+ 'body' => array(
+ '//div[@class="text"]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/peebleslab.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/peebleslab.com.php
new file mode 100644
index 00000000..ce4891d1
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/peebleslab.com.php
@@ -0,0 +1,9 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ // the extra space is required to strip the title cleanly
+ '%title="(.+) " */>%' => '/><br/>$1',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/penny-arcade.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/penny-arcade.com.php
new file mode 100644
index 00000000..dd39983b
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/penny-arcade.com.php
@@ -0,0 +1,21 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%/news/.*%' => array(
+ 'test_url' => 'http://penny-arcade.com/news/post/2015/04/15/101-part-two',
+ 'body' => array(
+ '//*[@class="postBody"]/*',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ '%/comic/.*%' => array(
+ 'test_url' => 'http://penny-arcade.com/comic/2015/04/15',
+ 'body' => array(
+ '//*[@id="comicFrame"]/a/img',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pixelbeat.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pixelbeat.org.php
new file mode 100644
index 00000000..fa9052ea
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pixelbeat.org.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.pixelbeat.org/programming/sigpipe_handling.html#1425573246',
+ 'body' => array(
+ '//div[@class="contentText"]',
+ ),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/plus.google.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/plus.google.com.php
new file mode 100644
index 00000000..5e48a6cf
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/plus.google.com.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://plus.google.com/+LarryPage/posts/Lh8SKC6sED1',
+ 'body' => array(
+ '//div[@role="article"]/div[contains(@class, "eE")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/popstrip.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/popstrip.com.php
new file mode 100644
index 00000000..801a2814
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/popstrip.com.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%(<img.+/s/[^"]+/)(.+)%' => '$1$2$1bonus.png"/>',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publicpolicyforum.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publicpolicyforum.org.php
new file mode 100644
index 00000000..5dc8be88
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publicpolicyforum.org.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://publicpolicyforum.org/blog/going-extra-mile',
+ 'body' => array(
+ '//div[contains(@class,"field-name-post-date")]',
+ '//div[contains(@class,"field-name-body")]',
+ ),
+ 'strip' => array(
+ '//img[@src="http://publicpolicyforum.org/sites/default/files/logo3.jpg"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publy.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publy.ru.php
new file mode 100644
index 00000000..bcfeeb99
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publy.ru.php
@@ -0,0 +1,24 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.publy.ru/post/19988',
+ 'body' => array(
+ '//div[@class="singlepost"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//form',
+ '//style',
+ '//*[@class="featured"]',
+ '//*[@class="toc_white no_bullets"]',
+ '//*[@class="toc_title"]',
+ '//*[@class="pba"]',
+ '//*[@class="comments"]',
+ '//*[contains(@class, "g-single")]',
+ '//*[@class="ts-fab-wrapper"]',
+ '//*[contains(@class, "wp_rp_wrap")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/putaindecode.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/putaindecode.fr.php
new file mode 100644
index 00000000..9fa5568c
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/putaindecode.fr.php
@@ -0,0 +1,16 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://putaindecode.fr/posts/js/etat-lieux-js-modulaire-front/',
+ 'body' => array(
+ '//*[@class="putainde-Post-md"]',
+ ),
+ 'strip' => array(
+ '//*[contains(@class, "inlineimg")]',
+ '//*[contains(@class, "comment-respond")]',
+ '//header',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/recode.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/recode.net.php
new file mode 100644
index 00000000..343cd12f
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/recode.net.php
@@ -0,0 +1,20 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://recode.net/2015/09/26/big-tech-rolls-out-red-carpet-for-indian-prime-minister-lobbies-behind-closed-doors/',
+ 'body' => array(
+ '//img[contains(@class,"attachment-large")]',
+ '//div[contains(@class,"postarea")]',
+ '//li[@class,"author"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//div[contains(@class,"sharedaddy")]',
+ '//div[@class="post-send-off"]',
+ '//div[@class="large-12 columns"]',
+ '//div[contains(@class,"inner-related-article")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/retractionwatch.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/retractionwatch.com.php
new file mode 100644
index 00000000..b97c73ed
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/retractionwatch.com.php
@@ -0,0 +1,18 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://retractionwatch.com/2015/11/12/psychologist-jens-forster-settles-case-by-agreeing-to-2-retractions/',
+ 'body' => array(
+ '//*[@class="main"]',
+ '//*[@class="entry-content"]',
+ ),
+ 'strip' => array(
+ '//*[contains(@class, "sharedaddy")]',
+ '//*[contains(@class, "jp-relatedposts")]',
+ '//p[@class="p1"]',
+ )
+ )
+ )
+);
+
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rockpapershotgun.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rockpapershotgun.com.php
new file mode 100644
index 00000000..71110786
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rockpapershotgun.com.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://www.rockpapershotgun.com/2016/08/26/the-divisions-expansions-delayed-to-improve-the-game/',
+ 'body' => array(
+ '//div[@class="entry"]',
+ )
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rue89.nouvelobs.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rue89.nouvelobs.com.php
new file mode 100644
index 00000000..cb9116a3
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rue89.nouvelobs.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://rue89.feedsportal.com/c/33822/f/608948/s/30999fa0/sc/24/l/0L0Srue890N0C20A130C0A80C30A0Cfaisait0Eboris0Eboillon0Eex0Esarko0Eboy0E350A0E0A0A0A0Eeuros0Egare0Enord0E245315/story01.htm',
+ 'body' => array(
+ '//*[@id="article"]/div[contains(@class, "content")]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rugbyrama.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rugbyrama.fr.php
new file mode 100644
index 00000000..9915c234
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rugbyrama.fr.php
@@ -0,0 +1,20 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.rugbyrama.fr/rugby/top-14/2015-2016/top-14-hayman-coupe-du-monde-finale-2012-lutte.-voici-levan-chilachava-toulon_sto5283863/story.shtml',
+ 'body' => array(
+ '//div[@class="storyfull__content"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//form',
+ '//style',
+ '//*[@class="share-buttons"]',
+ '//*[@class="ad"]',
+ '//*[@class="hide-desktop"]',
+ '//*[@id="tracking_img"]',
+ )
+ )
+ )
+); \ No newline at end of file
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/satwcomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/satwcomic.com.php
new file mode 100644
index 00000000..d63fc11c
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/satwcomic.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://satwcomic.com/day-at-the-beach',
+ 'body' => array(
+ '//div[@class="container"]/center/a/img',
+ '//span[@itemprop="articleBody"]',
+ ),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/scrumalliance.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/scrumalliance.org.php
new file mode 100644
index 00000000..7835fd9e
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/scrumalliance.org.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://www.scrumalliance.org/community/articles/2015/march/an-introduction-to-agile-project-intake?feed=articles',
+ 'body' => array(
+ '//div[@class="article_content"]',
+ ),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/securityfocus.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/securityfocus.com.php
new file mode 100644
index 00000000..01045148
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/securityfocus.com.php
@@ -0,0 +1,17 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.securityfocus.com/archive/1/540139',
+ 'body' => array(
+ '//div[@id="vulnerability"]',
+ '//div[@class="comments_reply"]',
+ ),
+ 'strip' => array(
+ '//span[@class="title"]',
+ '//div[@id="logo_new"]',
+ '//div[@id="bannerAd"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sentfromthemoon.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sentfromthemoon.com.php
new file mode 100644
index 00000000..f4354179
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sentfromthemoon.com.php
@@ -0,0 +1,18 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'body' => array(
+ '//div[@class="comicpane"]/a/img',
+ '//div[@class="entry"]',
+ ),
+ 'strip' => array(),
+ 'test_url' => 'http://sentfromthemoon.com/archives/1417',
+ ),
+ ),
+ 'filter' => array(
+ '%.*%' => array(
+ '%title="(.+)" */>%' => '/><br/>$1',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sitepoint.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sitepoint.com.php
new file mode 100644
index 00000000..ab0eb7d4
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sitepoint.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.sitepoint.com/creating-hello-world-app-swift/',
+ 'body' => array(
+ '//section[@class="article_body"]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/slashdot.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/slashdot.org.php
new file mode 100644
index 00000000..89ced8b6
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/slashdot.org.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://science.slashdot.org/story/15/04/20/0528253/pull-top-can-tabs-at-50-reach-historic-archaeological-status',
+ 'body' => array(
+ '//article/div[@class="body"] | //article[@class="layout-article"]/div[@class="elips"]', ),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smallhousebliss.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smallhousebliss.com.php
new file mode 100644
index 00000000..8c13c44c
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smallhousebliss.com.php
@@ -0,0 +1,19 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://smallhousebliss.com/2013/08/29/house-g-by-lode-architecture/',
+ 'body' => array(
+ '//div[@class="post-content"]',
+ ),
+ 'strip' => array(
+ '//*[contains(@class, "gallery")]',
+ '//*[contains(@class, "share")]',
+ '//*[contains(@class, "wpcnt")]',
+ '//*[contains(@class, "meta")]',
+ '//*[contains(@class, "postitle")]',
+ '//*[@id="nav-below"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smarthomewelt.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smarthomewelt.de.php
new file mode 100644
index 00000000..7463abc7
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smarthomewelt.de.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://smarthomewelt.de/apple-tv-amazon-echo-smart-home/',
+ 'body' => array('//div[@class="entry-inner"]/p | //div[@class="entry-inner"]/div[contains(@class,"wp-caption")]'),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smashingmagazine.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smashingmagazine.com.php
new file mode 100644
index 00000000..cbe10726
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smashingmagazine.com.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.smashingmagazine.com/2015/04/17/using-sketch-for-responsive-web-design-case-study/',
+ 'body' => array('//article[contains(@class,"post")]/p'),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smbc-comics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smbc-comics.com.php
new file mode 100644
index 00000000..42262dc9
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smbc-comics.com.php
@@ -0,0 +1,14 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.smbc-comics.com/comic/the-troll-toll',
+ 'body' => array(
+ '//div[@id="cc-comicbody"]',
+ '//div[@id="aftercomic"]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/snopes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/snopes.com.php
new file mode 100644
index 00000000..b0fe6557
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/snopes.com.php
@@ -0,0 +1,22 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.snopes.com/bacca-brides-on-tour/',
+ 'body' => array(
+ '//article',
+ ),
+ 'strip' => array(
+ '//span[@itemprop="author"]',
+ '//div[contains(@class,"author-")]',
+ '//h1',
+ '//style',
+ '//div[contains(@class,"socialShares")]',
+ '//div[contains(@class,"ad-unit")]',
+ '//aside',
+ '//div[contains(@class,"boomtrain")]',
+ '//footer'
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/soundandvision.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/soundandvision.com.php
new file mode 100644
index 00000000..6448bb05
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/soundandvision.com.php
@@ -0,0 +1,21 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.soundandvision.com/content/james-guthrie-mixing-roger-waters-and-pink-floyd-51',
+ 'body' => array(
+ '//div[@id="left"]',
+ ),
+ 'strip' => array(
+ '//div[@class="meta"]',
+ '//div[@class="ratingsbox"]',
+ '//h1',
+ '//h2',
+ '//addthis',
+ '//comment-links',
+ '//div[@class="book-navigation"]',
+ '//div[@class="comment-links"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/spiegel.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/spiegel.de.php
new file mode 100644
index 00000000..e2e3db00
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/spiegel.de.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.spiegel.de/politik/ausland/afrika-angola-geht-gegen-islam-vor-und-schliesst-moscheen-a-935788.html',
+ 'body' => array(
+ '//div[contains(@class, "article-section")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stereophile.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stereophile.com.php
new file mode 100644
index 00000000..8e410be4
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stereophile.com.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.stereophile.com/content/2015-rocky-mountain-audio-fest-starts-friday',
+ 'body' => array(
+ '//div[@class="content clear-block"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stupidfox.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stupidfox.net.php
new file mode 100644
index 00000000..61182d72
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stupidfox.net.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://stupidfox.net/134-sleepy-time',
+ 'body' => array(
+ '//div[@class="comicmid"]/center/a/img',
+ '//div[@class="stand_high"]',
+ ),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/subtraction.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/subtraction.com.php
new file mode 100644
index 00000000..6d744277
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/subtraction.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.subtraction.com/2015/06/06/time-lapse-video-of-one-world-trade-center/',
+ 'body' => array('//article/div[@class="entry-content"]'),
+ 'strip' => array(),
+ ),
+ ),
+ 'filter' => array(
+ '%.*%' => array(
+ '%\+<h3.*/ul>%' => '',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sz.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sz.de.php
new file mode 100644
index 00000000..90bde5a9
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sz.de.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://sz.de/1.2443161',
+ 'body' => array('//article[@id="sitecontent"]/section[@class="topenrichment"]//img | //article[@id="sitecontent"]/section[@class="body"]/section[@class="authors"]/preceding-sibling::*[not(contains(@class, "ad"))]'),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/takprosto.cc.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/takprosto.cc.php
new file mode 100644
index 00000000..624ef907
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/takprosto.cc.php
@@ -0,0 +1,21 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://takprosto.cc/kokteyl-dlya-pohudeniya-v-domashnih-usloviyah/',
+ 'body' => array(
+ '//div[contains(@class, "entry-contentt")]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//form',
+ '//style',
+ '//*[@class="views_post"]',
+ '//*[contains(@class, "mailchimp-box")]',
+ '//*[contains(@class, "essb_links")]',
+ '//*[contains(@rel, "nofollow")]',
+ '//*[contains(@class, "ads")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/techcrunch.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/techcrunch.com.php
new file mode 100644
index 00000000..d6e620f6
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/techcrunch.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://techcrunch.com/2013/08/31/indias-visa-maze/',
+ 'body' => array(
+ '//div[contains(@class, "media-container")]',
+ '//div[@class="body-copy"]',
+ ),
+ 'strip' => array(
+ '//*[contains(@class, "module-crunchbase")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/the-ebook-reader.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/the-ebook-reader.com.php
new file mode 100644
index 00000000..3b01eb96
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/the-ebook-reader.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://blog.the-ebook-reader.com/2015/09/25/kobo-glo-hd-and-kobo-touch-2-0-covers-and-cases-roundup/',
+ 'body' => array(
+ '//div[@class="entry"]',
+ ),
+ 'strip' => array(
+ '//div[@id="share"]',
+ '//div[contains(@class,"ItemCenter")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theatlantic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theatlantic.com.php
new file mode 100644
index 00000000..bfad4ab2
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theatlantic.com.php
@@ -0,0 +1,23 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.theatlantic.com/politics/archive/2015/09/what-does-it-mean-to-lament-the-poor-inside-panem/407317/',
+ 'body' => array(
+ '//picture[@class="img"]',
+ '//figure/figcaption/span',
+ '//div/p[@itemprop="description"]',
+ '//div[@class="article-body"]',
+ '//ul[@class="photos"]',
+ ),
+ 'strip' => array(
+ '//aside[@class="callout"]',
+ '//span[@class="credit"]',
+ '//figcaption[@class="credit"]',
+ '//aside[contains(@class,"partner-box")]',
+ '//div[contains(@class,"ad")]',
+ '//a[contains(@class,"social-icon")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theawkwardyeti.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theawkwardyeti.com.php
new file mode 100644
index 00000000..fd4f3d50
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theawkwardyeti.com.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%/comic/.*%' => array(
+ 'test_url' => 'http://theawkwardyeti.com/comic/things-to-do/',
+ 'body' => array(
+ '//div[@id="comic"]'
+ ),
+ 'strip' => array()
+ )
+ )
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thecodinglove.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thecodinglove.com.php
new file mode 100644
index 00000000..c6ec5bf5
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thecodinglove.com.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://thecodinglove.com/post/116897934767',
+ 'body' => array('//div[@class="bodytype"]'),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thedoghousediaries.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thedoghousediaries.com.php
new file mode 100644
index 00000000..d2f840d7
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thedoghousediaries.com.php
@@ -0,0 +1,18 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'body' => array(
+ '//div[@class="comicpane"]/a/img',
+ '//div[@class="entry"]',
+ ),
+ 'strip' => array(),
+ 'test_url' => 'http://thedoghousediaries.com/6023',
+ ),
+ ),
+ 'filter' => array(
+ '%.*%' => array(
+ '%title="(.+)" */>%' => '/><br/>$1',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thegamercat.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thegamercat.com.php
new file mode 100644
index 00000000..f9b4637b
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thegamercat.com.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.thegamercat.com/comic/just-no/',
+ 'body' => array('//div[@id="comic"] | //div[@class="post-content"]/div[@class="entry"]/p'),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thehindu.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thehindu.com.php
new file mode 100644
index 00000000..1e6735ba
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thehindu.com.php
@@ -0,0 +1,19 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.thehindu.com/sci-tech/science/why-is-the-shape-of-cells-in-a-honeycomb-always-hexagonal/article7692306.ece?utm_source=RSS_Feed&utm_medium=RSS&utm_campaign=RSS_Syndication',
+ 'body' => array(
+ '//div/img[@class="main-image"]',
+ '//div[@class="photo-caption"]',
+ '//div[@class="articleLead"]',
+ '//p',
+ '//span[@class="upper"]',
+ ),
+ 'strip' => array(
+ '//div[@id="articleKeywords"]',
+ '//div[@class="photo-source"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thelocal.se.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thelocal.se.php
new file mode 100644
index 00000000..c3ec250c
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thelocal.se.php
@@ -0,0 +1,17 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'www.thelocal.se/20161219/this-swede-can-memorize-hundreds-of-numbers-in-only-five-minutes',
+ 'body' => array(
+ '//div[@id="article-photo"]',
+ '//div[@id="article-description"]',
+ '//div[@id="article-body"]',
+ ),
+ 'strip' => array(
+ '//div[@id="article-info-middle"]',
+ )
+ )
+ )
+);
+
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themerepublic.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themerepublic.net.php
new file mode 100644
index 00000000..bc47b278
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themerepublic.net.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.themerepublic.net/2015/04/david-lopez-pitoko.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+blogspot%2FDngUJ+%28Theme+Republic%29&utm_content=FeedBurner',
+ 'body' => array('//*[@class="post-body"]'),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themoscowtimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themoscowtimes.com.php
new file mode 100644
index 00000000..0f5bf75e
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themoscowtimes.com.php
@@ -0,0 +1,18 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.themoscowtimes.com/business/article/535500.html',
+ 'body' => array(
+ '//div[@class="article_main_img"]',
+ '//div[@class="article_text"]',
+ ),
+ 'strip' => array(
+ '//div[@class="articlebottom"]',
+ '//p/b',
+ '//p/a[contains(@href, "/article.php?id=")]',
+ '//div[@class="disqus_wrap"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thenewslens.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thenewslens.com.php
new file mode 100644
index 00000000..75381707
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thenewslens.com.php
@@ -0,0 +1,21 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://international.thenewslens.com/post/255032/',
+ 'body' => array(
+ '//div[@class="article-section"]',
+ ),
+ 'strip' => array(
+ '//div[contains(@class,"ad-")]',
+ '//div[@class="article-title-box"]',
+ '//div[@class="function-box"]',
+ '//p/span',
+ '//aside',
+ '//footer',
+ '//div[@class="article-infoBot-box"]',
+ '//div[contains(@class,"standard-container")]'
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theodd1sout.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theodd1sout.com.php
new file mode 100644
index 00000000..d06ed124
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theodd1sout.com.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%-150x150%' => '',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theonion.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theonion.com.php
new file mode 100644
index 00000000..acbfd36b
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theonion.com.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.theonion.com/article/wild-eyed-jim-harbaugh-informs-players-they-must-k-51397?utm_medium=RSS&utm_campaign=feeds',
+ 'body' => array(
+ '//div[@class="content-masthead"]/figure/div/noscript/img',
+ '//div[@class="content-text"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thestandard.com.hk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thestandard.com.hk.php
new file mode 100644
index 00000000..1163b345
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thestandard.com.hk.php
@@ -0,0 +1,22 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.thestandard.com.hk/breaking_news_detail.asp?id=67156',
+ 'body' => array(
+ '//table/tr/td/span[@class="bodyCopy"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//br',
+ '//map[@name="gif_bar"]',
+ '//img[contains(@usemap,"gif_bar")]',
+ '//a',
+ '//span[@class="bodyHeadline"]',
+ '//i',
+ '//b',
+ '//table',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/threepanelsoul.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/threepanelsoul.com.php
new file mode 100644
index 00000000..4af6196e
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/threepanelsoul.com.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'body' => array(
+ '//img[@id="cc-comic"]',
+ ),
+ 'test_url' => 'http://www.threepanelsoul.com/comic/uncloaking',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/timesofindia.indiatimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/timesofindia.indiatimes.com.php
new file mode 100644
index 00000000..19246806
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/timesofindia.indiatimes.com.php
@@ -0,0 +1,14 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://timesofindia.indiatimes.com/city/mangaluru/Adani-UPCL-to-release-CSR-grant-of-Rs-3-74-crore-to-YellurGram-Panchayat/articleshow/50512116.cms',
+ 'body' => array(
+ '//div[@class="article_content clearfix"]',
+ '//section[@class="highlight clearfix"]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/travel-dealz.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/travel-dealz.de.php
new file mode 100644
index 00000000..4ee4fcdc
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/travel-dealz.de.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%^/blog.*%' => array(
+ 'test_url' => 'http://travel-dealz.de/blog/venere-gutschein/',
+ 'body' => array('//div[@class="post-entry"]'),
+ 'strip' => array(
+ '//*[@id="jp-relatedposts"]',
+ '//*[@class="post-meta"]',
+ '//*[@class="post-data"]',
+ '//*[@id="author-meta"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treehugger.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treehugger.com.php
new file mode 100644
index 00000000..55eb7e01
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treehugger.com.php
@@ -0,0 +1,14 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.treehugger.com/uncategorized/top-ten-posts-week-bunnies-2.html',
+ 'body' => array(
+ '//div[contains(@class, "promo-image")]',
+ '//div[contains(@id, "entry-body")]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treelobsters.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treelobsters.com.php
new file mode 100644
index 00000000..3214c62a
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treelobsters.com.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%title="(.+)" */>%' => '/><br/>$1',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twogag.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twogag.com.php
new file mode 100644
index 00000000..79f4f62e
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twogag.com.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%http://www.twogag.com/comics-rss/([^.]+)\\.jpg%' => 'http://www.twogag.com/comics/$1.jpg',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twokinds.keenspot.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twokinds.keenspot.com.php
new file mode 100644
index 00000000..3428fcb4
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twokinds.keenspot.com.php
@@ -0,0 +1,10 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://twokinds.keenspot.com/archive.php?p=0',
+ 'body' => array('//*[@class="comic"]/div/a/img | //*[@class="comic"]/div/img | //*[@id="cg_img"]/img | //*[@id="cg_img"]/a/img'),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/undeadly.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/undeadly.org.php
new file mode 100644
index 00000000..8b15900a
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/undeadly.org.php
@@ -0,0 +1,14 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://undeadly.org/cgi?action=article&sid=20141101181155',
+ 'body' => array(
+ '/html/body/table[3]/tbody/tr/td[1]/table[2]/tr/td[1]',
+ ),
+ 'strip' => array(
+ '//font',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/upi.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/upi.com.php
new file mode 100644
index 00000000..ec8d1a1a
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/upi.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.upi.com/Top_News/US/2015/09/26/Tech-giants-Hollywood-stars-among-guests-at-state-dinner-for-Chinas-Xi-Jinping/4541443281006/',
+ 'body' => array(
+ '//div[@class="img"]',
+ '//div/article[@itemprop="articleBody"]',
+ ),
+ 'strip' => array(
+ '//div[@align="center"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/usatoday.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/usatoday.com.php
new file mode 100644
index 00000000..edd6aa44
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/usatoday.com.php
@@ -0,0 +1,27 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.usatoday.com/story/life/music/2017/02/13/things-you-should-know-happened-grammy-awards-2017/97833734/',
+ 'body' => array(
+ '//div[@itemprop="articleBody"]',
+ ),
+ 'strip' => array(
+ '//script',
+ '//h1',
+ '//iframe',
+ '//span[@class="mycapture-small-btn mycapture-btn-with-text mycapture-expandable-photo-btn-small js-mycapture-btn-small"]',
+ '//div[@class="close-wrap"]',
+ '//div[contains(@class,"ui-video-wrapper")]',
+ '//div[contains(@class,"media-mob")]',
+ '//div[contains(@class,"left-mob")]',
+ '//div[contains(@class,"nerdbox")]',
+ '//div[contains(@class,"oembed-asset")]',
+ '//*[contains(@class,"share")]',
+ '//div[contains(@class,"gallery-asset")]',
+ '//div[contains(@class,"oembed-asset")]',
+ '//div[@class="article-print-url"]'
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/version2.dk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/version2.dk.php
new file mode 100644
index 00000000..a6d49f2e
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/version2.dk.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.version2.dk/artikel/surface-pro-2-fungerer-bedre-til-arbejde-end-fornoejelse-55195',
+ 'body' => array(
+ '//section[contains(@class, "teaser")]',
+ '//section[contains(@class, "body")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vgcats.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vgcats.com.php
new file mode 100644
index 00000000..b2830a37
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vgcats.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%/comics.*%' => array(
+ 'test_url' => 'http://www.vgcats.com/comics/?strip_id=358',
+ 'body' => array('//*[@align="center"]/img'),
+ 'strip' => array(),
+ ),
+ '%/super.*%' => array(
+ 'test_url' => 'http://www.vgcats.com/super/?strip_id=84',
+ 'body' => array('//*[@align="center"]/p/img'),
+ 'strip' => array(),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vuxml.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vuxml.org.php
new file mode 100644
index 00000000..b9bef7a4
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vuxml.org.php
@@ -0,0 +1,17 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.vuxml.org/freebsd/a5f160fa-deee-11e4-99f8-080027ef73ec.html',
+ 'body' => array(
+ '//body',
+ ),
+ 'strip' => array(
+ '//h1',
+ '//div[@class="blurb"]',
+ '//hr',
+ '//p[@class="copyright"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/wausaudailyherald.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/wausaudailyherald.com.php
new file mode 100644
index 00000000..58aceeaf
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/wausaudailyherald.com.php
@@ -0,0 +1,27 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.wausaudailyherald.com/story/news/2017/04/01/hundreds-gather-remember-attorney-killed-shooting-spree/99826062/?from=global&sessionKey=&autologin=',
+ 'body' => array(
+ '//div[@itemprop="articleBody"]',
+ ),
+ 'strip' => array(
+ '//h1',
+ '//iframe',
+ '//span[@class="mycapture-small-btn mycapture-btn-with-text mycapture-expandable-photo-btn-small js-mycapture-btn-small"]',
+ '//div[@class="close-wrap"]',
+ '//div[contains(@class,"ui-video-wrapper")]',
+ '//div[contains(@class,"media-mob")]',
+ '//div[contains(@class,"left-mob")]',
+ '//div[contains(@class,"nerdbox")]',
+ '//p/span',
+ '//div[contains(@class,"oembed-asset")]',
+ '//*[contains(@class,"share")]',
+ '//div[contains(@class,"gallery-asset")]',
+ '//div[contains(@class,"oembed-asset")]',
+ '//div[@class="article-print-url"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bbc.co.uk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bbc.co.uk.php
new file mode 100644
index 00000000..98fc368a
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bbc.co.uk.php
@@ -0,0 +1,33 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.bbc.co.uk/news/world-middle-east-23911833',
+ 'body' => array(
+ '//div[@class="story-body__inner"] | //div[@class="article"]',
+ '//div[@class="indPost"]',
+ ),
+ 'strip' => array(
+ '//form',
+ '//div[@id="headline"]',
+ '//*[@class="warning"]',
+ '//span[@class="off-screen"]',
+ '//span[@class="story-image-copyright"]',
+ '//ul[@class="story-body__unordered-list"]',
+ '//div[@class="ad_wrapper"]',
+ '//div[@id="article-sidebar"]',
+ '//div[@class="data-table-outer"]',
+ '//*[@class="story-date"]',
+ '//*[@class="story-header"]',
+ '//figure[contains(@class,"has-caption")]',
+ '//*[@class="story-related"]',
+ '//*[contains(@class, "byline")]',
+ '//p[contains(@class, "media-message")]',
+ '//*[contains(@class, "story-feature")]',
+ '//*[@id="video-carousel-container"]',
+ '//*[@id="also-related-links"]',
+ '//*[contains(@class, "share") or contains(@class, "hidden") or contains(@class, "hyper")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bdgest.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bdgest.com.php
new file mode 100644
index 00000000..41ef68d4
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bdgest.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.bdgest.com/chronique-6027-BD-Adrastee-Tome-2.html',
+ 'body' => array(
+ '//*[contains(@class, "chronique")]',
+ ),
+ 'strip' => array(
+ '//*[contains(@class, "post-review")]',
+ '//*[contains(@class, "footer-review")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bgr.in.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bgr.in.php
new file mode 100644
index 00000000..63ca069e
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bgr.in.php
@@ -0,0 +1,23 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.bgr.in/news/xiaomi-redmi-3-with-13-megapixel-camera-snapdragon-616-launched-price-specifications-and-features/',
+ 'body' => array(
+ '//div[@class="article-content"]',
+ ),
+ 'strip' => array(
+ '//*[@class="article-meta"]',
+ '//*[@class="contentAdsense300"]',
+ '//*[@class="iwpl-social-hide"]',
+ '//iframe[@class="iframeads"]',
+ '//*[@class="disqus_thread"]',
+ '//*[@class="outb-mobile OUTBRAIN"]',
+ '//*[@class="wdt_smart_alerts"]',
+ '//*[@class="footnote"]',
+ '//*[@id="gadget-widget"]',
+ '//header[@class="article-title entry-header"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.businessweek.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.businessweek.com.php
new file mode 100644
index 00000000..0acc44ec
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.businessweek.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.businessweek.com/articles/2013-09-18/elon-musks-hyperloop-will-work-says-some-very-smart-software',
+ 'body' => array(
+ '//div[@id="lead_graphic"]',
+ '//div[@id="article_body"]',
+ ),
+ 'strip' => array(
+ '//*[contains(@class, "related_item")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.cnn.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.cnn.com.php
new file mode 100644
index 00000000..31d03ed9
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.cnn.com.php
@@ -0,0 +1,24 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.cnn.com/2013/08/31/world/meast/syria-civil-war/index.html?hpt=hp_t1',
+ 'body' => array(
+ '//div[@class="cnn_strycntntlft"]',
+ ),
+ 'strip' => array(
+ '//div[@class="cnn_stryshrwdgtbtm"]',
+ '//div[@class="cnn_strybtmcntnt"]',
+ '//div[@class="cnn_strylftcntnt"]',
+ '//div[contains(@class, "cnnGalleryContainer")]',
+ '//div[contains(@class, "cnn_strylftcexpbx")]',
+ '//div[contains(@class, "articleGalleryNavContainer")]',
+ '//div[contains(@class, "cnnArticleGalleryCaptionControl")]',
+ '//div[contains(@class, "cnnArticleGalleryNavPrevNextDisabled")]',
+ '//div[contains(@class, "cnnArticleGalleryNavPrevNext")]',
+ '//div[contains(@class, "cnn_html_media_title_new")]',
+ '//div[contains(@id, "disqus")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.developpez.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.developpez.com.php
new file mode 100644
index 00000000..1535e437
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.developpez.com.php
@@ -0,0 +1,21 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.developpez.com/actu/81757/Mozilla-annonce-la-disponibilite-de-Firefox-36-qui-passe-au-HTTP-2-et-permet-la-synchronisation-de-son-ecran-d-accueil/',
+ 'body' => array(
+ '//*[@itemprop="articleBody"]',
+ ),
+ 'strip' => array(
+ '//form',
+ '//div[@class="content"]/img',
+ '//a[last()]/following-sibling::*',
+ '//*[contains(@class,"actuTitle")]',
+ '//*[contains(@class,"date")]',
+ '//*[contains(@class,"inlineimg")]',
+ '//*[@id="signaler"]',
+ '//*[@id="signalerFrame"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.egscomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.egscomics.com.php
new file mode 100644
index 00000000..263f0755
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.egscomics.com.php
@@ -0,0 +1,12 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.egscomics.com/index.php?id=1690',
+ 'title' => '/html/head/title',
+ 'body' => array(
+ '//img[@id="comic"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.fakingnews.firstpost.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.fakingnews.firstpost.com.php
new file mode 100644
index 00000000..c948c77b
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.fakingnews.firstpost.com.php
@@ -0,0 +1,17 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.fakingnews.firstpost.com/2016/01/engineering-student-creates-record-in-a-decade-becomes-the-first-to-completely-exhaust-ball-pen-refill/',
+ 'body' => array(
+ '//div[@class="entry"]',
+ ),
+ 'strip' => array(
+ '//*[@class="socialshare_bar"]',
+ '//*[@class="authorbox"]',
+ '//*[@class="cf5_rps"]',
+ '//*[@class="60563 fb-comments fb-social-plugin"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.forbes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.forbes.com.php
new file mode 100644
index 00000000..fd16ed57
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.forbes.com.php
@@ -0,0 +1,20 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.forbes.com/sites/andygreenberg/2013/09/05/follow-the-bitcoins-how-we-got-busted-buying-drugs-on-silk-roads-black-market/',
+ 'body' => array(
+ '//div[@id="leftRail"]/div[contains(@class, body)]',
+ ),
+ 'strip' => array(
+ '//aside',
+ '//div[contains(@class, "entity_block")]',
+ '//div[contains(@class, "vestpocket") and not contains(@class, "body")]',
+ '//div[contains(@style, "display")]',
+ '//div[contains(@id, "comment")]',
+ '//div[contains(@class, "widget")]',
+ '//div[contains(@class, "pagination")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.franceculture.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.franceculture.fr.php
new file mode 100644
index 00000000..f7ec0d8d
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.franceculture.fr.php
@@ -0,0 +1,14 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.franceculture.fr/emission-culture-eco-la-finance-aime-toujours-la-france-2016-01-08',
+ 'body' => array(
+ '//div[@class="text-zone"]',
+ ),
+ 'strip' => array(
+ '//ul[@class="tags"]',
+ ),
+ )
+ )
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.futura-sciences.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.futura-sciences.com.php
new file mode 100644
index 00000000..ea94a0fb
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.futura-sciences.com.php
@@ -0,0 +1,19 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.futura-sciences.com/magazines/espace/infos/actu/d/astronautique-curiosity-franchi-succes-dune-dingo-gap-52289/#xtor=RSS-8',
+ 'body' => array(
+ '//div[contains(@class, "content fiche-")]',
+ ),
+ 'strip' => array(
+ '//h1',
+ '//*[contains(@class, "content-date")]',
+ '//*[contains(@class, "diaporama")]',
+ '//*[contains(@class, "slider")]',
+ '//*[contains(@class, "cartouche")]',
+ '//*[contains(@class, "noprint")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.geekculture.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.geekculture.com.php
new file mode 100644
index 00000000..3d0b6c75
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.geekculture.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.geekculture.com/joyoftech/joyarchives/2180.html',
+ 'body' => array(
+ '//p[contains(@class,"Maintext")][2]/a/img[contains(@src,"joyimages")]',
+ ),
+ 'strip' => array(),
+ ),
+ ),
+);
+
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.howtogeek.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.howtogeek.com.php
new file mode 100644
index 00000000..6879e767
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.howtogeek.com.php
@@ -0,0 +1,14 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.howtogeek.com/235283/what-is-a-wireless-hard-drive-and-should-i-get-one/',
+ 'body' => array(
+ '//div[@class="thecontent"]',
+ ),
+ 'strip' => array(
+ '//*[@class="relatedside"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lepoint.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lepoint.fr.php
new file mode 100644
index 00000000..dcb7e484
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lepoint.fr.php
@@ -0,0 +1,18 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.lepoint.fr/c-est-arrive-aujourd-hui/19-septembre-1783-pour-la-premiere-fois-un-mouton-un-canard-et-un-coq-s-envoient-en-l-air-devant-louis-xvi-18-09-2012-1507704_494.php',
+ 'body' => array(
+ '//article',
+ ),
+ 'strip' => array(
+ '//*[contains(@class, "info_article")]',
+ '//*[contains(@class, "fildariane_titre")]',
+ '//*[contains(@class, "entete2_article")]',
+ '//*[contains(@class, "signature_article")]',
+ '//*[contains(@id, "share")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lesnumeriques.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lesnumeriques.com.php
new file mode 100644
index 00000000..0137e209
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lesnumeriques.com.php
@@ -0,0 +1,25 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.lesnumeriques.com/blender/kitchenaid-diamond-5ksb1585-p27473/test.html',
+ 'body' => array(
+ '//*[@id="product-content"]',
+ '//*[@id="news-content"]',
+ '//*[@id="article-content"]',
+ ),
+ 'strip' => array(
+ '//form',
+ '//div[contains(@class, "price-v4"])',
+ '//div[contains(@class, "authors-and-date")]',
+ '//div[contains(@class, "mini-product")]',
+ '//div[@id="articles-related-authors"]',
+ '//div[@id="tags-socials"]',
+ '//div[@id="user-reviews"]',
+ '//div[@id="product-reviews"]',
+ '//div[@id="publication-breadcrumbs-and-date"]',
+ '//div[@id="publication-breadcrumbs-and-date"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.mac4ever.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.mac4ever.com.php
new file mode 100644
index 00000000..60bc1bd4
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.mac4ever.com.php
@@ -0,0 +1,13 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.mac4ever.com/actu/87392_video-quand-steve-jobs-et-bill-gates-jouaient-au-bachelor-avec-le-mac',
+ 'body' => array(
+ '//div[contains(@class, "news-news-content")]',
+ ),
+ 'strip' => array(
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.makeuseof.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.makeuseof.com.php
new file mode 100644
index 00000000..a274564a
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.makeuseof.com.php
@@ -0,0 +1,18 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.makeuseof.com/tag/having-problems-with-audio-in-windows-10-heres-a-likely-fix/',
+ 'body' => array(
+ '//div[@class="entry"]',
+ ),
+ 'strip' => array(
+ '//*[@class="new_sharebar"]',
+ '//*[@class="author"]',
+ '//*[@class="wdt_grouvi"]',
+ '//*[@class="wdt_smart_alerts"]',
+ '//*[@class="modal fade grouvi"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.monsieur-le-chien.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.monsieur-le-chien.fr.php
new file mode 100644
index 00000000..5f5e987b
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.monsieur-le-chien.fr.php
@@ -0,0 +1,11 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.monsieur-le-chien.fr/index.php?planche=672',
+ 'body' => array(
+ '//img[starts-with(@src, "i/planches/")]',
+ ),
+ )
+ )
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.npr.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.npr.org.php
new file mode 100644
index 00000000..ecc0213a
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.npr.org.php
@@ -0,0 +1,28 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.npr.org/blogs/thesalt/2013/09/17/223345977/auto-brewery-syndrome-apparently-you-can-make-beer-in-your-gut',
+ 'body' => array(
+ '//article[contains(@class,"story")]',
+ ),
+ 'strip' => array(
+ '//div[@class="story-tools"]',
+ '//h3[@class="slug"]',
+ '//div[@class="storytitle"]',
+ '//div[@id="story-meta"]',
+ '//a[@id="mainContent"]',
+ '//div[@class="credit-caption"]',
+ '//div[@class="enlarge_html"]',
+ '//button',
+ '//div[contains(@id,"pullquote")]',
+ '//div[contains(@class,"internallink")]',
+ '//div[contains(@class,"video")]',
+ '//div[@class="simplenodate"]',
+ '//div[contains(@class,"share-")]',
+ '//div[@class="tags"]',
+ '//aside'
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.numerama.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.numerama.com.php
new file mode 100644
index 00000000..fe4971c5
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.numerama.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.numerama.com/sciences/125959-recherches-ladn-recompensees-nobel-de-chimie.html',
+ 'body' => array(
+ '//article',
+ ),
+ 'strip' => array(
+ '//footer',
+ '//section[@class="related-article"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.oneindia.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.oneindia.com.php
new file mode 100644
index 00000000..320c2147
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.oneindia.com.php
@@ -0,0 +1,14 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.oneindia.com/india/b-luru-govt-likely-remove-word-eunuch-from-sec-36-a-karnataka-police-act-1981173.html',
+ 'body' => array(
+ '//div[@class="ecom-ad-content"]',
+ ),
+ 'strip' => array(
+ '//*[@id="view_cmtns"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.pseudo-sciences.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.pseudo-sciences.org.php
new file mode 100644
index 00000000..9e467edf
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.pseudo-sciences.org.php
@@ -0,0 +1,16 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.pseudo-sciences.org/spip.php?article2275',
+ 'body' => array(
+ '//div[@id="art_main"]',
+ ),
+ 'strip' => array(
+ '//div[@id="art_print"]',
+ '//div[@id="art_chapo"]',
+ '//img[@class="puce"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.sciencemag.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.sciencemag.org.php
new file mode 100644
index 00000000..ae7a93ac
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.sciencemag.org.php
@@ -0,0 +1,16 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.sciencemag.org/news/2016/01/could-bright-foamy-wak$',
+ 'body' => array(
+ '//div[@class="row--hero"]',
+ '//article[contains(@class,"primary")]',
+ ),
+ 'strip' => array(
+ '//header[@class="article__header"]',
+ '//footer[@class="article__foot"]',
+ ),
+ ),
+ )
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.slate.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.slate.fr.php
new file mode 100644
index 00000000..8c8dc893
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.slate.fr.php
@@ -0,0 +1,19 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.slate.fr/monde/77034/allemagne-2013-couacs-campagne',
+ 'body' => array(
+ '//div[@class="article_content"]',
+ ),
+ 'strip' => array(
+ '//*[@id="slate_associated_bn"]',
+ '//*[@id="ligatus-article"]',
+ '//*[@id="article_sidebar"]',
+ '//div[contains(@id, "reseaux")]',
+ '//*[contains(@class, "smart") or contains(@class, "article_tags") or contains(@class, "article_reactions")]',
+ '//*[contains(@class, "OUTBRAIN") or contains(@class, "related_item") or contains(@class, "share")]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.universfreebox.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.universfreebox.com.php
new file mode 100644
index 00000000..0747d0fb
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.universfreebox.com.php
@@ -0,0 +1,15 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://www.universfreebox.com/article/24305/4G-Bouygues-Telecom-lance-une-vente-flash-sur-son-forfait-Sensation-3Go',
+ 'body' => array(
+ '//div[@id="corps_corps"]',
+ ),
+ 'strip' => array(
+ '//*[@id="formulaire"]',
+ '//*[@id="commentaire"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.zeit.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.zeit.de.php
new file mode 100644
index 00000000..316c2656
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.zeit.de.php
@@ -0,0 +1,41 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%^/zeit-magazin.*%' => array(
+ 'test_url' => 'http://www.zeit.de/zeit-magazin/2015/15/pegida-kathrin-oertel-lutz-bachmann',
+ 'body' => array(
+ '//article[@class="article"]',
+ ),
+ 'strip' => array(
+ '//header/div/h1',
+ '//header/div/div[@class="article__head__subtitle"]',
+ '//header/div/div[@class="article__column__author"]',
+ '//header/div/div[@class="article__column__author"]',
+ '//header/div/span[@class="article__head__meta-wrap"]',
+ '//form',
+ '//style',
+ '//div[contains(@class, "ad-tile")]',
+ '//div[@class="iqd-mobile-adplace"]',
+ '//div[@id="iq-artikelanker"]',
+ '//div[@id="js-social-services"]',
+ '//section[@id="js-comments"]',
+ '//aside',
+ ),
+ ),
+ '%.*%' => array(
+ 'test_url' => 'http://www.zeit.de/politik/ausland/2015-04/thessaloniki-krise-griechenland-yannis-boutaris/',
+ 'body' => array(
+ '//div[@class="article-body"]',
+ ),
+ 'strip' => array(
+ '//*[@class="articleheader"]',
+ '//*[@class="excerpt"]',
+ '//div[contains(@class, "ad")]',
+ '//div[@itemprop="video"]',
+ '//*[@class="articlemeta"]',
+ '//*[@class="articlemeta-clear"]',
+ '//*[@class="zol_inarticletools"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/xkcd.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/xkcd.com.php
new file mode 100644
index 00000000..84957268
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/xkcd.com.php
@@ -0,0 +1,8 @@
+<?php
+return array(
+ 'filter' => array(
+ '%.*%' => array(
+ '%alt="(.+)" */>%' => '/><br/>$1',
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ymatuhin.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ymatuhin.ru.php
new file mode 100644
index 00000000..9fd83f18
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ymatuhin.ru.php
@@ -0,0 +1,21 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'https://ymatuhin.ru/tools/git-default-editor/',
+ 'body' => array(
+ '//section',
+ ),
+ 'strip' => array(
+ "//script",
+ "//style",
+ "//h1",
+ "//time",
+ "//aside",
+ "/html/body/section/ul",
+ "//amp-iframe",
+ "/html/body/section/h4"
+ ),
+ )
+ )
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/zdnet.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/zdnet.com.php
new file mode 100644
index 00000000..79b35ddb
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/zdnet.com.php
@@ -0,0 +1,23 @@
+<?php
+return array(
+ 'grabber' => array(
+ '%.*%' => array(
+ 'test_url' => 'http://zdnet.com.feedsportal.com/c/35462/f/675637/s/4a33c93e/sc/11/l/0L0Szdnet0N0Carticle0Cchina0Eus0Eagree0Eon0Ecybercrime0Ecooperation0Eamid0Econtinued0Etension0C0Tftag0FRSSbaffb68/story01.htm',
+ 'body' => array(
+ '//p[@class="summary"]',
+ '//div[contains(@class,"storyBody")]',
+ ),
+ 'strip' => array(
+ '//*[contains(@class,"ad-")]',
+ '//p/span',
+ '//script',
+ '//p[@class="summary"]',
+ '//div[contains(@class,"relatedContent")]',
+ '//div[contains(@class,"loader")]',
+ '//p[@class="photoDetails"]',
+ '//div[@class="thumbnailSlider"]',
+ '//div[@class="shortcodeGalleryWrapper"]',
+ ),
+ ),
+ ),
+);
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php
new file mode 100644
index 00000000..6c53a289
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php
@@ -0,0 +1,283 @@
+<?php
+
+namespace PicoFeed\Scraper;
+
+use DomDocument;
+use DOMXPath;
+use PicoFeed\Logging\Logger;
+use PicoFeed\Parser\XmlParser;
+
+/**
+ * Candidate Parser.
+ *
+ * @author Frederic Guillot
+ */
+class CandidateParser implements ParserInterface
+{
+ private $dom;
+ private $xpath;
+
+ /**
+ * List of attributes to try to get the content, order is important, generic terms at the end.
+ *
+ * @var array
+ */
+ private $candidatesAttributes = array(
+ 'articleBody',
+ 'articlebody',
+ 'article-body',
+ 'articleContent',
+ 'articlecontent',
+ 'article-content',
+ 'articlePage',
+ 'post-content',
+ 'post_content',
+ 'entry-content',
+ 'entry-body',
+ 'main-content',
+ 'story_content',
+ 'storycontent',
+ 'entryBox',
+ 'entrytext',
+ 'comic',
+ 'post',
+ 'article',
+ 'content',
+ 'main',
+ );
+
+ /**
+ * List of attributes to strip.
+ *
+ * @var array
+ */
+ private $stripAttributes = array(
+ 'comment',
+ 'share',
+ 'links',
+ 'toolbar',
+ 'fb',
+ 'footer',
+ 'credit',
+ 'bottom',
+ 'nav',
+ 'header',
+ 'social',
+ 'tag',
+ 'metadata',
+ 'entry-utility',
+ 'related-posts',
+ 'tweet',
+ 'categories',
+ 'post_title',
+ 'by_line',
+ 'byline',
+ 'sponsors',
+ );
+
+ /**
+ * Tags to remove.
+ *
+ * @var array
+ */
+ private $stripTags = array(
+ 'nav',
+ 'header',
+ 'footer',
+ 'aside',
+ 'form',
+ );
+
+ /**
+ * Constructor.
+ *
+ * @param string $html
+ */
+ public function __construct($html)
+ {
+ $this->dom = XmlParser::getHtmlDocument('<?xml version="1.0" encoding="UTF-8">'.$html);
+ $this->xpath = new DOMXPath($this->dom);
+ }
+
+ /**
+ * Get the relevant content with the list of potential attributes.
+ *
+ * @return string
+ */
+ public function execute()
+ {
+ $content = $this->findContentWithCandidates();
+
+ if (strlen($content) < 200) {
+ $content = $this->findContentWithArticle();
+ }
+
+ if (strlen($content) < 50) {
+ $content = $this->findContentWithBody();
+ }
+
+ return $this->stripGarbage($content);
+ }
+
+ /**
+ * Find content based on the list of tag candidates.
+ *
+ * @return string
+ */
+ public function findContentWithCandidates()
+ {
+ foreach ($this->candidatesAttributes as $candidate) {
+ Logger::setMessage(get_called_class().': Try this candidate: "'.$candidate.'"');
+
+ $nodes = $this->xpath->query('//*[(contains(@class, "'.$candidate.'") or @id="'.$candidate.'") and not (contains(@class, "nav") or contains(@class, "page"))]');
+
+ if ($nodes !== false && $nodes->length > 0) {
+ Logger::setMessage(get_called_class().': Find candidate "'.$candidate.'"');
+
+ return $this->dom->saveXML($nodes->item(0));
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * Find <article/> tag.
+ *
+ * @return string
+ */
+ public function findContentWithArticle()
+ {
+ $nodes = $this->xpath->query('//article');
+
+ if ($nodes !== false && $nodes->length > 0) {
+ Logger::setMessage(get_called_class().': Find <article/> tag');
+
+ return $this->dom->saveXML($nodes->item(0));
+ }
+
+ return '';
+ }
+
+ /**
+ * Find <body/> tag.
+ *
+ * @return string
+ */
+ public function findContentWithBody()
+ {
+ $nodes = $this->xpath->query('//body');
+
+ if ($nodes !== false && $nodes->length > 0) {
+ Logger::setMessage(get_called_class().' Find <body/>');
+
+ return $this->dom->saveXML($nodes->item(0));
+ }
+
+ return '';
+ }
+
+ /**
+ * Strip useless tags.
+ *
+ * @param string $content
+ *
+ * @return string
+ */
+ public function stripGarbage($content)
+ {
+ $dom = XmlParser::getDomDocument($content);
+
+ if ($dom !== false) {
+ $xpath = new DOMXPath($dom);
+
+ $this->stripTags($xpath);
+ $this->stripAttributes($dom, $xpath);
+
+ $content = $dom->saveXML($dom->documentElement);
+ }
+
+ return $content;
+ }
+
+ /**
+ * Remove blacklisted tags.
+ *
+ * @param DOMXPath $xpath
+ */
+ public function stripTags(DOMXPath $xpath)
+ {
+ foreach ($this->stripTags as $tag) {
+ $nodes = $xpath->query('//'.$tag);
+
+ if ($nodes !== false && $nodes->length > 0) {
+ Logger::setMessage(get_called_class().': Strip tag: "'.$tag.'"');
+
+ foreach ($nodes as $node) {
+ $node->parentNode->removeChild($node);
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove blacklisted attributes.
+ *
+ * @param DomDocument $dom
+ * @param DOMXPath $xpath
+ */
+ public function stripAttributes(DomDocument $dom, DOMXPath $xpath)
+ {
+ foreach ($this->stripAttributes as $attribute) {
+ $nodes = $xpath->query('//*[contains(@class, "'.$attribute.'") or contains(@id, "'.$attribute.'")]');
+
+ if ($nodes !== false && $nodes->length > 0) {
+ Logger::setMessage(get_called_class().': Strip attribute: "'.$attribute.'"');
+
+ foreach ($nodes as $node) {
+ if ($this->shouldRemove($dom, $node)) {
+ $node->parentNode->removeChild($node);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Find link for next page of the article.
+ *
+ * @return string
+ */
+ public function findNextLink()
+ {
+ return null;
+ }
+
+ /**
+ * Return false if the node should not be removed.
+ *
+ * @param DomDocument $dom
+ * @param DomNode $node
+ *
+ * @return bool
+ */
+ public function shouldRemove(DomDocument $dom, $node)
+ {
+ $document_length = strlen($dom->textContent);
+ $node_length = strlen($node->textContent);
+
+ if ($document_length === 0) {
+ return true;
+ }
+
+ $ratio = $node_length * 100 / $document_length;
+
+ if ($ratio >= 90) {
+ Logger::setMessage(get_called_class().': Should not remove this node ('.$node->nodeName.') ratio: '.$ratio.'%');
+
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php
new file mode 100644
index 00000000..3ded4b1c
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace PicoFeed\Scraper;
+
+interface ParserInterface
+{
+ /**
+ * Execute the parser and return the contents.
+ *
+ * @return string
+ */
+ public function execute();
+
+ /**
+ * Find link for next page of the article.
+ *
+ * @return string
+ */
+ public function findNextLink();
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleLoader.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleLoader.php
new file mode 100644
index 00000000..6650682d
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleLoader.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace PicoFeed\Scraper;
+
+use PicoFeed\Base;
+use PicoFeed\Logging\Logger;
+
+/**
+ * RuleLoader class.
+ *
+ * @author Frederic Guillot
+ * @author Bernhard Posselt
+ */
+class RuleLoader extends Base
+{
+ /**
+ * Get the rules for an URL.
+ *
+ * @param string $url the URL that should be looked up
+ *
+ * @return array the array containing the rules
+ */
+ public function getRules($url)
+ {
+ $hostname = parse_url($url, PHP_URL_HOST);
+
+ if ($hostname !== false) {
+ $files = $this->getRulesFileList($hostname);
+
+ foreach ($this->getRulesFolders() as $folder) {
+ $rule = $this->loadRuleFile($folder, $files);
+
+ if (!empty($rule)) {
+ return $rule;
+ }
+ }
+ }
+
+ return array();
+ }
+
+ /**
+ * Get the list of possible rules file names for a given hostname.
+ *
+ * @param string $hostname Hostname
+ *
+ * @return array
+ */
+ public function getRulesFileList($hostname)
+ {
+ $files = array($hostname); // subdomain.domain.tld
+ $parts = explode('.', $hostname);
+ $len = count($parts);
+
+ if ($len > 2) {
+ $subdomain = array_shift($parts);
+ $files[] = implode('.', $parts); // domain.tld
+ $files[] = '.'.implode('.', $parts); // .domain.tld
+ $files[] = $subdomain; // subdomain
+ } elseif ($len === 2) {
+ $files[] = '.'.implode('.', $parts); // .domain.tld
+ $files[] = $parts[0]; // domain
+ }
+
+ return $files;
+ }
+
+ /**
+ * Load a rule file from the defined folder.
+ *
+ * @param string $folder Rule directory
+ * @param array $files List of possible file names
+ *
+ * @return array
+ */
+ public function loadRuleFile($folder, array $files)
+ {
+ foreach ($files as $file) {
+ $filename = $folder.'/'.$file.'.php';
+ if (file_exists($filename)) {
+ Logger::setMessage(get_called_class().' Load rule: '.$file);
+
+ return include $filename;
+ }
+ }
+
+ return array();
+ }
+
+ /**
+ * Get the list of folders that contains rules.
+ *
+ * @return array
+ */
+ public function getRulesFolders()
+ {
+ $folders = array();
+
+ if ($this->config !== null && $this->config->getGrabberRulesFolder() !== null) {
+ $folders[] = $this->config->getGrabberRulesFolder();
+ }
+
+ $folders[] = __DIR__ . '/../Rules';
+
+ return $folders;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php
new file mode 100644
index 00000000..9beb59c1
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace PicoFeed\Scraper;
+
+use DOMXPath;
+use PicoFeed\Parser\XmlParser;
+
+/**
+ * Rule Parser.
+ *
+ * @author Frederic Guillot
+ */
+class RuleParser implements ParserInterface
+{
+ private $dom;
+ private $xpath;
+ private $rules = array();
+
+ /**
+ * Constructor.
+ *
+ * @param string $html
+ * @param array $rules
+ */
+ public function __construct($html, array $rules)
+ {
+ $this->rules = $rules;
+ $this->dom = XmlParser::getHtmlDocument('<?xml version="1.0" encoding="UTF-8">'.$html);
+ $this->xpath = new DOMXPath($this->dom);
+ }
+
+ /**
+ * Get the relevant content with predefined rules.
+ *
+ * @return string
+ */
+ public function execute()
+ {
+ $this->stripTags();
+
+ return $this->findContent();
+ }
+
+ /**
+ * Remove HTML tags.
+ */
+ public function stripTags()
+ {
+ if (isset($this->rules['strip']) && is_array($this->rules['strip'])) {
+ foreach ($this->rules['strip'] as $pattern) {
+ $nodes = $this->xpath->query($pattern);
+
+ if ($nodes !== false && $nodes->length > 0) {
+ foreach ($nodes as $node) {
+ $node->parentNode->removeChild($node);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Fetch content based on Xpath rules.
+ */
+ public function findContent()
+ {
+ $content = '';
+ if (isset($this->rules['body']) && is_array($this->rules['body'])) {
+ foreach ($this->rules['body'] as $pattern) {
+ $nodes = $this->xpath->query($pattern);
+
+ if ($nodes !== false && $nodes->length > 0) {
+ foreach ($nodes as $node) {
+ $content .= $this->dom->saveXML($node);
+ }
+ }
+ }
+ }
+
+ return $content;
+ }
+
+ /**
+ * Fetch next link based on Xpath rules.
+ *
+ * @return string
+ */
+ public function findNextLink()
+ {
+ if (isset($this->rules['next_page']) && is_array($this->rules['next_page'])) {
+ foreach ($this->rules['next_page'] as $pattern) {
+ $nodes = $this->xpath->query($pattern);
+ if ($nodes !== false && $nodes->length > 0) {
+ foreach ($nodes as $node) {
+ return $node->getAttribute('href');
+ }
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php
new file mode 100644
index 00000000..e5b9817f
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php
@@ -0,0 +1,279 @@
+<?php
+
+namespace PicoFeed\Scraper;
+
+use PicoFeed\Base;
+use PicoFeed\Client\Client;
+use PicoFeed\Client\ClientException;
+use PicoFeed\Client\Url;
+use PicoFeed\Encoding\Encoding;
+use PicoFeed\Filter\Filter;
+use PicoFeed\Logging\Logger;
+use PicoFeed\Parser\XmlParser;
+
+/**
+ * Scraper class.
+ *
+ * @author Frederic Guillot
+ */
+class Scraper extends Base
+{
+ /**
+ * URL.
+ *
+ * @var string
+ */
+ private $url = '';
+
+ /**
+ * Relevant content.
+ *
+ * @var string
+ */
+ private $content = '';
+
+ /**
+ * HTML content.
+ *
+ * @var string
+ */
+ private $html = '';
+
+ /**
+ * HTML content encoding.
+ *
+ * @var string
+ */
+ private $encoding = '';
+
+ /**
+ * Flag to enable candidates parsing.
+ *
+ * @var bool
+ */
+ private $enableCandidateParser = true;
+
+ /**
+ * Disable candidates parsing.
+ *
+ * @return Scraper
+ */
+ public function disableCandidateParser()
+ {
+ $this->enableCandidateParser = false;
+ return $this;
+ }
+
+ /**
+ * Get encoding.
+ *
+ * @return string
+ */
+ public function getEncoding()
+ {
+ return $this->encoding;
+ }
+
+ /**
+ * Set encoding.
+ *
+ * @param string $encoding
+ *
+ * @return Scraper
+ */
+ public function setEncoding($encoding)
+ {
+ $this->encoding = $encoding;
+
+ return $this;
+ }
+
+ /**
+ * Get URL to download.
+ *
+ * @return string
+ */
+ public function getUrl()
+ {
+ return $this->url;
+ }
+
+ /**
+ * Set URL to download.
+ *
+ * @param string $url URL
+ *
+ * @return Scraper
+ */
+ public function setUrl($url)
+ {
+ $this->url = $url;
+
+ return $this;
+ }
+
+ /**
+ * Return true if the scraper found relevant content.
+ *
+ * @return bool
+ */
+ public function hasRelevantContent()
+ {
+ return !empty($this->content);
+ }
+
+ /**
+ * Get relevant content.
+ *
+ * @return string
+ */
+ public function getRelevantContent()
+ {
+ return $this->content;
+ }
+
+ /**
+ * Get raw content (unfiltered).
+ *
+ * @return string
+ */
+ public function getRawContent()
+ {
+ return $this->html;
+ }
+
+ /**
+ * Set raw content (unfiltered).
+ *
+ * @param string $html
+ *
+ * @return Scraper
+ */
+ public function setRawContent($html)
+ {
+ $this->html = $html;
+
+ return $this;
+ }
+
+ /**
+ * Get filtered relevant content.
+ *
+ * @return string
+ */
+ public function getFilteredContent()
+ {
+ $filter = Filter::html($this->content, $this->url);
+ $filter->setConfig($this->config);
+
+ return $filter->execute();
+ }
+
+ /**
+ * Download the HTML content.
+ *
+ * @return bool
+ */
+ public function download()
+ {
+ if (!empty($this->url)) {
+
+ // Clear everything
+ $this->html = '';
+ $this->content = '';
+ $this->encoding = '';
+
+ try {
+ $client = Client::getInstance();
+ $client->setConfig($this->config);
+ $client->setTimeout($this->config->getGrabberTimeout());
+ $client->setUserAgent($this->config->getGrabberUserAgent());
+ $client->execute($this->url);
+
+ $this->url = $client->getUrl();
+ $this->html = $client->getContent();
+ $this->encoding = $client->getEncoding();
+
+ return true;
+ } catch (ClientException $e) {
+ Logger::setMessage(get_called_class().': '.$e->getMessage());
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Execute the scraper.
+ */
+ public function execute($pageContent = '', $recursionDepth = 0)
+ {
+ $this->html = '';
+ $this->encoding = '';
+ $this->content = '';
+ $this->download();
+ $this->prepareHtml();
+
+ $parser = $this->getParser();
+
+ if ($parser !== null) {
+ $maxRecursions = $this->config->getMaxRecursions();
+ if(!isset($maxRecursions)){
+ $maxRecursions = 25;
+ }
+ $pageContent .= $parser->execute();
+ // check if there is a link to next page and recursively get content (max 25 pages)
+ if((($nextLink = $parser->findNextLink()) !== null) && $recursionDepth < $maxRecursions){
+ $nextLink = Url::resolve($nextLink,$this->url);
+ $this->setUrl($nextLink);
+ $this->execute($pageContent,$recursionDepth+1);
+ }
+ else{
+ $this->content = $pageContent;
+ }
+ Logger::setMessage(get_called_class().': Content length: '.strlen($this->content).' bytes');
+ }
+ }
+
+ /**
+ * Get the parser.
+ *
+ * @return ParserInterface
+ */
+ public function getParser()
+ {
+ $ruleLoader = new RuleLoader($this->config);
+ $rules = $ruleLoader->getRules($this->url);
+
+ if (!empty($rules['grabber'])) {
+ Logger::setMessage(get_called_class().': Parse content with rules');
+
+ foreach ($rules['grabber'] as $pattern => $rule) {
+ $url = new Url($this->url);
+ $sub_url = $url->getFullPath();
+
+ if (preg_match($pattern, $sub_url)) {
+ Logger::setMessage(get_called_class().': Matched url '.$sub_url);
+ return new RuleParser($this->html, $rule);
+ }
+ }
+ } elseif ($this->enableCandidateParser) {
+ Logger::setMessage(get_called_class().': Parse content with candidates');
+ }
+
+ return new CandidateParser($this->html);
+ }
+
+ /**
+ * Normalize encoding and strip head tag.
+ */
+ public function prepareHtml()
+ {
+ $html_encoding = XmlParser::getEncodingFromMetaTag($this->html);
+
+ $this->html = Encoding::convert($this->html, $html_encoding ?: $this->encoding);
+ $this->html = Filter::stripHeadTags($this->html);
+
+ Logger::setMessage(get_called_class().': HTTP Encoding "'.$this->encoding.'" ; HTML Encoding "'.$html_encoding.'"');
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php
new file mode 100644
index 00000000..12eccfd5
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php
@@ -0,0 +1,175 @@
+<?php
+
+namespace PicoFeed\Serialization;
+
+/**
+ * Class Subscription
+ *
+ * @package PicoFeed\Serialization
+ * @author Frederic Guillot
+ */
+class Subscription
+{
+ protected $title = '';
+ protected $feedUrl = '';
+ protected $siteUrl = '';
+ protected $category = '';
+ protected $description = '';
+ protected $type = '';
+
+ /**
+ * Create object instance
+ *
+ * @static
+ * @access public
+ * @return Subscription
+ */
+ public static function create()
+ {
+ return new static();
+ }
+
+ /**
+ * Set title
+ *
+ * @access public
+ * @param string $title
+ * @return Subscription
+ */
+ public function setTitle($title)
+ {
+ $this->title = $title;
+ return $this;
+ }
+
+ /**
+ * Get title
+ *
+ * @access public
+ * @return string
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * Set feed URL
+ *
+ * @access public
+ * @param string $feedUrl
+ * @return Subscription
+ */
+ public function setFeedUrl($feedUrl)
+ {
+ $this->feedUrl = $feedUrl;
+ return $this;
+ }
+
+ /**
+ * Get feed URL
+ *
+ * @access public
+ * @return string
+ */
+ public function getFeedUrl()
+ {
+ return $this->feedUrl;
+ }
+
+ /**
+ * Set site URL
+ *
+ * @access public
+ * @param string $siteUrl
+ * @return Subscription
+ */
+ public function setSiteUrl($siteUrl)
+ {
+ $this->siteUrl = $siteUrl;
+ return $this;
+ }
+
+ /**
+ * Get site URL
+ *
+ * @access public
+ * @return string
+ */
+ public function getSiteUrl()
+ {
+ return $this->siteUrl;
+ }
+
+ /**
+ * Set category
+ *
+ * @access public
+ * @param string $category
+ * @return Subscription
+ */
+ public function setCategory($category)
+ {
+ $this->category = $category;
+ return $this;
+ }
+
+ /**
+ * Get category
+ *
+ * @access public
+ * @return string
+ */
+ public function getCategory()
+ {
+ return $this->category;
+ }
+
+ /**
+ * Set description
+ *
+ * @access public
+ * @param string $description
+ * @return Subscription
+ */
+ public function setDescription($description)
+ {
+ $this->description = $description;
+ return $this;
+ }
+
+ /**
+ * Get description
+ *
+ * @access public
+ * @return string
+ */
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * Set type
+ *
+ * @access public
+ * @param string $type
+ * @return Subscription
+ */
+ public function setType($type)
+ {
+ $this->type = $type;
+ return $this;
+ }
+
+ /**
+ * Get type
+ *
+ * @access public
+ * @return string
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php
new file mode 100644
index 00000000..b173f89b
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace PicoFeed\Serialization;
+
+/**
+ * Class SubscriptionList
+ *
+ * @package PicoFeed\Serialization
+ * @author Frederic Guillot
+ */
+class SubscriptionList
+{
+ /**
+ * OPML entries
+ *
+ * @var Subscription[]
+ */
+ public $subscriptions = array();
+
+ /**
+ * Title
+ *
+ * @var string
+ */
+ protected $title = '';
+
+ /**
+ * Create object instance
+ *
+ * @static
+ * @access public
+ * @return SubscriptionList
+ */
+ public static function create()
+ {
+ return new static();
+ }
+
+ /**
+ * Set title
+ *
+ * @access public
+ * @param string $title
+ * @return SubscriptionList
+ */
+ public function setTitle($title)
+ {
+ $this->title = $title;
+ return $this;
+ }
+
+ /**
+ * Get title
+ *
+ * @access public
+ * @return string
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * Add subscription
+ *
+ * @access public
+ * @param Subscription $subscription
+ * @return SubscriptionList
+ */
+ public function addSubscription(Subscription $subscription)
+ {
+ $this->subscriptions[] = $subscription;
+ return $this;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php
new file mode 100644
index 00000000..838e4cb5
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php
@@ -0,0 +1,204 @@
+<?php
+
+namespace PicoFeed\Serialization;
+
+use DOMDocument;
+use DOMElement;
+
+/**
+ * Class SubscriptionListBuilder
+ *
+ * @package PicoFeed\Serialization
+ * @author Frederic Guillot
+ */
+class SubscriptionListBuilder
+{
+ /**
+ * @var SubscriptionList
+ */
+ protected $subscriptionList;
+
+ /**
+ * @var DOMDocument
+ */
+ protected $document;
+
+ /**
+ * Constructor.
+ *
+ * @access public
+ * @param SubscriptionList $subscriptionList
+ */
+ public function __construct(SubscriptionList $subscriptionList)
+ {
+ $this->subscriptionList = $subscriptionList;
+ }
+
+ /**
+ * Get object instance
+ *
+ * @static
+ * @access public
+ * @param SubscriptionList $subscriptionList
+ * @return SubscriptionListBuilder
+ */
+ public static function create(SubscriptionList $subscriptionList)
+ {
+ return new static($subscriptionList);
+ }
+
+ /**
+ * Build OPML feed
+ *
+ * @access public
+ * @param string $filename
+ * @return string
+ */
+ public function build($filename = '')
+ {
+ $this->document = new DomDocument('1.0', 'UTF-8');
+ $this->document->formatOutput = true;
+
+ $opmlElement = $this->document->createElement('opml');
+ $opmlElement->setAttribute('version', '1.0');
+
+ $headElement = $this->document->createElement('head');
+
+ if ($this->subscriptionList->getTitle() !== '') {
+ $titleElement = $this->document->createElement('title');
+ $titleElement->appendChild($this->document->createTextNode($this->subscriptionList->getTitle()));
+ $headElement->appendChild($titleElement);
+ }
+
+ $opmlElement->appendChild($headElement);
+ $opmlElement->appendChild($this->buildBody());
+ $this->document->appendChild($opmlElement);
+
+ if ($filename !== '') {
+ $this->document->save($filename);
+ return '';
+ }
+
+ return $this->document->saveXML();
+ }
+
+ /**
+ * Return true if the list has categories
+ *
+ * @access public
+ * @return bool
+ */
+ public function hasCategories()
+ {
+ foreach ($this->subscriptionList->subscriptions as $subscription) {
+ if ($subscription->getCategory() !== '') {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Build OPML body
+ *
+ * @access protected
+ * @return DOMElement
+ */
+ protected function buildBody()
+ {
+ $bodyElement = $this->document->createElement('body');
+
+ if ($this->hasCategories()) {
+ $this->buildCategories($bodyElement);
+ return $bodyElement;
+ }
+
+ foreach ($this->subscriptionList->subscriptions as $subscription) {
+ $bodyElement->appendChild($this->buildSubscription($subscription));
+ }
+
+ return $bodyElement;
+ }
+
+ /**
+ * Build categories section
+ *
+ * @access protected
+ * @param DOMElement $bodyElement
+ */
+ protected function buildCategories(DOMElement $bodyElement)
+ {
+ $categories = $this->groupByCategories();
+
+ foreach ($categories as $category => $subscriptions) {
+ $bodyElement->appendChild($this->buildCategory($category, $subscriptions));
+ }
+ }
+
+ /**
+ * Build category tag
+ *
+ * @access protected
+ * @param string $category
+ * @param array $subscriptions
+ * @return DOMElement
+ */
+ protected function buildCategory($category, array $subscriptions)
+ {
+ $outlineElement = $this->document->createElement('outline');
+ $outlineElement->setAttribute('text', $category);
+
+ foreach ($subscriptions as $subscription) {
+ $outlineElement->appendChild($this->buildSubscription($subscription));
+ }
+
+ return $outlineElement;
+ }
+
+ /**
+ * Build subscription entry
+ *
+ * @access public
+ * @param Subscription $subscription
+ * @return DOMElement
+ */
+ protected function buildSubscription(Subscription $subscription)
+ {
+ $outlineElement = $this->document->createElement('outline');
+ $outlineElement->setAttribute('type', $subscription->getType() ?: 'rss');
+ $outlineElement->setAttribute('text', $subscription->getTitle() ?: $subscription->getFeedUrl());
+ $outlineElement->setAttribute('xmlUrl', $subscription->getFeedUrl());
+
+ if ($subscription->getTitle() !== '') {
+ $outlineElement->setAttribute('title', $subscription->getTitle());
+ }
+
+ if ($subscription->getDescription() !== '') {
+ $outlineElement->setAttribute('description', $subscription->getDescription());
+ }
+
+ if ($subscription->getSiteUrl() !== '') {
+ $outlineElement->setAttribute('htmlUrl', $subscription->getSiteUrl());
+ }
+
+ return $outlineElement;
+ }
+
+ /**
+ * Group subscriptions by category
+ *
+ * @access private
+ * @return array
+ */
+ private function groupByCategories()
+ {
+ $categories = array();
+
+ foreach ($this->subscriptionList->subscriptions as $subscription) {
+ $categories[$subscription->getCategory()][] = $subscription;
+ }
+
+ return $categories;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php
new file mode 100644
index 00000000..9085588c
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace PicoFeed\Serialization;
+
+use PicoFeed\Parser\MalformedXmlException;
+use PicoFeed\Parser\XmlParser;
+use SimpleXMLElement;
+
+/**
+ * Class SubscriptionListParser
+ *
+ * @package PicoFeed\Serialization
+ * @author Frederic Guillot
+ */
+class SubscriptionListParser
+{
+ /**
+ * @var SubscriptionList
+ */
+ protected $subscriptionList;
+
+ /**
+ * @var string
+ */
+ protected $data;
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param string $data
+ */
+ public function __construct($data)
+ {
+ $this->subscriptionList = new SubscriptionList();
+ $this->data = trim($data);
+ }
+
+ /**
+ * Get object instance
+ *
+ * @static
+ * @access public
+ * @param string $data
+ * @return SubscriptionListParser
+ */
+ public static function create($data)
+ {
+ return new static($data);
+ }
+
+ /**
+ * Parse a subscription list entry
+ *
+ * @access public
+ * @throws MalformedXmlException
+ * @return SubscriptionList
+ */
+ public function parse()
+ {
+ $xml = XmlParser::getSimpleXml($this->data);
+
+ if (! $xml || !isset($xml->head) || !isset($xml->body)) {
+ throw new MalformedXmlException('Unable to parse OPML file: invalid XML');
+ }
+
+ $this->parseTitle($xml->head);
+ $this->parseEntries($xml->body);
+
+ return $this->subscriptionList;
+ }
+
+ /**
+ * Parse title
+ *
+ * @access protected
+ * @param SimpleXMLElement $xml
+ */
+ protected function parseTitle(SimpleXMLElement $xml)
+ {
+ $this->subscriptionList->setTitle((string) $xml->title);
+ }
+
+ /**
+ * Parse entries
+ *
+ * @access protected
+ * @param SimpleXMLElement $body
+ */
+ private function parseEntries(SimpleXMLElement $body)
+ {
+ foreach ($body->outline as $outlineElement) {
+ if (isset($outlineElement->outline)) {
+ $this->parseEntries($outlineElement);
+ } else {
+ $this->subscriptionList->subscriptions[] = SubscriptionParser::create($body, $outlineElement)->parse();
+ }
+ }
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php
new file mode 100644
index 00000000..caff07c2
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace PicoFeed\Serialization;
+
+use SimpleXMLElement;
+
+/**
+ * Class SubscriptionParser
+ *
+ * @package PicoFeed\Serialization
+ * @author Frederic Guillot
+ */
+class SubscriptionParser
+{
+ /**
+ * @var Subscription
+ */
+ protected $subscription;
+
+ /**
+ * @var SimpleXMLElement
+ */
+ private $outlineElement;
+
+ /**
+ * @var SimpleXMLElement
+ */
+ private $parentElement;
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param SimpleXMLElement $parentElement
+ * @param SimpleXMLElement $outlineElement
+ */
+ public function __construct(SimpleXMLElement $parentElement, SimpleXMLElement $outlineElement)
+ {
+ $this->parentElement = $parentElement;
+ $this->outlineElement = $outlineElement;
+ $this->subscription = new Subscription();
+ }
+
+ /**
+ * Get object instance
+ *
+ * @static
+ * @access public
+ * @param SimpleXMLElement $parentElement
+ * @param SimpleXMLElement $outlineElement
+ * @return SubscriptionParser
+ */
+ public static function create(SimpleXMLElement $parentElement, SimpleXMLElement $outlineElement)
+ {
+ return new static($parentElement, $outlineElement);
+ }
+
+ /**
+ * Parse subscription entry
+ *
+ * @access public
+ * @return Subscription
+ */
+ public function parse()
+ {
+ $this->subscription->setCategory($this->findCategory());
+ $this->subscription->setTitle($this->findTitle());
+ $this->subscription->setFeedUrl($this->findFeedUrl());
+ $this->subscription->setSiteUrl($this->findSiteUrl());
+ $this->subscription->setType($this->findType());
+ $this->subscription->setDescription($this->findDescription());
+
+ return $this->subscription;
+ }
+
+ /**
+ * Find category.
+ *
+ * @access protected
+ * @return string
+ */
+ protected function findCategory()
+ {
+ return isset($this->parentElement['text']) ? (string) $this->parentElement['text'] : '';
+ }
+
+ /**
+ * Find title.
+ *
+ * @access protected
+ * @return string
+ */
+ protected function findTitle()
+ {
+ return isset($this->outlineElement['title']) ? (string) $this->outlineElement['title'] : (string) $this->outlineElement['text'];
+ }
+
+ /**
+ * Find feed url.
+ *
+ * @access protected
+ * @return string
+ */
+ protected function findFeedUrl()
+ {
+ return (string) $this->outlineElement['xmlUrl'];
+ }
+
+ /**
+ * Find site url.
+ *
+ * @access protected
+ * @return string
+ */
+ protected function findSiteUrl()
+ {
+ return isset($this->outlineElement['htmlUrl']) ? (string) $this->outlineElement['htmlUrl'] : $this->findFeedUrl();
+ }
+
+ /**
+ * Find type.
+ *
+ * @access protected
+ * @return string
+ */
+ protected function findType()
+ {
+ return isset($this->outlineElement['version']) ? (string) $this->outlineElement['version'] :
+ isset($this->outlineElement['type']) ? (string) $this->outlineElement['type'] : 'rss';
+ }
+
+ /**
+ * Find description.
+ *
+ * @access protected
+ * @return string
+ */
+ protected function findDescription()
+ {
+ return isset($this->outlineElement['description']) ? (string) $this->outlineElement['description'] : $this->findTitle();
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php
new file mode 100644
index 00000000..34f37800
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace PicoFeed\Syndication;
+
+use DOMAttr;
+use DOMElement;
+
+/**
+ * Atom Feed Builder
+ *
+ * @package PicoFeed\Syndication
+ * @author Frederic Guillot
+ */
+class AtomFeedBuilder extends FeedBuilder
+{
+ /**
+ * @var DOMElement
+ */
+ protected $feedElement;
+
+ /**
+ * @var AtomHelper
+ */
+ protected $helper;
+
+ /**
+ * Build feed
+ *
+ * @access public
+ * @param string $filename
+ * @return string
+ */
+ public function build($filename = '')
+ {
+ $this->helper = new AtomHelper($this->getDocument());
+
+ $this->feedElement = $this->getDocument()->createElement('feed');
+ $this->feedElement->setAttributeNodeNS(new DomAttr('xmlns', 'http://www.w3.org/2005/Atom'));
+
+ $generator = $this->getDocument()->createElement('generator', 'PicoFeed');
+ $generator->setAttribute('uri', 'https://github.com/miniflux/picoFeed');
+ $this->feedElement->appendChild($generator);
+
+ $this->helper
+ ->buildTitle($this->feedElement, $this->feedTitle)
+ ->buildId($this->feedElement, $this->feedUrl)
+ ->buildDate($this->feedElement, $this->feedDate)
+ ->buildLink($this->feedElement, $this->siteUrl)
+ ->buildLink($this->feedElement, $this->feedUrl, 'self', 'application/atom+xml')
+ ->buildAuthor($this->feedElement, $this->authorName, $this->authorEmail, $this->authorUrl)
+ ;
+
+ foreach ($this->items as $item) {
+ $this->feedElement->appendChild($item->build());
+ }
+
+ $this->getDocument()->appendChild($this->feedElement);
+
+ if ($filename !== '') {
+ $this->getDocument()->save($filename);
+ }
+
+ return $this->getDocument()->saveXML();
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php
new file mode 100644
index 00000000..def6b0b9
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace PicoFeed\Syndication;
+
+use DateTime;
+use DOMDocument;
+use DOMElement;
+
+/**
+ * Class AtomHelper
+ *
+ * @package PicoFeed\Syndication
+ * @author Frederic Guillot
+ */
+class AtomHelper
+{
+ /**
+ * @var DOMDocument
+ */
+ protected $document;
+
+ /**
+ * Constructor
+ *
+ * @param DOMDocument $document
+ */
+ public function __construct(DOMDocument $document)
+ {
+ $this->document = $document;
+ }
+
+ /**
+ * Build node
+ *
+ * @access public
+ * @param DOMElement $element
+ * @param string $tag
+ * @param string $value
+ * @return AtomHelper
+ */
+ public function buildNode(DOMElement $element, $tag, $value)
+ {
+ $node = $this->document->createElement($tag);
+ $node->appendChild($this->document->createTextNode($value));
+ $element->appendChild($node);
+ return $this;
+ }
+
+ /**
+ * Build title
+ *
+ * @access public
+ * @param DOMElement $element
+ * @param string $title
+ * @return AtomHelper
+ */
+ public function buildTitle(DOMElement $element, $title)
+ {
+ return $this->buildNode($element, 'title', $title);
+ }
+
+ /**
+ * Build id
+ *
+ * @access public
+ * @param DOMElement $element
+ * @param string $id
+ * @return AtomHelper
+ */
+ public function buildId(DOMElement $element, $id)
+ {
+ return $this->buildNode($element, 'id', $id);
+ }
+
+ /**
+ * Build date element
+ *
+ * @access public
+ * @param DOMElement $element
+ * @param DateTime $date
+ * @param string $type
+ * @return AtomHelper
+ */
+ public function buildDate(DOMElement $element, DateTime $date, $type = 'updated')
+ {
+ return $this->buildNode($element, $type, $date->format(DateTime::ATOM));
+ }
+
+ /**
+ * Build link element
+ *
+ * @access public
+ * @param DOMElement $element
+ * @param string $url
+ * @param string $rel
+ * @param string $type
+ * @return AtomHelper
+ */
+ public function buildLink(DOMElement $element, $url, $rel = 'alternate', $type = 'text/html')
+ {
+ $node = $this->document->createElement('link');
+ $node->setAttribute('rel', $rel);
+ $node->setAttribute('type', $type);
+ $node->setAttribute('href', $url);
+ $element->appendChild($node);
+
+ return $this;
+ }
+
+ /**
+ * Build author element
+ *
+ * @access public
+ * @param DOMElement $element
+ * @param string $authorName
+ * @param string $authorEmail
+ * @param string $authorUrl
+ * @return AtomHelper
+ */
+ public function buildAuthor(DOMElement $element, $authorName, $authorEmail, $authorUrl)
+ {
+ if (!empty($authorName)) {
+ $author = $this->document->createElement('author');
+ $this->buildNode($author, 'name', $authorName);
+
+ if (!empty($authorEmail)) {
+ $this->buildNode($author, 'email', $authorEmail);
+ }
+
+ if (!empty($authorUrl)) {
+ $this->buildNode($author, 'uri', $authorUrl);
+ }
+
+ $element->appendChild($author);
+ }
+
+ return $this;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php
new file mode 100644
index 00000000..dfdfe68d
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace PicoFeed\Syndication;
+
+use DOMElement;
+
+/**
+ * Atom Item Builder
+ *
+ * @package PicoFeed\Syndication
+ * @author Frederic Guillot
+ */
+class AtomItemBuilder extends ItemBuilder
+{
+ /**
+ * @var DOMElement
+ */
+ protected $itemElement;
+
+ /**
+ * @var AtomHelper
+ */
+ protected $helper;
+
+ /**
+ * Build item
+ *
+ * @access public
+ * @return DOMElement
+ */
+ public function build()
+ {
+ $this->itemElement = $this->feedBuilder->getDocument()->createElement('entry');
+ $this->helper = new AtomHelper($this->feedBuilder->getDocument());
+
+ if (!empty($this->itemId)) {
+ $this->helper->buildId($this->itemElement, $this->itemId);
+ } else {
+ $this->helper->buildId($this->itemElement, $this->itemUrl);
+ }
+
+ $this->helper
+ ->buildTitle($this->itemElement, $this->itemTitle)
+ ->buildLink($this->itemElement, $this->itemUrl)
+ ->buildDate($this->itemElement, $this->itemUpdatedDate, 'updated')
+ ->buildDate($this->itemElement, $this->itemPublishedDate, 'published')
+ ->buildAuthor($this->itemElement, $this->authorName, $this->authorEmail, $this->authorUrl)
+ ;
+
+ if (!empty($this->itemSummary)) {
+ $this->helper->buildNode($this->itemElement, 'summary', $this->itemSummary);
+ }
+
+ if (!empty($this->itemContent)) {
+ $node = $this->feedBuilder->getDocument()->createElement('content');
+ $node->setAttribute('type', 'html');
+ $node->appendChild($this->feedBuilder->getDocument()->createCDATASection($this->itemContent));
+ $this->itemElement->appendChild($node);
+ }
+
+ return $this->itemElement;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php
new file mode 100644
index 00000000..cf9d024e
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php
@@ -0,0 +1,185 @@
+<?php
+
+namespace PicoFeed\Syndication;
+
+use DateTime;
+use DOMDocument;
+
+/**
+ * Class FeedBuilder
+ *
+ * @package PicoFeed\Syndication
+ * @author Frederic Guillot
+ */
+abstract class FeedBuilder
+{
+ /**
+ * @var DOMDocument
+ */
+ protected $document;
+
+ /**
+ * @var string
+ */
+ protected $feedTitle;
+
+ /**
+ * @var string
+ */
+ protected $feedUrl;
+
+ /**
+ * @var string
+ */
+ protected $siteUrl;
+
+ /**
+ * @var string
+ */
+ protected $authorName;
+
+ /**
+ * @var string
+ */
+ protected $authorEmail;
+
+ /**
+ * @var string
+ */
+ protected $authorUrl;
+
+ /**
+ * @var DateTime
+ */
+ protected $feedDate;
+
+ /**
+ * @var ItemBuilder[]
+ */
+ protected $items = array();
+
+ /**
+ * Constructor
+ *
+ * @access public
+ */
+ public function __construct()
+ {
+ $this->document = new DomDocument('1.0', 'UTF-8');
+ $this->document->formatOutput = true;
+ }
+
+ /**
+ * Get new object instance
+ *
+ * @access public
+ * @return static
+ */
+ public static function create()
+ {
+ return new static();
+ }
+
+ /**
+ * Add feed title
+ *
+ * @access public
+ * @param string $title
+ * @return $this
+ */
+ public function withTitle($title)
+ {
+ $this->feedTitle = $title;
+ return $this;
+ }
+
+ /**
+ * Add feed url
+ *
+ * @access public
+ * @param string $url
+ * @return $this
+ */
+ public function withFeedUrl($url)
+ {
+ $this->feedUrl = $url;
+ return $this;
+ }
+
+ /**
+ * Add website url
+ *
+ * @access public
+ * @param string $url
+ * @return $this
+ */
+ public function withSiteUrl($url)
+ {
+ $this->siteUrl = $url;
+ return $this;
+ }
+
+ /**
+ * Add feed date
+ *
+ * @access public
+ * @param DateTime $date
+ * @return $this
+ */
+ public function withDate(DateTime $date)
+ {
+ $this->feedDate = $date;
+ return $this;
+ }
+
+ /**
+ * Add feed author
+ *
+ * @access public
+ * @param string $name
+ * @param string $email
+ * @param string $url
+ * @return $this
+ */
+ public function withAuthor($name, $email = '', $url ='')
+ {
+ $this->authorName = $name;
+ $this->authorEmail = $email;
+ $this->authorUrl = $url;
+ return $this;
+ }
+
+ /**
+ * Add feed item
+ *
+ * @access public
+ * @param ItemBuilder $item
+ * @return $this
+ */
+ public function withItem(ItemBuilder $item)
+ {
+ $this->items[] = $item;
+ return $this;
+ }
+
+ /**
+ * Get DOM document
+ *
+ * @access public
+ * @return DOMDocument
+ */
+ public function getDocument()
+ {
+ return $this->document;
+ }
+
+ /**
+ * Build feed
+ *
+ * @abstract
+ * @access public
+ * @param string $filename
+ * @return string
+ */
+ abstract public function build($filename = '');
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php
new file mode 100644
index 00000000..86985bc7
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php
@@ -0,0 +1,209 @@
+<?php
+
+namespace PicoFeed\Syndication;
+
+use DateTime;
+use DOMElement;
+
+/**
+ * Class ItemBuilder
+ *
+ * @package PicoFeed\Syndication
+ * @author Frederic Guillot
+ */
+abstract class ItemBuilder
+{
+ /**
+ * @var string
+ */
+ protected $itemTitle;
+
+ /**
+ * @var string
+ */
+ protected $itemId;
+
+ /**
+ * @var string
+ */
+ protected $itemSummary;
+
+ /**
+ * @var string
+ */
+ protected $authorName;
+
+ /**
+ * @var string
+ */
+ protected $authorEmail;
+
+ /**
+ * @var string
+ */
+ protected $authorUrl;
+
+ /**
+ * @var DateTime
+ */
+ protected $itemPublishedDate;
+
+ /**
+ * @var DateTime
+ */
+ protected $itemUpdatedDate;
+
+ /**
+ * @var string
+ */
+ protected $itemContent;
+
+ /**
+ * @var string
+ */
+ protected $itemUrl;
+
+ /**
+ * @var FeedBuilder
+ */
+ protected $feedBuilder;
+
+ /**
+ * Constructor
+ *
+ * @param FeedBuilder $feedBuilder
+ */
+ public function __construct(FeedBuilder $feedBuilder)
+ {
+ $this->feedBuilder = $feedBuilder;
+ }
+
+ /**
+ * Get new object instance
+ *
+ * @access public
+ * @param FeedBuilder $feedBuilder
+ * @return static
+ */
+ public static function create(FeedBuilder $feedBuilder)
+ {
+ return new static($feedBuilder);
+ }
+
+ /**
+ * Add item title
+ *
+ * @access public
+ * @param string $title
+ * @return $this
+ */
+ public function withTitle($title)
+ {
+ $this->itemTitle = $title;
+ return $this;
+ }
+
+ /**
+ * Add item id
+ *
+ * @access public
+ * @param string $id
+ * @return $this
+ */
+ public function withId($id)
+ {
+ $this->itemId = $id;
+ return $this;
+ }
+
+ /**
+ * Add item url
+ *
+ * @access public
+ * @param string $url
+ * @return $this
+ */
+ public function withUrl($url)
+ {
+ $this->itemUrl = $url;
+ return $this;
+ }
+
+ /**
+ * Add item summary
+ *
+ * @access public
+ * @param string $summary
+ * @return $this
+ */
+ public function withSummary($summary)
+ {
+ $this->itemSummary = $summary;
+ return $this;
+ }
+
+ /**
+ * Add item content
+ *
+ * @access public
+ * @param string $content
+ * @return $this
+ */
+ public function withContent($content)
+ {
+ $this->itemContent = $content;
+ return $this;
+ }
+
+ /**
+ * Add item updated date
+ *
+ * @access public
+ * @param DateTime $date
+ * @return $this
+ */
+ public function withUpdatedDate(DateTime $date)
+ {
+ $this->itemUpdatedDate = $date;
+ return $this;
+ }
+
+ /**
+ * Add item published date
+ *
+ * @access public
+ * @param DateTime $date
+ * @return $this
+ */
+ public function withPublishedDate(DateTime $date)
+ {
+ $this->itemPublishedDate = $date;
+ return $this;
+ }
+
+ /**
+ * Add item author
+ *
+ * @access public
+ * @param string $name
+ * @param string $email
+ * @param string $url
+ * @return $this
+ */
+ public function withAuthor($name, $email = '', $url ='')
+ {
+ $this->authorName = $name;
+ $this->authorEmail = $email;
+ $this->authorUrl = $url;
+ return $this;
+ }
+
+ /**
+ * Build item
+ *
+ * @abstract
+ * @access public
+ * @return DOMElement
+ */
+ abstract public function build();
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php
new file mode 100644
index 00000000..bc3f5135
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace PicoFeed\Syndication;
+
+use DOMAttr;
+use DOMElement;
+
+/**
+ * Rss20 Feed Builder
+ *
+ * @package PicoFeed\Syndication
+ * @author Frederic Guillot
+ */
+class Rss20FeedBuilder extends FeedBuilder
+{
+ /**
+ * @var DOMElement
+ */
+ protected $rssElement;
+
+ /**
+ * @var Rss20Helper
+ */
+ protected $helper;
+
+ /**
+ * @var DOMElement
+ */
+ protected $channelElement;
+
+ /**
+ * Build feed
+ *
+ * @access public
+ * @param string $filename
+ * @return string
+ */
+ public function build($filename = '')
+ {
+ $this->helper = new Rss20Helper($this->getDocument());
+
+ $this->rssElement = $this->getDocument()->createElement('rss');
+ $this->rssElement->setAttribute('version', '2.0');
+ $this->rssElement->setAttributeNodeNS(new DomAttr('xmlns:content', 'http://purl.org/rss/1.0/modules/content/'));
+ $this->rssElement->setAttributeNodeNS(new DomAttr('xmlns:atom', 'http://www.w3.org/2005/Atom'));
+
+ $this->channelElement = $this->getDocument()->createElement('channel');
+ $this->helper
+ ->buildNode($this->channelElement, 'generator', 'PicoFeed (https://github.com/miniflux/picoFeed)')
+ ->buildTitle($this->channelElement, $this->feedTitle)
+ ->buildNode($this->channelElement, 'description', $this->feedTitle)
+ ->buildDate($this->channelElement, $this->feedDate)
+ ->buildAuthor($this->channelElement, 'webMaster', $this->authorName, $this->authorEmail)
+ ->buildLink($this->channelElement, $this->siteUrl)
+ ;
+
+ $link = $this->getDocument()->createElement('atom:link');
+ $link->setAttribute('href', $this->feedUrl);
+ $link->setAttribute('rel', 'self');
+ $link->setAttribute('type', 'application/rss+xml');
+ $this->channelElement->appendChild($link);
+
+ foreach ($this->items as $item) {
+ $this->channelElement->appendChild($item->build());
+ }
+
+ $this->rssElement->appendChild($this->channelElement);
+ $this->getDocument()->appendChild($this->rssElement);
+
+ if ($filename !== '') {
+ $this->getDocument()->save($filename);
+ }
+
+ return $this->getDocument()->saveXML();
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php
new file mode 100644
index 00000000..72a19e56
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php
@@ -0,0 +1,115 @@
+<?php
+
+namespace PicoFeed\Syndication;
+
+use DateTime;
+use DOMDocument;
+use DOMElement;
+
+/**
+ * Class Rss20Helper
+ *
+ * @package PicoFeed\Syndication
+ * @author Frederic Guillot
+ */
+class Rss20Helper
+{
+ /**
+ * @var DOMDocument
+ */
+ protected $document;
+
+ /**
+ * Constructor
+ *
+ * @param DOMDocument $document
+ */
+ public function __construct(DOMDocument $document)
+ {
+ $this->document = $document;
+ }
+
+ /**
+ * Build node
+ *
+ * @access public
+ * @param DOMElement $element
+ * @param string $tag
+ * @param string $value
+ * @return $this
+ */
+ public function buildNode(DOMElement $element, $tag, $value)
+ {
+ $node = $this->document->createElement($tag);
+ $node->appendChild($this->document->createTextNode($value));
+ $element->appendChild($node);
+ return $this;
+ }
+
+ /**
+ * Build title
+ *
+ * @access public
+ * @param DOMElement $element
+ * @param string $title
+ * @return $this
+ */
+ public function buildTitle(DOMElement $element, $title)
+ {
+ return $this->buildNode($element, 'title', $title);
+ }
+
+ /**
+ * Build date element
+ *
+ * @access public
+ * @param DOMElement $element
+ * @param DateTime $date
+ * @param string $type
+ * @return $this
+ */
+ public function buildDate(DOMElement $element, DateTime $date, $type = 'pubDate')
+ {
+ return $this->buildNode($element, $type, $date->format(DateTime::RSS));
+ }
+
+ /**
+ * Build link element
+ *
+ * @access public
+ * @param DOMElement $element
+ * @param string $url
+ * @return $this
+ */
+ public function buildLink(DOMElement $element, $url)
+ {
+ return $this->buildNode($element, 'link', $url);
+ }
+
+ /**
+ * Build author element
+ *
+ * @access public
+ * @param DOMElement $element
+ * @param string $tag
+ * @param string $authorName
+ * @param string $authorEmail
+ * @return $this
+ */
+ public function buildAuthor(DOMElement $element, $tag, $authorName, $authorEmail)
+ {
+ if (!empty($authorName)) {
+ $value = '';
+
+ if (!empty($authorEmail)) {
+ $value .= $authorEmail.' ('.$authorName.')';
+ } else {
+ $value = $authorName;
+ }
+
+ $this->buildNode($element, $tag, $value);
+ }
+
+ return $this;
+ }
+}
diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php
new file mode 100644
index 00000000..125dc6ac
--- /dev/null
+++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace PicoFeed\Syndication;
+
+use DOMElement;
+
+/**
+ * Rss20 Item Builder
+ *
+ * @package PicoFeed\Syndication
+ * @author Frederic Guillot
+ */
+class Rss20ItemBuilder extends ItemBuilder
+{
+ /**
+ * @var DOMElement
+ */
+ protected $itemElement;
+
+ /**
+ * @var Rss20Helper
+ */
+ protected $helper;
+
+ /**
+ * Build item
+ *
+ * @access public
+ * @return DOMElement
+ */
+ public function build()
+ {
+ $this->itemElement = $this->feedBuilder->getDocument()->createElement('item');
+ $this->helper = new Rss20Helper($this->feedBuilder->getDocument());
+
+ if (!empty($this->itemId)) {
+ $guid = $this->feedBuilder->getDocument()->createElement('guid');
+ $guid->setAttribute('isPermaLink', 'false');
+ $guid->appendChild($this->feedBuilder->getDocument()->createTextNode($this->itemId));
+ $this->itemElement->appendChild($guid);
+ } else {
+ $guid = $this->feedBuilder->getDocument()->createElement('guid');
+ $guid->setAttribute('isPermaLink', 'true');
+ $guid->appendChild($this->feedBuilder->getDocument()->createTextNode($this->itemUrl));
+ $this->itemElement->appendChild($guid);
+ }
+
+ $this->helper
+ ->buildTitle($this->itemElement, $this->itemTitle)
+ ->buildLink($this->itemElement, $this->itemUrl)
+ ->buildDate($this->itemElement, $this->itemPublishedDate)
+ ->buildAuthor($this->itemElement, 'author', $this->authorName, $this->authorEmail)
+ ;
+
+ if (!empty($this->itemSummary)) {
+ $this->helper->buildNode($this->itemElement, 'description', $this->itemSummary);
+ }
+
+ if (!empty($this->itemContent)) {
+ $node = $this->feedBuilder->getDocument()->createElement('content:encoded');
+ $node->appendChild($this->feedBuilder->getDocument()->createCDATASection($this->itemContent));
+ $this->itemElement->appendChild($node);
+ }
+
+ return $this->itemElement;
+ }
+}