├── VERSION ├── .github └── FUNDING.yml ├── .gitignore ├── tests ├── bootstrap.php ├── ParserTest.php └── YallTest.php ├── .editorconfig ├── docs ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE.md ├── .travis.yml ├── phpunit.xml.dist ├── LICENSE ├── composer.json ├── CONTRIBUTING.md ├── CHANGELOG.md ├── src ├── Parser.php └── Yall.php └── README.md /VERSION: -------------------------------------------------------------------------------- 1 | 0.2.2 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: adhocore 2 | custom: ['https://paypal.me/ji10'] 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # standards 2 | /.cache/ 3 | /.env 4 | /.idea/ 5 | /vendor/ 6 | composer.lock 7 | coverage.xml 8 | clover.xml 9 | pre.lock 10 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * 9 | * Licensed under MIT license. 10 | */ 11 | 12 | require_once __DIR__ . '/../vendor/autoload.php'; 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ; http://editorconfig.org 2 | ; 3 | ; Sublime: https://github.com/sindresorhus/editorconfig-sublime 4 | ; Phpstorm: https://plugins.jetbrains.com/plugin/7294-editorconfig 5 | 6 | root = true 7 | 8 | [*] 9 | indent_style = space 10 | indent_size = 4 11 | end_of_line = lf 12 | charset = utf-8 13 | trim_trailing_whitespace = true 14 | insert_final_newline = true 15 | 16 | [{*.js,*.css,*.scss,*.html}] 17 | indent_size = 2 18 | -------------------------------------------------------------------------------- /docs/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | 3 | **This is a** 4 | 5 | 6 | - [ ] New feature 7 | - [ ] Fix for ... 8 | - [ ] General improvement 9 | - [ ] Backward incompatible change 10 | 11 | It closes # 12 | 13 | ## Describe the change 14 | 15 | ... 16 | 17 | ## How to use it 18 | 19 | ... 20 | 21 | #### Sample code 22 | 23 | ```php 24 | 25 | ``` 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | cache: 2 | directories: 3 | - $HOME/.composer/cache/files 4 | 5 | language: php 6 | 7 | php: 8 | - 7.0 9 | - 7.1 10 | - 7.2 11 | - 7.3 12 | - 7.4 13 | # - nightly 14 | 15 | matrix: 16 | allow_failures: 17 | - php: nightly 18 | 19 | install: 20 | - composer install --prefer-dist 21 | 22 | before_script: 23 | - for P in src tests; do find $P -type f -name '*.php' -exec php -l {} \;; done 24 | 25 | script: 26 | - composer test:cov 27 | 28 | after_success: 29 | - bash <(curl -s https://codecov.io/bash) 30 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | 19 | 20 | ./src 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /docs/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | 3 | **This is a** 4 | 5 | 6 | - [ ] Bug/Issue 7 | - [ ] Feature request 8 | - [ ] General stuff 9 | 10 | 37 | 38 | 43 | 44 | 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jitendra Adhikari 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 NONINFRINGEMENT. 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 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adhocore/twig-yall", 3 | "description": "Lazy load image in twig using malchata/yall.js", 4 | "type": "library", 5 | "keywords": ["php","twig-yall"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Jitendra Adhikari", 10 | "email": "jiten.adhikary@gmail.com" 11 | } 12 | ], 13 | "autoload": { 14 | "psr-4": { 15 | "Ahc\\TwigYall\\": "src/" 16 | }, 17 | "files": [] 18 | }, 19 | "autoload-dev": { 20 | "psr-4": { 21 | "Ahc\\TwigYall\\Test\\": "tests/" 22 | } 23 | }, 24 | "require": { 25 | "php": ">=7.0.0" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "^6.5 || ^7.5", 29 | "twig/twig": "^1.42 || ^2.12 || ^3.0" 30 | }, 31 | "config": { 32 | "optimize-autoloader": true, 33 | "preferred-install": { 34 | "*": "dist" 35 | } 36 | }, 37 | "scripts": { 38 | "test": "phpunit", 39 | "test:cov": "phpunit --coverage-text --coverage-clover coverage.xml --coverage-html vendor/cov" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## How to contribute 2 | 3 | ### Before you start 4 | 5 | 6 | 7 | 8 | ### Setting up 9 | 10 | You may need to fork this project in [GitHub](https://github.com/adhocore/twig-yall). 11 | 12 | ```sh 13 | git clone git@github.com:adhocore/twig-yall.git 14 | 15 | # OR if you have a fork 16 | git clone git@github.com:/twig-yall.git 17 | 18 | # You may also add upstream 19 | git remote add upstream https://github.com/adhocore/twig-yall.git 20 | 21 | cd twig-yall 22 | 23 | # Create a new branch 24 | git checkout -b $branch_name 25 | 26 | # Install deps 27 | composer install -o 28 | ``` 29 | 30 | ### Moving forward 31 | 32 | ```sh 33 | # Open twig-yall in IDE 34 | subl twig-yall 35 | 36 | # ... and do the needful 37 | 38 | # Optionally run the lint 39 | for P in src tests; do find $P -type f -name '*.php' -exec php -l {} \;; done 40 | 41 | # ... and phpcs fixer or stuffs like that! 42 | 43 | # Run tests 44 | vendor/bin/phpunit --coverage-text 45 | 46 | 47 | # If your feature takes long your dev branch might be out of sync, you may want to 48 | git checkout $branch_name 49 | git pull upstream master # branch could be something else than master 50 | ``` 51 | 52 | ### Finalizing 53 | 54 | Everything looking good? 55 | 56 | ```sh 57 | # Commit your stuffs 58 | git add $file ...$files 59 | git commit -m "..." 60 | 61 | # Push 'em 62 | git push origin HEAD 63 | ``` 64 | 65 | Now goto [GitHub](https://github.com/adhocore/twig-yall/compare?expand=1), select your branch and create PR. 66 | 67 | ### Getting PR merged 68 | 69 | You have to wait. You have to address change requests. Be patient. 70 | 71 | Thank you for contribution! 72 | 73 | **Lastly** Please be informed that your works will be licensed same as the project [license](./LICENSE) 74 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.2.2](https://github.com/adhocore/twig-yall/releases/tag/0.2.2) (2019-12-23) 2 | 3 | ### Internal Refactors 4 | - Add return typehint (Jitendra Adhikari) [_82644f1_](https://github.com/adhocore/twig-yall/commit/82644f1) 5 | 6 | ### Documentations 7 | - Add more intro (Jitendra Adhikari) [_1cbb125_](https://github.com/adhocore/twig-yall/commit/1cbb125) 8 | - Minor update (Jitendra Adhikari) [_6af6e4b_](https://github.com/adhocore/twig-yall/commit/6af6e4b) 9 | 10 | 11 | ## [0.2.1](https://github.com/adhocore/twig-yall/releases/tag/0.2.1) (2019-12-15) 12 | 13 | ### Internal Refactors 14 | - **Parser**: Extract getSrc, merge no-lazy with data- (Jitendra Adhikari) [_9a95713_](https://github.com/adhocore/twig-yall/commit/9a95713) 15 | - **Parser**: Extract needsSrc, merge data-src/poster check (Jitendra Adhikari) [_662c3c7_](https://github.com/adhocore/twig-yall/commit/662c3c7) 16 | 17 | 18 | ## [0.2.0](https://github.com/adhocore/twig-yall/releases/tag/0.2.0) (2019-12-15) 19 | 20 | ### Features 21 | - Register lazyload block parser (Jitendra Adhikari) [_8414673_](https://github.com/adhocore/twig-yall/commit/8414673) 22 | - Add lazyload block parser (Jitendra Adhikari) [_887ba05_](https://github.com/adhocore/twig-yall/commit/887ba05) 23 | 24 | ### Bug Fixes 25 | - **Parser**: Traverse single node first (Jitendra Adhikari) [_a04dada_](https://github.com/adhocore/twig-yall/commit/a04dada) 26 | 27 | ### Internal Refactors 28 | - **Parser**: Extract replacements, respect no-lazy, dont add src in source (Jitendra Adhikari) [_241f4a0_](https://github.com/adhocore/twig-yall/commit/241f4a0) 29 | 30 | ### Miscellaneous 31 | - Add typehint (Jitendra Adhikari) [_c350981_](https://github.com/adhocore/twig-yall/commit/c350981) 32 | 33 | ### Documentations 34 | - Add docblock (Jitendra Adhikari) [_68052c6_](https://github.com/adhocore/twig-yall/commit/68052c6) 35 | - About lazyload block parser (Jitendra Adhikari) [_03c8c64_](https://github.com/adhocore/twig-yall/commit/03c8c64) 36 | 37 | 38 | ## [0.1.0](https://github.com/adhocore/twig-yall/releases/tag/0.1.0) (2019-12-14) 39 | 40 | ### Features 41 | - Add Yall extension (Jitendra Adhikari) [_ba5ed17_](https://github.com/adhocore/twig-yall/commit/ba5ed17) 42 | 43 | ### Bug Fixes 44 | - **Lazify**: Use lazyClass from config instead (Jitendra Adhikari) [_52106da_](https://github.com/adhocore/twig-yall/commit/52106da) 45 | - Use list for PHP7.0 (Jitendra Adhikari) [_4fdeb46_](https://github.com/adhocore/twig-yall/commit/4fdeb46) 46 | 47 | ### Internal Refactors 48 | - Use yall.min.js by default (Jitendra Adhikari) [_2da9b27_](https://github.com/adhocore/twig-yall/commit/2da9b27) 49 | - **Yallify**: Make param nullable (Jitendra Adhikari) [_6b1e8e1_](https://github.com/adhocore/twig-yall/commit/6b1e8e1) 50 | 51 | ### Miscellaneous 52 | - Add github meta files (Jitendra Adhikari) [_71ecd20_](https://github.com/adhocore/twig-yall/commit/71ecd20) 53 | - Add project meta files (Jitendra Adhikari) [_1245c3b_](https://github.com/adhocore/twig-yall/commit/1245c3b) 54 | - Add phpunit xml (Jitendra Adhikari) [_34f0486_](https://github.com/adhocore/twig-yall/commit/34f0486) 55 | - Add composer.json (Jitendra Adhikari) [_8ade23e_](https://github.com/adhocore/twig-yall/commit/8ade23e) 56 | - Add editorconfig (Jitendra Adhikari) [_b1bff51_](https://github.com/adhocore/twig-yall/commit/b1bff51) 57 | - Update gitignore (Jitendra Adhikari) [_3ada464_](https://github.com/adhocore/twig-yall/commit/3ada464) 58 | - Wip (Jitendra Adhikari) [_6366532_](https://github.com/adhocore/twig-yall/commit/6366532) 59 | - Init (Jitendra Adhikari) [_6569d1c_](https://github.com/adhocore/twig-yall/commit/6569d1c) 60 | 61 | ### Documentations 62 | - Add readme (Jitendra Adhikari) [_f01e772_](https://github.com/adhocore/twig-yall/commit/f01e772) 63 | 64 | ### Builds 65 | - **Travis**: Retire nightly (Jitendra Adhikari) [_e0feaee_](https://github.com/adhocore/twig-yall/commit/e0feaee) 66 | - **Ci**: Add travis yml (Jitendra Adhikari) [_1f72f6b_](https://github.com/adhocore/twig-yall/commit/1f72f6b) 67 | 68 | 69 | ## 0.0.0 2019-12-07 10:41:54 UTC 70 | 71 | - init library 72 | -------------------------------------------------------------------------------- /src/Parser.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * 9 | * Licensed under MIT license. 10 | */ 11 | 12 | namespace Ahc\TwigYall; 13 | 14 | use Twig\Node\Node; 15 | use Twig\Node\TextNode; 16 | use Twig\Token; 17 | use Twig\TokenParser\AbstractTokenParser; 18 | 19 | class Parser extends AbstractTokenParser 20 | { 21 | /** @var string yall lazyClass */ 22 | protected $lazyClass; 23 | 24 | /** @var string URI for placeholder image */ 25 | protected $placeholder; 26 | 27 | /** 28 | * Constructor. 29 | * 30 | * @param string $lazyClass 31 | * @param string $placeholder 32 | */ 33 | public function __construct(string $lazyClass, string $placeholder) 34 | { 35 | $this->lazyClass = $lazyClass; 36 | $this->placeholder = $placeholder; 37 | } 38 | 39 | /** 40 | * Parse `{% lazyload %}...{% endlazyload %}` block. 41 | * 42 | * @param Token $token 43 | * 44 | * @return Node 45 | */ 46 | public function parse(Token $token): Node 47 | { 48 | $stream = $this->parser->getStream(); 49 | 50 | $stream->expect(Token::BLOCK_END_TYPE); 51 | $body = $this->parser->subparse([$this, 'isLazyloadEnd'], true); 52 | $stream->expect(Token::BLOCK_END_TYPE); 53 | 54 | return $this->traverse($body); 55 | } 56 | 57 | protected function traverse(Node $node): Node 58 | { 59 | if ($node instanceof TextNode) { 60 | $node->setAttribute('data', $this->doLazyload($node->getAttribute('data'))); 61 | } 62 | 63 | foreach ($node as $sub) { 64 | $this->traverse($sub); 65 | } 66 | 67 | return $node; 68 | } 69 | 70 | protected function doLazyload(string $html): string 71 | { 72 | return \preg_replace_callback('/<(img|source|video)([^>]+)>/m', function ($match) { 73 | list($all, $tag, $props) = $match; 74 | 75 | if (empty(\trim($props, ' /'))) { 76 | return $all; 77 | } 78 | 79 | // Already there or no-flagged 80 | if (\preg_match('/(data-(src|poster)|no-' . $this->lazyClass . ')/i', $props)) { 81 | return $all; 82 | } 83 | 84 | $src = $this->getSrc($tag, $props); 85 | if (\stripos($props, ' class') === false) { 86 | $tag .= " class=\"{$this->lazyClass} yall\""; 87 | } 88 | if (\stripos($props, ' poster') !== false) { 89 | $tag .= " poster=\"{$this->placeholder}\""; 90 | } 91 | 92 | return "<$tag$src" . $this->doReplacements($props) . '>'; 93 | }, $html); 94 | } 95 | 96 | protected function getSrc(string $tag, string $props): string 97 | { 98 | // For source no need src 99 | if ($tag === 'source') { 100 | return ''; 101 | } 102 | 103 | // For video no need src if not already there! 104 | if ($tag === 'video' && \stripos($props, 'src') === false) { 105 | return ''; 106 | } 107 | 108 | return ' src="' . $this->placeholder . '"'; 109 | } 110 | 111 | protected function doReplacements(string $props): string 112 | { 113 | $replacements = [ 114 | ' src=' => ' data-src=', 115 | ' src =' => ' data-src=', 116 | ' srcset=' => ' data-srcset=', 117 | ' srcset =' => ' data-srcset=', 118 | ' class="' => " class=\"{$this->lazyClass} yall ", 119 | ' class ="' => " class=\"{$this->lazyClass} yall ", 120 | ' class = "' => " class=\"{$this->lazyClass} yall ", 121 | " class='" => " class='{$this->lazyClass} yall ", 122 | " class ='" => " class='{$this->lazyClass} yall ", 123 | " class = '" => " class='{$this->lazyClass} yall ", 124 | ' poster=' => ' data-poster=', 125 | ' poster =' => ' data-poster=', 126 | ]; 127 | 128 | return \strtr($props, $replacements); 129 | } 130 | 131 | /** 132 | * @internal 133 | */ 134 | public function isLazyloadEnd(Token $token): bool 135 | { 136 | return $token->test('endlazyload'); 137 | } 138 | 139 | /** 140 | * Gets the tag name used in block. 141 | * 142 | * @return string 143 | */ 144 | public function getTag(): string 145 | { 146 | return 'lazyload'; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/Yall.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * 9 | * Licensed under MIT license. 10 | */ 11 | 12 | namespace Ahc\TwigYall; 13 | 14 | use Twig\Extension\AbstractExtension; 15 | use Twig\Markup; 16 | use Twig\TwigFunction; 17 | 18 | class Yall extends AbstractExtension 19 | { 20 | /** @var array Configuration */ 21 | protected $config = []; 22 | 23 | /** 24 | * Constructor. 25 | * 26 | * @param array $config Optinal configuration 27 | */ 28 | public function __construct(array $config = []) 29 | { 30 | $this->config = $config + [ 31 | 'polyfillJs' => 'https://polyfill.io/%s/polyfill.min.js?features=IntersectionObserver', 32 | // @see: https://stackoverflow.com/a/15960901 33 | 'placeholder' => 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEAAAAALAAAAAABAAEAAAI=', 34 | 'yallJs' => 'https://unpkg.com/yall-js@%s/dist/yall.min.js', 35 | 'lazyClass' => 'lazy', 36 | ]; 37 | } 38 | 39 | /** 40 | * Get twig functions defined by this extension. 41 | * 42 | * @return array 43 | */ 44 | public function getFunctions(): array 45 | { 46 | return [ 47 | new TwigFunction('lazify', [$this, 'lazify']), 48 | new TwigFunction('yallify', [$this, 'yallify']), 49 | ]; 50 | } 51 | 52 | /** 53 | * Get token parsers defined by this extension. 54 | * 55 | * @return array 56 | */ 57 | public function getTokenParsers(): array 58 | { 59 | return [new Parser($this->config['lazyClass'], $this->config['placeholder'])]; 60 | } 61 | 62 | /** 63 | * Loads yall and polyfill scripts then triggers lazy loading. 64 | * 65 | * @param string|null $yall Yall version 66 | * @param string|null $polyfill Polyfill version ('' = off) 67 | * @param array $options Options for `yall({})` callback 68 | * 69 | * @return Markup 70 | */ 71 | public function yallify(string $yall = null, string $polyfill = null, array $options = []): Markup 72 | { 73 | $yallJs = \sprintf($this->config['yallJs'], $yall ?: '3.1.7'); 74 | $options += ['lazyClass' => $this->config['lazyClass']]; 75 | $jsonFlag = \JSON_UNESCAPED_SLASHES | \JSON_FORCE_OBJECT; 76 | 77 | $jsonOpts = \json_encode($options, $jsonFlag); 78 | $jsonOpts = \str_replace(['"', '"'], ['', ''], $jsonOpts); 79 | 80 | $markup = [ 81 | $polyfill ?? 'v2' 82 | ? \sprintf('', \sprintf($this->config['polyfillJs'], $polyfill ?? 'v2')) 83 | : '', 84 | \sprintf('', $yallJs), 85 | '', 90 | ]; 91 | 92 | return new Markup(\implode("\n", $markup), 'UTF-8'); 93 | } 94 | 95 | /** 96 | * Lazify resources. 97 | * 98 | * @param string|string[] $src The sources to lazy load 99 | * @param string $classes The optional element classes 100 | * @param string $dummy The optional placeholder image 101 | * 102 | * @return Markup 103 | */ 104 | public function lazify($src, string $classes = '', string $dummy = ''): Markup 105 | { 106 | $attr = 'src'; 107 | if (\is_array($src)) { 108 | list($attr, $src) = $this->normalizeSrc($src); 109 | } 110 | 111 | $classes = \trim("$classes {$this->config['lazyClass']} yall"); 112 | if ('srcset' !== $attr) { 113 | $dummy = \sprintf(' %s="%s"', $attr, $dummy ?: $this->config['placeholder']); 114 | } 115 | 116 | $markup = \sprintf('class="%s"%s data-%s="%s"', $classes, $dummy, $attr, $src); 117 | 118 | return new Markup($markup, 'UTF-8'); 119 | } 120 | 121 | protected function normalizeSrc(array $src): array 122 | { 123 | if ($src['poster'] ?? false) { 124 | return ['poster', $src['poster']]; 125 | } 126 | 127 | if ($src['srcset'] ?? false) { 128 | return ['srcset', $src['srcset']]; 129 | } 130 | 131 | $srcset = $src; 132 | $src = \array_shift($srcset); 133 | 134 | return ['src', $src . '" data-srcset="' . \implode(', ', $srcset)]; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /tests/ParserTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * 9 | * Licensed under MIT license. 10 | */ 11 | 12 | namespace Ahc\TwigYall\Test; 13 | 14 | use Ahc\TwigYall\Yall; 15 | use PHPUnit\Framework\TestCase; 16 | use Twig\Environment; 17 | use Twig\Loader\ArrayLoader; 18 | 19 | class ParserTest extends TestCase 20 | { 21 | protected static $twig; 22 | 23 | public static function setUpBeforeClass() 24 | { 25 | static::$twig = new Environment(new ArrayLoader); 26 | 27 | static::$twig->addExtension(new Yall([ 28 | 'placeholder' => 'img/default.png', 29 | 'lazyClass' => 'defer', 30 | ])); 31 | } 32 | 33 | public function testLazyloadSimple() 34 | { 35 | $simple = $this->render(' 36 | {% lazyload %} 37 | 38 | 39 | {% endlazyload %} 40 | '); 41 | 42 | $this->assertContains( 43 | '', 44 | $simple, 45 | 'should defer image' 46 | ); 47 | 48 | $this->assertContains( 49 | '', 50 | $simple, 51 | 'should defer image' 52 | ); 53 | } 54 | 55 | public function testLazyLoadComplex() 56 | { 57 | $complex = $this->render(' 58 | {% lazyload %} 59 | {% if true %} 60 | 61 | {% else %} 62 | 63 | {% endif %} 64 | {% endlazyload %} 65 | '); 66 | 67 | $this->assertContains( 68 | '', 69 | $complex, 70 | 'should defer image' 71 | ); 72 | 73 | $this->assertNotContains( 74 | '', 75 | $complex, 76 | 'should defer image' 77 | ); 78 | 79 | $this->assertNotContains( 80 | '', 81 | $complex, 82 | 'should not defer image when not met' 83 | ); 84 | } 85 | 86 | public function testLazyLoadMixed() 87 | { 88 | $mixed = $this->render(' 89 | 90 | {% lazyload %} 91 | 92 | {% if true %} 93 | 94 | {% else %} 95 | 96 | {% endif %} 97 | 98 | {% endlazyload %} 99 | 100 | '); 101 | 102 | $this->assertContains( 103 | '', 104 | $mixed, 105 | 'should not defer image above lazyload block' 106 | ); 107 | 108 | $this->assertContains( 109 | '', 110 | $mixed, 111 | 'should not defer image below lazyload block' 112 | ); 113 | 114 | $this->assertContains( 115 | '', 116 | $mixed, 117 | 'should defer image inside block' 118 | ); 119 | 120 | $this->assertContains( 121 | '', 122 | $mixed, 123 | 'should defer image when condition met' 124 | ); 125 | 126 | $this->assertContains( 127 | '', 128 | $mixed, 129 | 'should defer image already using lazify' 130 | ); 131 | } 132 | 133 | public function testLazyLoadNoDefer() 134 | { 135 | $irregular = " 136 | 137 | 138 | 139 | 140 |