diff --git a/system/src/Grav/Common/Markdown/ParsedownGravTrait.php b/system/src/Grav/Common/Markdown/ParsedownGravTrait.php index 4674af187..55e4e3b47 100644 --- a/system/src/Grav/Common/Markdown/ParsedownGravTrait.php +++ b/system/src/Grav/Common/Markdown/ParsedownGravTrait.php @@ -45,7 +45,7 @@ trait ParsedownGravTrait /** * Make the element function publicly accessible, Medium uses this to render from Twig - * + * * @param array $Element * @return string markup */ @@ -168,6 +168,11 @@ trait ParsedownGravTrait $medium = call_user_func_array(array($medium, $action['method']), explode(',', $action['params'])); } + if (isset($url['fragment'])) { + $medium->urlHash($url['fragment']); + } + self::$grav['debugger']->addMessage($url); + $excerpt['element'] = $medium->parseDownElement($title, $alt, $class); } else { diff --git a/system/src/Grav/Common/Page/Media.php b/system/src/Grav/Common/Page/Media.php index b54d26db3..65bcba96a 100644 --- a/system/src/Grav/Common/Page/Media.php +++ b/system/src/Grav/Common/Page/Media.php @@ -76,7 +76,6 @@ class Media extends Getters if (!empty($types['base'])) { $medium = MediumFactory::fromFile($types['base']); } else if (!empty($types['alternative'])) { - $altMedium = reset($types['alternative']); $ratio = key($types['alternative']); @@ -101,7 +100,7 @@ class Media extends Getters if (!empty($types['alternative'])) { $alternatives = $types['alternative']; - + $max = max(array_keys($alternatives)); for ($i=2; $i < $max; $i++) { @@ -112,7 +111,7 @@ class Media extends Getters $types['alternative'][$i] = MediumFactory::scaledFromMedium($alternatives[$max], $max, $i); } - + foreach ($types['alternative'] as $ratio => $altMedium) { $medium->addAlternative($ratio, $altMedium); } diff --git a/system/src/Grav/Common/Page/Medium/Factory.php b/system/src/Grav/Common/Page/Medium/Factory.php index 2f0beeb74..7babbeae9 100644 --- a/system/src/Grav/Common/Page/Medium/Factory.php +++ b/system/src/Grav/Common/Page/Medium/Factory.php @@ -1,17 +1,12 @@ get('media.defaults'); - $params += array( + $params += [ 'type' => 'file', 'thumb' => 'media/thumb.png', 'mime' => 'application/octet-stream', @@ -55,7 +57,7 @@ class Factory 'path' => $path, 'modified' => filemtime($file), 'thumbnails' => [] - ); + ]; $locator = self::getGrav()['locator']; @@ -70,7 +72,14 @@ class Factory return static::fromArray($params); } - public static function fromArray($items = array(), Blueprint $blueprint = null) + /** + * Create Medium from array of parameters + * + * @param array $items + * @param Blueprint|null $blueprint + * @return Medium + */ + public static function fromArray(array $items = [], Blueprint $blueprint = null) { $type = isset($items['type']) ? $items['type'] : null; @@ -94,6 +103,14 @@ class Factory } } + /** + * Create a new ImageMedium by scaling another ImageMedium object. + * + * @param ImageMedium $medium + * @param int $from + * @param int $to + * @return Medium + */ public static function scaledFromMedium($medium, $from, $to) { if (! $medium instanceof ImageMedium) @@ -125,4 +142,4 @@ class Factory return $medium; } -} \ No newline at end of file +} diff --git a/system/src/Grav/Common/Page/Medium/ImageMedium.php b/system/src/Grav/Common/Page/Medium/ImageMedium.php index 80cb85a09..588af85c1 100644 --- a/system/src/Grav/Common/Page/Medium/ImageMedium.php +++ b/system/src/Grav/Common/Page/Medium/ImageMedium.php @@ -1,12 +1,7 @@ saveImage(); if ($reset) $this->reset(); - - return GRAV_ROOT . '/' . $output; + + return $output; } /** @@ -116,13 +111,11 @@ class ImageMedium extends Medium */ public function url($reset = true) { - $output = '/' . $this->saveImage(); + $output = preg_replace('|^' . GRAV_ROOT . '|', '', $this->saveImage()); - if ($reset) { - $this->reset(); - } + if ($reset) $this->reset(); - return self::$grav['base_url'] . $output; + return self::$grav['base_url'] . $output . $this->urlHash(); } @@ -151,16 +144,20 @@ class ImageMedium extends Medium } /** - * Called from Parsedown (ParsedownGravTrait::inlineImage calls this method on the Medium) + * Parsedown element for source display mode + * + * @param array $attributes + * @param boolean $reset + * @return array */ - public function sourceParsedownElement($attributes, $reset = true) + public function sourceParsedownElement(array $attributes, $reset = true) { empty($attributes['src']) && $attributes['src'] = $this->url(false); - + $srcset = $this->srcset($reset); if ($srcset) { empty($attributes['srcset']) && $attributes['srcset'] = $srcset; - empty($attributes['sizes']) && $attributes['sizes'] = '100vw'; + empty($attributes['sizes']) && $attributes['sizes'] = $this->sizes(); } return [ 'name' => 'image', 'attributes' => $attributes ]; @@ -173,51 +170,46 @@ class ImageMedium extends Medium */ public function reset() { - $this->image = null; + parent::reset(); - $this->image(); - $this->filter(); + if ($this->image) { + $this->image(); + $this->filter(); + } $this->format = 'guess'; - $this->quality = 80; + $this->quality = 85; + $this->debug_watermarked = false; - return parent::reset(); + return $this; } /** - * Enable link for the medium object. + * Turn the current Medium into a Link * - * @param null $width - * @param null $height - * @return $this + * @param boolean $reset + * @param array $attributes + * @return Link */ - public function link($reset = true) + public function link($reset = true, array $attributes = []) { - if ($this->mode !== 'source') { - $this->display('source'); - } - - $this->linkAttributes['href'] = $this->url(false); - $srcset = $this->srcset($reset); - + $attributes['href'] = $this->url(false); + $srcset = $this->srcset(false); if ($srcset) { - $this->linkAttributes['data-srcset'] = $srcset; + $attributes['data-srcset'] = $srcset; } - $this->thumbnail('auto'); - $thumb = $this->display('thumbnail'); - $thumb->linked = true; - - return $thumb; + return parent::link($reset, $attributes); } /** - * Enable lightbox for the medium. + * Turn the current Medium inta a Link with lightbox enabled * - * @param null $width - * @param null $height - * @return Medium + * @param int $width + * @param int $height + * @param boolean $reset + * @return Link */ public function lightbox($width = null, $height = null, $reset = true) { @@ -234,11 +226,16 @@ class ImageMedium extends Medium /** * Sets the quality of the image - * @param Int $quality 0-100 quality + * + * @param int $quality 0-100 quality * @return Medium */ public function quality($quality) { + if (!$this->image) { + $this->image(); + } + $this->quality = $quality; return $this; } @@ -247,15 +244,34 @@ class ImageMedium extends Medium * Sets image output format. * * @param string $format - * @param int $quality * @return $this */ public function format($format) { + if (!$this->image) { + $this->image(); + } + $this->format = $format; return $this; } + /** + * Set or get sizes parameter for srcset media action + * + * @param string $sizes + * @return $this + */ + public function sizes($sizes = null) { + + if ($sizes) { + $this->attributes['sizes'] = $sizes; + return $this; + } + + return empty($this->attributes['sizes']) ? '100vw' : $this->attributes['sizes']; + } + /** * Forward the call to the image processing method. * @@ -279,7 +295,7 @@ class ImageMedium extends Medium } try { - $result = call_user_func_array(array($this->image, $method), $args); + $result = call_user_func_array([$this->image, $method], $args); foreach ($this->alternatives as $ratio => $medium) { $args_copy = $args; @@ -294,7 +310,7 @@ class ImageMedium extends Medium } } - call_user_func_array(array($medium, $method), $args_copy); + call_user_func_array([$medium, $method], $args_copy); } } catch (\BadFunctionCallException $e) { } @@ -311,11 +327,12 @@ class ImageMedium extends Medium { $locator = self::$grav['locator']; - // TODO: add default file $file = $this->get('filepath'); + $cacheDir = $locator->findResource('cache://images', true); + $this->image = ImageFile::open($file) - ->setCacheDir($locator->findResource('cache://images', false)) - ->setActualCacheDir($locator->findResource('cache://images', true)) + ->setCacheDir($cacheDir) + ->setActualCacheDir($cacheDir) ->setPrettyName(basename($this->get('basename'))); $this->filter(); @@ -331,7 +348,7 @@ class ImageMedium extends Medium protected function saveImage() { if (!$this->image) { - $this->image(); + return parent::path(false); } if ($this->get('debug') && !$this->debug_watermarked) { @@ -345,7 +362,9 @@ class ImageMedium extends Medium $this->image->merge(ImageFile::open($overlay)); } - return $this->image->cacheFile($this->format, $this->quality); + $result = $this->image->cacheFile($this->format, $this->quality); + + return $result; } /** @@ -355,7 +374,7 @@ class ImageMedium extends Medium */ public function filter($filter = 'image.filters.default') { - $filters = (array) $this->get($filter, array()); + $filters = (array) $this->get($filter, []); foreach ($filters as $params) { $params = (array) $params; $method = array_shift($params); diff --git a/system/src/Grav/Common/Page/Medium/Link.php b/system/src/Grav/Common/Page/Medium/Link.php new file mode 100644 index 000000000..88c8bb08d --- /dev/null +++ b/system/src/Grav/Common/Page/Medium/Link.php @@ -0,0 +1,64 @@ +attributes = $attributes; + $this->source = $medium->reset()->thumbnail('auto')->display('thumbnail'); + $this->source->linked = true; + } + + /** + * Get an element (is array) that can be rendered by the Parsedown engine + * + * @param string $title + * @param string $alt + * @param string $class + * @param boolean $reset + * @return array + */ + public function parsedownElement($title = null, $alt = null, $class = null, $reset = true) + { + $innerElement = $this->source->parsedownElement($title, $alt, $class, $reset); + + return [ + 'name' => 'a', + 'attributes' => $this->attributes, + 'handler' => is_string($innerElement) ? 'line' : 'element', + 'text' => $innerElement + ]; + } + + /** + * Forward the call to the source element + * + * @param string $method + * @param mixed $args + * @return $this|mixed + */ + public function __call($method, $args) + { + $this->source = call_user_func_array(array($this->source, $method), $args); + + // Don't start nesting links, if user has multiple link calls in his + // actions, we will drop the previous links. + return $this->source instanceof LinkMedium ? $this->source : $this; + } +} diff --git a/system/src/Grav/Common/Page/Medium/Medium.php b/system/src/Grav/Common/Page/Medium/Medium.php index 3fc255d8f..0c2ca4845 100644 --- a/system/src/Grav/Common/Page/Medium/Medium.php +++ b/system/src/Grav/Common/Page/Medium/Medium.php @@ -1,25 +1,21 @@ get('filepath')); - return self::$grav['base_url'] . $output; + if ($reset) $this->reset(); + + return self::$grav['base_url'] . $output . $this->urlHash(); } /** - * Return HTML markup from the medium. + * Get/set hash for the file's url * - * @param string $title - * @param string $class - * @param bool $reset + * @param string $hash + * @param boolean $withHash * @return string */ - public function html($title = null, $alt = null, $class = null, $reset = true) + public function urlHash($hash = null, $withHash = true) { - $element = $this->parsedownElement($title, $alt, $class, $reset); - - if (!$this->parsedown) { - $this->parsedown = new Parsedown(null); + if ($hash) { + $this->set('urlHash', ltrim($hash, '#')); } - return $this->parsedown->elementToHtml($element); + $hash = $this->get('urlHash', ''); + + if ($withHash && !empty($hash)) { + return '#' . $hash; + } else { + return $hash; + } } + /** + * Get an element (is array) that can be rendered by the Parsedown engine + * + * @param string $title + * @param string $alt + * @param string $class + * @param boolean $reset + * @return array + */ public function parsedownElement($title = null, $alt = null, $class = null, $reset = true) { $element; $attributes = $this->attributes; - $link_attributes = $this->linkAttributes; !empty($title) && empty($attributes['title']) && $attributes['title'] = $title; !empty($alt) && empty($attributes['alt']) && $attributes['alt'] = $alt; @@ -163,29 +162,18 @@ class Medium extends Data switch ($this->mode) { case 'text': - $element = $this->textParsedownElement($attributes, $reset); + $element = $this->textParsedownElement($attributes, false); break; case 'thumbnail': - $element = $this->getThumbnail()->sourceParsedownElement($attributes, $reset); + $element = $this->getThumbnail()->sourceParsedownElement($attributes, false); break; case 'source': - $element = $this->sourceParsedownElement($attributes, $reset); + $element = $this->sourceParsedownElement($attributes, false); break; } - if ($link_attributes) { - - $innerElement = $element; - $element = [ - 'name' => 'a', - 'attributes' => $this->linkAttributes, - 'handler' => is_string($innerElement) ? 'line' : 'element', - 'text' => $innerElement - ]; - - if ($reset) { - $this->linkAttributes = []; - } + if ($reset) { + $this->reset(); } $this->display('source'); @@ -193,12 +181,26 @@ class Medium extends Data return $element; } - public function sourceParsedownElement($attributes, $reset) + /** + * Parsedown element for source display mode + * + * @param array $attributes + * @param boolean $reset + * @return array + */ + protected function sourceParsedownElement(array $attributes, $reset = true) { return $this->textParsedownElement($attributes, $reset); } - public function textParsedownElement($attributes, $reset) + /** + * Parsedown element for text display mode + * + * @param array $attributes + * @param boolean $reset + * @return array + */ + protected function textParsedownElement(array $attributes, $reset = true) { $text = empty($attributes['title']) ? empty($attributes['alt']) ? $this->get('filename') : $attributes['alt'] : $attributes['title']; @@ -222,6 +224,7 @@ class Medium extends Data */ public function reset() { + $this->attributes = []; return $this; } @@ -232,7 +235,7 @@ class Medium extends Data * * @return $this */ - public function display($mode) + public function display($mode = 'source') { if ($this->mode === $mode) return $this; @@ -249,10 +252,10 @@ class Medium extends Data * * @return $this */ - public function thumbnail($type) + public function thumbnail($type = 'auto') { if (!in_array($type, $this->thumbnailTypes)) - return; + return $this; if ($this->thumbnailType !== $type) { $this->_thumbnail = null; @@ -264,58 +267,64 @@ class Medium extends Data } /** - * Enable link for the medium object. + * Turn the current Medium into a Link * - * @param null $width - * @param null $height - * @return $this + * @param boolean $reset + * @param array $attributes + * @return Link */ - public function link($reset = true) + public function link($reset = true, array $attributes = []) { if ($this->mode !== 'source') { $this->display('source'); } - $this->linkAttributes['href'] = $this->url(); + foreach ($this->attributes as $key => $value) { + empty($attributes['data-' . $key]) && $attributes['data-' . $key] = $value; + } - $this->thumbnail('auto'); - $thumb = $this->display('thumbnail'); - $thumb->linked = true; + empty($attributes['href']) && $attributes['href'] = $this->url(); - return $thumb; + return new Link($attributes, $this); } /** - * Enable lightbox for the medium. + * Turn the current Medium inta a Link with lightbox enabled * - * @param null $width - * @param null $height - * @return Medium + * @param int $width + * @param int $height + * @param boolean $reset + * @return Link */ public function lightbox($width = null, $height = null, $reset = true) { - $this->linkAttributes['rel'] = 'lightbox'; + $attributes = ['rel' => 'lightbox']; if ($width && $height) { - $this->linkAttributes['data-width'] = $width; - $this->linkAttributes['data-height'] = $height; + $attributes['data-width'] = $width; + $attributes['data-height'] = $height; } - return $this->link($reset); + return $this->link($reset, $attributes); } /** - * Forward the call to the image processing method. + * Allow any action to be called on this medium from twig or markdown * * @param string $method * @param mixed $args - * @return $this|mixed + * @return $this */ public function __call($method, $args) { return $this; } + /** + * Get the thumbnail Medium object + * + * @return ThumbnailImageMedium + */ protected function getThumbnail() { if (!$this->_thumbnail) { diff --git a/system/src/Grav/Common/Page/Medium/MediumInterface.php b/system/src/Grav/Common/Page/Medium/MediumInterface.php deleted file mode 100644 index 6343bed8a..000000000 --- a/system/src/Grav/Common/Page/Medium/MediumInterface.php +++ /dev/null @@ -1,157 +0,0 @@ -parsedownElement($title, $alt, $class, $reset); + + if (!$this->parsedown) { + $this->parsedown = new Parsedown(null); + } + + return $this->parsedown->elementToHtml($element); + } +} diff --git a/system/src/Grav/Common/Page/Medium/RenderableInterface.php b/system/src/Grav/Common/Page/Medium/RenderableInterface.php new file mode 100644 index 000000000..f1ebf0dfc --- /dev/null +++ b/system/src/Grav/Common/Page/Medium/RenderableInterface.php @@ -0,0 +1,34 @@ +url($reset); - !empty($this->width) && $attributes['width'] = $this->width; - !empty($this->height) && $attributes['height'] = $this->height; - return [ 'name' => 'image', 'attributes' => $attributes ]; } - - /** - * Enable link for the medium object. - * - * @param null $width - * @param null $height - * @return $this - */ - public function link($reset = true) - { - if ($this->mode !== 'source') { - $this->display('source'); - } - - !empty($this->width) && $this->linkAttributes['data-width'] = $this->width; - !empty($this->height) && $this->linkAttributes['data-height'] = $this->height; - - return parent::link($reset); - } - - protected function resize($width = null, $height = null) - { - $this->attributes['width'] = $width; - $this->attributes['height'] = $height; - - return $this; - } } diff --git a/system/src/Grav/Common/Page/Medium/StaticResizeTrait.php b/system/src/Grav/Common/Page/Medium/StaticResizeTrait.php new file mode 100644 index 000000000..6b37cef4b --- /dev/null +++ b/system/src/Grav/Common/Page/Medium/StaticResizeTrait.php @@ -0,0 +1,20 @@ +attributes['width'] = $width; + $this->attributes['height'] = $height; + + return $this; + } +} diff --git a/system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php b/system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php index de8ead96c..ff916b4f6 100644 --- a/system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php +++ b/system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php @@ -1,18 +1,16 @@ bubble('parsedownElement', [$title, $alt, $class, $reset]); } + /** + * Return HTML markup from the medium. + * + * @param string $title + * @param string $alt + * @param string $class + * @param bool $reset + * @return string + */ public function html($title = null, $alt = null, $class = null, $reset = true) { return $this->bubble('html', [$title, $alt, $class, $reset]); } - public function display($mode) + /** + * Switch display mode. + * + * @param string $mode + * + * @return $this + */ + public function display($mode = 'source') { return $this->bubble('display', [$mode], false); } - public function link($reset = true) + /** + * Turn the current Medium into a Link + * + * @param boolean $reset + * @param array $attributes + * @return Link + */ + public function link($reset = true, array $attributes = []) { - return $this->bubble('link', [$reset], false); + return $this->bubble('link', [$reset, $attributes], false); } + /** + * Turn the current Medium inta a Link with lightbox enabled + * + * @param int $width + * @param int $height + * @param boolean $reset + * @return Link + */ public function lightbox($width = null, $height = null, $reset = true) { return $this->bubble('lightbox', [$width, $height, $reset], false); } - public function bubble($method, $arguments = [], $testLinked = true) + /** + * Bubble a function call up to either the superclass function or the parent Medium instance + * + * @param string $method + * @param array $arguments + * @param boolean $testLinked + * @return Medium + */ + protected function bubble($method, array $arguments = [], $testLinked = true) { if (!$testLinked || $this->linked) { return call_user_func_array(array($this->parent, $method), $arguments); diff --git a/system/src/Grav/Common/Page/Medium/VideoMedium.php b/system/src/Grav/Common/Page/Medium/VideoMedium.php index 3a9dd5440..e67d3fbfc 100644 --- a/system/src/Grav/Common/Page/Medium/VideoMedium.php +++ b/system/src/Grav/Common/Page/Medium/VideoMedium.php @@ -11,31 +11,19 @@ use Gregwar\Image\Image as ImageFile; class VideoMedium extends Medium { - /** - * @var int - */ - protected $width = null; + use StaticResizeTrait; /** - * @var int + * Parsedown element for source display mode + * + * @param array $attributes + * @param boolean $reset + * @return array */ - protected $height = null; - - public function __construct($items = array(), Blueprint $blueprint = null) + protected function sourceParsedownElement(array $attributes, $reset = true) { - parent::__construct($items, $blueprint); - - $this->attributes['controls'] = true; - } - - public function sourceParsedownElement($attributes, $reset) - { - $attributes = $this->attributes; $location = $this->url($reset); - !empty($this->width) && $attributes['width'] = $this->width; - !empty($this->height) && $attributes['height'] = $this->height; - return [ 'name' => 'video', 'text' => 'Your browser does not support the video tag.', @@ -44,29 +32,15 @@ class VideoMedium extends Medium } /** - * Enable link for the medium object. + * Reset medium. * - * @param null $width - * @param null $height * @return $this */ - public function link($reset = true) + public function reset() { - if ($this->mode !== 'source') { - $this->display('source'); - } - - !empty($this->width) && $this->linkAttributes['data-width'] = $this->width; - !empty($this->height) && $this->linkAttributes['data-height'] = $this->height; - - return parent::link($reset); - } - - protected function resize($width = null, $height = null) - { - $this->attributes['width'] = $width; - $this->attributes['height'] = $height; + parent::reset(); + $this->attributes['controls'] = true; return $this; } -} \ No newline at end of file +}