mirror of
https://github.com/Universal-Omega/PortableInfobox.git
synced 2024-11-15 11:59:56 +00:00
Add audio support; NodeImage -> NodeMedia (#11)
This commit is contained in:
commit
626710caf9
|
@ -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",
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
|
|
25
includes/services/Parser/Nodes/NodeAudio.php
Normal file
25
includes/services/Parser/Nodes/NodeAudio.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
namespace PortableInfobox\Parser\Nodes;
|
||||
|
||||
class NodeAudio extends NodeMedia {
|
||||
/*
|
||||
* @return bool
|
||||
*/
|
||||
protected function allowImage() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* @return bool
|
||||
*/
|
||||
protected function allowVideo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* @return bool
|
||||
*/
|
||||
protected function allowAudio() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,209 +1,18 @@
|
|||
<?php
|
||||
namespace PortableInfobox\Parser\Nodes;
|
||||
|
||||
use PortableInfobox\Helpers\FileNamespaceSanitizeHelper;
|
||||
use PortableInfobox\Helpers\HtmlHelper;
|
||||
use PortableInfobox\Helpers\PortableInfoboxDataBag;
|
||||
use PortableInfobox\Sanitizers\SanitizerBuilder;
|
||||
|
||||
class NodeImage extends Node {
|
||||
const GALLERY = 'GALLERY';
|
||||
const TABBER = 'TABBER';
|
||||
|
||||
const ALT_TAG_NAME = 'alt';
|
||||
const CAPTION_TAG_NAME = 'caption';
|
||||
|
||||
public static function getMarkers( $value, $ext ) {
|
||||
if ( preg_match_all('/' . \Parser::MARKER_PREFIX . '-' . $ext . '-[A-F0-9]{8}' . \Parser::MARKER_SUFFIX . '/is', $value, $out ) ) {
|
||||
return $out[0];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public static function getGalleryData( $marker ) {
|
||||
$gallery = PortableInfoboxDataBag::getInstance()->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;
|
||||
}
|
||||
}
|
||||
}
|
252
includes/services/Parser/Nodes/NodeMedia.php
Normal file
252
includes/services/Parser/Nodes/NodeMedia.php
Normal file
|
@ -0,0 +1,252 @@
|
|||
<?php
|
||||
namespace PortableInfobox\Parser\Nodes;
|
||||
|
||||
use PortableInfobox\Helpers\FileNamespaceSanitizeHelper;
|
||||
use PortableInfobox\Helpers\HtmlHelper;
|
||||
use PortableInfobox\Helpers\PortableInfoboxDataBag;
|
||||
use PortableInfobox\Sanitizers\SanitizerBuilder;
|
||||
|
||||
class NodeMedia extends Node {
|
||||
const GALLERY = 'GALLERY';
|
||||
const TABBER = 'TABBER';
|
||||
|
||||
const ALLOWIMAGE_ATTR_NAME = 'image';
|
||||
const ALLOWVIDEO_ATTR_NAME = 'video';
|
||||
const ALLOWAUDIO_ATTR_NAME = 'audio';
|
||||
|
||||
const ALT_TAG_NAME = 'alt';
|
||||
const CAPTION_TAG_NAME = 'caption';
|
||||
|
||||
public static function getMarkers( $value, $ext ) {
|
||||
if ( preg_match_all('/' . \Parser::MARKER_PREFIX . '-' . $ext . '-[A-F0-9]{8}' . \Parser::MARKER_SUFFIX . '/is', $value, $out ) ) {
|
||||
return $out[0];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public static function getGalleryData( $marker ) {
|
||||
$gallery = PortableInfoboxDataBag::getInstance()->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' );
|
||||
}
|
||||
}
|
25
includes/services/Parser/Nodes/NodeVideo.php
Normal file
25
includes/services/Parser/Nodes/NodeVideo.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
namespace PortableInfobox\Parser\Nodes;
|
||||
|
||||
class NodeVideo extends NodeMedia {
|
||||
/*
|
||||
* @return bool
|
||||
*/
|
||||
protected function allowImage() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* @return bool
|
||||
*/
|
||||
protected function allowVideo() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* @return bool
|
||||
*/
|
||||
protected function allowAudio() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -82,8 +82,11 @@ class PortableInfoboxRenderService {
|
|||
case 'header':
|
||||
$result = $this->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 ] );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
/* global beforeEach, describe, it, expect, jasmine, mw, $ */
|
||||
|
||||
describe('Portable Infobox', function () {
|
||||
describe('collapsible sections', function () {
|
||||
var portableInfoboxHtml = `
|
||||
<section class="pi-item pi-group pi-border-color pi-collapse">
|
||||
<h2 class="pi-item pi-header pi-secondary-font pi-item-spacing pi-secondary-background">Bugged</h2>
|
||||
<div class="pi-item pi-data pi-item-spacing pi-border-color">
|
||||
<div class="pi-data-value pi-font">
|
||||
<figure class="article-thumb tleft show-info-icon" style="width: 250px">
|
||||
<a href="/wiki/User:PanSola" class="image image-thumbnail link-internal" title="User:PanSola">
|
||||
<img src="data:image/gif;base64,R0lGODlhAQABAIABAAAAAP///yH5BAEAAAEALAAAAAABAAEAQAICTAEAOw%3D%3D" alt="Wookieepedia" class="thumbimage lzy lzyPlcHld " data-image-key="Wookieepedia.png" data-image-name="Wookieepedia.png" data-src="https://vignette.wikia.nocookie.net/infobox/images/b/b1/Wookieepedia.png/revision/latest?cb=20160107195313" width="250" height="65" onload="if(typeof ImgLzy==='object'){ImgLzy.load(this)}">
|
||||
<noscript><img src="https://vignette.wikia.nocookie.net/infobox/images/b/b1/Wookieepedia.png/revision/latest?cb=20160107195313" alt="Wookieepedia" class="thumbimage " data-image-key="Wookieepedia.png" data-image-name="Wookieepedia.png" width="250" height="65" ></noscript></a>
|
||||
<figcaption><a href="/wiki/File:Wookieepedia.png" class="sprite info-icon"></a></figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
`,
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,7 +0,0 @@
|
|||
<figure class="pi-item pi-image{{#if isVideo}} pi-video{{/if}}">
|
||||
<a href="{{url}}" class="image image-thumbnail{{#isVideo}} video video-thumbnail{{/isVideo}}" title="{{alt}}">
|
||||
{{#unless isVideo}}<img src="{{thumbnail}}" srcset="{{thumbnail}} 1x, {{thumbnail2x}} 2x" class="pi-image-thumbnail" alt="{{alt}}" width="{{{width}}}" height="{{{height}}}"/>
|
||||
{{else}}<video src="{{url}}" class="pi-video-player" controls="true" controlsList="nodownload" preload="metadata">{{alt}}</video>{{/unless}}
|
||||
</a>
|
||||
{{#if caption}}<figcaption class="pi-item-spacing pi-caption">{{{caption}}}</figcaption>{{/if}}
|
||||
</figure>
|
|
@ -1,15 +0,0 @@
|
|||
<div class="pi-image-collection">
|
||||
<ul class="pi-image-collection-tabs">
|
||||
{{#each images}}<li class="pi-tab-link pi-item-spacing{{#if isFirst}} current{{/if}}" data-pi-tab="pi-tab-{{ref}}">{{{caption}}}</li>{{/each}}
|
||||
</ul>
|
||||
{{#each images}}
|
||||
<div class="pi-image-collection-tab-content{{#if isFirst}} current{{/if}}" id="pi-tab-{{ref}}">
|
||||
<figure class="pi-item pi-image{{#if isVideo}} pi-video{{/if}}">
|
||||
<a href="{{url}}" class="image image-thumbnail{{#isVideo}} video video-thumbnail{{/isVideo}}" title="{{alt}}">
|
||||
{{#unless isVideo}}<img src="{{thumbnail}}" srcset="{{thumbnail}} 1x, {{thumbnail2x}} 2x" class="pi-image-thumbnail" alt="{{alt}}" width="{{{width}}}" height="{{{height}}}"/>
|
||||
{{else}}<video src="{{url}}" class="pi-video-player" controls="true" controlsList="nodownload" preload="metadata">{{alt}}</video>{{/unless}}
|
||||
</a>
|
||||
</figure>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
9
templates/PortableInfoboxItemMedia.hbs
Normal file
9
templates/PortableInfoboxItemMedia.hbs
Normal file
|
@ -0,0 +1,9 @@
|
|||
<figure class="pi-item pi-media{{#if isImage}} pi-image{{/if}}{{#if isVideo}} pi-video{{/if}}{{#if isAudio}} pi-audio{{/if}}">
|
||||
<a href="{{url}}" class="{{#if isImage}}image image-thumbnail{{/if}}{{#if isVideo}}video{{/if}}{{#if isAudio}}audio{{/if}}" title="{{alt}}">
|
||||
{{#if isImage}}<img src="{{thumbnail}}" srcset="{{thumbnail}} 1x, {{thumbnail2x}} 2x" class="pi-image-thumbnail" alt="{{alt}}" width="{{{width}}}" height="{{{height}}}"/>
|
||||
{{else}}{{#if isVideo}}<video src="{{url}}" class="pi-video-player" controls="true" controlsList="nodownload" preload="metadata">{{alt}}</video>
|
||||
{{else}}{{#if isAudio}}<audio src="{{url}}" class="pi-audio-player" controls="true" controlsList="nodownload">{{alt}}</audio>
|
||||
{{else}}{{alt}}{{/if}}{{/if}}{{/if}}
|
||||
</a>
|
||||
{{#if caption}}<figcaption class="pi-item-spacing pi-caption">{{{caption}}}</figcaption>{{/if}}
|
||||
</figure>
|
17
templates/PortableInfoboxItemMediaCollection.hbs
Normal file
17
templates/PortableInfoboxItemMediaCollection.hbs
Normal file
|
@ -0,0 +1,17 @@
|
|||
<div class="pi-media-collection">
|
||||
<ul class="pi-media-collection-tabs">
|
||||
{{#each images}}<li class="pi-tab-link pi-item-spacing{{#if isFirst}} current{{/if}}" data-pi-tab="pi-tab-{{ref}}">{{{caption}}}</li>{{/each}}
|
||||
</ul>
|
||||
{{#each images}}
|
||||
<div class="pi-media-collection-tab-content{{#if isFirst}} current{{/if}}" id="pi-tab-{{ref}}">
|
||||
<figure class="pi-item pi-media{{#if isImage}} pi-image{{/if}}{{#if isVideo}} pi-video{{/if}}{{#if isAudio}} pi-audio{{/if}}">
|
||||
<a href="{{url}}" class="{{#if isImage}}image image-thumbnail{{/if}}{{#if isVideo}}video{{/if}}{{#if isAudio}}audio{{/if}}" title="{{alt}}">
|
||||
{{#if isImage}}<img src="{{thumbnail}}" srcset="{{thumbnail}} 1x, {{thumbnail2x}} 2x" class="pi-image-thumbnail" alt="{{alt}}" width="{{{width}}}" height="{{{height}}}"/>
|
||||
{{else}}{{#if isVideo}}<video src="{{url}}" class="pi-video-player" controls="true" controlsList="nodownload" preload="metadata">{{alt}}</video>
|
||||
{{else}}{{#if isAudio}}<audio src="{{url}}" class="pi-audio-player" controls="true" controlsList="nodownload">{{alt}}</audio>
|
||||
{{else}}{{alt}}{{/if}}{{/if}}{{/if}}
|
||||
</a>
|
||||
</figure>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
|
@ -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' => '<aside class="portable-infobox pi-background">
|
||||
<figure class="pi-item pi-image">
|
||||
<figure class="pi-item pi-media pi-image">
|
||||
<a href="http://image.jpg" class="image image-thumbnail" title="image alt">
|
||||
<img src="http://thumbnail.jpg" srcset="http://thumbnail.jpg 1x, http://thumbnail2x.jpg 2x" class="pi-image-thumbnail" alt="image alt"
|
||||
width="400" height="200"/>
|
||||
|
@ -286,14 +119,11 @@ class PortableInfoboxRenderServiceTest extends MediaWikiTestCase {
|
|||
'alt' => 'image alt',
|
||||
'url' => 'http://image.jpg',
|
||||
'caption' => 'Lorem ipsum dolor',
|
||||
'name' => 'image',
|
||||
'key' => 'image',
|
||||
'width' => '400',
|
||||
'height' => '200',
|
||||
'thumbnail' => 'http://thumbnail.jpg',
|
||||
'thumbnail2x' => 'http://thumbnail2x.jpg',
|
||||
'media-type' => 'image',
|
||||
'isVideo' => false
|
||||
'isImage' => true
|
||||
]
|
||||
],
|
||||
'accentColor' => '',
|
||||
|
@ -307,19 +137,15 @@ class PortableInfoboxRenderServiceTest extends MediaWikiTestCase {
|
|||
[
|
||||
'alt' => 'image alt',
|
||||
'url' => 'http://image.jpg',
|
||||
'caption' => 'Lorem ipsum dolor',
|
||||
'isVideo' => true,
|
||||
'duration' => '1:20',
|
||||
'name' => 'test',
|
||||
'key' => 'test'
|
||||
'caption' => 'Lorem ipsum dolor'
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
'output' => '<aside class="portable-infobox pi-background">
|
||||
<figure class="pi-item pi-image pi-video">
|
||||
<figure class="pi-item pi-media pi-video">
|
||||
<a href="http://image.jpg"
|
||||
class="image image-thumbnail video video-thumbnail"
|
||||
class="video"
|
||||
title="image alt">
|
||||
<video src="http://image.jpg" class="pi-video-player" controls="true" controlsList="nodownload" preload="metadata">image alt</video>
|
||||
</a>
|
||||
|
@ -389,10 +215,7 @@ class PortableInfoboxRenderServiceTest extends MediaWikiTestCase {
|
|||
'data' => [
|
||||
[
|
||||
'alt' => 'image alt',
|
||||
'url' => 'http://image.jpg',
|
||||
'name' => 'image',
|
||||
'key' => 'image',
|
||||
'isVideo' => false
|
||||
'url' => 'http://image.jpg'
|
||||
]
|
||||
]
|
||||
],
|
||||
|
@ -406,7 +229,7 @@ class PortableInfoboxRenderServiceTest extends MediaWikiTestCase {
|
|||
],
|
||||
'output' => '<aside class="portable-infobox pi-background">
|
||||
<h2 class="pi-item pi-item-spacing pi-title">Test Title</h2>
|
||||
<figure class="pi-item pi-image">
|
||||
<figure class="pi-item pi-media pi-image">
|
||||
<a href="http://image.jpg" class="image image-thumbnail" title="image alt">
|
||||
<img src="http://thumbnail.jpg" srcset="http://thumbnail.jpg 1x, http://thumbnail2x.jpg 2x" class="pi-image-thumbnail" alt="image alt"
|
||||
width="400" height="200"/>
|
||||
|
@ -422,14 +245,11 @@ class PortableInfoboxRenderServiceTest extends MediaWikiTestCase {
|
|||
'extendImageData' => [
|
||||
'alt' => 'image alt',
|
||||
'url' => 'http://image.jpg',
|
||||
'name' => 'image',
|
||||
'key' => 'image',
|
||||
'width' => '400',
|
||||
'height' => '200',
|
||||
'thumbnail' => 'http://thumbnail.jpg',
|
||||
'thumbnail2x' => 'http://thumbnail2x.jpg',
|
||||
'media-type' => 'image',
|
||||
'isVideo' => false
|
||||
'isImage' => true
|
||||
]
|
||||
],
|
||||
'accentColor' => '',
|
||||
|
|
|
@ -100,7 +100,10 @@ class PortableInfoboxImagesHelperTest extends MediaWikiTestCase {
|
|||
'thumbnail' => null,
|
||||
'thumbnail2x' => null,
|
||||
'width' => $resultDimensions[ 'width' ],
|
||||
'height' => $resultDimensions[ 'height' ]
|
||||
'height' => $resultDimensions[ 'height' ],
|
||||
'isImage' => true,
|
||||
'isVideo' => false,
|
||||
'isAudio' => false
|
||||
];
|
||||
$thumb = $this->getMockBuilder( 'ThumbnailImage' )
|
||||
->disableOriginalConstructor()
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<?php
|
||||
|
||||
use PortableInfobox\Helpers\PortableInfoboxDataBag;
|
||||
use PortableInfobox\Parser\Nodes\NodeImage;
|
||||
use PortableInfobox\Parser\Nodes\NodeMedia;
|
||||
|
||||
/**
|
||||
* @group PortableInfobox
|
||||
* @covers PortableInfobox\Parser\Nodes\NodeImage
|
||||
* @covers PortableInfobox\Parser\Nodes\NodeMedia
|
||||
*/
|
||||
class NodeImageTest extends MediaWikiTestCase {
|
||||
class NodeMediaTest extends MediaWikiTestCase {
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
@ -17,13 +17,13 @@ class NodeImageTest extends MediaWikiTestCase {
|
|||
}
|
||||
|
||||
/**
|
||||
* @covers PortableInfobox\Parser\Nodes\NodeImage::getGalleryData
|
||||
* @covers PortableInfobox\Parser\Nodes\NodeMedia::getGalleryData
|
||||
* @dataProvider galleryDataProvider
|
||||
* @param $marker
|
||||
* @param $expected
|
||||
*/
|
||||
public function testGalleryData( $marker, $expected ) {
|
||||
$this->assertEquals( $expected, NodeImage::getGalleryData( $marker ) );
|
||||
$this->assertEquals( $expected, NodeMedia::getGalleryData( $marker ) );
|
||||
}
|
||||
|
||||
public function galleryDataProvider() {
|
||||
|
@ -83,7 +83,7 @@ class NodeImageTest extends MediaWikiTestCase {
|
|||
}
|
||||
|
||||
/**
|
||||
* @covers PortableInfobox\Parser\Nodes\NodeImage::getTabberData
|
||||
* @covers PortableInfobox\Parser\Nodes\NodeMedia::getTabberData
|
||||
*/
|
||||
public function testTabberData() {
|
||||
$input = '<div class="tabber"><div class="tabbertab" title="_title_"><p><a><img src="_src_"></a></p></div></div>';
|
||||
|
@ -93,18 +93,18 @@ class NodeImageTest extends MediaWikiTestCase {
|
|||
'title' => '_src_',
|
||||
]
|
||||
];
|
||||
$this->assertEquals( $expected, NodeImage::getTabberData( $input ) );
|
||||
$this->assertEquals( $expected, NodeMedia::getTabberData( $input ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers PortableInfobox\Parser\Nodes\NodeImage::getMarkers
|
||||
* @covers PortableInfobox\Parser\Nodes\NodeMedia::getMarkers
|
||||
* @dataProvider markersProvider
|
||||
* @param $ext
|
||||
* @param $value
|
||||
* @param $expected
|
||||
*/
|
||||
public function testMarkers( $ext, $value, $expected ) {
|
||||
$this->assertEquals( $expected, PortableInfobox\Parser\Nodes\NodeImage::getMarkers( $value, $ext ) );
|
||||
$this->assertEquals( $expected, PortableInfobox\Parser\Nodes\NodeMedia::getMarkers( $value, $ext ) );
|
||||
}
|
||||
|
||||
public function markersProvider() {
|
||||
|
@ -129,7 +129,7 @@ class NodeImageTest extends MediaWikiTestCase {
|
|||
|
||||
|
||||
/**
|
||||
* @covers PortableInfobox\Parser\Nodes\NodeImage::getData
|
||||
* @covers PortableInfobox\Parser\Nodes\NodeMedia::getData
|
||||
* @dataProvider dataProvider
|
||||
*
|
||||
* @param $markup
|
||||
|
@ -137,44 +137,50 @@ class NodeImageTest extends MediaWikiTestCase {
|
|||
* @param $expected
|
||||
*/
|
||||
public function testData( $markup, $params, $expected ) {
|
||||
$node = PortableInfobox\Parser\Nodes\NodeFactory::newFromXML( $markup, $params );
|
||||
$imageMock = empty( $params ) ? NULL : new ImageMock();
|
||||
$xmlObj = PortableInfobox\Parser\XmlParser::parseXmlString( $markup );
|
||||
|
||||
$this->assertEquals( $expected, $node->getData() );
|
||||
$mock = $this->getMock(NodeMedia::class, [ 'getFilefromTitle' ], [ $xmlObj, $params ]);
|
||||
$mock->expects( $this->any( ))
|
||||
->method( 'getFilefromTitle' )
|
||||
->willReturn( $imageMock );
|
||||
|
||||
$this->assertEquals( $expected, $mock->getData() );
|
||||
}
|
||||
|
||||
public function dataProvider() {
|
||||
// markup, params, expected
|
||||
return [
|
||||
[
|
||||
'<image source="img"></image>',
|
||||
'<media source="img"></media>',
|
||||
[ ],
|
||||
[ [ 'url' => '', 'name' => '', 'key' => '', 'alt' => null, 'caption' => null, 'isVideo' => false ] ]
|
||||
[ [ ] ]
|
||||
],
|
||||
[
|
||||
'<image source="img"></image>',
|
||||
'<media source="img"></media>',
|
||||
[ 'img' => 'test.jpg' ],
|
||||
[ [ 'url' => '', 'name' => 'Test.jpg', 'key' => 'Test.jpg', 'alt' => 'Test.jpg', 'caption' => null, 'isVideo' => false ] ]
|
||||
[ [ 'url' => '', 'name' => 'Test.jpg', 'alt' => 'Test.jpg', 'caption' => null ] ]
|
||||
],
|
||||
[
|
||||
'<image source="img"><alt><default>test alt</default></alt></image>',
|
||||
'<media source="img"><alt><default>test alt</default></alt></media>',
|
||||
[ 'img' => 'test.jpg' ],
|
||||
[ [ 'url' => '', 'name' => 'Test.jpg', 'key' => 'Test.jpg', 'alt' => 'test alt', 'caption' => null, 'isVideo' => false ] ]
|
||||
[ [ 'url' => '', 'name' => 'Test.jpg', 'alt' => 'test alt', 'caption' => null ] ]
|
||||
],
|
||||
[
|
||||
'<image source="img"><alt source="alt source"><default>test alt</default></alt></image>',
|
||||
'<media source="img"><alt source="alt source"><default>test alt</default></alt></media>',
|
||||
[ 'img' => 'test.jpg', 'alt source' => 2 ],
|
||||
[ [ 'url' => '', 'name' => 'Test.jpg', 'key' => 'Test.jpg', 'alt' => 2, 'caption' => null, 'isVideo' => false ] ]
|
||||
[ [ 'url' => '', 'name' => 'Test.jpg', 'alt' => 2, 'caption' => null ] ]
|
||||
],
|
||||
[
|
||||
'<image source="img"><alt><default>test alt</default></alt><caption source="img"/></image>',
|
||||
'<media source="img"><alt><default>test alt</default></alt><caption source="img"/></media>',
|
||||
[ 'img' => 'test.jpg' ],
|
||||
[ [ 'url' => '', 'name' => 'Test.jpg', 'key' => 'Test.jpg', 'alt' => 'test alt', 'caption' => 'test.jpg', 'isVideo' => false ] ]
|
||||
[ [ 'url' => '', 'name' => 'Test.jpg', 'alt' => 'test alt', 'caption' => 'test.jpg' ] ]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers PortableInfobox\Parser\Nodes\NodeImage::isEmpty
|
||||
* @covers PortableInfobox\Parser\Nodes\NodeMedia::isEmpty
|
||||
* @dataProvider isEmptyProvider
|
||||
*
|
||||
* @param $markup
|
||||
|
@ -189,12 +195,12 @@ class NodeImageTest extends MediaWikiTestCase {
|
|||
|
||||
public function isEmptyProvider() {
|
||||
return [
|
||||
[ '<image></image>', [ ], true ],
|
||||
[ '<media></media>', [ ], true ],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers PortableInfobox\Parser\Nodes\NodeImage::getSources
|
||||
* @covers PortableInfobox\Parser\Nodes\NodeMedia::getSources
|
||||
* @dataProvider sourcesProvider
|
||||
*
|
||||
* @param $markup
|
||||
|
@ -209,22 +215,22 @@ class NodeImageTest extends MediaWikiTestCase {
|
|||
public function sourcesProvider() {
|
||||
return [
|
||||
[
|
||||
'<image source="img"/>',
|
||||
'<media source="img"/>',
|
||||
[ 'img' ]
|
||||
],
|
||||
[
|
||||
'<image source="img"><default>{{{img}}}</default><alt source="img" /></image>',
|
||||
'<media source="img"><default>{{{img}}}</default><alt source="img" /></media>',
|
||||
[ 'img' ]
|
||||
],
|
||||
[
|
||||
'<image source="img"><alt source="alt"/><caption source="cap"/></image>',
|
||||
'<media source="img"><alt source="alt"/><caption source="cap"/></media>',
|
||||
[ 'img', 'alt', 'cap' ]
|
||||
],
|
||||
[
|
||||
'<image source="img"><alt source="alt"><default>{{{def}}}</default></alt><caption source="cap"/></image>',
|
||||
'<media source="img"><alt source="alt"><default>{{{def}}}</default></alt><caption source="cap"/></media>',
|
||||
[ 'img', 'alt', 'def', 'cap' ] ],
|
||||
[
|
||||
'<image/>',
|
||||
'<media/>',
|
||||
[ ]
|
||||
],
|
||||
[
|
||||
|
@ -244,8 +250,8 @@ class NodeImageTest extends MediaWikiTestCase {
|
|||
public function metadataProvider() {
|
||||
return [
|
||||
[
|
||||
'<image source="img"><caption source="cap"><format>Test {{{cap}}} and {{{fcap}}}</format></caption></image>',
|
||||
[ 'type' => 'image', 'sources' => [
|
||||
'<media source="img"><caption source="cap"><format>Test {{{cap}}} and {{{fcap}}}</format></caption></media>',
|
||||
[ 'type' => 'media', 'sources' => [
|
||||
'img' => [ 'label' => '', 'primary' => true ],
|
||||
'cap' => [ 'label' => '' ],
|
||||
'fcap' => [ 'label' => '' ]
|
||||
|
@ -262,13 +268,13 @@ class NodeImageTest extends MediaWikiTestCase {
|
|||
* @throws PortableInfobox\Parser\XmlMarkupParseErrorException
|
||||
*/
|
||||
public function testVideo( $markup, $params, $expected ) {
|
||||
$fileMock = new FileMock();
|
||||
$videoMock = new VideoMock();
|
||||
$xmlObj = PortableInfobox\Parser\XmlParser::parseXmlString( $markup );
|
||||
|
||||
$mock = $this->getMock(NodeImage::class, [ 'getFilefromTitle' ], [ $xmlObj, $params ]);
|
||||
$mock = $this->getMock(NodeMedia::class, [ 'getFilefromTitle' ], [ $xmlObj, $params ]);
|
||||
$mock->expects( $this->any( ))
|
||||
->method( 'getFilefromTitle' )
|
||||
->willReturn( $fileMock );
|
||||
->willReturn( $videoMock );
|
||||
|
||||
$this->assertEquals( $expected, $mock->getData() );
|
||||
}
|
||||
|
@ -276,26 +282,90 @@ class NodeImageTest extends MediaWikiTestCase {
|
|||
public function videoProvider() {
|
||||
return [
|
||||
[
|
||||
'<image source="img" />',
|
||||
[ 'img' => 'test.jpg' ],
|
||||
'<media source="media" />',
|
||||
[ 'media' => 'test.webm' ],
|
||||
[
|
||||
[
|
||||
'url' => 'http://test.url',
|
||||
'name' => 'Test.jpg',
|
||||
'key' => 'Test.jpg',
|
||||
'alt' => 'Test.jpg',
|
||||
'caption' => null,
|
||||
'isVideo' => true
|
||||
'name' => 'Test.webm',
|
||||
'alt' => 'Test.webm',
|
||||
'caption' => null
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'<media source="media" video="false" />',
|
||||
[ 'media' => 'test.webm' ],
|
||||
[ [ ] ]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider audioProvider
|
||||
* @param $markup
|
||||
* @param $params
|
||||
* @param $expected
|
||||
* @throws PortableInfobox\Parser\XmlMarkupParseErrorException
|
||||
*/
|
||||
public function testAudio( $markup, $params, $expected ) {
|
||||
$audioMock = new AudioMock();
|
||||
$xmlObj = PortableInfobox\Parser\XmlParser::parseXmlString( $markup );
|
||||
|
||||
$mock = $this->getMock(NodeMedia::class, [ 'getFilefromTitle' ], [ $xmlObj, $params ]);
|
||||
$mock->expects( $this->any( ))
|
||||
->method( 'getFilefromTitle' )
|
||||
->willReturn( $audioMock );
|
||||
|
||||
$this->assertEquals( $expected, $mock->getData() );
|
||||
}
|
||||
|
||||
public function audioProvider() {
|
||||
return [
|
||||
[
|
||||
'<media source="media" />',
|
||||
[ 'media' => 'test.ogg' ],
|
||||
[
|
||||
[
|
||||
'url' => 'http://test.url',
|
||||
'name' => 'Test.ogg',
|
||||
'alt' => 'Test.ogg',
|
||||
'caption' => null
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'<media source="media" audio="false" />',
|
||||
[ 'media' => 'test.ogg' ],
|
||||
[ [ ] ]
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class FileMock {
|
||||
class ImageMock {
|
||||
public function getMediaType() {
|
||||
return "VIDEO";
|
||||
return MEDIATYPE_BITMAP;
|
||||
}
|
||||
|
||||
public function getUrl() {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
class VideoMock {
|
||||
public function getMediaType() {
|
||||
return MEDIATYPE_VIDEO;
|
||||
}
|
||||
|
||||
public function getUrl() {
|
||||
return 'http://test.url';
|
||||
}
|
||||
}
|
||||
|
||||
class AudioMock {
|
||||
public function getMediaType() {
|
||||
return MEDIATYPE_AUDIO;
|
||||
}
|
||||
|
||||
public function getUrl() {
|
||||
|
@ -305,7 +375,7 @@ class FileMock {
|
|||
|
||||
class GalleryMock {
|
||||
private $images;
|
||||
public function __construct( Array $images = [] ) {
|
||||
public function __construct( array $images = [] ) {
|
||||
$this->images = $images;
|
||||
}
|
||||
|
Loading…
Reference in a new issue