diff --git a/extension.json b/extension.json index 1c595e5..d6dad95 100644 --- a/extension.json +++ b/extension.json @@ -6,7 +6,7 @@ ], "url": "https://github.com/Luqgreg/mediawiki-PortableInfobox", "descriptionmsg": "portable-infobox-desc", - "version": "0.2.1", + "version": "0.2.1-audio_support", "type": "parserhook", "license-name": "GPL-3.0-or-later", "config": { @@ -51,13 +51,16 @@ "PortableInfobox\\Parser\\MediaWikiParserService": "includes/services/Parser/MediaWikiParserService.php", "PortableInfobox\\Parser\\Nodes\\NodeFactory": "includes/services/Parser/Nodes/NodeFactory.class.php", "PortableInfobox\\Parser\\Nodes\\Node": "includes/services/Parser/Nodes/Node.php", - "PortableInfobox\\Parser\\Nodes\\NodeNavigation": "includes/services/Parser/Nodes/NodeNavigation.php", + "PortableInfobox\\Parser\\Nodes\\NodeAudio": "includes/services/Parser/Nodes/NodeAudio.php", + "PortableInfobox\\Parser\\Nodes\\NodeData": "includes/services/Parser/Nodes/NodeData.php", + "PortableInfobox\\Parser\\Nodes\\NodeInfobox": "includes/services/Parser/Nodes/NodeInfobox.php", "PortableInfobox\\Parser\\Nodes\\NodeGroup": "includes/services/Parser/Nodes/NodeGroup.php", "PortableInfobox\\Parser\\Nodes\\NodeHeader": "includes/services/Parser/Nodes/NodeHeader.php", "PortableInfobox\\Parser\\Nodes\\NodeImage": "includes/services/Parser/Nodes/NodeImage.php", - "PortableInfobox\\Parser\\Nodes\\NodeInfobox": "includes/services/Parser/Nodes/NodeInfobox.php", - "PortableInfobox\\Parser\\Nodes\\NodeData": "includes/services/Parser/Nodes/NodeData.php", + "PortableInfobox\\Parser\\Nodes\\NodeMedia": "includes/services/Parser/Nodes/NodeMedia.php", + "PortableInfobox\\Parser\\Nodes\\NodeNavigation": "includes/services/Parser/Nodes/NodeNavigation.php", "PortableInfobox\\Parser\\Nodes\\NodeTitle": "includes/services/Parser/Nodes/NodeTitle.php", + "PortableInfobox\\Parser\\Nodes\\NodeVideo": "includes/services/Parser/Nodes/NodeVideo.php", "PortableInfobox\\Parser\\Nodes\\NodeUnimplemented": "includes/services/Parser/Nodes/NodeUnimplemented.php", "PortableInfobox\\Helpers\\FileNamespaceSanitizeHelper": "includes/services/Helpers/FileNamespaceSanitizeHelper.php", "PortableInfobox\\Helpers\\HtmlHelper": "includes/services/Helpers/HtmlHelper.class.php", diff --git a/includes/services/Helpers/PortableInfoboxImagesHelper.php b/includes/services/Helpers/PortableInfoboxImagesHelper.php index 3dbe98b..6d0e455 100644 --- a/includes/services/Helpers/PortableInfoboxImagesHelper.php +++ b/includes/services/Helpers/PortableInfoboxImagesHelper.php @@ -21,18 +21,20 @@ class PortableInfoboxImagesHelper { $title = $data['name']; $file = $this->getFileFromTitle( $title ); - if ( - !$file || !$file->exists() || - !in_array( $file->getMediaType(), [ MEDIATYPE_BITMAP, MEDIATYPE_DRAWING, MEDIATYPE_VIDEO ] ) - ) { + if ( !$file || !$file->exists() ) { return false; } - // we don't need failing thumbnail creation for videos - if( $file->getMediaType() == MEDIATYPE_VIDEO ) { - return array_merge( $data, [ - 'ref' => ++self::$count - ] ); + $mediatype = $file->getMediaType(); + $data['isImage'] = in_array( $mediatype, [ MEDIATYPE_BITMAP, MEDIATYPE_DRAWING ] ); + $data['isVideo'] = $mediatype === MEDIATYPE_VIDEO; + $data['isAudio'] = $mediatype === MEDIATYPE_AUDIO; + + $data['ref'] = ++self::$count; + + // we don't need failing thumbnail creation for videos and audio files + if( !$data['isImage'] ) { + return $data; } // get dimensions @@ -65,7 +67,6 @@ class PortableInfoboxImagesHelper { } return array_merge( $data, [ - 'ref' => ++self::$count, 'height' => intval( $imgTagDimensions['height'] ), 'width' => intval( $imgTagDimensions['width'] ), 'thumbnail' => $thumbnail->getUrl(), diff --git a/includes/services/Helpers/PortableInfoboxTemplateEngine.php b/includes/services/Helpers/PortableInfoboxTemplateEngine.php index 2793574..d88caf5 100644 --- a/includes/services/Helpers/PortableInfoboxTemplateEngine.php +++ b/includes/services/Helpers/PortableInfoboxTemplateEngine.php @@ -13,13 +13,16 @@ class PortableInfoboxTemplateEngine { 'wrapper' => 'PortableInfoboxWrapper.hbs', 'title' => 'PortableInfoboxItemTitle.hbs', 'header' => 'PortableInfoboxItemHeader.hbs', - 'image' => 'PortableInfoboxItemImage.hbs', + 'media' => 'PortableInfoboxItemMedia.hbs', + 'audio' => 'PortableInfoboxItemMedia.hbs', + 'image' => 'PortableInfoboxItemMedia.hbs', + 'video' => 'PortableInfoboxItemMedia.hbs', 'data' => 'PortableInfoboxItemData.hbs', 'group' => 'PortableInfoboxItemGroup.hbs', 'smart-group' => 'PortableInfoboxItemSmartGroup.hbs', 'horizontal-group-content' => 'PortableInfoboxHorizontalGroupContent.hbs', 'navigation' => 'PortableInfoboxItemNavigation.hbs', - 'image-collection' => 'PortableInfoboxItemImageCollection.hbs', + 'media-collection' => 'PortableInfoboxItemMediaCollection.hbs', 'xml-parse-error' => 'PortableInfoboxMarkupDebug.hbs' ]; @@ -48,7 +51,7 @@ class PortableInfoboxTemplateEngine { if ( !empty( self::$cache[ $type ] ) ) { return self::$cache[ $type ]; } - + $path = self::getTemplatesDir() . DIRECTORY_SEPARATOR . self::$templates[ $type ]; // @see https://github.com/wikimedia/mediawiki-vendor/tree/master/zordius/lightncandy diff --git a/includes/services/Parser/Nodes/NodeAudio.php b/includes/services/Parser/Nodes/NodeAudio.php new file mode 100644 index 0000000..257b1ea --- /dev/null +++ b/includes/services/Parser/Nodes/NodeAudio.php @@ -0,0 +1,25 @@ +getGallery( $marker ); - return isset( $gallery ) ? array_map( function ( $image ) { - return [ - 'label' => $image[1] ?: $image[0]->getText(), - 'title' => $image[0] - ]; - }, $gallery->getimages() ) : []; - } - - public static function getTabberData( $html ) { - $data = []; - $doc = HtmlHelper::createDOMDocumentFromText( $html ); - $sxml = simplexml_import_dom( $doc ); - $divs = $sxml->xpath( '//div[@class=\'tabbertab\']' ); - foreach ( $divs as $div ) { - if ( preg_match( '/ src="(?:[^"]*\/)?([^"]*?)"/', $div->asXML(), $out ) ) { - $data[] = [ - 'label' => (string) $div['title'], - 'title' => $out[1] - ]; - } - } - return $data; - } - - public function getData() { - if ( !isset( $this->data ) ) { - $this->data = []; - - // value passed to source parameter (or default) - $value = $this->getRawValueWithDefault( $this->xmlNode ); - - if ( $this->containsTabberOrGallery( $value ) ) { - $this->data = $this->getImagesData( $value ); - } else { - $this->data = [ $this->getImageData( - $value, - $this->getValueWithDefault( $this->xmlNode->{self::ALT_TAG_NAME} ), - $this->getValueWithDefault( $this->xmlNode->{self::CAPTION_TAG_NAME} ) - ) ]; - } - } - return $this->data; - } - - /** - * @desc Checks if parser preprocessed string containg Tabber or Gallery extension - * @param string $str String to check +class NodeImage extends NodeMedia { + /* * @return bool */ - private function containsTabberOrGallery( $str ) { - return !empty( self::getMarkers( $str, self::TABBER ) ) || !empty( self::getMarkers( $str, self::GALLERY ) ); - } - - private function getImagesData( $value ) { - $data = []; - $items = array_merge( $this->getGalleryItems( $value ), $this->getTabberItems( $value ) ); - foreach( $items as $item ) { - $data[] = $this->getImageData( $item['title'], $item['label'], $item['label'] ); - } - return $data; - } - - private function getGalleryItems( $value ) { - $galleryItems = []; - $galleryMarkers = self::getMarkers( $value, self::GALLERY ); - foreach ( $galleryMarkers as $marker ) { - $galleryItems = array_merge( $galleryItems, self::getGalleryData( $marker ) ); - } - return $galleryItems; - } - - private function getTabberItems( $value ) { - $tabberItems = []; - $tabberMarkers = self::getMarkers( $value, self::TABBER ); - foreach ( $tabberMarkers as $marker ) { - $tabberHtml = $this->getExternalParser()->parseRecursive( $marker ); - $tabberItems = array_merge( $tabberItems, self::getTabberData( $tabberHtml ) ); - } - return $tabberItems; - } - - /** - * @desc prepare infobox image node data. - * - * @param $title - * @param $alt - * @param $caption - * @return array - */ - private function getImageData( $title, $alt, $caption ) { - $titleObj = $title instanceof \Title ? $title : $this->getImageAsTitleObject( $title ); - $fileObj = $this->getFileFromTitle( $titleObj ); - - if ( $titleObj instanceof \Title ) { - $this->getExternalParser()->addImage( $titleObj->getDBkey() ); - } - - $image = [ - 'url' => $this->resolveImageUrl( $fileObj ), - 'name' => $titleObj ? $titleObj->getText() : '', - 'key' => $titleObj ? $titleObj->getDBKey() : '', - 'alt' => $alt ?? ( $titleObj ? $titleObj->getText() : null ), - 'caption' => SanitizerBuilder::createFromType( 'image' ) - ->sanitize( [ 'caption' => $caption ] )['caption'] ?: null, - 'isVideo' => $this->isVideo( $fileObj ) - ]; - - return $image; - } - - public function isEmpty() { - $data = $this->getData(); - foreach ( $data as $dataItem ) { - if ( !empty( $dataItem[ 'url' ] ) ) { - return false; - } - } + protected function allowImage() { return true; } - public function getSources() { - $sources = $this->extractSourcesFromNode( $this->xmlNode ); - if ( $this->xmlNode->{self::ALT_TAG_NAME} ) { - $sources = array_merge( $sources, - $this->extractSourcesFromNode( $this->xmlNode->{self::ALT_TAG_NAME} ) ); - } - if ( $this->xmlNode->{self::CAPTION_TAG_NAME} ) { - $sources = array_merge( $sources, - $this->extractSourcesFromNode( $this->xmlNode->{self::CAPTION_TAG_NAME} ) ); - } - - return array_unique( $sources ); - } - - private function getImageAsTitleObject( $imageName ) { - global $wgContLang; - $title = \Title::makeTitleSafe( - NS_FILE, - FileNamespaceSanitizeHelper::getInstance()->sanitizeImageFileName( $imageName, $wgContLang ) - ); - - return $title; - } - - /** - * NOTE: Protected to override in unit tests - * - * @desc get file object from title object - * @param Title|null $title - * @return File|null - */ - protected function getFileFromTitle( $title ) { - if( is_string( $title ) ) { - $title = \Title::newFromText( $title, NS_FILE ); - } - - if( $title instanceof \Title ) { - $file = wfFindFile( $title ); - if( $file instanceof \File && $file->exists() ) { - return $file; - } - } - - return null; - } - - /** - * @desc returns image url for given image title - * @param File|null $file - * @return string url or '' if image doesn't exist - */ - public function resolveImageUrl( $file ) { - return $file ? $file->getUrl() : ''; - } - - /** - * @desc checks if file media type is VIDEO - * @param File|null $file + /* * @return bool */ - private function isVideo( $file ) { - return $file ? $file->getMediaType() === MEDIATYPE_VIDEO : false; + protected function allowAudio() { + return false; } -} +} \ No newline at end of file diff --git a/includes/services/Parser/Nodes/NodeMedia.php b/includes/services/Parser/Nodes/NodeMedia.php new file mode 100644 index 0000000..fa67b95 --- /dev/null +++ b/includes/services/Parser/Nodes/NodeMedia.php @@ -0,0 +1,252 @@ +getGallery( $marker ); + return isset( $gallery ) ? array_map( function ( $image ) { + return [ + 'label' => $image[1] ?: $image[0]->getText(), + 'title' => $image[0] + ]; + }, $gallery->getimages() ) : []; + } + + public static function getTabberData( $html ) { + $data = []; + $doc = HtmlHelper::createDOMDocumentFromText( $html ); + $sxml = simplexml_import_dom( $doc ); + $divs = $sxml->xpath( '//div[@class=\'tabbertab\']' ); + foreach ( $divs as $div ) { + if ( preg_match( '/ src="(?:[^"]*\/)?([^"]*?)"/', $div->asXML(), $out ) ) { + $data[] = [ + 'label' => (string) $div['title'], + 'title' => $out[1] + ]; + } + } + return $data; + } + + public function getData() { + if ( !isset( $this->data ) ) { + $this->data = []; + + // value passed to source parameter (or default) + $value = $this->getRawValueWithDefault( $this->xmlNode ); + + if ( $this->containsTabberOrGallery( $value ) ) { + $this->data = $this->getImagesData( $value ); + } else { + $this->data = [ $this->getImageData( + $value, + $this->getValueWithDefault( $this->xmlNode->{self::ALT_TAG_NAME} ), + $this->getValueWithDefault( $this->xmlNode->{self::CAPTION_TAG_NAME} ) + ) ]; + } + } + return $this->data; + } + + /** + * @desc Checks if parser preprocessed string containg Tabber or Gallery extension + * @param string $str String to check + * @return bool + */ + private function containsTabberOrGallery( $str ) { + return !empty( self::getMarkers( $str, self::TABBER ) ) || !empty( self::getMarkers( $str, self::GALLERY ) ); + } + + private function getImagesData( $value ) { + $data = []; + $items = array_merge( $this->getGalleryItems( $value ), $this->getTabberItems( $value ) ); + foreach( $items as $item ) { + $data[] = $this->getImageData( $item['title'], $item['label'], $item['label'] ); + } + return $data; + } + + private function getGalleryItems( $value ) { + $galleryItems = []; + $galleryMarkers = self::getMarkers( $value, self::GALLERY ); + foreach ( $galleryMarkers as $marker ) { + $galleryItems = array_merge( $galleryItems, self::getGalleryData( $marker ) ); + } + return $galleryItems; + } + + private function getTabberItems( $value ) { + $tabberItems = []; + $tabberMarkers = self::getMarkers( $value, self::TABBER ); + foreach ( $tabberMarkers as $marker ) { + $tabberHtml = $this->getExternalParser()->parseRecursive( $marker ); + $tabberItems = array_merge( $tabberItems, self::getTabberData( $tabberHtml ) ); + } + return $tabberItems; + } + + /** + * @desc prepare infobox image node data. + * + * @param $title + * @param $alt + * @param $caption + * @return array + */ + private function getImageData( $title, $alt, $caption ) { + $titleObj = $title instanceof \Title ? $title : $this->getImageAsTitleObject( $title ); + $fileObj = $this->getFileFromTitle( $titleObj ); + + if( !isset( $fileObj ) || !$this->isTypeAllowed( $fileObj->getMediaType() ) ) { + return []; + } + + if ( $titleObj instanceof \Title ) { + $this->getExternalParser()->addImage( $titleObj->getDBkey() ); + } + + $image = [ + 'url' => $this->resolveImageUrl( $fileObj ), + 'name' => $titleObj ? $titleObj->getText() : '', + 'alt' => $alt ?? ( $titleObj ? $titleObj->getText() : null ), + 'caption' => SanitizerBuilder::createFromType( 'image' ) + ->sanitize( [ 'caption' => $caption ] )['caption'] ?: null + ]; + + return $image; + } + + public function isEmpty() { + $data = $this->getData(); + foreach ( $data as $dataItem ) { + if ( !empty( $dataItem[ 'url' ] ) ) { + return false; + } + } + return true; + } + + public function getSources() { + $sources = $this->extractSourcesFromNode( $this->xmlNode ); + if ( $this->xmlNode->{self::ALT_TAG_NAME} ) { + $sources = array_merge( $sources, + $this->extractSourcesFromNode( $this->xmlNode->{self::ALT_TAG_NAME} ) ); + } + if ( $this->xmlNode->{self::CAPTION_TAG_NAME} ) { + $sources = array_merge( $sources, + $this->extractSourcesFromNode( $this->xmlNode->{self::CAPTION_TAG_NAME} ) ); + } + + return array_unique( $sources ); + } + + private function getImageAsTitleObject( $imageName ) { + global $wgContLang; + $title = \Title::makeTitleSafe( + NS_FILE, + FileNamespaceSanitizeHelper::getInstance()->sanitizeImageFileName( $imageName, $wgContLang ) + ); + + return $title; + } + + /** + * NOTE: Protected to override in unit tests + * + * @desc get file object from title object + * @param Title|null $title + * @return File|null + */ + protected function getFileFromTitle( $title ) { + if( is_string( $title ) ) { + $title = \Title::newFromText( $title, NS_FILE ); + } + + if( $title instanceof \Title ) { + $file = wfFindFile( $title ); + if( $file instanceof \File && $file->exists() ) { + return $file; + } + } + + return null; + } + + /** + * @desc returns image url for given image title + * @param File|null $file + * @return string url or '' if image doesn't exist + */ + public function resolveImageUrl( $file ) { + return $file ? $file->getUrl() : ''; + } + + /** + * @desc checks if file media type is allowed + * @param string $type + * @return bool + */ + private function isTypeAllowed( $type ) { + switch( $type ) { + case MEDIATYPE_BITMAP: + case MEDIATYPE_DRAWING: + return $this->allowImage(); + case MEDIATYPE_VIDEO: + return $this->allowVideo(); + case MEDIATYPE_AUDIO: + return $this->allowAudio(); + default: + return false; + } + } + + /** + * @return bool + */ + protected function allowImage() { + $attr = $this->getXmlAttribute( $this->xmlNode, self::ALLOWIMAGE_ATTR_NAME ); + + return !( isset( $attr ) && strtolower( $attr ) === 'false' ); + } + + /** + * @return bool + */ + protected function allowVideo() { + $attr = $this->getXmlAttribute( $this->xmlNode, self::ALLOWVIDEO_ATTR_NAME ); + + return !( isset( $attr ) && strtolower( $attr ) === 'false' ); + } + + /* + * @return bool + */ + protected function allowAudio() { + $attr = $this->getXmlAttribute( $this->xmlNode, self::ALLOWAUDIO_ATTR_NAME ); + + return !( isset( $attr ) && strtolower( $attr ) === 'false' ); + } +} diff --git a/includes/services/Parser/Nodes/NodeVideo.php b/includes/services/Parser/Nodes/NodeVideo.php new file mode 100644 index 0000000..126a826 --- /dev/null +++ b/includes/services/Parser/Nodes/NodeVideo.php @@ -0,0 +1,25 @@ +renderHeader( $data ); break; + case 'media': + case 'audio': case 'image': - $result = $this->renderImage( $data ); + case 'video': + $result = $this->renderMedia( $data ); break; case 'title': $result = $this->renderTitle( $data ); @@ -104,7 +107,7 @@ class PortableInfoboxRenderService { * @return string - group HTML markup */ protected function renderGroup( $groupData ) { - $cssClasses = [ ]; + $cssClasses = []; $groupHTMLContent = ''; $children = $groupData['value']; $layout = $groupData['layout']; @@ -140,15 +143,13 @@ class PortableInfoboxRenderService { * @param $data * @return string */ - protected function renderImage( $data ) { + protected function renderMedia( $data ) { $helper = $this->getImageHelper(); - $data = $this->filterImageData( $data ); - $images = [ ]; + $images = []; foreach ( $data as $dataItem ) { $extendedItem = $dataItem; - $extendedItem['context'] = null; $extendedItem = $helper->extendImageData( $extendedItem, $this->imagesWidth, $this->infoboxWidth ); if ( !!$extendedItem ) { @@ -162,11 +163,11 @@ class PortableInfoboxRenderService { if ( count( $images ) === 1 ) { $data = $images[0]; - $templateName = 'image'; + $templateName = 'media'; } else { // More than one image means image collection $data = $helper->extendImageCollectionData( $images ); - $templateName = 'image-collection'; + $templateName = 'media-collection'; } return $this->render( $templateName, $data ); @@ -196,22 +197,6 @@ class PortableInfoboxRenderService { return $result; } - private function filterImageData( $data ) { - $dataWithCaption = array_filter($data, function( $item ) { - return !empty( $item['caption'] ); - }); - - $result = []; - - if ( !empty( $dataWithCaption ) ) { - $result = $dataWithCaption; - } elseif ( !empty( $data ) ) { - $result = [ $data[0] ]; - } - - return $result; - } - private function getInlineStyles( $accentColor, $accentColorText ) { $backgroundColor = empty( $accentColor ) ? '' : "background-color:{$accentColor};"; $color = empty( $accentColorText ) ? '' : "color:{$accentColorText};"; @@ -221,8 +206,8 @@ class PortableInfoboxRenderService { private function createHorizontalGroupData( $groupData ) { $horizontalGroupData = [ - 'labels' => [ ], - 'values' => [ ], + 'labels' => [], + 'values' => [], 'renderLabels' => false ]; @@ -246,9 +231,9 @@ class PortableInfoboxRenderService { } private function createSmartGroups( $groupData, $rowCapacity ) { - $result = [ ]; + $result = []; $rowSpan = 0; - $rowItems = [ ]; + $rowItems = []; foreach ( $groupData as $item ) { $data = $item['data']; @@ -258,7 +243,7 @@ class PortableInfoboxRenderService { if ( !empty( $rowItems ) && $rowSpan + $data['span'] > $rowCapacity ) { $result[] = $this->createSmartGroupItem( $rowItems, $rowSpan ); $rowSpan = 0; - $rowItems = [ ]; + $rowItems = []; } $rowSpan += $data['span']; $rowItems[] = $item; @@ -298,6 +283,6 @@ class PortableInfoboxRenderService { $result['values'][] = [ 'value' => $item['data']['value'], 'inlineStyles' => $styles ]; return $result; - }, [ 'labels' => [ ], 'values' => [ ], 'renderLabels' => false ] ); + }, [ 'labels' => [], 'values' => [], 'renderLabels' => false ] ); } } diff --git a/resources/PortableInfobox.js b/resources/PortableInfobox.js index 5b84ba2..6addb20 100644 --- a/resources/PortableInfobox.js +++ b/resources/PortableInfobox.js @@ -1,14 +1,14 @@ (function (window, $) { 'use strict'; - var ImageCollection = { + var MediaCollection = { init: function($content) { - var $imageCollections = $content.find('.pi-image-collection'); + var $mediaCollections = $content.find('.pi-media-collection'); - $imageCollections.each( function( index ) { - var $collection = $imageCollections.eq(index), - $tabs = $collection.find('ul.pi-image-collection-tabs li'), - $tabContent = $collection.find('.pi-image-collection-tab-content'); + $mediaCollections.each( function( index ) { + var $collection = $mediaCollections.eq(index), + $tabs = $collection.find('ul.pi-media-collection-tabs li'), + $tabContent = $collection.find('.pi-media-collection-tab-content'); $tabs.click( function() { var $target = $(this), @@ -43,7 +43,7 @@ }; mw.hook('wikipage.content').add(function($content) { - ImageCollection.init($content); + MediaCollection.init($content); CollapsibleGroup.init($content); }); })(window, jQuery); diff --git a/resources/PortableInfobox.less b/resources/PortableInfobox.less index 9e854b8..1e168da 100644 --- a/resources/PortableInfobox.less +++ b/resources/PortableInfobox.less @@ -44,7 +44,7 @@ font-weight: bold; line-height: 1.3; } - .pi-image { + .pi-media { margin: 0; max-width: 100%; text-align: center; @@ -71,54 +71,55 @@ } } .pi { - &-image { - &-thumbnail { - max-width: var(--pi-width); - height: auto; + &-media-collection { + overflow: hidden; + margin: 0; + padding-top: 1px; + & &-tabs { + list-style: none; + margin: 0 -1px; + padding: 1px 0; + text-align: center; } - &-collection { - overflow: hidden; - margin: 0; - padding-top: 1px; - & &-tabs { - list-style: none; - margin: 0 -1px; - padding: 1px 0; - text-align: center; + &-tabs { + li { + border: 1px solid var(--pi-secondary-background); + box-sizing: border-box; + cursor: pointer; + display: inline-block; + margin: -1px -1px 0 0; + max-width: 50%; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: bottom; + white-space: nowrap; } - &-tabs { - li { - border: 1px solid var(--pi-secondary-background); - box-sizing: border-box; - cursor: pointer; - display: inline-block; - margin: -1px -1px 0 0; - max-width: 50%; - overflow: hidden; - text-overflow: ellipsis; - vertical-align: bottom; - white-space: nowrap; - } - li.current { - background: var(--pi-secondary-background); - font-weight: bold; - } - } - &-tab-content { - display: none; - .video-thumbnail { - display: block; - } - &.current { - display: inherit; - } + li.current { + background: var(--pi-secondary-background); + font-weight: bold; } } + &-tab-content { + display: none; + &.current { + display: inherit; + } + } + } + &-image-thumbnail { + max-width: var(--pi-width); + height: auto; } &-video-player { width: var(--pi-width); height: auto; } + &-audio-player { + width: var(--pi-width); + &::-webkit-media-controls-enclosure { + border-radius: 0; + } + } &-caption { -webkit-hyphens: auto; -moz-hyphens: auto; diff --git a/resources/spec/PortableInfobox.spec.js b/resources/spec/PortableInfobox.spec.js deleted file mode 100644 index 35e7744..0000000 --- a/resources/spec/PortableInfobox.spec.js +++ /dev/null @@ -1,52 +0,0 @@ -/* global beforeEach, describe, it, expect, jasmine, mw, $ */ - -describe('Portable Infobox', function () { - describe('collapsible sections', function () { - var portableInfoboxHtml = ` -
-

Bugged

-
- -
-
- `, - div = document.createElement('div'); - - beforeEach(function () { - div.innerHTML = portableInfoboxHtml; - - mw.hook('wikipage.content').fire($(div)); - }); - - it('collapsed state is toggled on collapsible header click', function () { - var portableInfoboxHeader = div.querySelector('.pi-header'); - - portableInfoboxHeader.click(); - - expect(div.querySelector('.pi-collapse').className).toContain('pi-collapse-closed'); - - portableInfoboxHeader.click(); - - expect(div.querySelector('.pi-collapse').className).not.toContain('pi-collapse-closed'); - }); - - it('emit global scroll event on collapsible header click', function () { - var portableInfoboxHeader = div.querySelector('.pi-header'), - scrollSpy = jasmine.createSpy('scrollSpy'); - - $(window).on('scroll', scrollSpy); - - portableInfoboxHeader.click(); - portableInfoboxHeader.click(); - - expect(scrollSpy).toHaveBeenCalledTimes(2); - }); - }); -}); diff --git a/templates/PortableInfoboxItemImage.hbs b/templates/PortableInfoboxItemImage.hbs deleted file mode 100644 index 83074ae..0000000 --- a/templates/PortableInfoboxItemImage.hbs +++ /dev/null @@ -1,7 +0,0 @@ -
- - {{#unless isVideo}}{{alt}} - {{else}}{{/unless}} - - {{#if caption}}
{{{caption}}}
{{/if}} -
\ No newline at end of file diff --git a/templates/PortableInfoboxItemImageCollection.hbs b/templates/PortableInfoboxItemImageCollection.hbs deleted file mode 100644 index f2a027c..0000000 --- a/templates/PortableInfoboxItemImageCollection.hbs +++ /dev/null @@ -1,15 +0,0 @@ -
- - {{#each images}} -
-
- - {{#unless isVideo}}{{alt}} - {{else}}{{/unless}} - -
-
- {{/each}} -
\ No newline at end of file diff --git a/templates/PortableInfoboxItemMedia.hbs b/templates/PortableInfoboxItemMedia.hbs new file mode 100644 index 0000000..b1b7474 --- /dev/null +++ b/templates/PortableInfoboxItemMedia.hbs @@ -0,0 +1,9 @@ +
+ + {{#if isImage}}{{alt}} + {{else}}{{#if isVideo}} + {{else}}{{#if isAudio}} + {{else}}{{alt}}{{/if}}{{/if}}{{/if}} + + {{#if caption}}
{{{caption}}}
{{/if}} +
\ No newline at end of file diff --git a/templates/PortableInfoboxItemMediaCollection.hbs b/templates/PortableInfoboxItemMediaCollection.hbs new file mode 100644 index 0000000..f3192e3 --- /dev/null +++ b/templates/PortableInfoboxItemMediaCollection.hbs @@ -0,0 +1,17 @@ +
+ + {{#each images}} +
+
+ + {{#if isImage}}{{alt}} + {{else}}{{#if isVideo}} + {{else}}{{#if isAudio}} + {{else}}{{alt}}{{/if}}{{/if}}{{/if}} + +
+
+ {{/each}} +
\ No newline at end of file diff --git a/tests/phpunit/PortableInfoboxRenderServiceTest.php b/tests/phpunit/PortableInfoboxRenderServiceTest.php index 251d587..a4d437c 100644 --- a/tests/phpunit/PortableInfoboxRenderServiceTest.php +++ b/tests/phpunit/PortableInfoboxRenderServiceTest.php @@ -22,170 +22,6 @@ class PortableInfoboxRenderServiceTest extends MediaWikiTestCase { return $DOM->saveXML(); } - /** - * @param $data - * @param $expected - * - * @dataProvider filterImagesDataProvider - */ - public function testFilterImages( $data, $expected ) { - $method = ( new ReflectionClass( 'PortableInfoboxRenderService' ) )->getMethod( 'filterImageData' ); - $method->setAccessible( true ); - - $renderService = new PortableInfoboxRenderService(); - - $this->assertEquals( $expected, $method->invokeArgs( $renderService, [ $data ] ) ); - } - - public function filterImagesDataProvider() { - return [ - [ - 'data' => [ - [ - 'url' => 'some.url.com', - 'name' => 'name1', - 'key' => 'key1', - 'alt' => 'alt1', - 'caption' => 'caption1', - 'isVideo' => false, - ], - [ - 'url' => 'some.url.com', - 'name' => 'name2', - 'key' => 'key2', - 'alt' => 'alt2', - 'caption' => 'caption2', - 'isVideo' => false, - ], - ], - 'expected' => [ - [ - 'url' => 'some.url.com', - 'name' => 'name1', - 'key' => 'key1', - 'alt' => 'alt1', - 'caption' => 'caption1', - 'isVideo' => false, - ], - [ - 'url' => 'some.url.com', - 'name' => 'name2', - 'key' => 'key2', - 'alt' => 'alt2', - 'caption' => 'caption2', - 'isVideo' => false, - ], - ] - ], - [ - 'data' => [ - [ - 'url' => 'some.url.com', - 'name' => 'name1', - 'key' => 'key1', - 'alt' => 'alt1', - 'caption' => '', - 'isVideo' => false, - ], - [ - 'url' => 'some.url.com', - 'name' => 'name2', - 'key' => 'key2', - 'alt' => 'alt2', - 'caption' => 'caption2', - 'isVideo' => false, - ], - [ - 'url' => 'some.url.com', - 'name' => 'name3', - 'key' => 'key3', - 'alt' => 'alt3', - 'caption' => 'caption3', - 'isVideo' => false, - ], - ], - 'expected' => [ - 1 => [ - 'url' => 'some.url.com', - 'name' => 'name2', - 'key' => 'key2', - 'alt' => 'alt2', - 'caption' => 'caption2', - 'isVideo' => false, - ], - 2 => [ - 'url' => 'some.url.com', - 'name' => 'name3', - 'key' => 'key3', - 'alt' => 'alt3', - 'caption' => 'caption3', - 'isVideo' => false, - ] - ] - ], - [ - 'data' => [ - [ - 'url' => 'some.url.com', - 'name' => 'name1', - 'key' => 'key1', - 'alt' => 'alt1', - 'caption' => 'caption1', - 'isVideo' => false, - ], - [ - 'url' => 'some.url.com', - 'name' => 'name2', - 'key' => 'key2', - 'alt' => 'alt2', - 'caption' => '', - 'isVideo' => false, - ], - ], - 'expected' => [ - [ - 'url' => 'some.url.com', - 'name' => 'name1', - 'key' => 'key1', - 'alt' => 'alt1', - 'caption' => 'caption1', - 'isVideo' => false, - ], - ], - ], - [ - 'data' => [ - [ - 'url' => 'some.url.com', - 'name' => 'name1', - 'key' => 'key1', - 'alt' => 'alt1', - 'caption' => '', - 'isVideo' => false, - ], - [ - 'url' => 'some.url.com', - 'name' => 'name2', - 'key' => 'key2', - 'alt' => 'alt2', - 'caption' => '', - 'isVideo' => false, - ], - ], - 'expected' => [ - [ - 'url' => 'some.url.com', - 'name' => 'name1', - 'key' => 'key1', - 'alt' => 'alt1', - 'caption' => '', - 'isVideo' => false, - ], - ] - ], - ]; - } - /** * @param $input * @param $expectedOutput @@ -263,16 +99,13 @@ class PortableInfoboxRenderServiceTest extends MediaWikiTestCase { [ 'alt' => 'image alt', 'url' => 'http://image.jpg', - 'name' => 'image', - 'key' => 'image', - 'caption' => 'Lorem ipsum dolor', - 'isVideo' => false + 'caption' => 'Lorem ipsum dolor' ] ] ] ], 'output' => '