Compare commits

...
153 Commits
Author SHA1 Message Date
Jérémy BenoistandGitHub 38870cdff1 Merge pull request #80 from jtojnar/stricter
Fix some CI issues
2023-04-03 14:47:32 +02:00
Jan Tojnar 9bdd3b6b2e ci: Add PHP 8.2 and 8.3 2023-03-31 05:26:07 +02:00
Jan Tojnar f14428e4c0 Do not use mb_convert_encoding with HTML-ENTITIES as target encoding
This is deprecated since PHP 8.2:

    Deprecated: mb_convert_encoding(): Handling HTML entities via mbstring is deprecated; use htmlspecialchars, htmlentities, or mb_encode_numericentity/mb_decode_numericentity instead

It was used because `DOMDocument`, which uses libxml2 internally, will parse the HTML as ISO-8859-1, unless the document contains an XML encoding declaration or HTML meta tag setting character set.
Since first such element wins, putting the `meta[charset]` up front will ensure the parser uses the correct encoding, even if the document contains incorrect meta tag (e.g. when the document is converted to UTF-8 without also updating the metadata by the software passing it to Readability).

https://stackoverflow.com/a/39148511/160386
2023-03-31 05:26:07 +02:00
Jan Tojnar 23f824a1ce tests: Fix “THE ERROR HANDLER HAS CHANGED!” 2023-03-31 03:19:22 +02:00
Jan Tojnar 2a57124528 composer: upgrade rector 2023-03-31 03:19:22 +02:00
Jan Tojnar 0975574bdb Rector: Upgrade configuration 2023-03-31 03:19:22 +02:00
Jan Tojnar 9ed89bde92 Fix PHP-Cs-Fixer changes
1) src/Readability.php (braces, no_unneeded_control_parentheses, single_line_comment_spacing, global_namespace_import, no_unused_imports, phpdoc_align)
   2) src/JSLikeHTMLElement.php (phpdoc_separation)

Switch code blocks to Markdown syntax to work around `phpdoc_separation`, ApiGen uses Markdown these days anyway.
2023-03-31 03:14:00 +02:00
Jan Tojnar 2c6c6d5987 PHPStan: Use stable PHPUnit path
phpunit-bridge will create a symlink.
2023-03-31 03:14:00 +02:00
Jan Tojnar c5407ec07c composer: Add scripts for development 2023-03-31 03:14:00 +02:00
Jérémy BenoistandGitHub 7cd8476d38 Merge pull request #79 from j0k3r/fix/psr-log-2-3
Allow `psr/log` 2.0 & 3.0
2022-10-17 22:44:36 +02:00
Jeremy Benoist 82083c872b Allow psr/log 2.0 & 3.0 2022-10-17 22:42:47 +02:00
Kevin DecherfandJeremy Benoist 6689f19956 Strip script and style tags through ::clean() method instead of preg_replace
Huge tags can lead to a failure of preg_replace, thus erasing the whole
fetched content.

Fixes https://github.com/wallabag/wallabag/issues/5847

Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2022-06-13 09:13:23 +02:00
Jérémy BenoistandGitHub 0c0653dad6 Merge pull request #73 from Kdecherf/fix/impr
Fix `isPhrasingContent` conditions, text node replacement
2022-02-16 00:03:37 +01:00
Kevin Decherf 2ab87d7445 Fix isPhrasingContent conditions, text node replacement
It also disables reverting forced paragraph elements as it can break
layouts or corrupt content.

Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2022-02-15 20:53:28 +01:00
Jérémy BenoistandGitHub 8af69ad68c Merge pull request #71 from j0k3r/feature/enable-rector
Add Rector
2022-02-04 12:15:57 +01:00
Jeremy Benoist c2a1639b34 Add Rector 2022-02-04 12:13:37 +01:00
Jérémy BenoistandGitHub ccf1b336c5 Merge pull request #64 from Kdecherf/improvements 2022-02-04 05:27:57 +01:00
Kevin Decherf a44c4e5482 Add routine to remove invisible nodes
Readability was previously removing (was trying to actually, see next
section) invisible nodes using a pattern from `unlikelyCandidates`. This
was quite hacky and was removed during a backport of logics from
mozilla/readability. There is still a need to remove them so here we
are. We still use a pattern but specifically against the style
attribute. We also remove nodes with the attribute `hidden`.

The clean feature of tidy actually replaces inline style attributes
with css classes thus preventing readability to detect invisible nodes,
see https://github.com/htacg/tidy-html5/blob/5.6.0/src/clean.c#L1488
We therefore set clean configuration to false.

Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2022-02-04 01:23:11 +01:00
Kevin Decherf b580cf216d Backport some logics from mozilla/readability
This change backports several things from mozilla/readability:

- Add child score to all ancestors instead of the first parent only
- Check 5 top candidates and try to find alternative candidates within
  ancestors, this can help to find a better parent and grab more content
- Reduce patterns from `unlikelyCandidates` to the one used by Mozilla
  as ours tend to remove useful nodes
- Score headers (h2 to h6) by default in addition to div, p, td and
  section

Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2022-02-04 01:05:13 +01:00
Jérémy BenoistandGitHub 2e9349f076 Merge pull request #69 from j0k3r/feature/php-7.2
Require PHP >= 7.2
2022-02-02 12:53:28 +01:00
Jeremy Benoist c4bba53dbe Remove Scrutinizer 2022-02-02 12:52:12 +01:00
Jeremy Benoist 66215a6c80 Require PHP >= 7.2
- remove test on Composer v1
- remove deprecated function
- move `loadHtml()` into `init()` instead of `__construct`

Kinda prepare 2.0 version :)
2022-02-02 12:44:24 +01:00
Jérémy BenoistandGitHub b1a20a9575 Merge pull request #68 from open-source-contributions/master
Using assertSame to make assertion equal strict
2021-12-17 12:17:44 +01:00
peter279k 97c02e8ad4 Using assertSame to make assertion equal strict 2021-12-17 19:09:36 +08:00
Jérémy BenoistandGitHub c506b7ebd7 Merge pull request #67 from j0k3r/fix/psr-log-void
Fix deprecated message
2021-11-29 21:06:47 +01:00
Jeremy Benoist d0af21814a Ditch assertContains & assertNotContains 2021-11-29 21:04:56 +01:00
Jeremy Benoist 5b1eba79bd Test on PHP 8 & drop Travis 2021-11-29 21:00:36 +01:00
Jérémy BenoistandGitHub fabf096ce6 Fix deprecated message
> Method "Psr\Log\LoggerAwareInterface::setLogger()" might add "void" as a native return type declaration in the future. Do the same in implementation "Readability\Readability" now to avoid errors or add an explicit @return annotation to suppress this message.
2021-11-29 20:50:10 +01:00
Jérémy BenoistandGitHub 8ce1663238 Merge pull request #66 from Kdecherf/fix/figure 2021-10-29 16:24:41 +02:00
Kevin Decherf eb72a315c4 Clean empty figure tags without ending
See 'Tag omission' https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure

Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2021-10-29 15:36:19 +02:00
Jérémy BenoistandGitHub d5330a9c28 Merge pull request #65 from j0k3r/fix/php-cs
Fix PHPCS config file
2021-10-04 11:42:34 +02:00
Jeremy Benoist 635f8963dc Fix Travis & PHPStan 2021-10-04 11:37:33 +02:00
Jeremy Benoist 19b2a25d96 Fix PHPCS config file 2021-10-04 11:32:34 +02:00
Jérémy BenoistandGitHub 9a490fac07 Merge pull request #52 from nicofrand/master
Skip empty (empty innerHTML) nodes when grabbing article
2021-03-09 11:14:29 +01:00
Jérémy BenoistandGitHub 6f6b1f9e2b Merge pull request #62 from j0k3r/fix/avoid-wiped-body
Body can be wiped without tidy
2021-03-09 10:49:54 +01:00
Jeremy Benoist ea1368fac0 Body can be wiped without tidy
Re-create it in that case.

Also run CS-Fixer.
2021-03-08 11:59:24 +01:00
Jérémy BenoistandGitHub be81eb2a4f Create FUNDING.yml 2020-12-08 09:47:38 +01:00
Jérémy BenoistandGitHub 9632c4df8c Merge pull request #61 from j0k3r/github-actions
Ditch Travis to use GitHub Actions
2020-11-30 14:34:46 +01:00
Jeremy Benoist bd9ca1b2cd Ditch Travis to use GitHub Actions 2020-11-30 14:18:40 +01:00
Jérémy BenoistandGitHub 6c917794a7 Merge pull request #60 from jtojnar/patch-1
readability: stop tidy from wrapping noscript text
2020-11-16 12:02:02 +01:00
Jan TojnarandGitHub 7cea79c23a readability: stop tidy from wrapping noscript text
HTML 4.01 Strict only allows block-level elements within noscript, form and
blockquote. The `enclose-block-text` option fixes the instances when those
elements contain inline elements or text by wrapping the children in paragraphs.

HTML 5 has looser content model and allows noscript elements basically anywhere,
including paragraphs, making the noscript elements inherit the parent element’s
content model. This means that tidy will produce invalid HTML nesting paragraphs
for `p > noscript > text`, a structure that would be invalid on two counts
in HTML 4 Strict profile but is completely valid in HTML 5.

Popular WordPress image lazy-loading code produces precisely that structure
so tidy “corrects” it to invalid code. In a proper HTML parser, the produced
code would force close the outer paragraph, making the noscript element
its sibling instead of a child. The only reason this does not break Graby’s code
for stripping the lazy-loading HTML is that libxml2 contains a bug
counteracting this:

https://gitlab.gnome.org/GNOME/libxml2/-/issues/205

Since all three elements allow flow content in HTML 5, it does not make much
sense to enable this option any more. The only possible issues that could occur
is producing HTML code not conforming to 4.01 Strict but that was never guaranteed,
as our example shows, and having blockquotes contain text nodes not wrapped
in paragraphs, which might be expected by some ancient stylesheets
but that is only minor and easily fixable visual backwards incompatibility.
2020-11-14 22:13:53 +01:00
Jérémy BenoistandGitHub c6425cc28b Merge pull request #58 from j0k3r/fix/test-php8
Enable tests for PHP 8
2020-06-08 09:58:28 +02:00
Jeremy Benoist e6ad806460 Enable tests on PHP 8 2020-06-08 09:24:50 +02:00
Jérémy BenoistandGitHub 3fa88461a0 Merge pull request #57 from j0k3r/fix/html5-parser
Use a new deps for HTML5 parser
2020-06-08 07:59:42 +02:00
Jeremy Benoist 6a8ecf232f Use a new deps for HTML5 parser
`electrolinux/php-html5lib` was quite old and incompatible with the upcoming Composer 2.0.
Jumping to `masterminds/html5` for the same result. Also the lib is maintained.

Also:
- keep README in vendors
- use new Scrutinizer engine
- test with lower deps
- remove php-coveralls dev deps and download the phar during the CI build
2020-06-08 07:04:00 +02:00
Jérémy BenoistandGitHub 52b1ddba57 Merge pull request #55 from j0k3r/fix/cleanup-travis
Cleanup travis
2020-04-12 16:38:09 +02:00
Jeremy Benoist 90c625bb57 Cleanup travis 2020-04-12 16:30:02 +02:00
Jérémy BenoistandGitHub 4d1c3b1777 Merge pull request #54 from j0k3r/fix/phpstan
Fix PHPStan (again)
2020-01-06 14:26:41 +01:00
Jeremy Benoist d649b59414 Fixing PHPUnit versions 2020-01-06 14:22:15 +01:00
Jeremy Benoist 44bebfc3d6 Only install PHPStan when we need it
This is to avoid error when installing it on PHP < 7.1
2020-01-06 14:03:30 +01:00
Jeremy Benoist b1acc9ed73 Fix PHPStan (again)
Also cleanup
2019-11-19 14:09:29 +01:00
Jérémy BenoistandGitHub 9306996b47 Merge pull request #53 from j0k3r/openload.co
Add openload.co to media detection
2019-06-25 17:15:37 +02:00
Jeremy Benoist bb75b4f089 Fix PHPStan 2019-06-25 17:06:49 +02:00
Jeremy Benoist 11d2946904 Add openload.co to media detection 2019-06-25 16:54:38 +02:00
nicofrand ff78c63e6d Skip empty (empty innerHTML) nodes when grabbing article 2019-05-25 16:12:52 +02:00
Jérémy BenoistandGitHub f808c1b0a2 Merge pull request #50 from j0k3r/fix/non-well-formed-numeric-value
Fix “A non well formed numeric value encountered”
2019-05-11 22:07:19 +02:00
Jeremy Benoist bb65caf864 Fix “A non well formed numeric value encountered” 2019-05-11 21:58:11 +02:00
Jérémy BenoistandGitHub de1b1d9775 Merge pull request #48 from Simounet/feature/out-removed-from-negative
\bout removed from negative content
2019-04-23 10:45:13 +02:00
Simounet 2e20f76195 \bout removed from negative content 2019-04-19 12:12:41 +02:00
Jérémy BenoistandGitHub 3c0289bf89 Merge pull request #46 from j0k3r/phpstan
Enable PHPStan
2019-02-07 16:08:33 +01:00
Jeremy Benoist 74d9cc605a Enable PHPStan 2019-02-07 15:51:31 +01:00
Jérémy BenoistandGitHub 6a0f3337a6 Merge pull request #45 from j0k3r/fix/update-fixer-rules
Update fixer rules
2019-02-04 11:29:55 +01:00
Jeremy Benoist 2dce2879bf Update fixer rules
Following graby, wallabag, etc.
2019-02-04 11:21:34 +01:00
Jérémy BenoistandGitHub 49ce4233fa Merge pull request #42 from Kdecherf/fix-tidy
tidy: use tidy_repair_string instead of tidy_parse_string+tidy_clean_repair
2019-02-04 11:17:13 +01:00
Kevin DecherfandJeremy Benoist 26c881d864 tidy: use tidy_repair_string instead of tidy_parse_string+tidy_clean_repair
A change released in tidy 5.6.0 breaks php-tidy when using
tidy_parse_string+tidy_clean_repair and wrap=0, incorrectly wrapping
every single word. Also it seems that $tidy->value should not be used to
retrieve the repaired html as far as it is undocumented and for internal
use.

We replace the call with tidy_repair_string which directly returns the
repaired string.

Relates to https://github.com/htacg/tidy-html5/issues/673
Relates to https://bugs.php.net/bug.php?id=75947

Tests pass.

Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2019-02-04 11:08:18 +01:00
Jérémy BenoistandGitHub db69fe59a2 Merge pull request #44 from j0k3r/fix/coveralls-upload
Update path to coveralls bin
2019-02-04 11:08:02 +01:00
Jeremy Benoist a78f01f656 Update path to coveralls bin 2019-02-04 11:01:47 +01:00
Jérémy BenoistandGitHub db4508003b Merge pull request #43 from Kdecherf/composer-bump
Composer bump, php-cs bump to v2, travis update
2019-02-04 10:50:17 +01:00
Kevin Decherf 15b12ea2d6 travis: remove PHP < 5.6 and related stuff, phpcs on 7.2, 7.3 not fail
Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2019-02-03 16:50:40 +01:00
Kevin Decherf 694f0308fe composer: bump dependencies, move to php-cs v2
Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2019-02-03 16:49:21 +01:00
Jérémy BenoistandGitHub bbe9021fe7 Merge pull request #36 from Kdecherf/failing-test
tests: fix possible typo in testPostFilters() leading to failure
2018-12-10 12:02:43 +01:00
Jérémy BenoistandGitHub 9dee4a240d Add some badges 2018-11-28 10:05:23 +01:00
Jérémy BenoistandGitHub eef6d6d456 Merge pull request #41 from j0k3r/testing-7.2-7.3
Updating to 7.2
2018-11-26 16:54:20 +01:00
Jeremy Benoist 30a577c617 Dumping HHVM 2018-11-26 16:42:28 +01:00
Jeremy Benoist 70b393a2a3 Remove custom stuff for 7.2 2018-11-26 16:41:42 +01:00
Jeremy Benoist 9ab6d0d9e8 Updating to 7.2 2018-11-26 16:33:38 +01:00
Jérémy BenoistandGitHub 8712db510e Merge pull request #40 from Simounet/giphy-allowed
Giphy added to allowed medias
2018-11-26 16:31:18 +01:00
Simounet 422c74f29c Giphy added to allowed medias 2018-11-20 19:27:41 +01:00
Jérémy BenoistandGitHub ed3393a79f Merge pull request #38 from Simounet/feature/media-added-to-positive
Media class added to positive candidates
2018-06-05 11:54:53 +00:00
Simounet 63cd304dba Media class added to positive candidates
Fix Mediapart images.
2018-06-05 13:26:39 +02:00
Jérémy BenoistandGitHub 43939f88d9 Merge pull request #37 from Kdecherf/footnotes-candidates
Keep elements with 'footnote' as possible candidates
2017-11-03 18:21:40 +02:00
Kevin Decherf 3a7350a8a7 tests: fix possible typo in testPostFilters() leading to failure
Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2017-11-01 16:46:01 +01:00
Kevin Decherf 4c68cc9f09 Keep elements with 'footnote' as possible candidates
Should fix https://github.com/wallabag/wallabag/issues/3100

Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2017-11-01 16:45:07 +01:00
Jérémy BenoistandGitHub 276684ab6d Merge pull request #35 from j0k3r/travis
Fix PHP 5.3 builds
2017-10-09 17:16:58 +02:00
Jeremy Benoist 163058aeff Fix PHP 5.3 builds 2017-10-09 16:58:21 +02:00
Jérémy BenoistandGitHub 6427dd7371 Merge pull request #32 from j0k3r/html5
Add missing HTML5 class
2017-06-30 16:50:09 +02:00
Jeremy Benoist 613a63c062 CS 2017-06-30 16:42:29 +02:00
Jeremy Benoist 05089bbd03 Add missing HTML5 class 2017-06-30 16:32:37 +02:00
Jérémy BenoistandGitHub 5ac82f3c87 Merge pull request #30 from j0k3r/avoid-php-warning
Avoid php warning
2017-05-19 16:16:20 +02:00
Jeremy Benoist 85fb92a042 Fix tests 2017-05-19 16:02:36 +02:00
Jeremy Benoist f2a43b476c Avoid PHP Warning
This isn't the best solution but the previous one using `@` wasn't really better.
Appending a string into a fragment might generate some warning if the string contains bad entity.
For example `&plus;`.
2017-05-19 15:37:46 +02:00
Jérémy BenoistandGitHub 6def743902 Merge pull request #27 from jtojnar/ext-mbstring
Add mbstring as a dependency
2017-03-18 09:16:30 +01:00
Jan Tojnar 8a44926392 Add mbstring as a dependency 2017-03-18 04:02:50 +01:00
Jérémy BenoistandGitHub 8c7740f073 Merge pull request #26 from j0k3r/fix-phpunit-7.x
Fix PHPUnit error on PHP 7+
2017-03-09 09:41:33 +01:00
Jeremy Benoist 6a4720c951 Fix PHPUnit error on PHP 7+ 2017-03-09 09:21:13 +01:00
Jérémy BenoistandGitHub 7a9a82b543 Update README.md 2017-03-09 09:02:04 +01:00
Jérémy BenoistandGitHub 8a91d36294 Merge pull request #25 from jtojnar/patch-1
Add tidy to suggested dependenies
2017-03-09 09:01:05 +01:00
Jan TojnarandGitHub 669adfb20f Add tidy to suggested dependenies 2017-02-16 15:12:45 +01:00
Jeremy BenoistandGitHub 445b889efb Merge pull request #24 from j0k3r/links
Don't be to hard on 'links' attribute
2017-02-02 16:12:48 +01:00
Jeremy Benoist 8b1c3f147d Don't be to hard on 'links' attribute 2017-02-02 15:57:15 +01:00
Jeremy BenoistandGitHub a505a13d0c Merge pull request #22 from jtojnar/master
Depend on psr/log instead of Monolog
2017-01-14 17:09:58 +01:00
Jan Tojnar 9fe0d07805 Depend on psr/log instead of Monolog 2017-01-14 09:08:15 +01:00
Jeremy BenoistandGitHub e5d31b93d1 Merge pull request #21 from j0k3r/childnode-goes-null
Avoid childnode becoming null to generate a warning
2017-01-11 10:54:24 +01:00
Jeremy BenoistandJeremy Benoist 6e24a5d731 Add PHP 7.1 to Travis & fix php-cs-fixer
I don't want to update the .php_cs to be compatible with php-cs-fixer 2.
It works great like that atm.
2017-01-10 11:13:09 +01:00
Jeremy Benoist ff754b80bd Avoid childnode becoming null to generate a warning 2017-01-10 10:58:12 +01:00
Jeremy BenoistandGitHub ade850534c Merge pull request #18 from j0k3r/tool
Don’t be too agressive
2016-10-21 07:24:11 +02:00
Jeremy Benoist d97bece7c5 Don’t be too agressive
Some links got a “tooltip-link” and shouldn’t be removed by php-readability because they are usefull to the content
2016-10-20 23:40:52 +02:00
Jeremy BenoistandGitHub 47ce4fb7f9 Merge pull request #16 from j0k3r/info
“info” is too agressive in unlikelyCandidates
2016-10-03 09:13:35 +02:00
Jeremy Benoist 3de4e918b4 Convert header & section to p
And took `pre` element in score
2016-10-02 14:55:52 +02:00
Jeremy Benoist 5182d6cb11 “info” is too agressive in unlikelyCandidates
Some contents have a `infocontent` node (ot sth different) and they are real content.
Using only `info` as regex is too agressive and remove legitimate content.
Matching the whole word `info` (or `infos`) should be a better choice
2016-10-02 14:49:43 +02:00
Jeremy BenoistandGitHub fb9810a827 Merge pull request #15 from j0k3r/cs
Enable php-cs-fixer
2016-06-24 09:36:50 +02:00
Jeremy Benoist 22aa4c3b31 Fix composer & TLS 2016-06-23 07:44:54 +02:00
Jeremy Benoist 2ef400bf73 Enable php-cs-fixer 2016-06-23 07:28:10 +02:00
Jeremy Benoist dec4514c00 Merge pull request #14 from j0k3r/revert-bc
Revert BC changes
2016-03-01 15:14:36 +01:00
Jeremy Benoist 00f622e9b7 Revert BC changes
- avoid method signature update
- revert moving logic out of the constructor
2016-03-01 15:07:32 +01:00
Jeremy Benoist 7b47e2f1de Merge pull request #13 from j0k3r/monolog
Use Monolog instead of custom solution
2016-03-01 14:09:42 +01:00
Jeremy Benoist c756ec067e Fix tests
`getInnerText` might receive a null DOMElement if the xpath or query return no element.
2016-02-29 13:09:16 +01:00
Jeremy Benoist 8ab7d76cd5 Use Monolog instead of custom solution
Remove that ugly `openlog` & `syslog`
2016-02-29 13:01:40 +01:00
Jeremy Benoist 149a333b40 Remove addPreFilter
Pre filters are used in the __construct so adding more pre filters once the object is instantiated is useless.
2016-02-29 12:10:56 +01:00
Jeremy Benoist 209c404d7b Fix instanceof DOMElement
We previously checked `instanceof DOMElement` which was wrong since we
are in the namespace class, the class `Readability\DOMElement` does not
exists.
2016-02-29 11:53:03 +01:00
Jeremy Benoist 6e3f2e8c0b Merge pull request #12 from j0k3r/travis-coveralls
Switch to Coveralls
2016-02-29 11:18:53 +01:00
Jeremy Benoist 8cbecd7e99 Use cache for Travis
And also avoid Github api limit
2016-02-29 11:13:43 +01:00
Jeremy Benoist 438699ab15 Disable html coverage
Required only on local.
2016-02-29 11:13:15 +01:00
Jeremy Benoist ea7a62d352 Switch to Coveralls 2016-02-29 11:13:15 +01:00
Jeremy Benoist 9181fd1a2c Merge pull request #11 from j0k3r/append-id
Avoid adding id that might already exists
2016-02-29 11:12:52 +01:00
Jeremy Benoist 2951936e00 CS & PHPDoc 2016-02-29 11:00:45 +01:00
Jeremy Benoist 92965f76cf Fix composer TLS exception on PHP 5.3.3 2016-02-29 10:30:33 +01:00
Jeremy Benoist 850ade16b6 Cleanup 2016-02-29 10:22:07 +01:00
Jeremy Benoist dc590542f0 Avoid adding id that might already exists
We append a new node when it isn't a `div` or `p` (like when it's an `article`) with the same id which generate a DOM error "DOMElement::setAttribute(): ID blabla already defined".
2016-02-29 10:21:52 +01:00
Jeremy Benoist 41d7440c6e Merge pull request #10 from j0k3r/remove-tidy-constraint
Remove tidy from requirement
2015-11-10 09:55:29 +01:00
Jeremy Benoist 252bb4ef42 Add Tidy requirement in README 2015-11-09 20:25:39 +01:00
Jeremy Benoist 7c30d76b6e Ensure tests are running without Tidy 2015-11-09 19:59:18 +01:00
Jeremy Benoist 111cb08034 Improve negative element
- add unlikelyCandidates: head
- add negative: recommend
2015-11-09 19:44:11 +01:00
Jeremy Benoist 74fbf6f009 Remove tidy from requirement
Sadly we can't suggest user to install the extension
2015-11-09 11:12:07 +01:00
Jeremy Benoist 1565a6819a Add .gitattributes to slim down composer packages
When composer is used as a dependency manager for large web apps, transferring lots of extra files (such as tests and documentation) can be very time and bandwidth consuming. By using .gitattributes we can tell composer what files to ignore when distributing through composer.
2015-10-29 13:56:13 +01:00
Jeremy Benoist f71c3a4196 Do not remove html tag attributes
They might contains useful information (at least language)
2015-09-23 21:09:38 +02:00
Jeremy Benoist 255a2fc7bc Merge pull request #8 from j0k3r/nofollow
Do not remove nofollow links
2015-09-23 17:58:32 +02:00
Jeremy Benoist b77876b30a Do not remove nofollow links
Most the time, they can be usefull.
At least, it'll be a link to something unrelated. But we won't lose a link inside the content.

Also, adding some extra space.
2015-09-22 19:25:57 +02:00
Jeremy Benoist 1830dc45d4 Merge pull request #7 from j0k3r/fix-nbsp
Avoid error with &nbsp;
2015-09-20 21:05:55 +02:00
Jeremy Benoist 6be1f9b984 Fix link to fivefilters fork 2015-09-18 19:19:26 +02:00
Jeremy Benoist 175196d6c2 Avoid error with &nbsp;
Fix #5
2015-09-18 19:10:48 +02:00
Jeremy Benoist 2b5af601d5 Do not format output to avoid breaking apps
It'll require to jump to 2.0.0 and I think it's to soon
2015-09-15 22:25:08 +02:00
Jeremy Benoist d01eb2ac1e Use class instead of id to avoid error
It generates error like `ID XXX already defined`
2015-09-14 21:49:40 +02:00
Jeremy Benoist c5a4a490e1 CS 2015-08-24 11:10:54 +02:00
Jeremy Benoist 908a49824f Add test on title 2015-08-24 11:09:47 +02:00
Jeremy Benoist c67189248e Backport changes from wallabag
https://github.com/wallabag/php-readability/commit/e9e4ff87f8fc56d406ccdd5a9a7f1d3d6af07e79
2015-08-24 11:09:38 +02:00
Jeremy Benoist 91b80b70e2 Update HTML5 tags
From https://github.com/htacg/tidy-html5/blob/master/src/tags.c#L296
2015-08-19 23:12:31 +02:00
Jeremy Benoist 8667dae74b Merge pull request #4 from j0k3r/php53
Restore compatibility with PHP 5.3
2015-06-11 09:39:26 +02:00
Jeremy Benoist eecae93161 Try hhvm instead of nightly
HHVM nightly is no longer supported on Ubuntu Precise.
See https://github.com/travis-ci/travis-ci/issues/3788 and https://github.com/facebook/hhvm/issues/5220
2015-06-10 09:33:25 +02:00
Jeremy Benoist 814c6e4730 Restore compatibility with PHP 5.3 2015-06-10 09:33:21 +02:00
Jeremy Benoist b69619d386 Merge pull request #3 from j0k3r/phpunit-travis
Improve Travis
2015-04-29 10:27:50 +02:00
Jeremy Benoist 1963319a55 Improve Travis & add Scrutinizer
+ CS
+ Update README
2015-04-29 10:24:24 +02:00
Jeremy Benoist f5d473780d Fix javascript typo
And add coverage
2015-04-29 10:24:20 +02:00
19 changed files with 2154 additions and 932 deletions
+3 -2
View File
@@ -4,9 +4,10 @@ root = true
; Unix-style newlines
[*]
end_of_line = LF
[*.php]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
[.github/**.yml]
indent_size = 2
+11
View File
@@ -0,0 +1,11 @@
/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.scrutinizer.yml export-ignore
/.travis.yml export-ignore
/.php_cs export-ignore
/phpunit.xml.dist export-ignore
/phpstan.neon export-ignore
/rector.php export-ignore
/.github export-ignore
/tests export-ignore
+1
View File
@@ -0,0 +1 @@
github: j0k3r
+47
View File
@@ -0,0 +1,47 @@
name: "CS"
on:
pull_request:
branches:
- master
push:
branches:
- master
jobs:
coding-standards:
name: "CS Fixer & PHPStan"
runs-on: "ubuntu-20.04"
strategy:
matrix:
php:
- "7.2"
steps:
- name: "Checkout"
uses: "actions/checkout@v3"
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "${{ matrix.php }}"
tools: cs2pr, composer:v2
ini-values: "date.timezone=Europe/Paris"
env:
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v2"
with:
composer-options: "--optimize-autoloader --prefer-dist"
- name: "Run PHP CS Fixer"
run: "php vendor/bin/php-cs-fixer fix --verbose --dry-run --format=checkstyle | cs2pr"
- name: "Install PHPUnit for PHPStan"
run: "php vendor/bin/simple-phpunit install"
- name: "Run PHPStan"
run: "php vendor/bin/phpstan analyse --error-format=checkstyle | cs2pr"
@@ -0,0 +1,151 @@
name: "CI"
on:
pull_request:
branches:
- "master"
push:
branches:
- "master"
env:
fail-fast: true
jobs:
phpunit:
name: "PHPUnit (PHP ${{ matrix.php }})"
runs-on: "ubuntu-20.04"
strategy:
matrix:
php:
- "7.2"
- "7.3"
- "7.4"
- "8.0"
- "8.1"
- "8.2"
- "8.3"
steps:
- name: "Checkout"
uses: "actions/checkout@v3"
with:
fetch-depth: 2
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php }}"
coverage: "none"
tools: composer:v2
extensions: tidy
ini-values: "date.timezone=Europe/Paris"
env:
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: "Remove useless deps"
run: "composer remove friendsofphp/php-cs-fixer --dev --no-progress --no-update"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v2"
with:
composer-options: "--optimize-autoloader --prefer-dist"
- name: "Setup logs"
run: "mkdir -p build/logs"
- name: "Run PHPUnit"
run: "php vendor/bin/simple-phpunit -v"
phpunit-coverage:
name: "PHPUnit coverage (PHP ${{ matrix.php }})"
runs-on: "ubuntu-20.04"
strategy:
matrix:
php:
- "7.4"
steps:
- name: "Checkout"
uses: "actions/checkout@v3"
with:
fetch-depth: 2
- name: "Install PHP with Xdebug"
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php }}"
coverage: "xdebug"
tools: composer:v2
extensions: tidy
ini-values: "date.timezone=Europe/Paris"
env:
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: "Remove useless deps"
run: "composer remove friendsofphp/php-cs-fixer --dev --no-progress --no-update"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v2"
with:
composer-options: "--optimize-autoloader --prefer-dist"
- name: "Setup logs"
run: "mkdir -p build/logs"
- name: "Run PHPUnit (with coverage)"
run: "php vendor/bin/simple-phpunit -v --coverage-clover build/logs/clover.xml"
- name: "Retrieve Coveralls phar"
run: "wget https://github.com/php-coveralls/php-coveralls/releases/download/v2.4.2/php-coveralls.phar"
- name: "Enable Coveralls phar"
run: "chmod +x php-coveralls.phar"
- name: "Upload to Coveralls"
run: "php php-coveralls.phar -v -x build/logs/clover.xml"
env:
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
phpunit-lowest:
name: "PHPUnit lowest deps (PHP ${{ matrix.php }})"
runs-on: "ubuntu-20.04"
strategy:
matrix:
php:
- "7.2"
steps:
- name: "Checkout"
uses: "actions/checkout@v3"
with:
fetch-depth: 2
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php }}"
coverage: "none"
tools: composer:v2
extensions: tidy
ini-values: "date.timezone=Europe/Paris"
env:
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: "Remove useless deps"
run: "composer remove friendsofphp/php-cs-fixer --dev --no-progress --no-update"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v2"
with:
composer-options: "--optimize-autoloader --prefer-dist"
dependency-versions: "lowest"
- name: "Setup logs"
run: "mkdir -p build/logs"
- name: "Run PHPUnit"
run: "php vendor/bin/simple-phpunit -v"
+5
View File
@@ -1 +1,6 @@
vendor/
coverage/
composer.lock
.php_cs.cache
.php-cs-fixer.cache
.phpunit.result.cache
+31
View File
@@ -0,0 +1,31 @@
<?php
$finder = (new PhpCsFixer\Finder())
->in(__DIR__)
->exclude(['vendor', 'var', 'web'])
;
return (new PhpCsFixer\Config())
->setUsingCache(true)
->setRiskyAllowed(true)
->setRules([
'@Symfony' => true,
'@Symfony:risky' => true,
'array_syntax' => ['syntax' => 'short'],
'combine_consecutive_unsets' => true,
'heredoc_to_nowdoc' => true,
'no_extra_blank_lines' => ['tokens' => ['break', 'continue', 'extra', 'return', 'throw', 'use', 'parenthesis_brace_block', 'square_brace_block', 'curly_brace_block']],
'no_unreachable_default_argument_value' => true,
'no_useless_else' => true,
'no_useless_return' => true,
'ordered_class_elements' => true,
'ordered_imports' => true,
'php_unit_strict' => false,
'phpdoc_order' => true,
// 'psr4' => true,
'strict_comparison' => true,
'strict_param' => true,
'concat_space' => ['spacing' => 'one'],
])
->setFinder($finder)
;
-13
View File
@@ -1,13 +0,0 @@
language: php
php:
- 5.4
- 5.5
- 5.6
before_script:
- composer self-update
- composer install --prefer-dist --no-interaction
script:
- phpunit --coverage-text
+31 -3
View File
@@ -1,12 +1,15 @@
# Readability
[![Build Status](https://travis-ci.org/j0k3r/php-readability.svg?branch=master)](https://travis-ci.org/j0k3r/php-readability)
![CI](https://github.com/j0k3r/php-readability/workflows/CI/badge.svg)
[![Coverage Status](https://coveralls.io/repos/j0k3r/php-readability/badge.svg?branch=master&service=github)](https://coveralls.io/github/j0k3r/php-readability/?branch=master)
[![Total Downloads](https://poser.pugx.org/j0k3r/php-readability/downloads)](https://packagist.org/packages/j0k3r/php-readability)
[![License](https://poser.pugx.org/j0k3r/php-readability/license)](https://packagist.org/packages/j0k3r/php-readability)
This is an extract of the Readability class from the [full-text-rss](https://github.com/Dither/full-text-rss) fork. It kind be defined as a better version of the original [php-readability](http://code.fivefilters.org/php-readability).
This is an extract of the Readability class from this [full-text-rss](https://github.com/Dither/full-text-rss) fork. It can be defined as a better version of the original [php-readability](https://bitbucket.org/fivefilters/php-readability).
## Differences
The default php-readability lib is really old and needs to be improved. I found a great fork of [full-text-rss](http://fivefilters.org/content-only/) from @Dither which improve the Readability class.
The default php-readability lib is really old and needs to be improved. I found a great fork of full-text-rss from [@Dither](https://github.com/Dither/full-text-rss) which improve the Readability class.
- I've extracted the class from its fork to be able to use it out of the box
- I've added some simple tests
@@ -14,6 +17,12 @@ The default php-readability lib is really old and needs to be improved. I found
**But** the code is still really hard to understand / read ...
## Requirements
By default, this lib will use the [Tidy extension](https://github.com/htacg/tidy-html5) if it's available. Tidy is only used to cleanup the given HTML and avoid problems with bad HTML structure, etc .. It'll be suggested by Composer.
Also, if you got problem from parsing a content without Tidy installed, please install it and try again.
## Usage
```php
@@ -25,6 +34,8 @@ $url = 'http://www.medialens.org/index.php/alerts/alert-archive/alerts-2013/729-
$html = file_get_contents($url);
$readability = new Readability($html, $url);
// or without Tidy
// $readability = new Readability($html, $url, 'libxml', false);
$result = $readability->init();
if ($result) {
@@ -36,3 +47,20 @@ if ($result) {
echo 'Looks like we couldn\'t find the content. :(';
}
```
If you want to debug it, or check what's going on, you can inject a logger (which must follow `Psr\Log\LoggerInterface`, Monolog for example):
```php
use Readability\Readability;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$url = 'http://www.medialens.org/index.php/alerts/alert-archive/alerts-2013/729-thatcher.html';
$html = file_get_contents($url);
$logger = new Logger('readability');
$logger->pushHandler(new StreamHandler('path/to/your.log', Logger::DEBUG));
$readability = new Readability($html, $url);
$readability->setLogger($logger);
```
+24 -2
View File
@@ -24,10 +24,32 @@
"role": "Developer (original JS version)"
}],
"require": {
"php": ">=5.4",
"ext-tidy": ">=1.2"
"php": ">=7.2.0",
"ext-mbstring": "*",
"psr/log": "^1.0.1 || ^2.0 || ^3.0",
"masterminds/html5": "^2.7"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.0",
"monolog/monolog": "^1.24|^2.1",
"symfony/phpunit-bridge": "^4.4|^5.3|^6.0",
"phpstan/phpstan": "^1.3",
"phpstan/phpstan-phpunit": "^1.0",
"rector/rector": "^0.15.0"
},
"suggest": {
"ext-tidy": "Used to clean up given HTML and to avoid problems with bad HTML structure."
},
"autoload": {
"psr-4": { "Readability\\": "src/" }
},
"autoload-dev": {
"psr-4": { "Tests\\Readability\\": "tests/" }
},
"scripts": {
"fix": "php-cs-fixer fix --verbose --diff",
"phpstan": "phpstan analyze --memory-limit 512M",
"rector": "rector process",
"test": "simple-phpunit -v"
}
}
+15
View File
@@ -0,0 +1,15 @@
parameters:
level: 1
paths:
- src
- tests
# https://github.com/phpstan/phpstan/issues/694#issuecomment-350724288
bootstrapFiles:
- vendor/bin/.phpunit/phpunit/vendor/autoload.php
checkMissingIterableValueType: false
includes:
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
+5 -3
View File
@@ -7,23 +7,25 @@
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="vendor/autoload.php"
>
<testsuites>
<testsuite name="Readability Test Suite">
<testsuite name="Readability">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./src/TubeLink/</directory>
<directory>./src/</directory>
<exclude>
<directory>./tests</directory>
</exclude>
</whitelist>
</filter>
<!-- <logging>
<log type="coverage-html" target="coverage" lowUpperBound="35" highLowerBound="70"/>
</logging> -->
</phpunit>
+31
View File
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
use Rector\Config\RectorConfig;
use Rector\Core\ValueObject\PhpVersion;
use Rector\Set\ValueObject\LevelSetList;
return static function (RectorConfig $rectorConfig): void {
// paths to refactor; solid alternative to CLI arguments
$rectorConfig->paths([
__DIR__ . '/src',
__DIR__ . '/tests',
]);
// Path to phpstan with extensions, that PHPSTan in Rector uses to determine types
$rectorConfig->phpstanConfig(__DIR__ . '/phpstan.neon');
$rectorConfig->bootstrapFiles([
__DIR__ . '/vendor/bin/.phpunit/phpunit/vendor/autoload.php',
__DIR__ . '/vendor/autoload.php',
]);
// Define what rule sets will be applied
$rectorConfig->sets([
LevelSetList::UP_TO_PHP_72,
]);
// is your PHP version different from the one your refactor to?
$rectorConfig->phpVersion(PhpVersion::PHP_72);
};
+54 -26
View File
@@ -3,7 +3,7 @@
namespace Readability;
/**
* JavaScript-like HTML DOM Element
* JavaScript-like HTML DOM Element.
*
* This class extends PHP's DOMElement to allow
* users to get and set the innerHTML property of
@@ -31,28 +31,47 @@ namespace Readability;
* echo $doc->saveXML();
*
* @author Keyvan Minoukadeh - http://www.keyvan.net - keyvan@keyvan.net
*
* @see http://fivefilters.org (the project this was written for)
*/
class JSLikeHTMLElement extends \DOMElement
{
/**
* Used for setting innerHTML like it's done in JavaScript:
* @code
* Used for setting innerHTML like it's done in JavaScript:.
*
* ```php
* $div->innerHTML = '<h2>Chapter 2</h2><p>The story begins...</p>';
* @endcode
* ```
*/
public function __set($name, $value)
{
if ($name == 'innerHTML') {
if ('innerHTML' !== $name) {
$trace = debug_backtrace();
trigger_error('Undefined property via __set(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], \E_USER_NOTICE);
return;
}
// first, empty the element
for ($x=$this->childNodes->length-1; $x>=0; $x--) {
if (isset($this->childNodes)) {
for ($x = $this->childNodes->length - 1; $x >= 0; --$x) {
$this->removeChild($this->childNodes->item($x));
}
}
// $value holds our new inner HTML
if ($value != '') {
$value = trim($value);
if (empty($value)) {
return;
}
// ensure bad entity won't generate warning
$previousError = libxml_use_internal_errors(true);
$f = $this->ownerDocument->createDocumentFragment();
// appendXML() expects well-formed markup (XHTML)
$result = @$f->appendXML($value); // @ to suppress PHP warnings
$result = $f->appendXML($value);
if ($result) {
if ($f->hasChildNodes()) {
$this->appendChild($f);
@@ -60,56 +79,65 @@ class JSLikeHTMLElement extends \DOMElement
} else {
// $value is probably ill-formed
$f = new \DOMDocument();
$value = mb_convert_encoding($value, 'HTML-ENTITIES', 'UTF-8');
// Using <htmlfragment> will generate a warning, but so will bad HTML
// (and by this point, bad HTML is what we've got).
// We use it (and suppress the warning) because an HTML fragment will
// be wrapped around <html><body> tags which we don't really want to keep.
// Note: despite the warning, if loadHTML succeeds it will return true.
$result = @$f->loadHTML('<htmlfragment>'.$value.'</htmlfragment>');
$result = $f->loadHTML('<meta charset="utf-8"><htmlfragment>' . $value . '</htmlfragment>');
if ($result) {
$import = $f->getElementsByTagName('htmlfragment')->item(0);
foreach ($import->childNodes as $child) {
$importedNode = $this->ownerDocument->importNode($child, true);
$this->appendChild($importedNode);
}
} else {
// oh well, we tried, we really did. :(
// this element is now empty
}
}
}
} else {
$trace = debug_backtrace();
trigger_error('Undefined property via __set(): '.$name.' in '.$trace[0]['file'].' on line '.$trace[0]['line'], E_USER_NOTICE);
}
}
libxml_clear_errors();
libxml_use_internal_errors($previousError);
}
/**
* Used for getting innerHTML like it's done in JavaScript:
* @code
* Used for getting innerHTML like it's done in JavaScript:.
*
* ```php
* $string = $div->innerHTML;
* @endcode
* ```
*/
public function __get($name)
{
if ($name == 'innerHTML') {
if ('innerHTML' === $name) {
$inner = '';
if (isset($this->childNodes)) {
foreach ($this->childNodes as $child) {
$inner .= $this->ownerDocument->saveXML($child);
}
}
return $inner;
}
$trace = debug_backtrace();
trigger_error('Undefined property via __get(): '.$name.' in '.$trace[0]['file'].' on line '.$trace[0]['line'], E_USER_NOTICE);
return null;
trigger_error('Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], \E_USER_NOTICE);
}
public function __toString()
{
return '[' . $this->tagName . ']';
}
public function getInnerHtml()
{
return $this->__get('innerHTML');
}
public function setInnerHtml($value)
{
return $this->__set('innerHTML', $value);
}
}
+999 -738
View File
File diff suppressed because it is too large Load Diff
+452 -109
View File
@@ -2,219 +2,562 @@
namespace Tests\Readability;
use Readability\Readability;
use Monolog\Handler\TestHandler;
use Monolog\Logger;
use Psr\Log\LoggerInterface;
use Readability\JSLikeHTMLElement;
use Readability\Readability;
class ReadabilityTested extends Readability
class ReadabilityTest extends \PHPUnit\Framework\TestCase
{
public function getDebugText()
{
return $this->debugText;
}
/** @var TestHandler */
public $logHandler;
/** @var LoggerInterface */
public $logger;
public function getDomainRegexp()
/**
* @requires extension tidy
*/
public function testConstructDefault(): void
{
return $this->domainRegExp;
}
}
class ReadabilityTest extends \PHPUnit_Framework_TestCase
{
public function testConstructDefault()
{
$readability = new ReadabilityTested('');
$readability = $this->getReadability('');
$readability->init();
$this->assertNull($readability->url);
$this->assertContains('Parsing URL', $readability->getDebugText());
$this->assertContains('Tidying document', $readability->getDebugText());
$this->assertNull($readability->getDomainRegexp());
$this->assertInstanceOf('DomDocument', $readability->dom);
}
public function testConstructSimple()
public function testConstructHtml5Parser(): void
{
$readability = new ReadabilityTested('<html/>', 'http://0.0.0.0');
$readability = $this->getReadability('<html/>', 'http://0.0.0.0', 'html5lib');
$readability->init();
$this->assertSame('http://0.0.0.0', $readability->url);
$this->assertInstanceOf('DomDocument', $readability->dom);
$this->assertSame('<html/>', $readability->original_html);
}
/**
* @requires extension tidy
*/
public function testConstructSimple(): void
{
$readability = $this->getReadability('<html/>', 'http://0.0.0.0');
$readability->init();
$this->assertSame('http://0.0.0.0', $readability->url);
$this->assertInstanceOf('DomDocument', $readability->dom);
$this->assertSame('<html/>', $readability->original_html);
$this->assertTrue($readability->tidied);
}
public function testConstructDefaultWithoutTidy(): void
{
$readability = $this->getReadability('', null, 'libxml', false);
$readability->init();
$this->assertNull($readability->url);
$this->assertSame('', $readability->original_html);
$this->assertFalse($readability->tidied);
$this->assertEquals('http://0.0.0.0', $readability->url);
$this->assertContains('Parsing URL', $readability->getDebugText());
$this->assertContains('Tidying document', $readability->getDebugText());
$this->assertEquals('/0\.0\.0\.0/', $readability->getDomainRegexp());
$this->assertInstanceOf('DomDocument', $readability->dom);
}
public function testInitNoContent()
public function testConstructSimpleWithoutTidy(): void
{
$readability = new ReadabilityTested('<html/>', 'http://0.0.0.0');
$readability = $this->getReadability('<html/>', 'http://0.0.0.0', 'libxml', false);
$readability->init();
$this->assertSame('http://0.0.0.0', $readability->url);
$this->assertInstanceOf('DomDocument', $readability->dom);
$this->assertSame('<html/>', $readability->original_html);
$this->assertFalse($readability->tidied);
}
public function testInitNoContent(): void
{
$readability = $this->getReadability('<html/>', 'http://0.0.0.0');
$res = $readability->init();
$this->assertFalse($res);
$this->assertInstanceOf('Readability\JSLikeHTMLElement', $readability->getContent());
$this->assertContains('Sorry, Readability was unable to parse this page for content.', $readability->getContent()->innerHTML);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('Sorry, Readability was unable to parse this page for content.', $readability->getContent()->getInnerHtml());
}
public function testInitP()
public function testInitP(): void
{
$readability = new ReadabilityTested(str_repeat('<p>This is the awesome content :)</p>', 7), 'http://0.0.0.0');
$readability = $this->getReadability(str_repeat('<p>This is the awesome content :)</p>', 7), 'http://0.0.0.0');
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf('Readability\JSLikeHTMLElement', $readability->getContent());
$this->assertContains('<div readability=', $readability->getContent()->innerHTML);
$this->assertContains('This is the awesome content :)', $readability->getContent()->innerHTML);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('<div readability=', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is the awesome content :)', $readability->getContent()->getInnerHtml());
}
public function testInitDivP()
public function testInitDivP(): void
{
$readability = new ReadabilityTested('<div>'.str_repeat('<p>This is the awesome content :)</p>', 7).'</div>', 'http://0.0.0.0');
$readability = $this->getReadability('<div>' . str_repeat('<p>This is the awesome content :)</p>', 7) . '</div>', 'http://0.0.0.0');
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf('Readability\JSLikeHTMLElement', $readability->getContent());
$this->assertContains('<div readability=', $readability->getContent()->innerHTML);
$this->assertContains('This is the awesome content :)', $readability->getContent()->innerHTML);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('<div readability=', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is the awesome content :)', $readability->getContent()->getInnerHtml());
}
public function testInitDiv()
public function testInitDiv(): void
{
$readability = new ReadabilityTested('<div>'.str_repeat('This is the awesome content :)', 7).'</div>', 'http://0.0.0.0');
$readability = $this->getReadability('<div>' . str_repeat('This is the awesome content :)', 7) . '</div>', 'http://0.0.0.0');
$readability->debug = true;
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf('Readability\JSLikeHTMLElement', $readability->getContent());
$this->assertContains('<div readability=', $readability->getContent()->innerHTML);
$this->assertContains('This is the awesome content :)', $readability->getContent()->innerHTML);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('<div readability=', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is the awesome content :)', $readability->getContent()->getInnerHtml());
}
public function testWithFootnotes()
public function testWithFootnotes(): void
{
$readability = new ReadabilityTested('<div>'.str_repeat('<p>This is an awesome text with some links, here there are: <a href="http://0.0.0.0/test.html">the awesome</a></p>', 7).'</div>', 'http://0.0.0.0');
$readability = $this->getReadability('<div>' . str_repeat('<p>This is an awesome text with some links, here there are: <a href="http://0.0.0.0/test.html">the awesome</a></p>', 7) . '</div>', 'http://0.0.0.0');
$readability->debug = true;
$readability->convertLinksToFootnotes = true;
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf('Readability\JSLikeHTMLElement', $readability->getContent());
$this->assertContains('<div readability=', $readability->getContent()->innerHTML);
$this->assertContains('This is an awesome text with some links, here there are', $readability->getContent()->innerHTML);
$this->assertContains('readabilityFootnoteLink', $readability->getContent()->innerHTML);
$this->assertContains('readabilityLink-3', $readability->getContent()->innerHTML);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('<div readability=', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringContainsString('readabilityFootnoteLink', $readability->getContent()->getInnerHtml());
$this->assertStringContainsString('readabilityLink-3', $readability->getContent()->getInnerHtml());
}
public function testStandardClean()
public function testStandardClean(): void
{
$readability = new ReadabilityTested('<div><h2>Title</h2>'.str_repeat('<p>This is an awesome text with some links, here there are: <a href="http://0.0.0.0/test.html">the awesome</a></p>', 7).'<a href="#nofollow" rel="nofollow">will be removed</a></div>', 'http://0.0.0.0');
$readability = $this->getReadability('<div><h2>Title</h2>' . str_repeat('<p>This is an awesome text with some links, here there are: <a href="http://0.0.0.0/test.html">the awesome</a></p>', 7) . '<a href="#nofollow" rel="nofollow">will NOT be removed</a></div>', 'http://0.0.0.0');
$readability->debug = true;
$readability->lightClean = false;
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf('Readability\JSLikeHTMLElement', $readability->getContent());
$this->assertContains('<div readability=', $readability->getContent()->innerHTML);
$this->assertContains('This is an awesome text with some links, here there are', $readability->getContent()->innerHTML);
$this->assertNotContains('will be removed', $readability->getContent()->innerHTML);
$this->assertNotContains('<h2>', $readability->getContent()->innerHTML);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('<div readability=', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringContainsString('will NOT be removed', $readability->getContent()->getInnerHtml());
$this->assertStringNotContainsString('<h2>', $readability->getContent()->getInnerHtml());
}
public function testWithIframe()
public function testWithIframe(): void
{
$readability = new ReadabilityTested('<div><h2>Title</h2>'.str_repeat('<p>This is an awesome text with some links, here there are: <a href="http://0.0.0.0/test.html">the awesome</a></p>', 7).'<p>This is an awesome text with some links, here there are <iframe src="http://youtube.com/test" href="#nofollow" rel="nofollow"></iframe><iframe>http://soundcloud.com/test</iframe></p></div>', 'http://0.0.0.0');
$readability = $this->getReadability('<div><h2>Title</h2>' . str_repeat('<p>This is an awesome text with some links, here there are: <a href="http://0.0.0.0/test.html">the awesome</a></p>', 7) . '<p>This is an awesome text with some links, here there are <iframe src="http://youtube.com/test" href="#nofollow" rel="nofollow"></iframe><iframe>http://soundcloud.com/test</iframe></p></div>', 'http://0.0.0.0');
$readability->debug = true;
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf('Readability\JSLikeHTMLElement', $readability->getContent());
$this->assertContains('<div readability=', $readability->getContent()->innerHTML);
$this->assertContains('This is an awesome text with some links, here there are', $readability->getContent()->innerHTML);
$this->assertContains('nofollow', $readability->getContent()->innerHTML);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('<div readability=', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringContainsString('nofollow', $readability->getContent()->getInnerHtml());
}
public function testWithArticle()
public function testWithArticle(): void
{
$readability = new ReadabilityTested('<article><p>'.str_repeat('This is an awesome text with some links, here there are: the awesome', 20).'</p><p>This is an awesome text with some links, here there are <iframe src="http://youtube.com/test" href="#nofollow" rel="nofollow"></iframe></p></article>', 'http://0.0.0.0');
$readability = $this->getReadability('<article><p>' . str_repeat('This is an awesome text with some links, here there are: the awesome', 20) . '</p><p>This is an awesome text with some links, here there are <iframe src="http://youtube.com/test" href="#nofollow" rel="nofollow"></iframe></p></article>', 'http://0.0.0.0');
$readability->debug = true;
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf('Readability\JSLikeHTMLElement', $readability->getContent());
$this->assertContains('alt="article"', $readability->getContent()->innerHTML);
$this->assertContains('This is an awesome text with some links, here there are', $readability->getContent()->innerHTML);
$this->assertContains('nofollow', $readability->getContent()->innerHTML);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('alt="article"', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringContainsString('nofollow', $readability->getContent()->getInnerHtml());
}
public function testWithAside()
public function testWithAside(): void
{
$readability = new ReadabilityTested('<article>'.str_repeat('<p>This is an awesome text with some links, here there are: <a href="http://0.0.0.0/test.html">the awesome</a></p>', 7).'<footer><aside>'.str_repeat('<p>This is an awesome text with some links, here there are</p>', 8).'</aside></footer></article>', 'http://0.0.0.0');
$readability = $this->getReadability('<article>' . str_repeat('<p>This is an awesome text with some links, here there are: <a href="http://0.0.0.0/test.html">the awesome</a></p>', 7) . '<footer><aside>' . str_repeat('<p>This is an awesome text with some links, here there are</p>', 8) . '</aside></footer></article>', 'http://0.0.0.0');
$readability->debug = true;
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf('Readability\JSLikeHTMLElement', $readability->getContent());
$this->assertContains('alt="article"', $readability->getContent()->innerHTML);
$this->assertContains('This is an awesome text with some links, here there are', $readability->getContent()->innerHTML);
$this->assertNotContains('<aside>', $readability->getContent()->innerHTML);
$this->assertContains('<footer/>', $readability->getContent()->innerHTML);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringNotContainsString('<aside>', $readability->getContent()->getInnerHtml());
$this->assertStringContainsString('<footer readability="9"/>', $readability->getContent()->getInnerHtml());
}
public function testWithClasses()
public function testWithClasses(): void
{
$readability = new ReadabilityTested('<article>'.str_repeat('<p>This is an awesome text with some links, here there are: <a href="http://0.0.0.0/test.html">the awesome</a></p>', 7).'<div style="display:none">'.str_repeat('<p class="clock">This text should be removed</p>', 10).'</div></article>', 'http://0.0.0.0');
$readability = $this->getReadability('<article>' . str_repeat('<p>This is an awesome text with some links, here there are: <a href="http://0.0.0.0/test.html">the awesome</a></p>', 7) . '<div style="display:none">' . str_repeat('<p class="clock">This text should be removed</p>', 10) . '</div></article>', 'http://0.0.0.0');
$readability->debug = true;
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf('Readability\JSLikeHTMLElement', $readability->getContent());
$this->assertContains('alt="article"', $readability->getContent()->innerHTML);
$this->assertContains('This is an awesome text with some links, here there are', $readability->getContent()->innerHTML);
$this->assertNotContains('This text should be removed', $readability->getContent()->innerHTML);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('alt="article"', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringNotContainsString('This text should be removed', $readability->getContent()->getInnerHtml());
}
public function testWithTd()
public function testWithClassesWithoutLightClean(): void
{
$readability = new ReadabilityTested('<table><tr>'.str_repeat('<td><p>This is an awesome text with some links, here there are the awesome</td>', 7).'</tr></table>', 'http://0.0.0.0');
$readability = $this->getReadability('<article>' . str_repeat('<p>This is an awesome text with some links, here there are: <a href="http://0.0.0.0/test.html">the awesome</a></p>', 7) . '<div style="display:none">' . str_repeat('<p class="clock">This text should be removed</p>', 10) . '</div></article>', 'http://0.0.0.0');
$readability->debug = true;
$readability->lightClean = false;
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('alt="article"', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringNotContainsString('This text should be removed', $readability->getContent()->getInnerHtml());
}
public function testWithTd(): void
{
$readability = $this->getReadability('<table><tr>' . str_repeat('<td><p>This is an awesome text with some links, here there are the awesome</td>', 7) . '</tr></table>', 'http://0.0.0.0');
$readability->debug = true;
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf('Readability\JSLikeHTMLElement', $readability->getContent());
$this->assertContains('alt="tr"', $readability->getContent()->innerHTML);
$this->assertContains('This is an awesome text with some links, here there are', $readability->getContent()->innerHTML);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
}
public function testWithSameClasses()
public function testWithSameClasses(): void
{
$readability = new ReadabilityTested('<article class="awesomecontent">'.str_repeat('<p>This is an awesome text with some links, here there are the awesome</p>', 7).'<div class="awesomecontent">This text is also an awesome text and you should know that !</div></article>', 'http://0.0.0.0');
$readability = $this->getReadability('<article class="awesomecontent">' . str_repeat('<p>This is an awesome text with some links, here there are the awesome</p>', 7) . '<div class="awesomecontent">This text is also an awesome text and you should know that !</div></article>', 'http://0.0.0.0');
$readability->debug = true;
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf('Readability\JSLikeHTMLElement', $readability->getContent());
$this->assertContains('alt="article"', $readability->getContent()->innerHTML);
$this->assertContains('This is an awesome text with some links, here there are', $readability->getContent()->innerHTML);
$this->assertContains('This text is also an awesome text and you should know that', $readability->getContent()->innerHTML);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringContainsString('This text is also an awesome text and you should know that', $readability->getContent()->getInnerHtml());
}
public function testWithScript()
public function testWithScript(): void
{
$readability = new ReadabilityTested('<article class="awesomecontent">'.str_repeat('<p>This is an awesome text with some links, here there are the awesome</p>', 7).'<p><script>This text is also an awesome text and you should know that !</script></p></article>', 'http://0.0.0.0');
$readability = $this->getReadability('<article class="awesomecontent">' . str_repeat('<p>This is an awesome text with some links, here there are the awesome</p>', 7) . '<p><script>This text is also an awesome text and you should know that !</script></p></article>', 'http://0.0.0.0');
$readability->debug = true;
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf('Readability\JSLikeHTMLElement', $readability->getContent());
$this->assertContains('alt="article"', $readability->getContent()->innerHTML);
$this->assertContains('This is an awesome text with some links, here there are', $readability->getContent()->innerHTML);
$this->assertNotContains('This text is also an awesome text and you should know that', $readability->getContent()->innerHTML);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringNotContainsString('This text is also an awesome text and you should know that', $readability->getContent()->getInnerHtml());
}
// public function testConstructParser()
// {
// $readability = new ReadabilityTested('<html/>', 'http://0.0.0.0', 'html5lib');
public function testTitle(): void
{
$readability = $this->getReadability('<title>this is my title</title><article class="awesomecontent">' . str_repeat('<p>This is an awesome text with some links, here there are the awesome</p>', 7) . '<p></p></article>', 'http://0.0.0.0');
$readability->debug = true;
$res = $readability->init();
// $this->assertEquals('http://0.0.0.0', $readability->url);
// $this->assertContains('Parsing URL', $readability->getDebugText());
// $this->assertContains('Tidying document', $readability->getDebugText());
// $this->assertEquals('/0\.0\.0\.0/', $readability->getDomainRegexp());
// $this->assertInstanceOf('DomDocument', $readability->dom);
// }
$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertSame('this is my title', $readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringNotContainsString('This text is also an awesome text and you should know that', $readability->getContent()->getInnerHtml());
}
public function testTitleWithDash(): void
{
$readability = $this->getReadability('<title> title2 - title3 </title><article class="awesomecontent">' . str_repeat('<p>This is an awesome text with some links, here there are the awesome</p>', 7) . '<p></p></article>', 'http://0.0.0.0');
$readability->debug = true;
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertSame('title2 - title3', $readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringNotContainsString('This text is also an awesome text and you should know that', $readability->getContent()->getInnerHtml());
}
public function testTitleWithDoubleDot(): void
{
$readability = $this->getReadability('<title> title2 : title3 </title><article class="awesomecontent">' . str_repeat('<p>This is an awesome text with some links, here there are the awesome</p>', 7) . '<p></p></article>', 'http://0.0.0.0');
$readability->debug = true;
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertSame('title2 : title3', $readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringNotContainsString('This text is also an awesome text and you should know that', $readability->getContent()->getInnerHtml());
}
public function testTitleTooShortUseH1(): void
{
$readability = $this->getReadability('<title>too short</title><h1>this is my h1 title !</h1><article class="awesomecontent">' . str_repeat('<p>This is an awesome text with some links, here there are the awesome</p>', 7) . '<p></p></article>', 'http://0.0.0.0');
$readability->debug = true;
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertSame('this is my h1 title !', $readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringNotContainsString('This text is also an awesome text and you should know that', $readability->getContent()->getInnerHtml());
}
public function testAutoClosingIframeNotThrowingException(): void
{
$oldErrorReporting = error_reporting(\E_ALL | \E_STRICT);
$oldDisplayErrors = ini_set('display_errors', '1');
// dummy function to be used to the next test
set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline, array $errcontext) {
throw new \Exception($errstr, $errno);
}, \E_ALL | \E_STRICT);
try {
$data = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ru-RU" prefix="og: http://ogp.me/ns#">
<head profile="http://gmpg.org/xfn/11">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body class="single single-post postid-22030 single-format-standard">
<div id="wrapper">
<div id="content">
<div class="post-22030 post type-post status-publish format-standard has-post-thumbnail hentry category-video category-reviews tag-193" id="post-22030">
<h1>3D Touch &#8212; будущее мобильных игр</h1>
<div class="postdate">Автор: <strong>Сергей Пак</strong> | Просмотров: 1363 | Опубликовано: 14 сентября 2015 </div>
<div class="entry">
<p>Компания Apple представила новую технологию 3D Touch, которая является прямым потомком более ранней версии Force Touch &#8212; последняя, напомним, используется сейчас в трекпадах Macbook Pro и Macbook 2015. Теперь управлять устройством стало в разы проще, и Force Touch открывает перед пользователями новые возможности, но при этом 3D Touch &#8212; это про другое. Дело в том, что теперь и на мобильных устройствах интерфейс будет постепенно меняться, кардинальные перемены ждут мобильный гейминг, потому что здесь разработчики действительно могут разгуляться.<span id="more-22030"></span></p>
<p><iframe src="https://www.youtube.com/embed/PUep6xNeKjA" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>
<p>Итак, просто представьте себе, что iPhone 6S &#8212; это, по большому счету, отличная игровая приставка, которую вы носите с собой, а еще она может выдавать невероятной красоты картинку. Но проблема заключается, пожалуй, в том, что управлять персонажем в играх довольно трудно &#8212; он неповоротлив, обладает заторможенной реакцией, а игровой клиент зачастую требует перегруза интерфейса для того, чтобы обеспечить максимально большое количество возможностей. Благодаря трехуровневому нажатию можно избавиться от лишних кнопок и обеспечить более качественный обзор местности, и при этом пользователь будет закрывать пальцами минимальное пространство.</p>
</div>
</div>
</div>
</div>
</body>
</html>';
$readability = $this->getReadability($data, 'http://iosgames.ru/?p=22030');
$readability->debug = true;
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('<iframe src="https://www.youtube.com/embed/PUep6xNeKjA" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"> </iframe>', $readability->getContent()->getInnerHtml());
$this->assertStringContainsString('3D Touch', $readability->getTitle()->getInnerHtml());
} finally {
restore_error_handler();
if (false !== $oldDisplayErrors) {
ini_set('display_errors', $oldDisplayErrors);
}
error_reporting($oldErrorReporting);
}
}
/**
* This should generate an Exception "DOMElement::setAttribute(): ID post-60 already defined".
*/
public function testAppendIdAlreadyHere(): void
{
$data = '<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0">
</head>
<body>
<div class="container">
<header class="header sml-text-center med-text-left" role="banner">
<h1 class="no-margin"><a class="maintitle" href="https://0.0.0.0" title="Bloc-notes">Bloc-notes</a></h1>
<h2 class="h5 no-margin"></h2>
</header>
<nav class="nav" role="navigation">
<div class="responsive-menu">
<label for="menu">Menu</label>
<input type="checkbox" id="menu">
</div>
</nav>
<article class="article" role="article" id="post-60">
<section>
This is an awesome text with some links, here there are<br/>
This is an awesome text with some links, here there are<br/>
This is an awesome text with some links, here there are<br/>
This is an awesome text with some links, here there are<br/>
This is an awesome text with some links, here there are<br/>
This is an awesome text with some links, here there are<br/>
This is an awesome text with some links, here there are<br/>
This is an awesome text with some links, here there are<br/>
This is an awesome text with some links, here there are<br/>
This is an awesome text with some links, here there are<br/>
This is an awesome text with some links, here there are
</section>
<footer>
<small>
Classé dans : <a class="noactive" title="Services réseaux">Services réseaux</a>
</small>
</footer>
</article>
</div>
</body>
</html>';
$readability = $this->getReadability($data, 'http://0.0.0.0');
$readability->debug = true;
$res = $readability->init();
$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
}
public function testPostFilters(): void
{
$readability = $this->getReadability('<div>' . str_repeat('<p>This <strong>is</strong> the awesome content :)</p>', 10) . '</div>', 'http://0.0.0.0');
$readability->addPostFilter('!<strong[^>]*>(.*?)</strong>!is', '');
$res = $readability->init();
$this->assertTrue($res);
$this->assertStringContainsString('This the awesome content :)', $readability->getContent()->getInnerHtml());
}
public function testPreFilters(): void
{
$readability = $this->getReadability('<div>' . str_repeat('<p>This <b>is</b> the awesome and WONDERFUL content :)</p>', 7) . '</div>', 'http://0.0.0.0');
$readability->addPreFilter('!<b[^>]*>(.*?)</b>!is', '');
$res = $readability->init();
$this->assertTrue($res);
$this->assertStringContainsString('This the awesome and WONDERFUL content :)', $readability->getContent()->getInnerHtml());
}
public function testChildNodeGoneNull(): void
{
// from http://www.ayyaantuu.net/ethiopia-targets-opposition-lawmakers/
$html = (string) file_get_contents('tests/fixtures/childNodeGoesNull.html');
$readability = $this->getReadability($html, 'http://0.0.0.0');
$readability->debug = true;
$readability->convertLinksToFootnotes = true;
$res = $readability->init();
$this->assertTrue($res);
}
public function testKeepFootnotes(): void
{
// from https://www.schreibdichte.de/blog/feed-aggregator-und-spaeter-lesen-dienst-im-team
$html = (string) file_get_contents('tests/fixtures/keepFootnotes.html');
$readability = $this->getReadability($html, 'http://0.0.0.0');
$readability->debug = true;
$res = $readability->init();
$this->assertTrue($res);
$this->assertStringContainsString('<sup id="fnref1:fnfeed_2"><a href="#fn:fnfeed_2" class="footnote-ref">2</a></sup>', $readability->getContent()->getInnerHtml());
$this->assertStringContainsString('<a href="#fnref1:fnfeed_2" rev="footnote"', $readability->getContent()->getInnerHtml());
}
public function testWithWipedBody(): void
{
// from https://www.cs.cmu.edu/~rgs/alice-table.html
$html = (string) file_get_contents('tests/fixtures/wipedBody.html');
$readability = $this->getReadability($html, 'http://0.0.0.0', 'libxml', false);
$readability->debug = true;
$res = $readability->init();
$this->assertTrue($res);
$this->assertStringContainsString('<a href="alice-I.html">Down the Rabbit-Hole</a>', $readability->getContent()->getInnerHtml());
}
public function dataForVisibleNode(): array
{
return [
'visible node' => [
'<div>' . str_repeat('<p>This <b>is</b> the awesome and WONDERFUL content :)</p>', 7) . '</div>',
true,
],
'display=none' => [
'<div style="display:none;">' . str_repeat('<p>This <b>is</b> the awesome and WONDERFUL content :)</p>', 7) . '</div>',
false,
],
'display=inline' => [
'<div style="display:inline;">' . str_repeat('<p>This <b>is</b> the awesome and WONDERFUL content :)</p>', 7) . '</div>',
true,
],
'hidden attribute' => [
'<div hidden>' . str_repeat('<p>This <b>is</b> the awesome and WONDERFUL content :)</p>', 7) . '</div>',
false,
],
'missing display' => [
'<div style="color:#ccc;">' . str_repeat('<p>This <b>is</b> the awesome and WONDERFUL content :)</p>', 7) . '</div>',
true,
],
];
}
/**
* @dataProvider dataForVisibleNode
*/
public function testVisibleNode(string $content, bool $shouldBeVisible): void
{
$readability = $this->getReadability($content, 'http://0.0.0.0');
$readability->debug = true;
$res = $readability->init();
if ($shouldBeVisible) {
$this->assertStringContainsString('WONDERFUL content', $readability->getContent()->getInnerHtml());
} else {
$this->assertStringNotContainsString('WONDERFUL content', $readability->getContent()->getInnerHtml());
}
}
private function getReadability(string $html, string $url = null, string $parser = 'libxml', bool $useTidy = true): Readability
{
$readability = new Readability($html, $url, $parser, $useTidy);
$this->logHandler = new TestHandler();
$this->logger = new Logger('test', [$this->logHandler]);
$readability->setLogger($this->logger);
return $readability;
}
}
+137
View File
@@ -0,0 +1,137 @@
<div id="main-sidebar-container">
<!-- #main Starts -->
<section id="main">
<?php if (function_exists ('adinserter')) echo adinserter (1); ?><article class="post-23842 post type-post status-publish format-standard hentry category-ethiopia">
<header>
<h1 class="title entry-title">Ethiopia targets opposition who met with European lawmakers</h1> </header>
<div class="post-meta"><span class="Small"><abbr class="date time published updated" title="2017-01-09T15:25:17-0500">January 9, 2017</abbr> <span class="Small">in</span> <span class="categories"><a href="http://www.ayyaantuu.net/category/horn-of-africa/ethiopia/" title="View all items in Ethiopia">Ethiopia</a></span> </div>
<section class="entry">
<div id="fb-root"></div>
<p><strong>Ethiopia targets opposition who met with European lawmakers</strong></p>
<p><span class="dateline"><img class="alignleft wp-image-22789 size-full" src="http://www.ayyaantuu.net/wp-content/uploads/2016/12/Ana-Gomes-Merera-Gudina.jpg" alt="Ethiopia targets opposition who met with European lawmakers" width="300" height="160" srcset="http://www.ayyaantuu.net/wp-content/uploads/2016/12/Ana-Gomes-Merera-Gudina.jpg 300w, http://www.ayyaantuu.net/wp-content/uploads/2016/12/Ana-Gomes-Merera-Gudina-200x107.jpg 200w" sizes="(max-width: 300px) 100vw, 300px" />ADDIS ABABA (<a href="http://www.foxnews.com/world/2017/01/09/ethiopia-targets-opposition-who-met-with-european-lawmakers.html">Fox News</a>) – </span>Ethiopia says it will not release a leading opposition figure detained under the country’s state of emergency after meeting with European lawmakers in Belgium.</p>
<!-- WP QUADS Content Ad Plugin v. 1.3.9 -->
<div class="quads-location quads-ad2" id="quads-ad2" style="float:left;margin:2px 2px 2px 0;">
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- Ad2016_12_14_3 -->
<ins class="adsbygoogle"
style="display:inline-block;width:336px;height:280px"
data-ad-client="ca-pub-6953193962442323"
data-ad-slot="4950645476"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</div>
<p>Prime Minister Hailemariam Desalegn told reporters on Monday that Merara Gudina of the Oromo Federalist Congress party instead will face justice.</p>
<p>The prime minister says “individuals in the European Parliament who are harboring anti-peace elements cannot save those who trespass the law of the country.”</p>
<p>Merara is one of 22,000 people the prime minister says were detained under the state of emergency declared in October after widespread anti-government protests.</p>
<p>Merara was arrested immediately after he returned from Belgium. He was accused of meeting with members of an armed Ethiopian opposition group in Brussels, an act banned under the emergency law.</p>
<div style="font-size:0px;height:0px;line-height:0px;margin:0;padding:0;clear:both"></div><aside class="mashsb-container mashsb-main "><div class="mashsb-box"><div class="mashsb-count mash-small" style="float:left;"><div class="counts mashsbcount">0</div><span class="mashsb-sharetext">SHARES</span></div><div class="mashsb-buttons"><a class="mashicon-facebook mash-small" href="http://www.facebook.com/sharer.php?u=http%3A%2F%2Fwww.ayyaantuu.net%2Fethiopia-targets-opposition-lawmakers%2F" target="_blank" rel="nofollow"><span class="icon"></span><span class="text">Share</span></a><a class="mashicon-twitter mash-small" href="https://twitter.com/intent/tweet?text=Ethiopia%20targets%20opposition%20who%20met%20with%20European%20lawmakers&url=http://www.ayyaantuu.net/?p=23842&via= ayyaantuu" target="_blank" rel="nofollow"><span class="icon"></span><span class="text">Tweet</span></a><div class="onoffswitch mash-small"></div><div class="secondary-shares" style="display:none;"><a class="mashicon-subscribe mash-small" href="#" target="_blank" rel="nofollow"><span class="icon"></span><span class="text">Subscribe</span></a><div class="onoffswitch2 mash-small" style="display:none;"></div></div></div>
</div>
<div style="clear:both;"></div><div class="mashsb-toggle-container"></div></aside>
<!-- Share buttons by mashshare.net - Version: 3.3.6--> </section><!-- /.entry -->
<div class="fix"></div>
<aside id="connect">
<h3>Subscribe</h3>
<div class="col-left">
<p>Subscribe to our e-mail newsletter to receive updates.</p>
<div class="social">
<a href="http://www.ayyaantuu.net/feed/" class="subscribe" title="RSS"></a>
<a target="_blank" href="https://twitter.com/ayyaantuu" class="twitter" title="Twitter"></a>
<a target="_blank" href="https://www.facebook.com/ayyaantuu.oromiyaa" class="facebook" title="Facebook"></a>
</div>
</div><!-- col-left -->
<div class="related-posts col-right">
<h4>Related Posts:</h4>
<div class="woo-sc-related-posts">
<ul>
<li class="post-id-23812">
<a href="http://www.ayyaantuu.net/country-new-african-millionaire/" title="Africa Business: The life of TPLF millionaire in Oromia/Ethiopia?" class="related-title"><span>Africa Business: The life of TPLF millionaire in Oromia/Ethiopia?</span></a>
</li>
<li class="post-id-23794">
<a href="http://www.ayyaantuu.net/ethiopian-girl-band-funding-axed-uk/" title="Ethiopian girl band Yegna&#8217;s funding axed by UK &#8211; BBC News" class="related-title"><span>Ethiopian girl band Yegna&#8217;s funding axed by UK &#8211; BBC News</span></a>
</li>
<li class="post-id-23783">
<a href="http://www.ayyaantuu.net/infrastructure-led-chinese-approach/" title="Ethiopia’s political ripple a big test for infrastructure-led Chinese approach" class="related-title"><span>Ethiopia’s political ripple a big test for infrastructure-led Chinese approach</span></a>
</li>
<li class="post-id-23766">
<a href="http://www.ayyaantuu.net/defense-witness-testimonials/" title="Defense Witness Testimonials on Yonatan Tesfaye’s Terrorism Charges" class="related-title"><span>Defense Witness Testimonials on Yonatan Tesfaye’s Terrorism Charges</span></a>
</li>
<li class="post-id-23745">
<a href="http://www.ayyaantuu.net/foreign-government-internet-shutdowns-soared-2016/" title="Foreign government internet shutdowns soared in 2016" class="related-title"><span>Foreign government internet shutdowns soared in 2016</span></a>
</li>
</ul>
<div class="fix"></div><!--/.fix-->
</div><!--/.woo-sc-related-posts--> </div><!-- col-right -->
<div class="fix"></div>
</aside>
<div class="post-utility"></div>
</article><!-- /.post -->
<div class="post-entries">
<div class="nav-prev fl"><a href="http://www.ayyaantuu.net/many-bombs-united-states-dropped-2016/" rel="prev"><i class="fa fa-angle-left"></i> How Many Bombs the United States Dropped in 2016?</a></div>
<div class="nav-next fr"><a href="http://www.ayyaantuu.net/rockefeller-conspiracy-one-world-order/" rel="next">David Rockefeller Says Conspiracy About ‘One World Order’ Is True <i class="fa fa-angle-right"></i></a></div>
<div class="fix"></div>
</div>
<?php if (function_exists ('adinserter')) echo adinserter (1); ?><div id="comments"> <h3 id="comments-title">One Response to <em>Ethiopia targets opposition who met with European lawmakers</em></h3>
<ol class="commentlist">
<li id="comment-30772" class="comment even thread-even depth-1">
<div id="li-comment-30772" class="comment-container">
<div class="avatar"></div>
<div class="comment-head">
<span class="name">Addis Zemen</span>
<span class="date">January 9, 2017 at 5:57 pm</span>
<span class="perma"><a href="http://www.ayyaantuu.net/ethiopia-targets-opposition-lawmakers/#comment-30772" title="Direct link to this comment">#</a></span>
<span class="edit"></span>
</div><!-- /.comment-head -->
<div class="comment-entry">
<p>10,000 people that were areested in Oromia got released. When the people were arrested it was under the assumption “Violence broke out during an Oromo religious festival, and in some instances foreigners seem to have been targeted. ”</p>
<p>But now many prosecutors agree Violence was not the case. The people at the Erecha celebration thought aliens invaded their country. They thought the foreigners were aliens from another planet.<br />
Close to a thousand threw themselves of a cliff just because they saw a helicopter flying near them. Many are paralized hundreds died. Now the injured are trying to blame the helicopter pilot for their injuries by saying the pilot caused their injuries by scaring them, flying like an alien abductor UFO would . After the prosecutor agreed it was an accident or suicide that caused the deaths of hundreds of people at the Erecha celebration, the suspected 10,000 prisoners got released. The prosecutors are blaming those that spoke with the European parliament for spreading false information about what really happened at the Erecha celebration.</p>
<div class="reply">
<a rel='nofollow' class='comment-reply-link' href='http://www.ayyaantuu.net/ethiopia-targets-opposition-lawmakers/?replytocom=30772#respond' onclick='return addComment.moveForm( "comment-30772", "30772", "respond", "23842" )' aria-label='Reply to Addis Zemen'>Reply</a> </div><!-- /.reply -->
</div><!-- /comment-entry -->
</div><!-- /.comment-container -->
</li><!-- #comment-## -->
</ol>
</div> <div id="respond" class="comment-respond">
<h3 id="reply-title" class="comment-reply-title">Leave a Reply <small><a rel="nofollow" id="cancel-comment-reply-link" href="/ethiopia-targets-opposition-lawmakers/#respond" style="display:none;">Click here to cancel reply.</a></small></h3> <form action="http://www.ayyaantuu.net/wp-comments-post.php" method="post" id="commentform" class="comment-form">
<p class="comment-form-comment"><label class="hide" for="comment">Comment</label> <textarea tabindex="4" id="comment" name="comment" cols="50" rows="10" maxlength="65525" aria-required="true" required="required"></textarea></p><p class="comment-form-author"><input id="author" name="author" type="text" class="txt" tabindex="1" value="" size="30" aria-required='true' /><label for="author">Name <span class="required">(required)</span></label> </p>
<p class="comment-form-email"><input id="email" name="email" type="text" class="txt" tabindex="2" value="" size="30" aria-required='true' /><label for="email">Email (will not be published) <span class="required">(required)</span></label> </p>
<p class="comment-form-url"><input id="url" name="url" type="text" class="txt" tabindex="3" value="" size="30" /><label for="url">Website</label></p>
<p class="form-submit"><input name="submit" type="submit" id="submit" class="submit" value="Submit Comment" /> <input type='hidden' name='comment_post_ID' value='23842' id='comment_post_ID' />
<input type='hidden' name='comment_parent' id='comment_parent' value='0' />
</p><p style="display: none;"><input type="hidden" id="akismet_comment_nonce" name="akismet_comment_nonce" value="39bacbf69e" /></p><p style="display: none;"><input type="hidden" id="ak_js" name="ak_js" value="66"/></p> </form>
</div><!-- #respond -->
</section><!-- /#main -->
+54
View File
@@ -0,0 +1,54 @@
<article>
<h1>Feed-Aggregator und Sp&auml;ter-Lesen-Dienst im Team</h1>
<figure>
<img src="https://www.schreibdichte.de/content/1-blog/20170505-feed-aggregator-und-spaeter-lesen-dienst-im-team/workflow.png" alt="Feed-Aggregator und Sp&auml;ter-Lesen-Dienst im Team" >
</figure>
<time datetime="2017-05-05T18:40:00+00:00" >05.05.2017</time>
<p> <span class="tag">Tags:</span>
<ul class="tag_list">
<li><a href="https://www.schreibdichte.de/blog/tag:Software">Software</a></li>
<li><a href="https://www.schreibdichte.de/blog/tag:Web">Web</a></li>
<li><a href="https://www.schreibdichte.de/blog/tag:Tools">Tools</a></li>
</ul>
<p>
<div class="content">
<p>Dank einer Mitteilung <sup id="fnref1:fnfeed_1"><a href="#fn:fnfeed_1" class="footnote-ref">1</a></sup> im OSBN <sup id="fnref1:fnfeed_2"><a href="#fn:fnfeed_2" class="footnote-ref">2</a></sup>, bin ich auf den Feed-Aggregator Miniflux <sup id="fnref1:fnfeed_3"><a href="#fn:fnfeed_3" class="footnote-ref">3</a></sup> aufmerksam geworden. Ich hatte schon lange nach einer Alternative zu Fever <sup id="fnref1:fnfeed_4"><a href="#fn:fnfeed_4" class="footnote-ref">4</a></sup> gesucht, da der Anschein vorlag, dass die Entwicklung nicht mehr weiter ging. Was sich bestätigte.</p>
<p>Miniflux ist prima, einfach gehalten und erledigt genau dass, was es machen soll. Zudem wird die Fever-API unterstützt, was bedeutet, dass ich weiterhin meine Feed-Reader nutzen kann, was den Dialog mit Miniflux auf dem Server betrifft. Das ist deshalb interessant, um den Lesestatus oder das Favorisieren von Feeds zentral auf dem Server zu berücksichtigen und nicht lokal. So habe ich sowohl auf dem lokalen Rechner, als auch auf allen mobilen Geräten den gleichen Stand und Status meiner abonnierten Feeds.</p>
<p>Für das spätere Lesen von Webseiten, nutze ich seit längerem wallabag <sup id="fnref1:fnfeed_5"><a href="#fn:fnfeed_5" class="footnote-ref">5</a></sup>. Wallabag wird mit Inhalten über den Browser gefüttert. Die Webseiten werden bei der Konservierung von Werbung befreit, damit der reine Text vorliegt. Bilder dagegen nicht und gehen evtl. verloren, wenn es die Quelle mal nicht mehr geben sollte. Ich habe also zwei Orte, an denen Informationen gehalten werden. Miniflux zur Verwaltung von Feed-Nachrichten und Wallabag für Webseiten.</p>
<p>Bei einem Update von Miniflux ist mit aufgefallen, dass es nun eine API für Wallabag gibt. Dabei werden alle gespeicherten Feeds von Miniflux an Wallabag übergeben. Eine prima Angelegenheit, da ich jetzt nur noch einen Topf habe, woraus ich meine gesammelten Informationen aus dem Netz entnehme.</p>
<p>Miniflux als auch wallabag sind selbst gehostete Web-Applikationen, die auch über den Browser erreichbar sind. Native wallabag-Apps für iOS bzw. Android werden ebenfalls angeboten. Wallabag bietet zusätzlich einen Feed an, um die gesammelten Informationen per Feed-Reader zu lesen. Hierbei drängt sich evtl. die Idee auf, diesen Feed wieder in Miniflux zu abonnieren, da dann zentral alles aus verschiedenen Quellen vorliegt. Hier hätten wir allerdings eine gewisse Redundanz.</p>
<p>Ich nutze seit ein paar Monaten Devonthink <sup id="fnref1:fnfeed_6"><a href="#fn:fnfeed_6" class="footnote-ref">6</a></sup> auf dem Mac. Das ist eine lokale Dokumentenverwaltung (DMS), die heute u.a. meine Bücher, Notizen und mein Mail-Archiv verwaltet und ihre Stärke u.a. in der Suche von Informationen hat. Devonthink versteht es auch Feeds zu abonnieren. Für mich war dies der Moment, nun den Feed von wallabag mit all meinen gespeicherten Informationen aus dem Web mit zu integrieren. Ich muss sagen, eine runde Lösung, denn jetzt habe ich für die lokale Suche und das Lesen eine zentrale Stelle.</p>
<p>Die Grafik veranschaulicht das Teamplay zwischen Feed-Aggregator Miniflux und wallabag als Später-Lesen-Dienst, zusammengeführt in Devonthink. Ich kenne leider keine Dokumentenverwaltung für Linux oder Windows, die auch Feeds versteht. Wer etwas kennt, bitte melden. Wer zudem etwas zum Thema hören möchte, für dem ist ggf. die Podcast-Folge &quot;Der eigene Workflow&quot; <sup id="fnref1:fnfeed_7"><a href="#fn:fnfeed_7" class="footnote-ref">7</a></sup> interessant.</p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:fnfeed_1">
<p>Artikel zu Miniflux: <a href="Link"><a href="https://www.robbinaer.info/index.php?post/78">https://www.robbinaer.info/index.php?post/78</a></a>&#160;<a href="#fnref1:fnfeed_1" rev="footnote" class="footnote-backref">&#8617;</a></p>
</li>
<li id="fn:fnfeed_2">
<p>OSBN: <a href="Link"><a href="https://osbn.de">https://osbn.de</a></a>&#160;<a href="#fnref1:fnfeed_2" rev="footnote" class="footnote-backref">&#8617;</a></p>
</li>
<li id="fn:fnfeed_3">
<p>Miniflux: <a href="Link"><a href="https://miniflux.net">https://miniflux.net</a></a>&#160;<a href="#fnref1:fnfeed_3" rev="footnote" class="footnote-backref">&#8617;</a></p>
</li>
<li id="fn:fnfeed_4">
<p>Fever: <a href="Link"><a href="https://feedafever.com">https://feedafever.com</a></a>&#160;<a href="#fnref1:fnfeed_4" rev="footnote" class="footnote-backref">&#8617;</a></p>
</li>
<li id="fn:fnfeed_5">
<p>wallabag: <a href="Link"><a href="https://wallabag.org/de">https://wallabag.org/de</a></a>&#160;<a href="#fnref1:fnfeed_5" rev="footnote" class="footnote-backref">&#8617;</a></p>
</li>
<li id="fn:fnfeed_6">
<p>Devonthink: <a href="Link"><a href="http://www.devontechnologies.com/de/produkte/devonthink/ueberblick.html">http://www.devontechnologies.com/de/produkte/devonthink/ueberblick.html</a></a>&#160;<a href="#fnref1:fnfeed_6" rev="footnote" class="footnote-backref">&#8617;</a></p>
</li>
<li id="fn:fnfeed_7">
<p>Podcast MacMittwoch: <a href="Link"><a href="https://itunes.apple.com/de/podcast/macmittwoch/id1212750085?mt=2ueberblick.html">https://itunes.apple.com/de/podcast/macmittwoch/id1212750085?mt=2ueberblick.html</a></a>&#160;<a href="#fnref1:fnfeed_7" rev="footnote" class="footnote-backref">&#8617;</a></p>
</li>
</ol>
</div></div>
</article>
+67
View File
@@ -0,0 +1,67 @@
<HTML>
<HEAD>
<TITLE>Alice's Adventures in Wonderland (Project Gutenberg)</TITLE>
</HEAD>
<frameset Rows="50, *">
<frame src="alice-finfo.html">
<frame src="alice-ftitle.html" name="alice-main">
</frameset>
<noframes>
<BODY>
<H1>Alice's Adventures in Wonderland</H1>
<H1>Lewis Carroll</H1>
<H1>The Millennium Fulcrum Edition 3.0</H1>
NOTE: This is a hypertext formatted version of the Project Gutenberg edition.
For more information, check the
<A HREF="alice-small.txt">small print</A>
or check out the
<A HREF="ftp://uiarchive.cso.uiuc.edu/pub/etext/gutenberg/etext91/alice30.txt">
full ascii text</A>. The original Tenniel illustrations are also available
due to the efforts of Project Gutenberg. You can if you like, grab them as a
<A HREF="ftp://uiarchive.cso.uiuc.edu/pub/etext/gutenberg/etext94/algif10.zip">
"zip file"</A> or read the <A HREF="algif-small.txt">small print</A>
that comes with them.
This document is part of a small, but growing collection of html formatted
etexts. (Others may be found in either my <A
HREF="http://www.cs.cmu.edu/Web/People/rgs/rgs-home.html">home page</A> or
John Ockerbloom's indexes by <A
HREF="http://www.cs.cmu.edu/Web/bookauthors.html">author</A> and <A
HREF="http://www.cs.cmu.edu/Web/booktitles.html">title</A>.)
I am still trying to figure out whether anyone else is interested in these
on-line readable documents. If you appreciate this document or would like to
see more such, send me mail at "rgs@cs.cmu.edu".
<P>
<A HREF="alice01a.gif"><IMG SRC="alice01th.gif"></A>
<P>
<H2>CONTENTS</H2>
<PRE>
CHAPTER I: <A HREF="alice-I.html">Down the Rabbit-Hole</A>
CHAPTER II: <A HREF="alice-II.html">The Pool of Tears</A>
CHAPTER III: <A HREF="alice-III.html">A Caucus-Race and a Long Tale</A>
CHAPTER IV: <A HREF="alice-IV.html">The Rabbit Sends in a Little Bill</A>
CHAPTER V: <A HREF="alice-V.html">Advice from a Caterpillar</A>
CHAPTER VI: <A HREF="alice-VI.html">Pig and Pepper</A>
CHAPTER VII: <A HREF="alice-VII.html">A Mad Tea-Party</A>
CHAPTER VIII: <A HREF="alice-VIII.html">The Queen's Croquet-Ground</A>
CHAPTER IX: <A HREF="alice-IX.html">The Mock Turtle's Story</A>
CHAPTER X: <A HREF="alice-X.html">The Lobster Quadrille</A>
CHAPTER XI: <A HREF="alice-XI.html">Who Stole the Tarts?</A>
CHAPTER XII: <A HREF="alice-XII.html">Alice's Evidence</A>
</PRE>
<ADDRESS><A HREF="mailto:rgs@cs.cmu.edu">Robert Stockton</A></ADDRESS>
<P>
<!- Access counter added 5/25 1:49am ->
<A href="http://www.dbasics.com/cgi-bin/pages.cgi?143205747"><IMG SRC="http://www.dbasics.com/cgi-bin/counter.cgi?143205747.2&(none)"></A> Access statistics from htmlZine
<!- This page has been visited
A HREF="http://counter.digits.com/wc?--info=yes&--name=rgsalice"
IMG SRC="http://counter.digits.com/wc/-d/4/-r/-z/rgsalice"
ALIGN=absmiddle WIDTH=60 HEIGHT=20 BORDER=0 HSPACE=4 ALT="????"/A
times since March 2, 1996. ->
</BODY>
</noframes>
</HTML>