├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── other.md │ ├── feature_request.md │ └── bug_report.yaml ├── dependabot.yml ├── workflows │ └── ci.yaml ├── pull_request_template.md ├── CODE_OF_CONDUCT.md └── CONTRIBUTING.md ├── psalm.xml ├── phpunit.xml ├── .editorconfig ├── .php-cs-fixer.dist.php ├── .gitignore ├── tests ├── FootnotesTest.php ├── ReferencesTest.php ├── QuotesTest.php ├── ImageTest.php ├── ListsTest.php ├── DefinitionListsTest.php ├── DiagramsTest.php ├── SmartypantsTest.php ├── ThematicBreaksTest.php ├── CommentsTest.php ├── TypographerTest.php ├── CodeTest.php ├── AbbreviationsTest.php ├── EmojiTest.php ├── ConfigTest.php ├── MathTest.php ├── AlertsTest.php ├── TablesTest.php ├── TocTest.php ├── HeadingsTest.php ├── EmphasisTest.php └── LinksTest.php ├── composer.json ├── LICENSE.md ├── benchmarks ├── tests │ ├── homebrew-readme.md │ ├── angular-readme.md │ ├── rails-readme.md │ ├── textmate-readme.md │ ├── bootstrap-readme.md │ ├── markdown-readme.md │ └── jquery-readme.md └── benchmark.php └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ["https://paypal.me/BenjaminHoegh", "https://www.buymeacoffee.com/BenjaminHoegh"] 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Other 3 | about: For issues there aren't a bug og a feature request 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./tests 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | end_of_line = lf 4 | insert_final_newline = true 5 | trim_trailing_whitespace = true 6 | 7 | [*.php] 8 | indent_style = space 9 | indent_size = 4 10 | 11 | [*.json] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.md] 16 | indent_style = space 17 | indent_size = 4 18 | trim_trailing_whitespace = false 19 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | 2 | setRules([ 11 | '@PHP74Migration' => true, 12 | '@PSR12' => true, 13 | ]) 14 | ->setFinder( 15 | Finder::create() 16 | ->in(__DIR__) 17 | ->exclude('vendor') 18 | ) 19 | ; 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .nova/ 2 | vendor/ 3 | lab/ 4 | tools/ 5 | build/ 6 | docs/ 7 | 8 | # PHP-CS-Fixer 9 | .php_cs.cache 10 | .php-cs-fixer.cache 11 | 12 | # PHPUnit 13 | .phpunit.result.cache 14 | 15 | # Composer 16 | composer.phar 17 | 18 | # phpDocumentor 19 | .phpdoc/ 20 | phpDocumentor.phar 21 | 22 | # Windows 23 | Thumbs.db 24 | 25 | # macOS 26 | .DS_Store 27 | 28 | # IDE 29 | .vscode/ 30 | .idea/ 31 | qodana.yaml 32 | 33 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /tests/FootnotesTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/ReferencesTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: PHPUnit CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | phpunit: 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | php: [ '7.4', '8.0', '8.1', '8.2', '8.3', '8.4' ] 17 | 18 | steps: 19 | - name: Checkout the source code 20 | uses: actions/checkout@v4 21 | 22 | - name: Set up PHP 23 | uses: shivammathur/setup-php@v2 24 | with: 25 | php-version: ${{ matrix.php }} 26 | 27 | - name: Install dependencies 28 | run: composer update --prefer-dist --no-interaction 29 | 30 | - name: Run tests 31 | run: | 32 | vendor/bin/phpunit 33 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "benjaminhoegh/parsedown-extended", 3 | "description": "An extension for Parsedown.", 4 | "keywords": [ 5 | "markdown", 6 | "markdown extended", 7 | "parsedown", 8 | "parsedown extended" 9 | ], 10 | "homepage": "https://github.com/benjaminhoegh/parsedown-extended", 11 | "type": "library", 12 | "license": "MIT", 13 | "authors": [ 14 | { 15 | "name": "Benjamin Hoegh", 16 | "homepage": "https://github.com/benjaminhoegh" 17 | } 18 | ], 19 | "require": { 20 | "php": ">=7.4", 21 | "erusev/parsedown": ">=1.7.4 <2.0", 22 | "erusev/parsedown-extra": "^0.8.1", 23 | "ext-json": "*" 24 | }, 25 | "require-dev": { 26 | "phpunit/phpunit": "^9.6", 27 | "friendsofphp/php-cs-fixer": "^3.64" 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "BenjaminHoegh\\ParsedownExtended\\": "src/" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/QuotesTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | 21 | public function testBlockQuote() 22 | { 23 | $markdown = "> This is a quote."; 24 | $expectedHtml = "
\n

This is a quote.

\n
"; 25 | $result = $this->parsedownExtended->text($markdown); 26 | 27 | $this->assertEquals($expectedHtml, trim($result)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/ImageTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | 21 | public function testInlineImage() 22 | { 23 | $markdown = "![](image.png)"; 24 | $expectedHtml = '

'; 25 | $result = $this->parsedownExtended->text($markdown); 26 | 27 | $this->assertEquals($expectedHtml, trim($result)); 28 | } 29 | 30 | public function testImageWithAltText() 31 | { 32 | $markdown = "![alt text](image.png)"; 33 | $expectedHtml = '

alt text

'; 34 | $result = $this->parsedownExtended->text($markdown); 35 | 36 | $this->assertEquals($expectedHtml, trim($result)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/ListsTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | 21 | public function testBlockUnorderedList() 22 | { 23 | $markdown = "- Item 1\n- Item 2"; 24 | $expectedHtml = ""; 25 | $result = $this->parsedownExtended->text($markdown); 26 | 27 | $this->assertEquals($expectedHtml, trim($result)); 28 | } 29 | 30 | public function testBlockOrderedList() 31 | { 32 | $markdown = "1. Item 1\n2. Item 2"; 33 | $expectedHtml = "
    \n
  1. Item 1
  2. \n
  3. Item 2
  4. \n
"; 34 | $result = $this->parsedownExtended->text($markdown); 35 | 36 | $this->assertEquals($expectedHtml, trim($result)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Provide a general summary of your changes in the Title above 2 | 3 | ## Description 4 | Describe your changes in detail? 5 | 6 | 7 | ## Types of changes 8 | 9 | - [ ] Bug fix (non-breaking change which fixes an issue) 10 | - [ ] New feature (non-breaking change which adds functionality) 11 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 12 | 13 | 14 | ## Contributing guidelines 15 | description: What steps have you taken to make sure your code follow the community guides? 16 | options: 17 | 18 | - [ ] I have read the [CONTRIBUTING document](https://github.com/BenjaminHoegh/ParsedownExtended/blob/main/.github/CONTRIBUTING.md). 19 | - [ ] My code follows the [code style](https://github.com/BenjaminHoegh/ParsedownExtended/blob/main/.github/CONTRIBUTING.md#code-guidelines) of this project. 20 | 21 | 22 | ## Change to the documentation 23 | description: What types of changes does your code introduce? 24 | options: 25 | - [ ] My change requires a change to the documentation. 26 | - [ ] I have updated the documentation accordingly. 27 | 28 | ## Code of Conduct 29 | By submitting this pull request, you agree to follow our [Code of Conduct](https://github.com/BenjaminHoegh/ParsedownExtended/blob/main/.github/CODE_OF_CONDUCT.md) 30 | 31 | - [ ] I agree to follow this project's Code of Conduct 32 | -------------------------------------------------------------------------------- /tests/DefinitionListsTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | 21 | public function testEnableDefinitionList() 22 | { 23 | $this->parsedownExtended->config()->set('definition_lists', true); 24 | 25 | $markdown = "Term\n: Definition"; 26 | $expectedHtml = "
\n
Term
\n
Definition
\n
"; 27 | 28 | $result = $this->parsedownExtended->text($markdown); 29 | 30 | $this->assertEquals($expectedHtml, trim($result)); 31 | } 32 | 33 | public function testDisableDefinitionList() 34 | { 35 | $this->parsedownExtended->config()->set('definition_lists', false); 36 | 37 | $markdown = "Term\n: Definition"; 38 | $expectedHtml = "

Term\n: Definition

"; 39 | 40 | $result = $this->parsedownExtended->text($markdown); 41 | 42 | $this->assertEquals($expectedHtml, trim($result)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/DiagramsTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | 21 | public function testEnableDiagram() 22 | { 23 | $this->parsedownExtended->config()->set('diagrams', true); 24 | 25 | $markdown = "```mermaid\ngraph TD;\n A-->B;\n```"; 26 | $expectedHtml = "
graph TD;\n A-->B;
"; 27 | 28 | $result = $this->parsedownExtended->text($markdown); 29 | 30 | $this->assertEquals($expectedHtml, $result); 31 | } 32 | 33 | public function testDisableDiagram() 34 | { 35 | $this->parsedownExtended->config()->set('diagrams', false); 36 | 37 | $markdown = "```mermaid\ngraph TD;\n A-->B;\n```"; 38 | $expectedHtml = "
graph TD;\n    A-->B;
"; 39 | 40 | $result = $this->parsedownExtended->text($markdown); 41 | 42 | $this->assertEquals($expectedHtml, $result); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/SmartypantsTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | 21 | public function testEnableSmartypants() 22 | { 23 | $this->parsedownExtended->config()->set('smartypants', true); 24 | 25 | $markdown = <<“Hello,” he said.

31 | HTML; 32 | 33 | $this->assertEquals($expected, $this->parsedownExtended->text($markdown)); 34 | } 35 | 36 | public function testDisableSmartypants() 37 | { 38 | $this->parsedownExtended->config()->set('smartypants', false); 39 | 40 | $markdown = <<"Hello," he said.

46 | HTML; 47 | 48 | $this->assertEquals($expected, $this->parsedownExtended->text($markdown)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/ThematicBreaksTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | 21 | public function testEnableThematicBreaks() 22 | { 23 | $this->parsedownExtended->config()->set('thematic_breaks', true); 24 | 25 | $this->assertEquals("
", $this->parsedownExtended->text("***")); 26 | } 27 | 28 | public function testDisableThematicBreaks() 29 | { 30 | $this->parsedownExtended->config()->set('thematic_breaks', false); 31 | 32 | $this->assertEquals("

***

", $this->parsedownExtended->text("***")); 33 | } 34 | 35 | public function testDifferentThematicBreaks() 36 | { 37 | $this->parsedownExtended->config()->set('thematic_breaks', true); 38 | 39 | $this->assertEquals("
", $this->parsedownExtended->text("***")); 40 | $this->assertEquals("
", $this->parsedownExtended->text("---")); 41 | $this->assertEquals("
", $this->parsedownExtended->text("___")); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/CommentsTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | 21 | public function testEnableComment() 22 | { 23 | 24 | $this->parsedownExtended->setSafeMode(false); // Comments are not allowed in safe mode 25 | $this->parsedownExtended->config()->set('comments', true); // Comments are not turned into paragraphs 26 | 27 | $markdown = ""; 28 | $expectedHtml = ""; 29 | 30 | $result = $this->parsedownExtended->text($markdown); 31 | 32 | $this->assertEquals($expectedHtml, $result); 33 | } 34 | 35 | public function testDisableComment() 36 | { 37 | $this->parsedownExtended->setSafeMode(false); // Comments are not allowed in safe mode 38 | $this->parsedownExtended->config()->set('comments', false); // Comments are turned into paragraphs 39 | 40 | $markdown = ""; 41 | $expectedHtml = "

"; 42 | 43 | $result = $this->parsedownExtended->text($markdown); 44 | 45 | $this->assertEquals($expectedHtml, $result); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /benchmarks/tests/homebrew-readme.md: -------------------------------------------------------------------------------- 1 | Homebrew 2 | ======== 3 | Features, usage and installation instructions are [summarized on the homepage][home]. 4 | 5 | What Packages Are Available? 6 | ---------------------------- 7 | 1. You can [browse the Formula directory on GitHub][formula]. 8 | 2. Or type `brew search` for a list. 9 | 3. Or visit [braumeister.org][braumeister] to browse packages online. 10 | 11 | More Documentation 12 | ------------------ 13 | `brew help` or `man brew` or check our [wiki][]. 14 | 15 | Troubleshooting 16 | --------------- 17 | First, please run `brew update` and `brew doctor`. 18 | 19 | Second, read the [Troubleshooting Checklist](https://github.com/Homebrew/homebrew/wiki/troubleshooting). 20 | 21 | **If you don't read these it will take us far longer to help you with your problem.** 22 | 23 | Who Are You? 24 | ------------ 25 | Homebrew is maintained by the [core contributors][team]. 26 | 27 | Homebrew was originally created by [Max Howell][mxcl]. 28 | 29 | License 30 | ------- 31 | Code is under the [BSD 2 Clause (NetBSD) license][license]. 32 | 33 | Donations 34 | --------- 35 | We accept tips through [Gittip][tip]. 36 | 37 | [![Gittip](http://img.shields.io/gittip/Homebrew.png)](https://www.gittip.com/Homebrew/) 38 | 39 | [home]:http://brew.sh 40 | [wiki]:http://wiki.github.com/Homebrew/homebrew 41 | [mxcl]:http://twitter.com/mxcl 42 | [formula]:http://github.com/Homebrew/homebrew/tree/master/Library/Formula/ 43 | [braumeister]:http://braumeister.org 44 | [license]:https://github.com/Homebrew/homebrew/tree/master/Library/Homebrew/LICENSE 45 | [team]:https://github.com/Homebrew?tab=members 46 | [tip]:https://www.gittip.com/Homebrew/ 47 | -------------------------------------------------------------------------------- /tests/TypographerTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | 21 | public function testEnableTypographer(): void 22 | { 23 | $this->parsedownExtended->config()->set('typographer', true); 24 | 25 | $markdown = '(c) (r) (tm) ....'; 26 | $expected = '

© ® ™ ...

'; 27 | 28 | $result = $this->parsedownExtended->text($markdown); 29 | $this->assertEquals($expected, $result); 30 | } 31 | 32 | public function testDisableTypographer(): void 33 | { 34 | $this->parsedownExtended->config()->set('typographer', false); 35 | 36 | $markdown = '(c) (r) (tm) ....'; 37 | $expected = '

(c) (r) (tm) ....

'; 38 | 39 | $result = $this->parsedownExtended->text($markdown); 40 | $this->assertEquals($expected, $result); 41 | } 42 | 43 | public function testTypographerWithSmartypants(): void 44 | { 45 | $this->parsedownExtended->config()->set('typographer', true); 46 | $this->parsedownExtended->config()->set('smartypants', true); 47 | 48 | $markdown = '(c) (r) (tm) ...'; 49 | $expected = '

© ® ™ …

'; 50 | 51 | $result = $this->parsedownExtended->text($markdown); 52 | $this->assertEquals($expected, $result); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /benchmarks/tests/angular-readme.md: -------------------------------------------------------------------------------- 1 | AngularJS [![Build Status](https://travis-ci.org/angular/angular.js.png?branch=master)](https://travis-ci.org/angular/angular.js) 2 | ========= 3 | 4 | AngularJS lets you write client-side web applications as if you had a smarter browser. It lets you 5 | use good old HTML (or HAML, Jade and friends!) as your template language and lets you extend HTML’s 6 | syntax to express your application’s components clearly and succinctly. It automatically 7 | synchronizes data from your UI (view) with your JavaScript objects (model) through 2-way data 8 | binding. To help you structure your application better and make it easy to test, AngularJS teaches 9 | the browser how to do dependency injection and inversion of control. Oh yeah and it also helps with 10 | server-side communication, taming async callbacks with promises and deferreds; and make client-side 11 | navigation and deeplinking with hashbang urls or HTML5 pushState a piece of cake. The best of all: 12 | it makes development fun! 13 | 14 | * Web site: http://angularjs.org 15 | * Tutorial: http://docs.angularjs.org/tutorial 16 | * API Docs: http://docs.angularjs.org/api 17 | * Developer Guide: http://docs.angularjs.org/guide 18 | * Contribution guidelines: http://docs.angularjs.org/misc/contribute 19 | * Dashboard: http://dashboard.angularjs.org 20 | 21 | Building AngularJS 22 | --------- 23 | [Once you have your environment setup](http://docs.angularjs.org/misc/contribute) just run: 24 | 25 | grunt package 26 | 27 | 28 | Running Tests 29 | ------------- 30 | To execute all unit tests, use: 31 | 32 | grunt test:unit 33 | 34 | To execute end-to-end (e2e) tests, use: 35 | 36 | grunt package 37 | grunt test:e2e 38 | 39 | To learn more about the grunt tasks, run `grunt --help` and also read our 40 | [contribution guidelines](http://docs.angularjs.org/misc/contribute). 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report 3 | labels: [Bug] 4 | assignees: 5 | - BenjaminHoegh 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | 🐞 **Thank you for contributing to our project by reporting a bug!** 🐞 11 | Please provide as much detail as possible to help us efficiently resolve the issue. 12 | 13 | - type: textarea 14 | id: describe-the-bug 15 | attributes: 16 | label: Bug Description 17 | description: Provide a clear and concise description of what the bug is, including what you expected to happen and what actually happened. 18 | placeholder: Explain the bug in detail. Include what you were doing when it occurred and any error messages you saw. 19 | validations: 20 | required: true 21 | 22 | - type: input 23 | id: php-version 24 | attributes: 25 | label: PHP Version 26 | description: Specify the PHP version you were using. This information helps us replicate the issue under similar conditions. 27 | placeholder: e.g., 8.2.0 28 | validations: 29 | required: true 30 | 31 | - type: input 32 | id: parsedown-version 33 | attributes: 34 | label: Parsedown Version 35 | description: Mention the version of Parsedown you're using. If possible, confirm if the issue persists with the latest version. 36 | placeholder: e.g., 1.8.0 37 | validations: 38 | required: true 39 | 40 | - type: input 41 | id: parsedown-extra-version 42 | attributes: 43 | label: Parsedown Extra Version 44 | description: If you're using Parsedown Extra, please specify the version. Leave blank if not applicable 45 | placeholder: Optional, e.g., 0.8.0 46 | validations: 47 | required: false 48 | 49 | - type: checkboxes 50 | id: terms 51 | attributes: 52 | label: Code of Conduct 53 | description: Confirm your agreement to adhere to our project's [Code of Conduct](https://github.com/BenjaminHoegh/ParsedownExtended/blob/main/.github/CODE_OF_CONDUCT.md). This helps maintain a respectful and constructive environment. 54 | options: 55 | - label: I agree to follow the project's Code of Conduct. 56 | required: true 57 | -------------------------------------------------------------------------------- /tests/CodeTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | 21 | public function testEnableCode() 22 | { 23 | $this->parsedownExtended->config()->set('code', true); // Code blocks are not turned into paragraphs 24 | 25 | $markdown = "```php\n<?php echo \"Hello, World!\";"; 27 | 28 | $result = $this->parsedownExtended->text($markdown); 29 | 30 | $this->assertEquals($expectedHtml, trim($result)); 31 | } 32 | 33 | public function testDisableCode() 34 | { 35 | $this->parsedownExtended->config()->set('code', false); // Code blocks are turned into paragraphs 36 | 37 | $markdown = "```php\n```php\n<?php echo "Hello, World!";\n```

"; 39 | 40 | $result = $this->parsedownExtended->text($markdown); 41 | 42 | $this->assertEquals($expectedHtml, trim($result)); 43 | } 44 | 45 | public function testFencedCodeBlock() 46 | { 47 | $markdown = "```php\n<?php echo \"Hello, World!\";"; 49 | 50 | $result = $this->parsedownExtended->text($markdown); 51 | 52 | $this->assertEquals($expectedHtml, trim($result)); 53 | } 54 | 55 | public function testIndentedCodeBlock() 56 | { 57 | $markdown = " <?php echo \"Indented Code\";"; 59 | $result = $this->parsedownExtended->text($markdown); 60 | 61 | $this->assertEquals($expectedHtml, trim($result)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/AbbreviationsTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | 21 | public function testEnabledAbbreviation() 22 | { 23 | $markdown = "*[CSS]: Cascading stylesheet\n\nCSS is an abbreviation"; 24 | $expectedHtml = "

CSS is an abbreviation

"; 25 | 26 | $this->parsedownExtended->config() 27 | ->set('abbreviations', true) 28 | ->set('abbreviations.allow_custom', true); 29 | $this->assertEquals($expectedHtml, $this->parsedownExtended->text($markdown)); 30 | } 31 | 32 | public function testDisabledAbbreviation() 33 | { 34 | $markdown = "*[CSS]: Cascading stylesheet\n\nCSS is an abbreviation"; 35 | $expectedHtml = "

*[CSS]: Cascading stylesheet

\n

CSS is an abbreviation

"; 36 | 37 | $this->parsedownExtended->config()->set('abbreviations', false); 38 | $this->assertEquals($expectedHtml, $this->parsedownExtended->text($markdown)); 39 | } 40 | 41 | public function testDisabledCustomAbbreviation() 42 | { 43 | $markdown = "*[CSS]: Cascading stylesheet\n\nCSS is an abbreviation"; 44 | $expectedHtml = "

*[CSS]: Cascading stylesheet

\n

CSS is an abbreviation

"; 45 | 46 | $this->parsedownExtended->config() 47 | ->set('abbreviations', true) 48 | ->set('abbreviations.allow_custom', false); 49 | $this->assertEquals($expectedHtml, $this->parsedownExtended->text($markdown)); 50 | } 51 | 52 | public function testPredefinedAbbreviations() 53 | { 54 | 55 | $markdown = "HTML is an abbreviation."; 56 | $expectedHtml = "

HTML is an abbreviation.

"; 57 | 58 | $this->parsedownExtended->config()->set('abbreviations', true); 59 | $this->parsedownExtended->config()->set('abbreviations.predefined', ['HTML' => 'HyperText Markup Language']); 60 | 61 | $this->assertEquals($expectedHtml, $this->parsedownExtended->text($markdown)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/EmojiTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | 21 | public function testEnableEmoji() 22 | { 23 | $this->parsedownExtended->config()->set('emojis', true); 24 | 25 | $markdown = ":grinning_face:"; 26 | $expectedHtml = '

😀

'; 27 | 28 | $result = $this->parsedownExtended->text($markdown); 29 | 30 | $this->assertEquals($expectedHtml, $result); 31 | } 32 | 33 | public function testDisableEmoji() 34 | { 35 | $this->parsedownExtended->config()->set('emojis', false); 36 | 37 | $markdown = ":grinning_face:"; 38 | $expectedHtml = '

:grinning_face:

'; 39 | 40 | $result = $this->parsedownExtended->text($markdown); 41 | 42 | $this->assertEquals($expectedHtml, $result); 43 | } 44 | 45 | # Test that we dont parse emojis in code blocks 46 | public function testEmojiInCodeBlock() 47 | { 48 | $this->parsedownExtended->config()->set('emojis', true); 49 | 50 | $markdown = "```php\n:grinning_face:\n```"; 51 | $expectedHtml = '
:grinning_face:
'; 52 | 53 | $result = $this->parsedownExtended->text($markdown); 54 | 55 | $this->assertEquals($expectedHtml, $result); 56 | } 57 | 58 | # Test that we dont parse emojis in inline code 59 | public function testEmojiInInlineCode() 60 | { 61 | $this->parsedownExtended->config()->set('emojis', true); 62 | 63 | $markdown = "`:grinning_face:`"; 64 | $expectedHtml = '

:grinning_face:

'; 65 | 66 | $result = $this->parsedownExtended->text($markdown); 67 | 68 | $this->assertEquals($expectedHtml, $result); 69 | } 70 | 71 | # Test that we dont parse emojis if the emoji code is inside a word (e.g. :grinning_face:ing or testing:grinning_face:) 72 | public function testEmojiInWord() 73 | { 74 | $this->parsedownExtended->config()->set('emojis', true); 75 | 76 | $markdown = "testing:grinning_face:"; 77 | $expectedHtml = '

testing:grinning_face:

'; 78 | 79 | $result = $this->parsedownExtended->text($markdown); 80 | 81 | $this->assertEquals($expectedHtml, $result); 82 | 83 | $markdown = ":grinning_face:ing"; 84 | $expectedHtml = '

:grinning_face:ing

'; 85 | 86 | $result = $this->parsedownExtended->text($markdown); 87 | 88 | $this->assertEquals($expectedHtml, $result); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /tests/ConfigTest.php: -------------------------------------------------------------------------------- 1 | config(); 17 | 18 | // Test some default settings 19 | $this->assertTrue($config->get('code.enabled'), 'Code blocks should be enabled by default'); 20 | $this->assertTrue($config->get('emojis'), 'Emojis should be enabled by default'); 21 | $this->assertFalse($config->get('diagrams.enabled'), 'Diagrams should be disabled by default'); 22 | } 23 | 24 | /** 25 | * Test setting a configuration value 26 | */ 27 | public function testSetConfigValue() 28 | { 29 | $parsedownExtended = new ParsedownExtended(); 30 | 31 | // Set a new value 32 | $parsedownExtended->config()->set('emojis', false); 33 | 34 | // Assert the value has changed 35 | $this->assertFalse($parsedownExtended->config()->get('emojis'), 'Emojis should be disabled after setting to false'); 36 | } 37 | 38 | /** 39 | * Test setting a nested configuration value 40 | */ 41 | public function testSetNestedConfigValue() 42 | { 43 | $parsedownExtended = new ParsedownExtended(); 44 | 45 | // Set a nested value 46 | $parsedownExtended->config()->set('headings.auto_anchors.lowercase', false); 47 | 48 | // Assert the value has changed 49 | $this->assertFalse($parsedownExtended->config()->get('headings.auto_anchors.lowercase'), 'Headings auto anchor lowercase should be false after setting'); 50 | } 51 | 52 | /** 53 | * Test deprecated configuration paths 54 | */ 55 | public function testDeprecatedConfigPaths() 56 | { 57 | $parsedownExtended = new ParsedownExtended(); 58 | 59 | // Set and retrieve a deprecated path 60 | $parsedownExtended->config()->set('smartypants.substitutions.left_double_quote', '“'); 61 | $this->assertEquals('“', $parsedownExtended->config()->get('smartypants.substitutions.left_double_quote'), 'Left double quote substitution should be set correctly'); 62 | } 63 | 64 | /** 65 | * Test invalid configuration key path 66 | */ 67 | public function testInvalidConfigKeyPath() 68 | { 69 | $this->expectException(InvalidArgumentException::class); 70 | 71 | $parsedownExtended = new ParsedownExtended(); 72 | $parsedownExtended->config()->get('non.existent.path'); 73 | } 74 | 75 | /** 76 | * Test multiple settings at once 77 | */ 78 | public function testSetMultipleConfigValues() 79 | { 80 | $parsedownExtended = new ParsedownExtended(); 81 | 82 | // Set multiple values 83 | $parsedownExtended->config()->set([ 84 | 'emojis' => false, 85 | 'code.enabled' => false, 86 | 'lists.tasks' => false, 87 | ]); 88 | 89 | // Assert the values have changed 90 | $this->assertFalse($parsedownExtended->config()->get('emojis'), 'Emojis should be disabled'); 91 | $this->assertFalse($parsedownExtended->config()->get('code.enabled'), 'Code should be disabled'); 92 | $this->assertFalse($parsedownExtended->config()->get('lists.tasks'), 'Task lists should be disabled'); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /tests/MathTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | 21 | public function testInlineMath() 22 | { 23 | $markdown = '$E=mc^2$'; 24 | $expectedHtml = '

$E=mc^2$

'; 25 | 26 | $this->parsedownExtended->config()->set('math', true); 27 | $result = $this->parsedownExtended->text($markdown); 28 | 29 | $this->assertEquals($expectedHtml, $result); 30 | } 31 | 32 | public function testBlockMath() 33 | { 34 | $markdown = '$$E=mc^2$$'; 35 | $expectedHtml = 'E=mc^2'; 36 | 37 | $this->parsedownExtended->config()->set('math', true); 38 | $result = $this->parsedownExtended->text($markdown); 39 | 40 | $this->assertEquals($expectedHtml, $result); 41 | } 42 | 43 | public function testInlineMathWithPunctuation() 44 | { 45 | // Test that inline math works correctly when followed by various punctuation 46 | // This addresses the issue where certain punctuation would prevent math detection 47 | $this->parsedownExtended->config()->set('math', true); 48 | 49 | $testCases = [ 50 | 'Math with semicolon: $F=ma$; force formula' => '

Math with semicolon: $F=ma$; force formula

', 51 | 'Math with colon: $E=mc^2$: energy formula' => '

Math with colon: $E=mc^2$: energy formula

', 52 | 'Math with exclamation: $x=3$! Amazing!' => '

Math with exclamation: $x=3$! Amazing!

', 53 | 'Math with question: $x^2=1$? Solutions exist.' => '

Math with question: $x^2=1$? Solutions exist.

', 54 | 'Math with parenthesis: The value ($x=5$) is constant.' => '

Math with parenthesis: The value ($x=5$) is constant.

', 55 | 'Multiple math: $a=1$, $b=2$; $c=3$!' => '

Multiple math: $a=1$, $b=2$; $c=3$!

', 56 | ]; 57 | 58 | foreach ($testCases as $markdown => $expectedHtml) { 59 | $result = $this->parsedownExtended->text($markdown); 60 | $this->assertEquals($expectedHtml, $result, "Failed for: $markdown"); 61 | } 62 | } 63 | 64 | public function testInlineMathWithFollowingElements() 65 | { 66 | // Test that inline math doesn't interfere with subsequent markdown elements 67 | $this->parsedownExtended->config()->set('math', true); 68 | 69 | $markdown = '$E=mc^2$ 70 | 71 | > This is a blockquote'; 72 | 73 | $result = $this->parsedownExtended->text($markdown); 74 | 75 | // Should contain both the math expression and the blockquote 76 | $this->assertStringContainsString('$E=mc^2$', $result); 77 | $this->assertStringContainsString('
', $result); 78 | $this->assertStringContainsString('This is a blockquote', $result); 79 | } 80 | 81 | public function testOriginalIssueCase() 82 | { 83 | // Test the specific case mentioned in GitHub issue #65 84 | $this->parsedownExtended->config()->set('math', true); 85 | 86 | $markdown = '$C=(1,0,1,0;)$, $CA=(0,1,0,1;)$, $CA^2=(b,0,a,0;)$, $CA^3=(0,b,0,a;)$ 87 | 88 | > La matrice d\'observabilité s\'écrit donc :'; 89 | 90 | $result = $this->parsedownExtended->text($markdown); 91 | 92 | // Should preserve all 4 math expressions 93 | $mathCount = substr_count($result, '$') / 2; 94 | $this->assertEquals(4, $mathCount, 'All math expressions should be preserved'); 95 | 96 | // Should render the blockquote correctly 97 | $this->assertStringContainsString('
', $result); 98 | $this->assertStringContainsString('La matrice', $result); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /benchmarks/tests/rails-readme.md: -------------------------------------------------------------------------------- 1 | ## Welcome to Rails 2 | 3 | Rails is a web-application framework that includes everything needed to 4 | create database-backed web applications according to the 5 | [Model-View-Controller (MVC)](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) 6 | pattern. 7 | 8 | Understanding the MVC pattern is key to understanding Rails. MVC divides your 9 | application into three layers, each with a specific responsibility. 10 | 11 | The _Model layer_ represents your domain model (such as Account, Product, 12 | Person, Post, etc.) and encapsulates the business logic that is specific to 13 | your application. In Rails, database-backed model classes are derived from 14 | `ActiveRecord::Base`. Active Record allows you to present the data from 15 | database rows as objects and embellish these data objects with business logic 16 | methods. Although most Rails models are backed by a database, models can also 17 | be ordinary Ruby classes, or Ruby classes that implement a set of interfaces 18 | as provided by the Active Model module. You can read more about Active Record 19 | in its [README](activerecord/README.rdoc). 20 | 21 | The _Controller layer_ is responsible for handling incoming HTTP requests and 22 | providing a suitable response. Usually this means returning HTML, but Rails controllers 23 | can also generate XML, JSON, PDFs, mobile-specific views, and more. Controllers load and 24 | manipulate models, and render view templates in order to generate the appropriate HTTP response. 25 | In Rails, incoming requests are routed by Action Dispatch to an appropriate controller, and 26 | controller classes are derived from `ActionController::Base`. Action Dispatch and Action Controller 27 | are bundled together in Action Pack. You can read more about Action Pack in its 28 | [README](actionpack/README.rdoc). 29 | 30 | The _View layer_ is composed of "templates" that are responsible for providing 31 | appropriate representations of your application's resources. Templates can 32 | come in a variety of formats, but most view templates are HTML with embedded 33 | Ruby code (ERB files). Views are typically rendered to generate a controller response, 34 | or to generate the body of an email. In Rails, View generation is handled by Action View. 35 | You can read more about Action View in its [README](actionview/README.rdoc). 36 | 37 | Active Record, Action Pack, and Action View can each be used independently outside Rails. 38 | In addition to them, Rails also comes with Action Mailer ([README](actionmailer/README.rdoc)), a library 39 | to generate and send emails; and Active Support ([README](activesupport/README.rdoc)), a collection of 40 | utility classes and standard library extensions that are useful for Rails, and may also be used 41 | independently outside Rails. 42 | 43 | ## Getting Started 44 | 45 | 1. Install Rails at the command prompt if you haven't yet: 46 | 47 | gem install rails 48 | 49 | 2. At the command prompt, create a new Rails application: 50 | 51 | rails new myapp 52 | 53 | where "myapp" is the application name. 54 | 55 | 3. Change directory to `myapp` and start the web server: 56 | 57 | cd myapp 58 | rails server 59 | 60 | Run with `--help` or `-h` for options. 61 | 62 | 4. Using a browser, go to http://localhost:3000 and you'll see: "Welcome aboard: You're riding Ruby on Rails!" 63 | 64 | 5. Follow the guidelines to start developing your application. You may find 65 | the following resources handy: 66 | * [Getting Started with Rails](http://guides.rubyonrails.org/getting_started.html) 67 | * [Ruby on Rails Guides](http://guides.rubyonrails.org) 68 | * [The API Documentation](http://api.rubyonrails.org) 69 | * [Ruby on Rails Tutorial](http://ruby.railstutorial.org/ruby-on-rails-tutorial-book) 70 | 71 | ## Contributing 72 | 73 | We encourage you to contribute to Ruby on Rails! Please check out the 74 | [Contributing to Ruby on Rails guide](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html) for guidelines about how to proceed. [Join us!](http://contributors.rubyonrails.org) 75 | 76 | ## Code Status 77 | 78 | * [![Build Status](https://api.travis-ci.org/rails/rails.png)](https://travis-ci.org/rails/rails) 79 | * [![Dependencies](https://gemnasium.com/rails/rails.png?travis)](https://gemnasium.com/rails/rails) 80 | 81 | ## License 82 | 83 | Ruby on Rails is released under the [MIT License](http://www.opensource.org/licenses/MIT). 84 | -------------------------------------------------------------------------------- /tests/AlertsTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // Set any necessary configurations 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | 21 | public function testEnableAlerts() 22 | { 23 | $this->parsedownExtended->config()->set('alerts', true); 24 | 25 | $markdown = << [!NOTE] 27 | > This is a note. 28 | MARKDOWN; 29 | 30 | $expectedHtml = << 32 |

Note

33 |

This is a note.

34 | 35 | HTML; 36 | 37 | $result = $this->parsedownExtended->text($markdown); 38 | 39 | $this->assertEquals(trim($expectedHtml), trim($result)); 40 | } 41 | 42 | public function testDisableAlerts() 43 | { 44 | $this->parsedownExtended->config()->set('alerts', false); 45 | 46 | $markdown = << [!NOTE] 48 | > This is a note. 49 | MARKDOWN; 50 | 51 | $expectedHtml = << 53 |

[!NOTE] 54 | This is a note.

55 |
56 | HTML; 57 | 58 | $result = $this->parsedownExtended->text($markdown); 59 | 60 | $this->assertEquals(trim($expectedHtml), trim($result)); 61 | } 62 | 63 | public function testNoteAlert() 64 | { 65 | $markdown = << [!NOTE] 67 | > This is a note. 68 | MARKDOWN; 69 | 70 | $expectedHtml = << 72 |

Note

73 |

This is a note.

74 | 75 | HTML; 76 | 77 | $result = $this->parsedownExtended->text($markdown); 78 | 79 | $this->assertEquals(trim($expectedHtml), trim($result)); 80 | } 81 | 82 | public function testTipAlert() 83 | { 84 | $markdown = << [!TIP] 86 | > This is a tip. 87 | MARKDOWN; 88 | 89 | $expectedHtml = << 91 |

Tip

92 |

This is a tip.

93 | 94 | HTML; 95 | 96 | $result = $this->parsedownExtended->text($markdown); 97 | 98 | $this->assertEquals(trim($expectedHtml), trim($result)); 99 | } 100 | 101 | public function testImportantAlert() 102 | { 103 | $markdown = << [!IMPORTANT] 105 | > This is important. 106 | MARKDOWN; 107 | 108 | $expectedHtml = << 110 |

Important

111 |

This is important.

112 | 113 | HTML; 114 | 115 | $result = $this->parsedownExtended->text($markdown); 116 | 117 | $this->assertEquals(trim($expectedHtml), trim($result)); 118 | } 119 | 120 | public function testWarningAlert() 121 | { 122 | $markdown = << [!WARNING] 124 | > This is a warning. 125 | MARKDOWN; 126 | 127 | $expectedHtml = << 129 |

Warning

130 |

This is a warning.

131 | 132 | HTML; 133 | 134 | $result = $this->parsedownExtended->text($markdown); 135 | 136 | $this->assertEquals(trim($expectedHtml), trim($result)); 137 | } 138 | 139 | public function testCautionAlert() 140 | { 141 | $markdown = << [!CAUTION] 143 | > This is a caution. 144 | MARKDOWN; 145 | 146 | $expectedHtml = << 148 |

Caution

149 |

This is a caution.

150 | 151 | HTML; 152 | 153 | $result = $this->parsedownExtended->text($markdown); 154 | 155 | $this->assertEquals(trim($expectedHtml), trim($result)); 156 | } 157 | 158 | public function testCustomAlertTypes() 159 | { 160 | $this->parsedownExtended->config()->set('alerts.types', ['custom']); 161 | 162 | $markdown = << [!CUSTOM] 164 | > This is a custom alert. 165 | MARKDOWN; 166 | 167 | $expectedHtml = << 169 |

Custom

170 |

This is a custom alert.

171 | 172 | HTML; 173 | 174 | $result = $this->parsedownExtended->text($markdown); 175 | 176 | $this->assertEquals(trim($expectedHtml), trim($result)); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /tests/TablesTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | 21 | public function testEnableTables(): void 22 | { 23 | $this->parsedownExtended->config()->set('tables', true); 24 | 25 | $markdown = << 33 | 34 | 35 | Header 1 36 | Header 2 37 | 38 | 39 | 40 | 41 | Cell 1 42 | Cell 2 43 | 44 | 45 | 46 | HTML; 47 | 48 | $result = $this->parsedownExtended->text($markdown); 49 | 50 | $this->assertEquals(trim($expectedHtml), trim($result)); 51 | } 52 | 53 | public function testDisableTables(): void 54 | { 55 | $this->parsedownExtended->config()->set('tables', false); 56 | 57 | $markdown = <<| Header 1 | Header 2 | 65 | | -------- | -------- | 66 | | Cell 1 | Cell 2 |

67 | HTML; 68 | 69 | $result = $this->parsedownExtended->text($markdown); 70 | 71 | $this->assertEquals(trim($expectedHtml), trim($result)); 72 | } 73 | 74 | public function testTableAlign(): void 75 | { 76 | $this->parsedownExtended->config()->set('tables', true); 77 | 78 | $markdown = << 87 | 88 | 89 | Left-aligned 90 | Center-aligned 91 | Right-aligned 92 | 93 | 94 | 95 | 96 | git status 97 | git status 98 | git status 99 | 100 | 101 | git diff 102 | git diff 103 | git diff 104 | 105 | 106 | 107 | HTML; 108 | 109 | $result = $this->parsedownExtended->text($markdown); 110 | 111 | $this->assertEquals($expectedHtml, $result); 112 | } 113 | 114 | public function testTableSpan(): void 115 | { 116 | $this->parsedownExtended->config()->set('tables', true); 117 | $this->parsedownExtended->config()->set('tables.tablespan', true); 118 | 119 | $markdown = << | > | Colspan | > | for thead | 121 | | ----- | ----------- | --------------- | ----------- | --------- | 122 | | Lorem | ipsum | dolor | sit | amet | 123 | | ^ | - | > | more words | . | 124 | | , | > | some long text | > | 2x2 cell | 125 | | > | another 2x2 | + | > | ^ | 126 | | > | ^ | | | ! | 127 | 128 | MARKDOWN; 129 | 130 | $expectedHtml = << 132 | 133 | 134 | Colspan 135 | for thead 136 | 137 | 138 | 139 | 140 | Lorem 141 | ipsum 142 | dolor 143 | sit 144 | amet 145 | 146 | 147 | - 148 | more words 149 | . 150 | 151 | 152 | , 153 | some long text 154 | 2x2 cell 155 | 156 | 157 | another 2x2 158 | + 159 | 160 | 161 | 162 | 163 | ! 164 | 165 | 166 | 167 | HTML; 168 | 169 | $result = $this->parsedownExtended->text($markdown); 170 | 171 | $this->assertEquals(trim($expectedHtml), trim($result)); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | ParsedownExtended 5 | 6 | 7 |

Parsedown Extended

8 | 9 |

10 | Explore Documentation » 11 |
12 |
13 | Report bug 14 | · 15 | Request feature 16 | · 17 | Discussions 18 |

19 | 20 |

21 | 22 |
23 | 24 | ![GitHub Release](https://img.shields.io/github/v/release/BenjaminHoegh/ParsedownExtended?style=flat-square) 25 | ![Packagist Downloads](https://img.shields.io/packagist/dt/benjaminhoegh/parsedown-extended?style=flat-square) 26 | ![GitHub License](https://img.shields.io/github/license/BenjaminHoegh/ParsedownExtended?style=flat-square) 27 | 28 | 29 | Table of contents 30 | 31 | - [Introduction](#introduction) 32 | - [Features](#features) 33 | - [Getting started](#getting-started) 34 | - [Bugs and feature requests](#bugs-and-feature-requests) 35 | - [Contributing](#contributing) 36 | - [Community](#community) 37 | - [Copyright and license](#copyright-and-license) 38 | 39 | ## Introduction 40 | 41 | ParsedownExtended is an extention for Parsedown, offering additional features and functionalities. It is designed to provide an easy-to-use Markdown parsing solution while extending the capabilities of the base Parsedown library. 42 | 43 | Stand alone versions of the extended features are also available as separate libraries: 44 | - [Parsedown Toc](https://github.com/BenjaminHoegh/ParsedownToc) 45 | - [Parsedown Math](https://github.com/BenjaminHoegh/ParsedownMath) 46 | 47 | ## Features 48 | 49 | ParsedownExtended includes a variety of features to enhance your Markdown parsing experience: 50 | 51 | - **Task Lists:** Create simple task lists in Markdown. 52 | - **Smartypants:** Automatically convert straight quotes to curly, dashes to en-dash and em-dash, etc. 53 | - **Emoji shortcodes:** Support for rendering emojis. 54 | - **Heading Permalinks:** Generate permalinks for your headings. 55 | - **Table of Contents:** Automatically generate a table of contents based on headings. 56 | - **Keystrokes:** Render keystroke combinations. 57 | - **Marking:** Mark text within your documents for emphasis or distinction. 58 | - **Superscript and Subscript:** Render text as superscript or subscript. 59 | - **Diagrams Syntax Support:** Recognizes diagram syntax for integration with libraries like mermaid.js and chart.js. 60 | - **LaTeX Syntax Support:** Detects LaTeX syntax, suitable for mathematical expressions, to be rendered with libraries like KaTeX.js. 61 | - **Predefined Abbreviations:** Define and use abbreviations easily. 62 | - **GFM Alerts:** Create alerts using GitHub Flavored Markdown Alert syntax there can be customized into your own language, or even create your own. 63 | - **Customizable Options:** Extensive options for customizing each Markdown element. 64 | - **Additional Features:** ParsedownExtended continuously evolves, adding more features over time. 65 | 66 | ## Getting started 67 | 68 | ### Manual 69 | Download the source code from the latest release 70 | You must include `parsedown.php` 1.7+ 71 | Include `ParsedownExtended.php` 72 | 73 | ```php 74 | require 'Parsedown.php'; 75 | require 'ParsedownExtra.php'; // optional 76 | require 'ParsedownExtended.php'; 77 | 78 | use BenjaminHoegh\ParsedownExtended\ParsedownExtended; 79 | 80 | $ParsedownExtended = new ParsedownExtended(); 81 | 82 | echo $ParsedownExtended->text('Hello _Parsedown_!'); # prints:

Hello Parsedown!

83 | // you can also parse inline markdown only 84 | echo $ParsedownExtended->line('Hello _Parsedown_!'); # prints: Hello Parsedown! 85 | ``` 86 | 87 | ### Using composer 88 | 89 | From the command line interface, navigate to your project folder then run this command: 90 | ```shell 91 | composer require benjaminhoegh/parsedown-extended 92 | ``` 93 | Then require the auto-loader file: 94 | ```php 95 | require 'vendor/autoload.php'; 96 | 97 | use BenjaminHoegh\ParsedownExtended\ParsedownExtended; 98 | 99 | $ParsedownExtended = new ParsedownExtended(); 100 | 101 | echo $ParsedownExtended->text('Hello _Parsedown_!'); # prints:

Hello Parsedown!

102 | // you can also parse inline markdown only 103 | echo $ParsedownExtended->line('Hello _Parsedown_!'); # prints: Hello Parsedown! 104 | ``` 105 | 106 | ## Bugs and feature requests 107 | 108 | Have a bug or a feature request? Please first read the [issue guidelines](https://github.com/BenjaminHoegh/ParsedownExtended/blob/main/.github/CONTRIBUTING.md#using-the-issue-tracker) and search for existing and closed issues. If your problem or idea is not addressed yet, [please open a new issue](https://github.com/BenjaminHoegh/ParsedownExtended/issues/new/choose). 109 | 110 | ## Contributing 111 | 112 | Please read through our [contributing guidelines](https://github.com/BenjaminHoegh/ParsedownExtended/blob/main/.github/CONTRIBUTING.md). Included are directions for opening issues, coding standards, and notes on development. 113 | 114 | All PHP should conform to the [Code Guide](https://www.php-fig.org/psr/psr-12/). 115 | 116 | ## Community 117 | 118 | Get updates on ParsedownExtended's development and chat with the project maintainers and community members. 119 | 120 | - Join [GitHub discussions](https://github.com/BenjaminHoegh/ParsedownExtended/discussions). 121 | 122 | ## Copyright and license 123 | 124 | Code and documentation copyright 2024 the [ParsedownExtended Authors](https://github.com/BenjaminHoegh/ParsedownExtended/graphs/contributors). Code released under the [MIT License](https://github.com/BenjaminHoegh/ParsedownExtended/blob/main/LICENSE.md). Docs released under [Creative Commons](https://github.com/BenjaminHoegh/ParsedownExtended/blob/main/docs/LICENSE.md). 125 | -------------------------------------------------------------------------------- /benchmarks/tests/textmate-readme.md: -------------------------------------------------------------------------------- 1 | # TextMate 2 | 3 | ## Download 4 | 5 | You can [download TextMate from here](http://macromates.com/download). 6 | 7 | ## Feedback 8 | 9 | You can use [the TextMate mailing list](http://lists.macromates.com/listinfo/textmate) or [#textmate][] IRC channel on [freenode.net][] for questions, comments, and bug reports. 10 | 11 | You can also [contact MacroMates](http://macromates.com/contact). 12 | 13 | Before you submit a bug report please read the [writing bug reports](http://kb.textmate.org/writing_bug_reports) instructions. 14 | 15 | ## Screenshot 16 | 17 | ![textmate](https://raw.github.com/textmate/textmate/gh-pages/images/screenshot.png) 18 | 19 | # Building 20 | 21 | ## Bootstrap 22 | 23 | To bootstrap the build you need to run `./configure` (in the root of the source tree). You can set a few (environment) variables read by this script that change the generated build file: 24 | 25 | * `builddir` — location of built files. Defaults to `~/build/TextMate`. 26 | * `identity` — for Apple’s `codesign`. Defaults to ad-hoc signing, which does not use an identity at all. 27 | * `boostdir` — location of boost includes. By default it will search various locations including MacPorts and Homebrew. 28 | * `sparsedir` — location of sparsehash includes. By default it will search various locations including MacPorts and Homebrew. 29 | * `CC` and `CXX` — C and C++ compiler. 30 | 31 | In the simplest case you would run: 32 | 33 | git clone https://github.com/textmate/textmate.git 34 | cd textmate 35 | git submodule update --init 36 | ./configure && ninja 37 | 38 | Please note that if you downloaded the source code (rather than cloned via git) you likely miss the submodules and the build will therefore fail. 39 | 40 | ## Prerequisites 41 | 42 | To build the source the following must first be installed on your system: 43 | 44 | * [ninja][] — build system similar to `make` 45 | * [ragel][] — state machine compiler 46 | * [boost][] — portable C++ source libraries 47 | * [sparsehash][] — A cache friendly hash_map 48 | * [multimarkdown][] — marked-up plain text compiler 49 | * [mercurial][] — distributed SCM system 50 | * [Cap’n Proto][capnp] — serialization library 51 | 52 | You need to manually install [Cap’n Proto][capnp] if you're not using [homebrew][]. To install the other dependencies via [MacPorts][] run: 53 | 54 | sudo port install ninja ragel boost multimarkdown mercurial sparsehash 55 | 56 | If `port` fails with a build error then likely you need to agree (system-wide) to Apple’s Xcode license: 57 | 58 | sudo xcodebuild -license 59 | 60 | To install using [homebrew][] run: 61 | 62 | brew install ragel boost multimarkdown hg ninja capnp google-sparsehash 63 | 64 | In practice `hg` ([mercurial][]) is only required for the SCM library’s tests so you can skip this dependency if you don’t mind a failing test. 65 | 66 | ### OS X 10.7 (Lion) 67 | 68 | If you are on OS X 10.7 you need `pgrep` and `pkill` (used by the “relaunch” build targets). To install using [MacPorts][]: 69 | 70 | sudo port install proctools 71 | 72 | Or using [homebrew][]: 73 | 74 | brew install proctools 75 | 76 | 77 | ## Building from within TextMate 78 | 79 | You should install the [Ninja][NinjaBundle] and [CxxTest][] bundles. Both can be installed via _Preferences_ → _Bundles_. 80 | 81 | After this you can press ⌘B to build from within TextMate. In case you haven't already you also need to set up the `PATH` variable either in _Preferences_ → _Variables_ or `~/.tm_properties` so it can find `ninja` and related tools; an example could be `$PATH:/opt/local/bin`. 82 | 83 | The default target is `TextMate/run`. This will relaunch TextMate but when called from within TextMate, a dialog will appear before the current instance is killed. As there is full session restore, it is safe to relaunch even with unsaved changes. 84 | 85 | If the current file is a test file then the target to build is changed to build the library to which the test belongs (this is done by setting `TM_NINJA_TARGET` in the `.tm_properties` file found in the root of the source tree). 86 | 87 | Similarly, if the current file belongs to an application target (other than `TextMate.app`) then `TM_NINJA_TARGET` is set to build and run this application. 88 | 89 | ## Build Targets 90 | 91 | The build system classifies a target either as a library or an application. The latter can either be a bundled or non-bundled application. E.g. `mate` is non-bundled (just a `mate` executable) where `TextMate.app` is a bundled application. 92 | 93 | For each output there are a few symbolic targets you can build. While the examples below refer to a specific library or application, they exist for all targets of same type. 94 | 95 | For the `io` library: 96 | 97 | ninja io # Build the io library and run tests. 98 | ninja io/coerce # Build the io library and skip tests. 99 | ninja io/clean # Remove the build folder for the io library. 100 | ninja io/headers # Copy exported headers to $builddir/include. 101 | 102 | For the `mate` (non-bundled) application: 103 | 104 | ninja mate # Build the mate executable. 105 | ninja mate/run # Build and run the mate executable. 106 | ninja mate/clean # Remove the build folder for the mate executable. 107 | 108 | For the `TextMate.app` application: 109 | 110 | ninja TextMate # Build and sign TextMate.app. 111 | ninja TextMate/run # Build, sign, and run TextMate.app. 112 | ninja TextMate/clean # Remove the build folder for TextMate.app. 113 | ninja TextMate/dsym # Create a tarball with extracted dSYM files. 114 | ninja TextMate/tbz # Create a tarball of TextMate.app. Also produce the dsym tarball. 115 | ninja TextMate/deploy # Push a nightly build. Fails without proper credentials :) 116 | 117 | Note that `ninja TextMate/clean` only cleans the TextMate build folder (`$builddir/Applications/TextMate`), but all libraries and applications it depends on are not cleaned. 118 | 119 | To clean everything run: 120 | 121 | ninja -t clean 122 | 123 | # Legal 124 | 125 | The source for TextMate is released under the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 126 | 127 | TextMate is a trademark of Allan Odgaard. 128 | 129 | [boost]: http://www.boost.org/ 130 | [ninja]: http://martine.github.com/ninja/ 131 | [multimarkdown]: http://fletcherpenney.net/multimarkdown/ 132 | [ragel]: http://www.complang.org/ragel/ 133 | [mercurial]: http://mercurial.selenic.com/ 134 | [capnp]: http://kentonv.github.io/capnproto/ 135 | [clang 3.2]: http://clang.llvm.org/ 136 | [MacPorts]: http://www.macports.org/ 137 | [homebrew]: http://brew.sh/ 138 | [NinjaBundle]: https://github.com/textmate/ninja.tmbundle 139 | [CxxTest]: https://github.com/textmate/cxxtest.tmbundle 140 | [sparsehash]: https://code.google.com/p/sparsehash/ 141 | [#textmate]: irc://irc.freenode.net/#textmate 142 | [freenode.net]: http://freenode.net/ 143 | -------------------------------------------------------------------------------- /tests/TocTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | 21 | /** 22 | * Test case for table of contents. 23 | */ 24 | public function testTocEnabled() 25 | { 26 | $this->parsedownExtended->config()->set('headings.auto_anchors', true); 27 | $this->parsedownExtended->config()->set('toc', true); 28 | 29 | $markdown = << 40 |
  • Heading 1 41 |
  • 45 |
  • Heading 2 46 |
  • 50 | 51 | HTML; 52 | 53 | $actual = $this->parsedownExtended->body($markdown); 54 | $actual = $this->parsedownExtended->contentsList(); 55 | $this->assertEquals($expected, $actual); 56 | } 57 | 58 | public function testTocDisabled() 59 | { 60 | $this->parsedownExtended->config()->set('headings.auto_anchors', true); 61 | $this->parsedownExtended->config()->set('toc', false); 62 | 63 | $markdown = <<[toc]

    75 |

    Heading 1

    76 |

    Heading 1.1

    77 |

    Heading 1.2

    78 |

    Heading 2

    79 |

    Heading 2.1

    80 |

    Heading 2.2

    81 | HTML; 82 | 83 | $actual = $this->parsedownExtended->text($markdown); 84 | $this->assertEquals($expected, $actual); 85 | } 86 | 87 | /** 88 | * Test case for table of contents with custom heading levels. 89 | */ 90 | public function testTocWithCustomHeadingLevels() 91 | { 92 | $this->parsedownExtended->config()->set('headings.auto_anchors', true); 93 | $this->parsedownExtended->config()->set('toc.levels', ['h1', 'h2']); 94 | 95 | $markdown = << 110 |
  • Heading 1 111 |
  • 115 |
  • Heading 2 116 |
  • 120 | 121 | HTML; 122 | 123 | $actual = $this->parsedownExtended->body($markdown); 124 | $actual = $this->parsedownExtended->contentsList(); 125 | $this->assertEquals($expected, $actual); 126 | } 127 | 128 | /** 129 | * Test case for table of contents toc tag. 130 | */ 131 | 132 | public function testTocTag() 133 | { 134 | $this->parsedownExtended->config()->set('headings.auto_anchors', true); 135 | $this->parsedownExtended->config()->set('toc.tag', '[toc]'); 136 | 137 | $markdown = << 160 |

    Heading 1

    161 |

    Heading 1.1

    162 |

    Heading 1.2

    163 |

    Heading 2

    164 |

    Heading 2.1

    165 |

    Heading 2.2

    166 | HTML; 167 | 168 | $actual = $this->parsedownExtended->text($markdown); 169 | $this->assertEquals($expected, $actual); 170 | } 171 | 172 | /** 173 | * Test case for table of contents with multiple settings at once. 174 | */ 175 | 176 | public function testTocMultipleSettings() 177 | { 178 | $this->parsedownExtended->config()->set('toc', [ 179 | 'tag' => '[custom-toc-tag]', 180 | 'levels' => ['h1', 'h2'], 181 | ]); 182 | 183 | $markdown = << 207 |

    Heading 1

    208 |

    Heading 1.1

    209 |

    Heading 1.2

    210 |

    Heading 1.2.1

    211 |

    Heading 2

    212 |

    Heading 2.1

    213 |

    Heading 2.2

    214 | HTML; 215 | 216 | $actual = $this->parsedownExtended->text($markdown); 217 | $this->assertEquals($expected, $actual); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /benchmarks/benchmark.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Performance Benchmarks 7 | 8 | 9 | 10 | 77 | 78 | 79 | 80 |

    Performance Benchmarks

    81 | 82 | Warning: Xdebug is enabled. This may result in incorrect benchmark results."; 89 | } 90 | 91 | // Ensure ParsedownExtra and ParsedownExtended are available 92 | if (!class_exists('ParsedownExtra') || !class_exists('BenjaminHoegh\ParsedownExtended\ParsedownExtended')) { 93 | echo "
    Error: ParsedownExtra and ParsedownExtended are required but not found. Please make sure these packages are installed.
    "; 94 | exit; // Stop further execution 95 | } 96 | 97 | ?> 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | defaultTransform($markdown); 123 | } elseif ($parser instanceof League\CommonMark\CommonMarkConverter) { 124 | $parser->convert($markdown); 125 | } else { 126 | $parser->text($markdown); 127 | } 128 | 129 | $total_time += microtime(true) - $start; 130 | } 131 | 132 | return $total_time / $iterations; // Return average time 133 | } 134 | 135 | // Load your markdown files (adjust paths as necessary) 136 | $markdown_files = [ 137 | 'angular readme' => file_get_contents('tests/angular-readme.md'), 138 | 'bootstrap readme' => file_get_contents('tests/bootstrap-readme.md'), 139 | 'homebrew readme' => file_get_contents('tests/homebrew-readme.md'), 140 | 'jquery readme' => file_get_contents('tests/jquery-readme.md'), 141 | 'markdown readme' => file_get_contents('tests/markdown-readme.md'), 142 | 'rails readme' => file_get_contents('tests/rails-readme.md'), 143 | 'textmate readme' => file_get_contents('tests/textmate-readme.md'), 144 | // Add other markdown files similarly 145 | ]; 146 | 147 | // Initialize parsers 148 | $parsers = []; 149 | $parsers['ParsedownExtra'] = new ParsedownExtra(); 150 | $parsers['ParsedownExtended'] = new \BenjaminHoegh\ParsedownExtended\ParsedownExtended(); 151 | 152 | if (class_exists('Michelf\MarkdownExtra')) { 153 | $parsers['Markdown PHP'] = new \Michelf\MarkdownExtra(); 154 | } 155 | if (class_exists('League\CommonMark\CommonMarkConverter')) { 156 | $parsers['League'] = new \League\CommonMark\CommonMarkConverter(); 157 | } 158 | 159 | // Initialize variables to calculate averages 160 | $totals = []; 161 | $averages = []; 162 | $file_count = count($markdown_files); 163 | 164 | // Run the benchmarks 165 | foreach ($markdown_files as $name => $markdown) { 166 | echo ""; 167 | echo ""; 168 | 169 | // Benchmark ParsedownExtra first to establish the base speed 170 | $parsedown_extra_time = benchmark($parsers['ParsedownExtra'], $markdown); 171 | $parsedown_extra_time_ms = round($parsedown_extra_time * 1000, 2); // Convert to milliseconds 172 | $totals['ParsedownExtra'] = ($totals['ParsedownExtra'] ?? 0) + $parsedown_extra_time; 173 | 174 | echo ""; 175 | 176 | // Benchmark other parsers and compare to ParsedownExtra 177 | foreach ($parsers as $parser_name => $parser) { 178 | if ($parser_name !== 'ParsedownExtra') { 179 | $time = benchmark($parser, $markdown); 180 | $time_ms = round($time * 1000, 2); // Convert to milliseconds 181 | $totals[$parser_name] = ($totals[$parser_name] ?? 0) + $time; 182 | 183 | $speed_diff = $time / $parsedown_extra_time; 184 | $speed_text = $speed_diff < 1 ? 'faster' : 'slower'; 185 | $times_diff = round($speed_diff < 1 ? 1 / $speed_diff : $speed_diff, 2); 186 | 187 | echo ""; 188 | } 189 | } 190 | 191 | echo ""; 192 | } 193 | 194 | // Calculate the averages 195 | echo ""; 196 | echo ""; 197 | 198 | // Average for ParsedownExtra 199 | $average_parsedown_extra_time = round(($totals['ParsedownExtra'] / $file_count) * 1000, 2); 200 | echo ""; 201 | 202 | // Averages for other parsers relative to ParsedownExtra 203 | foreach ($parsers as $parser_name => $parser) { 204 | if ($parser_name !== 'ParsedownExtra') { 205 | $average_time = round(($totals[$parser_name] / $file_count) * 1000, 2); 206 | $average_speed_diff = $average_time / $average_parsedown_extra_time; 207 | $average_speed_text = $average_speed_diff < 1 ? 'faster' : 'slower'; 208 | $average_times_diff = round($average_speed_diff < 1 ? 1 / $average_speed_diff : $average_speed_diff, 1); 209 | 210 | echo ""; 211 | } 212 | } 213 | 214 | echo ""; 215 | ?> 216 | 217 |
    SourceParsedownExtra | base speed ParsedownExtended | our extension Markdown PHP | the original parser League | commonmark parser
    $name~ {$parsedown_extra_time_ms} ms~ {$time_ms} ms or {$times_diff} times {$speed_text}
    Averages~ {$average_parsedown_extra_time} ms~ {$average_time} ms or {$average_times_diff} times {$average_speed_text}
    218 | 219 | 220 | -------------------------------------------------------------------------------- /benchmarks/tests/bootstrap-readme.md: -------------------------------------------------------------------------------- 1 | # [Bootstrap](http://getbootstrap.com) [![Build Status](https://secure.travis-ci.org/twbs/bootstrap.png)](http://travis-ci.org/twbs/bootstrap) [![devDependency Status](https://david-dm.org/twbs/bootstrap/dev-status.png)](https://david-dm.org/twbs/bootstrap#info=devDependencies) 2 | 3 | Bootstrap is a sleek, intuitive, and powerful front-end framework for faster and easier web development, created and maintained by [Mark Otto](http://twitter.com/mdo) and [Jacob Thornton](http://twitter.com/fat). 4 | 5 | To get started, check out ! 6 | 7 | 8 | 9 | ## Quick start 10 | 11 | Three quick start options are available: 12 | 13 | * [Download the latest release](https://github.com/twbs/bootstrap/archive/v3.0.2.zip). 14 | * Clone the repo: `git clone https://github.com/twbs/bootstrap.git`. 15 | * Install with [Bower](http://bower.io): `bower install bootstrap`. 16 | 17 | Read the [Getting Started page](http://getbootstrap.com/getting-started/) for information on the framework contents, templates and examples, and more. 18 | 19 | ### What's included 20 | 21 | Within the download you'll find the following directories and files, logically grouping common assets and providing both compiled and minified variations. You'll see something like this: 22 | 23 | ``` 24 | bootstrap/ 25 | ├── css/ 26 | │ ├── bootstrap.css 27 | │ ├── bootstrap.min.css 28 | │ ├── bootstrap-theme.css 29 | │ └── bootstrap-theme.min.css 30 | ├── js/ 31 | │ ├── bootstrap.js 32 | │ └── bootstrap.min.js 33 | └── fonts/ 34 | ├── glyphicons-halflings-regular.eot 35 | ├── glyphicons-halflings-regular.svg 36 | ├── glyphicons-halflings-regular.ttf 37 | └── glyphicons-halflings-regular.woff 38 | ``` 39 | 40 | We provide compiled CSS and JS (`bootstrap.*`), as well as compiled and minified CSS and JS (`bootstrap.min.*`). Fonts from Glyphicons are included, as is the optional Bootstrap theme. 41 | 42 | 43 | 44 | ## Bugs and feature requests 45 | 46 | Have a bug or a feature request? [Please open a new issue](https://github.com/twbs/bootstrap/issues). Before opening any issue, please search for existing issues and read the [Issue Guidelines](https://github.com/necolas/issue-guidelines), written by [Nicolas Gallagher](https://github.com/necolas/). 47 | 48 | You may use [this JS Bin](http://jsbin.com/aKiCIDO/1/edit) as a template for your bug reports. 49 | 50 | 51 | 52 | ## Documentation 53 | 54 | Bootstrap's documentation, included in this repo in the root directory, is built with [Jekyll](http://jekyllrb.com) and publicly hosted on GitHub Pages at . The docs may also be run locally. 55 | 56 | ### Running documentation locally 57 | 58 | 1. If necessary, [install Jekyll](http://jekyllrb.com/docs/installation) (requires v1.x). 59 | 2. From the root `/bootstrap` directory, run `jekyll serve` in the command line. 60 | - **Windows users:** run `chcp 65001` first to change the command prompt's character encoding ([code page](http://en.wikipedia.org/wiki/Windows_code_page)) to UTF-8 so Jekyll runs without errors. 61 | 3. Open in your browser, and voilà. 62 | 63 | Learn more about using Jekyll by reading its [documentation](http://jekyllrb.com/docs/home/). 64 | 65 | ### Documentation for previous releases 66 | 67 | Documentation for v2.3.2 has been made available for the time being at while folks transition to Bootstrap 3. 68 | 69 | [Previous releases](https://github.com/twbs/bootstrap/releases) and their documentation are also available for download. 70 | 71 | 72 | 73 | ## Compiling CSS and JavaScript 74 | 75 | Bootstrap uses [Grunt](http://gruntjs.com/) with convenient methods for working with the framework. It's how we compile our code, run tests, and more. To use it, install the required dependencies as directed and then run some Grunt commands. 76 | 77 | ### Install Grunt 78 | 79 | From the command line: 80 | 81 | 1. Install `grunt-cli` globally with `npm install -g grunt-cli`. 82 | 2. Navigate to the root `/bootstrap` directory, then run `npm install`. npm will look at [package.json](package.json) and automatically install the necessary local dependencies listed there. 83 | 84 | When completed, you'll be able to run the various Grunt commands provided from the command line. 85 | 86 | **Unfamiliar with `npm`? Don't have node installed?** That's a-okay. npm stands for [node packaged modules](http://npmjs.org/) and is a way to manage development dependencies through node.js. [Download and install node.js](http://nodejs.org/download/) before proceeding. 87 | 88 | ### Available Grunt commands 89 | 90 | #### Build - `grunt` 91 | Run `grunt` to run tests locally and compile the CSS and JavaScript into `/dist`. **Uses [recess](http://twitter.github.io/recess/) and [UglifyJS](http://lisperator.net/uglifyjs/).** 92 | 93 | #### Only compile CSS and JavaScript - `grunt dist` 94 | `grunt dist` creates the `/dist` directory with compiled files. **Uses [recess](http://twitter.github.io/recess/) and [UglifyJS](http://lisperator.net/uglifyjs/).** 95 | 96 | #### Tests - `grunt test` 97 | Runs [JSHint](http://jshint.com) and [QUnit](http://qunitjs.com/) tests headlessly in [PhantomJS](http://phantomjs.org/) (used for CI). 98 | 99 | #### Watch - `grunt watch` 100 | This is a convenience method for watching just Less files and automatically building them whenever you save. 101 | 102 | ### Troubleshooting dependencies 103 | 104 | Should you encounter problems with installing dependencies or running Grunt commands, uninstall all previous dependency versions (global and local). Then, rerun `npm install`. 105 | 106 | 107 | 108 | ## Contributing 109 | 110 | Please read through our [contributing guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md). Included are directions for opening issues, coding standards, and notes on development. 111 | 112 | More over, if your pull request contains JavaScript patches or features, you must include relevant unit tests. All HTML and CSS should conform to the [Code Guide](http://github.com/mdo/code-guide), maintained by [Mark Otto](http://github.com/mdo). 113 | 114 | Editor preferences are available in the [editor config](.editorconfig) for easy use in common text editors. Read more and download plugins at . 115 | 116 | With v3.1, we're moving from the Apache 2 to the MIT license for the Bootstrap code (not the docs). Please see the [contributing guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md) for more information. 117 | 118 | 119 | ## Community 120 | 121 | Keep track of development and community news. 122 | 123 | * Follow [@twbootstrap on Twitter](http://twitter.com/twbootstrap). 124 | * Read and subscribe to [The Official Bootstrap Blog](http://blog.getbootstrap.com). 125 | * Have a question that's not a feature request or bug report? [Ask on the mailing list.](http://groups.google.com/group/twitter-bootstrap) 126 | * Chat with fellow Bootstrappers in IRC. On the `irc.freenode.net` server, in the `##twitter-bootstrap` channel. 127 | 128 | 129 | 130 | 131 | ## Versioning 132 | 133 | For transparency and insight into our release cycle, and for striving to maintain backward compatibility, Bootstrap will be maintained under the Semantic Versioning guidelines as much as possible. 134 | 135 | Releases will be numbered with the following format: 136 | 137 | `..` 138 | 139 | And constructed with the following guidelines: 140 | 141 | * Breaking backward compatibility bumps the major (and resets the minor and patch) 142 | * New additions without breaking backward compatibility bumps the minor (and resets the patch) 143 | * Bug fixes and misc changes bumps the patch 144 | 145 | For more information on SemVer, please visit . 146 | 147 | 148 | 149 | ## Authors 150 | 151 | **Mark Otto** 152 | 153 | + 154 | + 155 | 156 | **Jacob Thornton** 157 | 158 | + 159 | + 160 | 161 | 162 | 163 | ## Copyright and license 164 | 165 | Copyright 2013 Twitter, Inc under [the Apache 2.0 license](LICENSE). 166 | -------------------------------------------------------------------------------- /tests/HeadingsTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 15 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 16 | } 17 | 18 | protected function tearDown(): void 19 | { 20 | unset($this->parsedownExtended); 21 | } 22 | 23 | /** 24 | * Test case for heading without anchor. 25 | */ 26 | public function testHeadingWithoutAnchor() 27 | { 28 | $this->parsedownExtended->config()->set('headings.auto_anchors', false); 29 | 30 | $markdown = '# Heading 1'; 31 | $expected = '

    Heading 1

    '; 32 | $actual = $this->parsedownExtended->text($markdown); 33 | $this->assertEquals($expected, $actual); 34 | } 35 | 36 | /** 37 | * Test case for heading with anchor. 38 | */ 39 | public function testHeadingWithAnchor() 40 | { 41 | $this->parsedownExtended->config()->set('headings.auto_anchors', true); 42 | 43 | $markdown = '# Heading 1'; 44 | $expected = '

    Heading 1

    '; 45 | $actual = $this->parsedownExtended->text($markdown); 46 | $this->assertEquals($expected, $actual); 47 | } 48 | 49 | /** 50 | * Test case for heading with multiple occurrences. 51 | */ 52 | public function testHeadingWithMultipleOccurrences() 53 | { 54 | $this->parsedownExtended->config()->set('headings.auto_anchors', true); 55 | 56 | $markdown = <<Heading 1 64 |

    Heading 1

    65 |

    Heading 1

    66 | HTML; 67 | $actual = $this->parsedownExtended->text($markdown); 68 | $this->assertEquals($expected, $actual); 69 | } 70 | 71 | /** 72 | * Test case for heading with custom anchor. 73 | */ 74 | public function testHeadingWithCustomAnchor() 75 | { 76 | $this->parsedownExtended->config()->set('headings.auto_anchors', true); 77 | 78 | $markdown = '# Heading 1 {#custom-anchor}'; 79 | $expected = '

    Heading 1

    '; 80 | $actual = $this->parsedownExtended->text($markdown); 81 | $this->assertEquals($expected, $actual); 82 | } 83 | 84 | /** 85 | * Test case for heading with blacklisted header ids. 86 | */ 87 | public function testHeadingWithBlacklistedHeaderIds() 88 | { 89 | $this->parsedownExtended->config()->set('headings.auto_anchors', true); 90 | $this->parsedownExtended->config()->set('headings.auto_anchors.blacklist', ['heading-1']); 91 | 92 | $markdown = '# Heading 1'; 93 | $expected = '

    Heading 1

    '; 94 | $actual = $this->parsedownExtended->text($markdown); 95 | $this->assertEquals($expected, $actual); 96 | } 97 | 98 | /** 99 | * Test case for heading blacklist with multiple occurrences. 100 | */ 101 | public function testHeadingBlacklistWithMultipleOccurrences() 102 | { 103 | $this->parsedownExtended->config()->set('headings.auto_anchors', true); 104 | $this->parsedownExtended->config()->set('headings.auto_anchors.blacklist', ['heading-1', 'heading-4']); 105 | 106 | $markdown = <<Heading 115 |

    Heading

    116 |

    Heading

    117 |

    Heading

    118 | HTML; 119 | $actual = $this->parsedownExtended->text($markdown); 120 | $this->assertEquals($expected, $actual); 121 | } 122 | 123 | /** 124 | * Test case for heading with custom anchor and blacklisted header ids. 125 | */ 126 | public function testHeadingWithCustomAnchorAndBlacklistedHeaderIds() 127 | { 128 | $this->parsedownExtended->config()->set('headings.auto_anchors', true); 129 | $this->parsedownExtended->config()->set('headings.auto_anchors.blacklist', ['custom-anchor']); 130 | 131 | $markdown = '# Heading 1 {#custom-anchor}'; 132 | $expected = '

    Heading 1

    '; 133 | $actual = $this->parsedownExtended->text($markdown); 134 | $this->assertEquals($expected, $actual); 135 | } 136 | 137 | 138 | /** 139 | * Test case for heading with limited allowed levels. 140 | */ 141 | public function testHeadingWithLimitedAllowedLevels() 142 | { 143 | $this->parsedownExtended->config()->set('headings.auto_anchors', false); 144 | $this->parsedownExtended->config()->set('headings.allowed_levels', ['h1', 'h2'], true); 145 | 146 | $markdown = <<Heading 1 157 |

    Heading 2

    158 |

    ### Heading 3 159 | #### Heading 4 160 | ##### Heading 5 161 | ###### Heading 6

    162 | HTML; 163 | $actual = $this->parsedownExtended->text($markdown); 164 | $this->assertEquals($expected, $actual); 165 | } 166 | 167 | /** 168 | * Test case for heading with lowercase. 169 | */ 170 | public function testHeadingWithLowercase() 171 | { 172 | $this->parsedownExtended->config()->set('headings.auto_anchors', true); 173 | $this->parsedownExtended->config()->set('headings.auto_anchors.lowercase', true); 174 | 175 | $markdown = '# Heading 1'; 176 | $expected = '

    Heading 1

    '; 177 | $actual = $this->parsedownExtended->text($markdown); 178 | $this->assertEquals($expected, $actual); 179 | 180 | 181 | $this->parsedownExtended->config()->set('headings.auto_anchors.lowercase', false); 182 | 183 | $markdown = '# Heading 1'; 184 | $expected = '

    Heading 1

    '; 185 | $actual = $this->parsedownExtended->text($markdown); 186 | $this->assertEquals($expected, $actual); 187 | } 188 | 189 | /** 190 | * Test case for heading with custom delimiter. 191 | */ 192 | public function testHeadingWithDelimiter() 193 | { 194 | $this->parsedownExtended->config()->set('headings.auto_anchors', true); 195 | $this->parsedownExtended->config()->set('headings.auto_anchors.delimiter', '_'); 196 | 197 | $markdown = '# Heading 1'; 198 | $expected = '

    Heading 1

    '; 199 | $actual = $this->parsedownExtended->text($markdown); 200 | $this->assertEquals($expected, $actual); 201 | } 202 | 203 | /** 204 | * Test case for heading with replacement. 205 | */ 206 | public function testHeadingWithReplacement() 207 | { 208 | $this->parsedownExtended->config()->set('headings.auto_anchors', true); 209 | $this->parsedownExtended->config()->set('headings.auto_anchors.replacements', [ 210 | '/h/' => 'd', 211 | ]); 212 | 213 | $markdown = '# Heading 1'; 214 | $expected = '

    Heading 1

    '; 215 | $actual = $this->parsedownExtended->text($markdown); 216 | $this->assertEquals($expected, $actual); 217 | } 218 | 219 | /** 220 | * Test case for heading with custom logic using callback. 221 | */ 222 | public function testHeadingUsingCallback() 223 | { 224 | $this->parsedownExtended->config()->set('headings.auto_anchors', true); 225 | $this->parsedownExtended->setCreateAnchorIDCallback(function ($text) { 226 | return 'custom-anchor'; 227 | }); 228 | 229 | $markdown = '# Heading 1'; 230 | $expected = '

    Heading 1

    '; 231 | $actual = $this->parsedownExtended->text($markdown); 232 | $this->assertEquals($expected, $actual); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /benchmarks/tests/markdown-readme.md: -------------------------------------------------------------------------------- 1 | Download 2 | -------- 3 | 4 | [Markdown 1.0.1][dl] (18 KB) -- 17 Dec 2004 5 | 6 | [dl]: http://daringfireball.net/projects/downloads/Markdown_1.0.1.zip 7 | 8 | 9 | Introduction 10 | ------------ 11 | 12 | Markdown is a text-to-HTML conversion tool for web writers. Markdown 13 | allows you to write using an easy-to-read, easy-to-write plain text 14 | format, then convert it to structurally valid XHTML (or HTML). 15 | 16 | Thus, "Markdown" is two things: (1) a plain text formatting syntax; 17 | and (2) a software tool, written in Perl, that converts the plain text 18 | formatting to HTML. See the [Syntax][] page for details pertaining to 19 | Markdown's formatting syntax. You can try it out, right now, using the 20 | online [Dingus][]. 21 | 22 | [syntax]: /projects/markdown/syntax 23 | [dingus]: /projects/markdown/dingus 24 | 25 | The overriding design goal for Markdown's formatting syntax is to make 26 | it as readable as possible. The idea is that a Markdown-formatted 27 | document should be publishable as-is, as plain text, without looking 28 | like it's been marked up with tags or formatting instructions. While 29 | Markdown's syntax has been influenced by several existing text-to-HTML 30 | filters, the single biggest source of inspiration for Markdown's 31 | syntax is the format of plain text email. 32 | 33 | The best way to get a feel for Markdown's formatting syntax is simply 34 | to look at a Markdown-formatted document. For example, you can view 35 | the Markdown source for the article text on this page here: 36 | 37 | 38 | (You can use this '.text' suffix trick to view the Markdown source for 39 | the content of each of the pages in this section, e.g. the 40 | [Syntax][s_src] and [License][l_src] pages.) 41 | 42 | [s_src]: /projects/markdown/syntax.text 43 | [l_src]: /projects/markdown/license.text 44 | 45 | Markdown is free software, available under a BSD-style open source 46 | license. See the [License] [pl] page for more information. 47 | 48 | [pl]: /projects/markdown/license 49 | 50 | 51 | Discussion List 52 | --------------- 53 | 54 | I've set up a public [mailing list for discussion about Markdown] [ml]. 55 | Any topic related to Markdown -- both its formatting syntax and 56 | its software -- is fair game for discussion. Anyone who is interested 57 | is welcome to join. 58 | 59 | It's my hope that the mailing list will lead to good ideas for future 60 | improvements to Markdown. 61 | 62 | [ml]: http://six.pairlist.net/mailman/listinfo/markdown-discuss 63 | 64 | 65 | Installation and Requirements 66 | ----------------------------- 67 | 68 | Markdown requires Perl 5.6.0 or later. Welcome to the 21st Century. 69 | Markdown also requires the standard Perl library module [Digest::MD5] 70 | [md5], which is probably already installed on your server. 71 | 72 | [md5]: http://search.cpan.org/dist/Digest-MD5/MD5.pm 73 | 74 | 75 | ### Movable Type ### 76 | 77 | Markdown works with Movable Type version 2.6 or later (including 78 | Movable Type 3.0). 79 | 80 | 1. Copy the "Markdown.pl" file into your Movable Type "plugins" 81 | directory. The "plugins" directory should be in the same directory 82 | as "mt.cgi"; if the "plugins" directory doesn't already exist, use 83 | your FTP program to create it. Your installation should look like 84 | this: 85 | 86 | (mt home)/plugins/Markdown.pl 87 | 88 | 2. Once installed, Markdown will appear as an option in Movable Type's 89 | Text Formatting pop-up menu. This is selectable on a per-post basis: 90 | 91 | ![Screenshot of Movable Type 'Text Formatting' Menu][tfmenu] 92 | 93 | Markdown translates your posts to HTML when you publish; the posts 94 | themselves are stored in your MT database in Markdown format. 95 | 96 | 3. If you also install SmartyPants 1.5 (or later), Markdown will 97 | offer a second text formatting option: "Markdown With 98 | SmartyPants". This option is the same as the regular "Markdown" 99 | formatter, except that it automatically uses SmartyPants to create 100 | typographically correct curly quotes, em-dashes, and ellipses. See 101 | the [SmartyPants web page][sp] for more information. 102 | 103 | 4. To make Markdown (or "Markdown With SmartyPants") your default 104 | text formatting option for new posts, go to Weblog Config: 105 | Preferences. 106 | 107 | Note that by default, Markdown produces XHTML output. To configure 108 | Markdown to produce HTML 4 output, see "Configuration", below. 109 | 110 | [sp]: http://daringfireball.net/projects/smartypants/ 111 | 112 | 113 | 114 | ### Blosxom ### 115 | 116 | Markdown works with Blosxom version 2.0 or later. 117 | 118 | 1. Rename the "Markdown.pl" plug-in to "Markdown" (case is 119 | important). Movable Type requires plug-ins to have a ".pl" 120 | extension; Blosxom forbids it. 121 | 122 | 2. Copy the "Markdown" plug-in file to your Blosxom plug-ins folder. 123 | If you're not sure where your Blosxom plug-ins folder is, see the 124 | Blosxom documentation for information. 125 | 126 | 3. That's it. The entries in your weblog will now automatically be 127 | processed by Markdown. 128 | 129 | 4. If you'd like to apply Markdown formatting only to certain 130 | posts, rather than all of them, Markdown can optionally be used in 131 | conjunction with Blosxom's [Meta][] plug-in. First, install the 132 | Meta plug-in. Next, open the Markdown plug-in file in a text 133 | editor, and set the configuration variable `$g_blosxom_use_meta` 134 | to 1. Then, simply include a "`meta-markup: Markdown`" header line 135 | at the top of each post you compose using Markdown. 136 | 137 | [meta]: http://www.blosxom.com/plugins/meta/meta.htm 138 | 139 | 140 | ### BBEdit ### 141 | 142 | Markdown works with BBEdit 6.1 or later on Mac OS X. It also works 143 | with BBEdit 5.1 or later and MacPerl 5.6.1 on Mac OS 8.6 or later. If 144 | you're running Mac OS X 10.2 (Jaguar), you may need to install the 145 | Perl module [Digest::MD5] [md5] from CPAN; Digest::MD5 comes 146 | pre-installed on Mac OS X 10.3 (Panther). 147 | 148 | 1. Copy the "Markdown.pl" file to appropriate filters folder in your 149 | "BBEdit Support" folder. On Mac OS X, this should be: 150 | 151 | BBEdit Support/Unix Support/Unix Filters/ 152 | 153 | See the BBEdit documentation for more details on the location of 154 | these folders. 155 | 156 | You can rename "Markdown.pl" to whatever you wish. 157 | 158 | 2. That's it. To use Markdown, select some text in a BBEdit document, 159 | then choose Markdown from the Filters sub-menu in the "#!" menu, or 160 | the Filters floating palette 161 | 162 | 163 | 164 | Configuration 165 | ------------- 166 | 167 | By default, Markdown produces XHTML output for tags with empty elements. 168 | E.g.: 169 | 170 |
    171 | 172 | Markdown can be configured to produce HTML-style tags; e.g.: 173 | 174 |
    175 | 176 | 177 | ### Movable Type ### 178 | 179 | You need to use a special `MTMarkdownOptions` container tag in each 180 | Movable Type template where you want HTML 4-style output: 181 | 182 | 183 | ... put your entry content here ... 184 | 185 | 186 | The easiest way to use MTMarkdownOptions is probably to put the 187 | opening tag right after your `` tag, and the closing tag right 188 | before ``. 189 | 190 | To suppress Markdown processing in a particular template, i.e. to 191 | publish the raw Markdown-formatted text without translation into 192 | (X)HTML, set the `output` attribute to 'raw': 193 | 194 | 195 | ... put your entry content here ... 196 | 197 | 198 | 199 | ### Command-Line ### 200 | 201 | Use the `--html4tags` command-line switch to produce HTML output from a 202 | Unix-style command line. E.g.: 203 | 204 | % perl Markdown.pl --html4tags foo.text 205 | 206 | Type `perldoc Markdown.pl`, or read the POD documentation within the 207 | Markdown.pl source code for more information. 208 | 209 | 210 | Acknowledgements 211 | ---------------- 212 | 213 | [Aaron Swartz][] deserves a tremendous amount of credit for his feedback on the 214 | design of Markdown's formatting syntax. Markdown is *much* better thanks 215 | to Aaron's ideas, feedback, and testing. Also, Aaron's [html2text][] 216 | is a very handy (and free) utility for turning HTML into 217 | Markdown-formatted plain text. 218 | 219 | [Nathaniel Irons][], [Dan Benjamin][], [Daniel Bogan][], and [Jason Perkins][] 220 | also deserve thanks for their feedback. 221 | 222 | [Michel Fortin][] has ported Markdown to PHP; it's a splendid port, and highly recommended for anyone looking for a PHP implementation of Markdown. 223 | 224 | [Aaron Swartz]: http://www.aaronsw.com/ 225 | [Nathaniel Irons]: http://bumppo.net/ 226 | [Dan Benjamin]: http://hivelogic.com/ 227 | [Daniel Bogan]: http://waferbaby.com/ 228 | [Jason Perkins]: http://pressedpants.com/ 229 | [Michel Fortin]: http://www.michelf.com/projects/php-markdown/ 230 | [html2text]: http://www.aaronsw.com/2002/html2text/ 231 | 232 | [tfmenu]: /graphics/markdown/mt_textformat_menu.png 233 | -------------------------------------------------------------------------------- /tests/EmphasisTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); // As we always want to support safe mode 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | unset($this->parsedownExtended); 19 | } 20 | 21 | public function testInlineBoldUsingAsterisks() 22 | { 23 | $markdown = "**bold**"; 24 | $expectedHtml = "

    bold

    "; 25 | 26 | $this->parsedownExtended->config() 27 | ->set('emphasis', true) 28 | ->set('emphasis.bold', true); 29 | $result = $this->parsedownExtended->text($markdown); 30 | 31 | $this->assertEquals($expectedHtml, $result); 32 | } 33 | 34 | public function testInlineBoldUsingAsterisksDisabled() 35 | { 36 | $markdown = "**bold**"; 37 | $expectedHtml = "

    **bold**

    "; 38 | 39 | $this->parsedownExtended->config() 40 | ->set('emphasis', true) 41 | ->set('emphasis.bold', false); 42 | $result = $this->parsedownExtended->text($markdown); 43 | 44 | $this->assertEquals($expectedHtml, $result); 45 | } 46 | 47 | public function testInlineItalicUsingAsterisks() 48 | { 49 | $markdown = "*italic*"; 50 | $expectedHtml = "

    italic

    "; 51 | 52 | $this->parsedownExtended->config() 53 | ->set('emphasis', true) 54 | ->set('emphasis.italic', true); 55 | $result = $this->parsedownExtended->text($markdown); 56 | 57 | $this->assertEquals($expectedHtml, $result); 58 | } 59 | 60 | public function testInlineItalicUsingAsterisksDisabled() 61 | { 62 | $markdown = "*italic*"; 63 | $expectedHtml = "

    *italic*

    "; 64 | 65 | $this->parsedownExtended->config() 66 | ->set('emphasis', true) 67 | ->set('emphasis.italic', false); 68 | $result = $this->parsedownExtended->text($markdown); 69 | 70 | $this->assertEquals($expectedHtml, $result); 71 | } 72 | 73 | public function testInlineBoldUsingUnderscores() 74 | { 75 | $markdown = "__bold__"; 76 | $expectedHtml = "

    bold

    "; 77 | 78 | $this->parsedownExtended->config() 79 | ->set('emphasis', true) 80 | ->set('emphasis.bold', true); 81 | $result = $this->parsedownExtended->text($markdown); 82 | 83 | $this->assertEquals($expectedHtml, $result); 84 | } 85 | 86 | public function testInlineBoldUsingUnderscoresDisabled() 87 | { 88 | $markdown = "__bold__"; 89 | $expectedHtml = "

    __bold__

    "; 90 | 91 | $this->parsedownExtended->config() 92 | ->set('emphasis', true) 93 | ->set('emphasis.bold', false); 94 | $result = $this->parsedownExtended->text($markdown); 95 | 96 | $this->assertEquals($expectedHtml, $result); 97 | } 98 | 99 | public function testInlineItalicUsingUnderscores() 100 | { 101 | $markdown = "_italic_"; 102 | $expectedHtml = "

    italic

    "; 103 | 104 | $this->parsedownExtended->config() 105 | ->set('emphasis', true) 106 | ->set('emphasis.italic', true); 107 | $result = $this->parsedownExtended->text($markdown); 108 | 109 | $this->assertEquals($expectedHtml, $result); 110 | } 111 | 112 | public function testInlineItalicUsingUnderscoresDisabled() 113 | { 114 | $markdown = "_italic_"; 115 | $expectedHtml = "

    _italic_

    "; 116 | 117 | $this->parsedownExtended->config() 118 | ->set('emphasis', true) 119 | ->set('emphasis.italic', false); 120 | $result = $this->parsedownExtended->text($markdown); 121 | 122 | $this->assertEquals($expectedHtml, $result); 123 | } 124 | 125 | public function testInlineMark() 126 | { 127 | $markdown = "==marked=="; 128 | $expectedHtml = "

    marked

    "; 129 | 130 | $this->parsedownExtended->config() 131 | ->set('emphasis', true) 132 | ->set('emphasis.mark', true); 133 | $result = $this->parsedownExtended->text($markdown); 134 | 135 | $this->assertEquals($expectedHtml, $result); 136 | } 137 | 138 | public function testInlineMarkDisabled() 139 | { 140 | $markdown = "==marked=="; 141 | $expectedHtml = "

    ==marked==

    "; 142 | 143 | $this->parsedownExtended->config() 144 | ->set('emphasis', true) 145 | ->set('emphasis.mark', false); 146 | $result = $this->parsedownExtended->text($markdown); 147 | 148 | $this->assertEquals($expectedHtml, $result); 149 | } 150 | 151 | public function testInlineStrikethrough() 152 | { 153 | $markdown = "~~strikethrough~~"; 154 | $expectedHtml = "

    strikethrough

    "; 155 | 156 | $this->parsedownExtended->config() 157 | ->set('emphasis', true) 158 | ->set('emphasis.strikethroughs', true); 159 | $result = $this->parsedownExtended->text($markdown); 160 | 161 | $this->assertEquals($expectedHtml, $result); 162 | } 163 | 164 | public function testInlineStrikethroughDisabled() 165 | { 166 | $markdown = "~~strikethrough~~"; 167 | $expectedHtml = "

    ~~strikethrough~~

    "; 168 | 169 | $this->parsedownExtended->config() 170 | ->set('emphasis', true) 171 | ->set('emphasis.strikethroughs', false); 172 | $result = $this->parsedownExtended->text($markdown); 173 | 174 | $this->assertEquals($expectedHtml, $result); 175 | } 176 | 177 | public function testInlineInserted() 178 | { 179 | $markdown = "++inserted++"; 180 | $expectedHtml = "

    inserted

    "; 181 | 182 | $this->parsedownExtended->config() 183 | ->set('emphasis', true) 184 | ->set('emphasis.insertions', true); 185 | $result = $this->parsedownExtended->text($markdown); 186 | 187 | $this->assertEquals($expectedHtml, $result); 188 | } 189 | 190 | public function testInlineInsertedDisabled() 191 | { 192 | $markdown = "++inserted++"; 193 | $expectedHtml = "

    ++inserted++

    "; 194 | 195 | $this->parsedownExtended->config() 196 | ->set('emphasis', true) 197 | ->set('emphasis.insertions', false); 198 | $result = $this->parsedownExtended->text($markdown); 199 | 200 | $this->assertEquals($expectedHtml, $result); 201 | } 202 | 203 | public function testInlineSuperscript() 204 | { 205 | $markdown = "X^2^"; 206 | $expectedHtml = "

    X2

    "; 207 | 208 | $this->parsedownExtended->config() 209 | ->set('emphasis', true) 210 | ->set('emphasis.superscript', true); 211 | $result = $this->parsedownExtended->text($markdown); 212 | 213 | $this->assertEquals($expectedHtml, $result); 214 | } 215 | 216 | public function testInlineSuperscriptDisabled() 217 | { 218 | $markdown = "X^2^"; 219 | $expectedHtml = "

    X^2^

    "; 220 | 221 | $this->parsedownExtended->config() 222 | ->set('emphasis', true) 223 | ->set('emphasis.superscript', false); 224 | $result = $this->parsedownExtended->text($markdown); 225 | 226 | $this->assertEquals($expectedHtml, $result); 227 | } 228 | 229 | public function testInlineSubscript() 230 | { 231 | $markdown = "sub~script~"; 232 | $expectedHtml = "

    subscript

    "; 233 | 234 | $this->parsedownExtended->config() 235 | ->set('emphasis', true) 236 | ->set('emphasis.subscript', true); 237 | $result = $this->parsedownExtended->text($markdown); 238 | 239 | $this->assertEquals($expectedHtml, $result); 240 | } 241 | 242 | public function testInlineSubscriptDisabled() 243 | { 244 | $markdown = "sub~script~"; 245 | $expectedHtml = "

    sub~script~

    "; 246 | 247 | $this->parsedownExtended->config() 248 | ->set('emphasis', true) 249 | ->set('emphasis.subscript', false); 250 | $result = $this->parsedownExtended->text($markdown); 251 | 252 | $this->assertEquals($expectedHtml, $result); 253 | } 254 | 255 | public function testInlineKeystroke() 256 | { 257 | $markdown = "Press [[Ctrl]]>+[[C]] to copy"; 258 | $expectedHtml = "

    Press Ctrl>+C to copy

    "; 259 | 260 | $this->parsedownExtended->config() 261 | ->set('emphasis', true) 262 | ->set('emphasis.keystrokes', true); 263 | $result = $this->parsedownExtended->text($markdown); 264 | 265 | $this->assertEquals($expectedHtml, $result); 266 | } 267 | 268 | public function testInlineKeystrokeDisabled() 269 | { 270 | $markdown = "Press [[Ctrl]]>+[[C]] to copy"; 271 | $expectedHtml = "

    Press [[Ctrl]]>+[[C]] to copy

    "; 272 | 273 | $this->parsedownExtended->config() 274 | ->set('emphasis', true) 275 | ->set('emphasis.keystrokes', false); 276 | $result = $this->parsedownExtended->text($markdown); 277 | 278 | $this->assertEquals($expectedHtml, $result); 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to ParsedownExtended 2 | 3 | Looking to contribute something to ParsedownExtended? **Here's how you can help.** 4 | 5 | Please take a moment to review this document in order to make the contribution 6 | process easy and effective for everyone involved. 7 | 8 | Following these guidelines helps to communicate that you respect the time of 9 | the developers managing and developing this open source project. In return, 10 | they should reciprocate that respect in addressing your issue or assessing 11 | patches and features. 12 | 13 | 14 | ## Using the issue tracker 15 | 16 | The [issue tracker](https://github.com/BenjaminHoegh/ParsedownExtended/issues) is 17 | the preferred channel for [bug reports](#bug-reports), [features requests](#feature-requests) 18 | and [submitting pull requests](#pull-requests), but please respect the following 19 | restrictions: 20 | 21 | * Please **do not** use the issue tracker for personal support requests. 22 | 23 | * Please **do not** derail or troll issues. Keep the discussion on topic and 24 | respect the opinions of others. 25 | 26 | * Please **do not** post comments consisting solely of "+1" or ":thumbsup:". 27 | Use [GitHub's "reactions" feature](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments) 28 | instead. We reserve the right to delete comments which violate this rule. 29 | 30 | ## Issues and labels 31 | 32 | Our bug tracker utilizes several labels to help organize and identify issues. Here's what they represent and how we use them: 33 | 34 | - `Bug` - Issues that have been confirmed with a reduced test case and identify a bug in ParsedownExtended. 35 | - `Duplicate` - Issue or pull request already exists 36 | - `Enhancement` - Issues that will iterate on existing functionality. 37 | - `Feature` - Issues asking for a new feature to be added, or an existing one to be extended or modified. 38 | - `Fixed` - Issue related to a bug there has been fixed 39 | - `Help wanted` - Issues we need or would love help from the community to resolve. 40 | - `Hotfix` - Issue there has been resolved as a part of a hotfix 41 | - `In progress` - Issue there are currently been working on 42 | - `Invalid` - Issues where no actions are needed or possible. The issue is either fixed, addressed better by other 43 | - `Investigation` - Issues that require further investigation 44 | - `Meta` - Issues with the project itself or our GitHub repository. 45 | - `Need more info` - Issues that require further conversation to figure out how to proceed or what action steps are needed. 46 | - `On hold` - Issues where no actions are needed or possible. The issue is either fixed, addressed better by other 47 | - `Question` - Issues that require further conversation to figure out how to proceed or what action steps are needed 48 | - `Ready for release` - Issue where a new functionality or a bug is finished and will be released in the next version. 49 | - `Ready for review` - Issues that require further conversation to figure out how to proceed or what action steps are needed 50 | - `Released` - An issue where a new functionality or a bug is finished and has been released. 51 | - `Under consideration` - Issues where action can be taken, but has not yet. 52 | - `Docs` - Issues for improving or updating our documentation. 53 | - `Won't fix` - Issues where no actions are needed or possible. The issue is either fixed, addressed better by other 54 | - `Examples` - Issues involving the example templates included in our docs. 55 | 56 | For a complete look at our labels, see the [project labels page](https://github.com/BenjaminHoegh/ParsedownExtended/labels). 57 | 58 | 59 | ## Bug reports 60 | 61 | A bug is a _demonstrable problem_ that is caused by the code in the repository. 62 | Good bug reports are extremely helpful, so thanks! 63 | 64 | Guidelines for bug reports: 65 | 66 | 1. **Validate and lint your code** — [Validate and lint your PHP](https://phptools.online/php-checker) to ensure your problem isn't caused by a simple error in your own code. 67 | 68 | 2. **Use the GitHub issue search** — check if the issue has already been reported. 69 | 70 | 3. **Check if the issue has been fixed** — try to reproduce it using the 71 | latest `master` or development branch in the repository. 72 | 73 | A good bug report shouldn't leave others needing to chase you up for more 74 | information. Please try to be as detailed as possible in your report. What is 75 | your environment? What steps will reproduce the issue? What would you expect to be the outcome? All these details will help people to fix 76 | any potential bugs. 77 | 78 | Example: 79 | 80 | > Short and descriptive example bug report title 81 | > 82 | > A summary of the issue and the environment in which it occurs. If 83 | > suitable, include the steps required to reproduce the bug. 84 | > 85 | > 1. This is the first step 86 | > 2. This is the second step 87 | > 3. Further steps, etc. 88 | > 89 | > `` - a link to the reduced test case 90 | > 91 | > Any other information you want to share that is relevant to the issue being 92 | > reported. This might include the lines of code that you have identified as 93 | > causing the bug, and potential solutions (and your opinions on their 94 | > merits). 95 | 96 | ## Feature requests 97 | 98 | Before opening a feature request, please take a moment to find out whether your idea 99 | fits with the scope and aims of the project. It's up to *you* to make a strong 100 | case to convince the project's developers of the merits of this feature. Please 101 | provide as much detail and context as possible. 102 | 103 | ## Pull requests 104 | 105 | Good pull requests—patches, improvements, new features—are a fantastic 106 | help. They should remain focused in scope and avoid containing unrelated 107 | commits. 108 | 109 | **Please ask first** before embarking on any significant pull request (e.g. 110 | implementing features, refactoring code, porting to a different language), 111 | otherwise you risk spending a lot of time working on something that the 112 | project's developers might not want to merge into the project. 113 | 114 | Please adhere to the [coding guidelines](#code-guidelines) used throughout the 115 | project (indentation, accurate comments, etc.) and any other requirements 116 | (such as test coverage). 117 | 118 | Similarly, when contributing to ParsedownExtended's documentation, you should edit the 119 | documentation in the [wiki](https://github.com/BenjaminHoegh/ParsedownExtended/wiki). 120 | 121 | ### Which Branch? 122 | 123 | **All** bug fixes should be sent to the `master` branch. Bug fixes should **never** be sent to the `dev` branch unless they fix features that exist only in the upcoming release. 124 | 125 | **Minor** features that are fully backward compatible with the current release may be sent to the `master` branch. 126 | 127 | **Major** new features should always be sent to the `dev` branch, which contains the upcoming release. 128 | 129 | If you are unsure if your feature qualifies as a major or minor, please ask BenjaminHoegh in the #contributors channel in the [GitHub Discussions section](https://github.com/BenjaminHoegh/ParsedownExtended/discussions/categories/contributors). 130 | 131 | ### Get started 132 | 133 | Adhering to the following process is the best way to get your work 134 | included in the project: 135 | 136 | 1. [Fork](https://help.github.com/fork-a-repo/) the project, clone your fork, 137 | and configure the remotes: 138 | 139 | ```bash 140 | # Clone your fork of the repo into the current directory 141 | git clone https://github.com//ParsedownExtended.git 142 | # Navigate to the newly cloned directory 143 | cd ParsedownExtended 144 | # Assign the original repo to a remote called "upstream" 145 | git remote add upstream https://github.com/BenjaminHoegh/ParsedownExtended.git 146 | ``` 147 | 148 | 2. If you cloned a while ago, get the latest changes from upstream: 149 | 150 | ```bash 151 | git checkout developer 152 | git pull upstream developer 153 | ``` 154 | 155 | 3. Create a new topic branch (off the main project development branch) to 156 | contain your feature, change, or fix: 157 | 158 | ```bash 159 | git checkout -b 160 | ``` 161 | 162 | 4. Commit your changes in logical chunks. Please adhere to these [git commit 163 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 164 | or your code is unlikely to be merged into the main project. Use Git's 165 | [interactive rebase](https://help.github.com/articles/interactive-rebase) 166 | feature to tidy up your commits before making them public. 167 | 168 | 5. Locally merge (or rebase) the upstream development branch into your topic branch: 169 | 170 | ```bash 171 | git pull [--rebase] upstream developer 172 | ``` 173 | 174 | 6. Push your topic branch up to your fork: 175 | 176 | ```bash 177 | git push origin 178 | ``` 179 | 180 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 181 | with a clear title and description against the target branch. 182 | 183 | **IMPORTANT**: By submitting a patch, you agree to allow the project owners to 184 | license your work under the terms of the [MIT License](LICENSE) (if it 185 | includes code changes) and under the terms of the 186 | [Creative Commons Attribution 3.0 Unported License](docs/LICENSE) 187 | (if it includes documentation changes). 188 | 189 | 190 | ## Code guidelines 191 | 192 | ### PHP 193 | - Four (4) spaces indents, no tabs; 194 | - Ideally, 80-characters wide lines; 195 | - Comment your code, a little is better than nothing. 196 | - All PHP reserved keywords and types must be in lower case. 197 | - Blank lines MAY be added to improve readability and to indicate related blocks of code except where explicitly forbidden. 198 | - All code should follow the syntax guidelines on [php-fig.org](https://www.php-fig.org/psr/psr-12/) 199 | 200 | ### Markdown 201 | - Use 4 space indent. 202 | - Follow [Google's styleguide](https://google.github.io/styleguide/docguide/style.html) based on CommonMark. 203 | 204 | ## License 205 | 206 | By contributing your code, you agree to license your contribution under the [MIT License](LICENSE). 207 | By contributing to the documentation, you agree to license your contribution under the [Creative Commons Attribution 3.0 Unported License](docs/LICENSE). 208 | -------------------------------------------------------------------------------- /tests/LinksTest.php: -------------------------------------------------------------------------------- 1 | parsedownExtended = new ParsedownExtended(); 13 | $this->parsedownExtended->setSafeMode(true); 14 | $_SERVER['HTTP_HOST'] = 'www.example.com'; // Default host for testing 15 | } 16 | 17 | protected function tearDown(): void 18 | { 19 | unset($this->parsedownExtended); 20 | } 21 | 22 | // General Link Settings 23 | // ---------------------------- 24 | 25 | public function testLinksEnabled() 26 | { 27 | $this->parsedownExtended->config()->set('links.enabled', true); 28 | 29 | $markdown = '[Link](https://www.example.com)'; 30 | $html = $this->parsedownExtended->text($markdown); 31 | 32 | $this->assertStringContainsString('
    Link', $html); 33 | } 34 | 35 | public function testLinksDisabled() 36 | { 37 | $this->parsedownExtended->config()->set('links.enabled', false); 38 | 39 | $markdown = '[Link](https://www.example.com)'; 40 | $html = $this->parsedownExtended->text($markdown); 41 | 42 | $this->assertStringNotContainsString('Link', $html); 43 | } 44 | 45 | // Email Links 46 | // ---------------------------- 47 | 48 | public function testEmailLinksEnabled() 49 | { 50 | $this->parsedownExtended->config()->set('links.email_links', true); 51 | 52 | $markdown = ''; 53 | $html = $this->parsedownExtended->text($markdown); 54 | 55 | $this->assertStringContainsString('test@example.com', $html); 56 | } 57 | 58 | public function testEmailLinksDisabled() 59 | { 60 | $this->parsedownExtended->config()->set('links.email_links', false); 61 | 62 | $markdown = ''; 63 | $html = $this->parsedownExtended->text($markdown); 64 | 65 | $this->assertStringNotContainsString('test@example.com', $html); 66 | } 67 | 68 | // External Links Settings 69 | // ---------------------------- 70 | 71 | public function testExternalLinksEnabled() 72 | { 73 | $this->parsedownExtended->config()->set('links.external_links', true); 74 | $this->parsedownExtended->config()->set('links.external_links.nofollow', false); 75 | $this->parsedownExtended->config()->set('links.external_links.noopener', false); 76 | $this->parsedownExtended->config()->set('links.external_links.noreferrer', false); 77 | $this->parsedownExtended->config()->set('links.external_links.open_in_new_window', false); 78 | 79 | $markdown = '[External](https://www.google.com)'; 80 | $html = $this->parsedownExtended->text($markdown); 81 | 82 | $this->assertStringContainsString('External', $html); 83 | } 84 | 85 | public function testExternalLinksDisabled() 86 | { 87 | $this->parsedownExtended->config()->set('links.external_links', false); 88 | 89 | $markdown = '[External](https://www.google.com)'; 90 | $html = $this->parsedownExtended->text($markdown); 91 | 92 | $this->assertStringNotContainsString('External', $html); 93 | } 94 | 95 | public function testNofollowEnabled() 96 | { 97 | $this->parsedownExtended->config()->set('links.external_links', true); 98 | $this->parsedownExtended->config()->set('links.external_links.nofollow', true); 99 | $this->parsedownExtended->config()->set('links.external_links.noopener', false); 100 | $this->parsedownExtended->config()->set('links.external_links.noreferrer', false); 101 | $this->parsedownExtended->config()->set('links.external_links.open_in_new_window', false); 102 | 103 | $markdown = '[External](https://www.google.com)'; 104 | $html = $this->parsedownExtended->text($markdown); 105 | 106 | $this->assertStringContainsString('rel="nofollow"', $html); 107 | } 108 | 109 | public function testNofollowDisabled() 110 | { 111 | $this->parsedownExtended->config()->set('links.external_links', true); 112 | $this->parsedownExtended->config()->set('links.external_links.nofollow', false); 113 | $this->parsedownExtended->config()->set('links.external_links.noopener', false); 114 | $this->parsedownExtended->config()->set('links.external_links.noreferrer', false); 115 | $this->parsedownExtended->config()->set('links.external_links.open_in_new_window', false); 116 | 117 | $markdown = '[External](https://www.google.com)'; 118 | $html = $this->parsedownExtended->text($markdown); 119 | 120 | $this->assertStringNotContainsString('rel="nofollow"', $html); 121 | } 122 | 123 | public function testNoopenerEnabled() 124 | { 125 | $this->parsedownExtended->config()->set('links.external_links', true); 126 | $this->parsedownExtended->config()->set('links.external_links.nofollow', false); 127 | $this->parsedownExtended->config()->set('links.external_links.noopener', true); 128 | $this->parsedownExtended->config()->set('links.external_links.noreferrer', false); 129 | $this->parsedownExtended->config()->set('links.external_links.open_in_new_window', false); 130 | 131 | $markdown = '[External](https://www.google.com)'; 132 | $html = $this->parsedownExtended->text($markdown); 133 | 134 | $this->assertStringContainsString('rel="noopener"', $html); 135 | } 136 | 137 | public function testNoopenerDisabled() 138 | { 139 | $this->parsedownExtended->config()->set('links.external_links', true); 140 | $this->parsedownExtended->config()->set('links.external_links.nofollow', false); 141 | $this->parsedownExtended->config()->set('links.external_links.noopener', false); 142 | $this->parsedownExtended->config()->set('links.external_links.noreferrer', false); 143 | $this->parsedownExtended->config()->set('links.external_links.open_in_new_window', false); 144 | 145 | $markdown = '[External](https://www.google.com)'; 146 | $html = $this->parsedownExtended->text($markdown); 147 | 148 | $this->assertStringNotContainsString('rel="noopener"', $html); 149 | } 150 | 151 | public function testNoreferrerEnabled() 152 | { 153 | $this->parsedownExtended->config()->set('links.external_links', true); 154 | $this->parsedownExtended->config()->set('links.external_links.nofollow', false); 155 | $this->parsedownExtended->config()->set('links.external_links.noopener', false); 156 | $this->parsedownExtended->config()->set('links.external_links.noreferrer', true); 157 | $this->parsedownExtended->config()->set('links.external_links.open_in_new_window', false); 158 | 159 | $markdown = '[External](https://www.google.com)'; 160 | $html = $this->parsedownExtended->text($markdown); 161 | 162 | $this->assertStringContainsString('rel="noreferrer"', $html); 163 | } 164 | 165 | public function testNoreferrerDisabled() 166 | { 167 | $this->parsedownExtended->config()->set('links.external_links', true); 168 | $this->parsedownExtended->config()->set('links.external_links.nofollow', false); 169 | $this->parsedownExtended->config()->set('links.external_links.noopener', false); 170 | $this->parsedownExtended->config()->set('links.external_links.noreferrer', false); 171 | $this->parsedownExtended->config()->set('links.external_links.open_in_new_window', false); 172 | 173 | $markdown = '[External](https://www.google.com)'; 174 | $html = $this->parsedownExtended->text($markdown); 175 | 176 | $this->assertStringNotContainsString('rel="noreferrer"', $html); 177 | } 178 | 179 | public function testOpenInNewWindowEnabled() 180 | { 181 | $this->parsedownExtended->config()->set('links.external_links', true); 182 | $this->parsedownExtended->config()->set('links.external_links.nofollow', false); 183 | $this->parsedownExtended->config()->set('links.external_links.noopener', false); 184 | $this->parsedownExtended->config()->set('links.external_links.noreferrer', false); 185 | $this->parsedownExtended->config()->set('links.external_links.open_in_new_window', true); 186 | 187 | $markdown = '[External](https://www.google.com)'; 188 | $html = $this->parsedownExtended->text($markdown); 189 | 190 | $this->assertStringContainsString('target="_blank"', $html); 191 | } 192 | 193 | public function testOpenInNewWindowDisabled() 194 | { 195 | $this->parsedownExtended->config()->set('links.external_links', true); 196 | $this->parsedownExtended->config()->set('links.external_links.nofollow', false); 197 | $this->parsedownExtended->config()->set('links.external_links.noopener', false); 198 | $this->parsedownExtended->config()->set('links.external_links.noreferrer', false); 199 | $this->parsedownExtended->config()->set('links.external_links.open_in_new_window', false); 200 | 201 | $markdown = '[External](https://www.google.com)'; 202 | $html = $this->parsedownExtended->text($markdown); 203 | 204 | $this->assertStringNotContainsString('target="_blank"', $html); 205 | } 206 | 207 | // Internal and Same-Domain Links 208 | // ---------------------------- 209 | 210 | 211 | public function testCustomInternalHostLink() 212 | { 213 | $this->parsedownExtended->config()->set('links.external_links.internal_hosts', ['google.com']); 214 | 215 | $markdown = '[Home](https://www.google.com)'; 216 | $html = $this->parsedownExtended->text($markdown); 217 | 218 | 219 | $this->assertStringNotContainsString('noopener"', $html); 220 | $this->assertStringNotContainsString('nofollow', $html); 221 | $this->assertStringNotContainsString('noreferrer', $html); 222 | $this->assertStringNotContainsString('target="_blank"', $html); 223 | } 224 | 225 | public function testSameDomainLinkWithoutNewWindow() 226 | { 227 | $markdown = '[Home](https://www.example.com)'; 228 | $html = $this->parsedownExtended->text($markdown); 229 | 230 | $this->assertStringNotContainsString('target="_blank"', $html); 231 | } 232 | 233 | public function testSameDomainLinkWithAttributes() 234 | { 235 | $markdown = '[Home](https://www.example.com)'; 236 | $html = $this->parsedownExtended->text($markdown); 237 | 238 | $this->assertStringNotContainsString('nofollow"', $html); 239 | $this->assertStringNotContainsString('noopener"', $html); 240 | $this->assertStringNotContainsString('noreferrer"', $html); 241 | } 242 | 243 | public function testNoServerVars() 244 | { 245 | $this->parsedownExtended->config()->set('links.enabled', true); 246 | 247 | // this is not set e.g. when used from CLI 248 | $temp = $_SERVER['HTTP_HOST']; 249 | unset($_SERVER['HTTP_HOST']); 250 | 251 | $markdown = '[Link](https://www.example.com/blah)'; 252 | $html = $this->parsedownExtended->line($markdown); 253 | 254 | // just checking if above throws some error/warning 255 | $this->assertTrue(true); 256 | 257 | // restore for other tests 258 | $_SERVER['HTTP_HOST'] = $temp; 259 | } 260 | 261 | // Markdown Edge Cases 262 | // ---------------------------- 263 | 264 | // public function testMultipleLinksInText() 265 | // { 266 | // $markdown = '[Google](https://www.google.com) and [Bing](https://www.bing.com)'; 267 | // $html = $this->parsedownExtended->text($markdown); 268 | // $this->assertStringContainsString('href="https://www.google.com"', $html); 269 | // $this->assertStringContainsString('href="https://www.bing.com"', $html); 270 | // } 271 | 272 | // public function testLinkWithSpecialCharacters() 273 | // { 274 | // $markdown = '[Google](https://www.google.com/search?q=hello+world)'; 275 | // $html = $this->parsedownExtended->text($markdown); 276 | // $this->assertStringContainsString('href="https://www.google.com/search?q=hello+world"', $html); 277 | // } 278 | 279 | // public function testNestedMarkdownElements() 280 | // { 281 | // $markdown = '![Image](https://www.example.com/image.jpg) and [Link](https://www.example.com)'; 282 | // $html = $this->parsedownExtended->text($markdown); 283 | // $this->assertStringContainsString('assertStringContainsString('Link', $html); 285 | // } 286 | } 287 | -------------------------------------------------------------------------------- /benchmarks/tests/jquery-readme.md: -------------------------------------------------------------------------------- 1 | [jQuery](http://jquery.com/) - New Wave JavaScript 2 | ================================================== 3 | 4 | Contribution Guides 5 | -------------------------------------- 6 | 7 | In the spirit of open source software development, jQuery always encourages community code contribution. To help you get started and before you jump into writing code, be sure to read these important contribution guidelines thoroughly: 8 | 9 | 1. [Getting Involved](http://docs.jquery.com/Getting_Involved) 10 | 2. [Core Style Guide](http://docs.jquery.com/JQuery_Core_Style_Guidelines) 11 | 3. [Tips For Bug Patching](http://docs.jquery.com/Tips_for_jQuery_Bug_Patching) 12 | 13 | 14 | What you need to build your own jQuery 15 | -------------------------------------- 16 | 17 | In order to build jQuery, you need to have Node.js/npm latest and git 1.7 or later. 18 | (Earlier versions might work OK, but are not tested.) 19 | 20 | For Windows you have to download and install [git](http://git-scm.com/downloads) and [Node.js](http://nodejs.org/download/). 21 | 22 | Mac OS users should install [Homebrew](http://mxcl.github.com/homebrew/). Once Homebrew is installed, run `brew install git` to install git, 23 | and `brew install node` to install Node.js. 24 | 25 | Linux/BSD users should use their appropriate package managers to install git and Node.js, or build from source 26 | if you swing that way. Easy-peasy. 27 | 28 | 29 | How to build your own jQuery 30 | ---------------------------- 31 | 32 | Clone a copy of the main jQuery git repo by running: 33 | 34 | ```bash 35 | git clone git://github.com/jquery/jquery.git 36 | ``` 37 | 38 | Enter the jquery directory and run the build script: 39 | ```bash 40 | cd jquery && npm run-script build 41 | ``` 42 | The built version of jQuery will be put in the `dist/` subdirectory, along with the minified copy and associated map file. 43 | 44 | If you want create custom build or help with jQuery development, it would be better to install grunt command line interface as a global package: 45 | 46 | ``` 47 | npm install -g grunt-cli 48 | ``` 49 | Make sure you have `grunt` installed by testing: 50 | ``` 51 | grunt -v 52 | ``` 53 | 54 | Now by running `grunt` command, in the jquery directory, you could build full version of jQuery, just like with `npm run-script build` command: 55 | ``` 56 | grunt 57 | ``` 58 | 59 | There are many other tasks avaliable for jQuery Core: 60 | ``` 61 | grunt -help 62 | ``` 63 | 64 | ### Modules 65 | 66 | Special builds can be created that exclude subsets of jQuery functionality. 67 | This allows for smaller custom builds when the builder is certain that those parts of jQuery are not being used. 68 | For example, an app that only used JSONP for `$.ajax()` and did not need to calculate offsets or positions of elements could exclude the offset and ajax/xhr modules. 69 | 70 | Any module may be excluded except for `core`, and `selector`. To exclude a module, pass its path relative to the `src` folder (without the `.js` extension). 71 | 72 | Some example modules that can be excluded are: 73 | 74 | - **ajax**: All AJAX functionality: `$.ajax()`, `$.get()`, `$.post()`, `$.ajaxSetup()`, `.load()`, transports, and ajax event shorthands such as `.ajaxStart()`. 75 | - **ajax/xhr**: The XMLHTTPRequest AJAX transport only. 76 | - **ajax/script**: The `