asset tests

This commit is contained in:
Etienne Roudeix
2013-11-25 10:03:48 +01:00
parent 0cc48ecdb7
commit 2bc1bbed64
25 changed files with 256 additions and 115 deletions

View File

@@ -56,7 +56,7 @@ class HttpException extends BaseAction implements EventSubscriberInterface
$parser = $this->container->get("thelia.parser");
// Define the template thant shoud be used
$parser->setTemplate(TemplateHelper::getInstance()->getActiveFrontTemplate());
$parser->setTemplateDefinition(TemplateHelper::getInstance()->getActiveFrontTemplate());
//$event->getRequest()->attributes->set('_view', ConfigQuery::getPageNotFoundView());

View File

@@ -201,7 +201,7 @@ class BaseAdminController extends BaseController
$parser = $this->container->get("thelia.parser");
// Define the template that should be used
$parser->setTemplate($template ?: TemplateHelper::getInstance()->getActiveAdminTemplate());
$parser->setTemplateDefinition($template ?: TemplateHelper::getInstance()->getActiveAdminTemplate());
return $parser;
}

View File

@@ -97,7 +97,7 @@ class BaseFrontController extends BaseController
$parser = $this->container->get("thelia.parser");
// Define the template that should be used
$parser->setTemplate($template ?: TemplateHelper::getInstance()->getActiveFrontTemplate());
$parser->setTemplateDefinition($template ?: TemplateHelper::getInstance()->getActiveFrontTemplate());
return $parser;
}

View File

@@ -78,7 +78,7 @@ class ViewListener implements EventSubscriberInterface
{
$parser = $this->container->get('thelia.parser');
$parser->setTemplate(TemplateHelper::getInstance()->getActiveFrontTemplate());
$parser->setTemplateDefinition(TemplateHelper::getInstance()->getActiveFrontTemplate());
$request = $this->container->get('request');
try {

View File

@@ -27,27 +27,38 @@ interface AssetManagerInterface {
/**
* Prepare an asset directory.
*
* @param string $source_assets_directory the full path to the source asstes directory
* @param string $web_assets_directory_base the base directory of the web based asset directory
* @throws \RuntimeException if something goes wrong.
* @param $sourceAssetsDirectory
* @param $webAssetsDirectoryBase
* @param $webAssetsTemplate
* @param $webAssetsKey
*
* @return
* @internal param string $source_assets_directory the full path to the source asstes directory
* @internal param string $web_assets_directory_base the base directory of the web based asset directory
* @internal param string $key the assets key : module name or 0 for base template
*/
public function prepareAssets($source_assets_directory, $web_assets_directory_base);
public function prepareAssets($sourceAssetsDirectory, $webAssetsDirectoryBase, $webAssetsTemplate, $webAssetsKey);
/**
* Generates assets from $asset_path in $output_path, using $filters.
*
* @param string $asset_path the full path to the asset file (or file collection, e.g. *.less)
* @param $assetSource
* @param $webAssetsDirectoryBase
* @param $webAssetsTemplate
* @param $webAssetsKey
* @param $outputUrl
* @param $assetType
* @param array $filters a list of filters, as defined below (see switch($filter_name) ...)
*
* @param string $web_assets_directory_base the full disk path to the base assets output directory in the web space
* @param string $output_url the URL to the base assets output directory in the web space
* @param boolean $debug the debug mode, true or false
*
* @param string $asset_type the asset type: css, js, ... The generated files will have this extension. Pass an empty string to use the asset source extension.
* @param array $filters a list of filters, as defined below (see switch($filter_name) ...)
* @internal param string $asset_path the full path to the asset file (or file collection, e.g. *.less)
*
* @param boolean $debug the debug mode, true or false
* @internal param string $web_assets_directory_base the full disk path to the base assets output directory in the web space
* @internal param string $output_url the URL to the base assets output directory in the web space
*
* @throws \InvalidArgumentException if an invalid filter name is found
* @internal param string $asset_type the asset type: css, js, ... The generated files will have this extension. Pass an empty string to use the asset source extension.
* @return string The URL to the generated asset file.
*/
public function processAsset($asset_path, $web_assets_directory_base, $output_url, $asset_type, $filters, $debug);
public function processAsset($assetSource, $webAssetsDirectoryBase, $webAssetsTemplate, $webAssetsKey, $outputUrl, $assetType, $filters, $debug);
}

View File

@@ -31,7 +31,6 @@ use Assetic\AssetWriter;
use Thelia\Model\ConfigQuery;
use Thelia\Log\Tlog;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Exception\IOException;
/**
* This class is a simple helper for generating assets using Assetic.
@@ -73,7 +72,9 @@ class AsseticAssetManager implements AssetManagerInterface
/**
* Check if a file is a source asset file
*
* @param \DirectoryIterator $fileInfo
* @param \SplFileInfo $fileInfo
*
* @return bool
*/
protected function isSourceFile(\SplFileInfo $fileInfo) {
return in_array($fileInfo->getExtension(), $this->source_file_extensions);
@@ -81,8 +82,9 @@ class AsseticAssetManager implements AssetManagerInterface
/**
* Recursively copy assets from the source directory to the destination
* directory in the web space, ommiting source files.
* directory in the web space, omitting source files.
*
* @param Filesystem $fs
* @param string $from_directory the source
* @param string $to_directory the destination
* @throws \RuntimeException if a problem occurs.
@@ -121,38 +123,21 @@ class AsseticAssetManager implements AssetManagerInterface
}
}
/**
* Compite the assets path relative to the base template directory
*
* @param string $source_assets_directory the source directory
* @param string $web_assets_directory_base base directory of the web assets
* @return the full path of the destination directory
*/
protected function getRelativeDirectoryPath($source_assets_directory, $web_assets_directory_base)
{
$source_assets_directory = realpath($source_assets_directory);
// Remove base path from asset source path to get a path relative to the template base
// and use it to create the destination path.
return str_replace(
realpath(THELIA_ROOT),
'',
$source_assets_directory
);
}
/**
* Compute the destination directory path, from the source directory and the
* base directory of the web assets
*
* @param string $source_assets_directory the source directory
* @param string $web_assets_directory_base base directory of the web assets
* @param string $webAssetsDirectoryBase base directory of the web assets
* @param $webAssetsTemplate
* @param string $webAssetsKey the assests key : module name or 0 for base template
*
* @internal param string $source_assets_directory the source directory
* @return the full path of the destination directory
*/
protected function getDestinationDirectory($source_assets_directory, $web_assets_directory_base)
protected function getDestinationDirectory($webAssetsDirectoryBase, $webAssetsTemplate, $webAssetsKey)
{
// Compute the absolute path of the output directory
return $web_assets_directory_base . $this->getRelativeDirectoryPath($source_assets_directory, $web_assets_directory_base);
return $webAssetsDirectoryBase . DS . $webAssetsTemplate . DS . $webAssetsKey;
}
/**
@@ -160,14 +145,17 @@ class AsseticAssetManager implements AssetManagerInterface
* the source directory. If any change is detected, the whole asset directory
* is copied in the web space.
*
* @param string $source_assets_directory the full path to the source asstes directory
* @param string $web_assets_directory_base the base directory of the web based asset directory
* @param string $sourceAssetsDirectory the full path to the source asstes directory
* @param string $webAssetsDirectoryBase the base directory of the web based asset directory
* @param $webAssetsTemplate
* @param string $webAssetsKey the assets key : module name or 0 for base template
*
* @throws \RuntimeException if something goes wrong.
*/
public function prepareAssets($source_assets_directory, $web_assets_directory_base) {
public function prepareAssets($sourceAssetsDirectory, $webAssetsDirectoryBase, $webAssetsTemplate, $webAssetsKey) {
// Compute the absolute path of the output directory
$to_directory = $this->getDestinationDirectory($source_assets_directory, $web_assets_directory_base);
$to_directory = $this->getDestinationDirectory($webAssetsDirectoryBase, $webAssetsTemplate, $webAssetsKey);
// Get a path to the stamp file
$stamp_file_path = $to_directory . DS . '.source-stamp';
@@ -176,7 +164,7 @@ class AsseticAssetManager implements AssetManagerInterface
$prev_stamp = @file_get_contents($stamp_file_path);
// Get the current stamp of the source directory
$curr_stamp = $this->getStamp($source_assets_directory);
$curr_stamp = $this->getStamp($sourceAssetsDirectory);
if ($prev_stamp !== $curr_stamp) {
@@ -197,7 +185,7 @@ class AsseticAssetManager implements AssetManagerInterface
$fs->remove($tmp_dir);
// Copy the whole source dir in a temp directory
$this->copyAssets($fs, $source_assets_directory, $tmp_dir);
$this->copyAssets($fs, $sourceAssetsDirectory, $tmp_dir);
// Remove existing directory
if ($fs->exists($to_directory)) $fs->remove($to_directory);
@@ -284,67 +272,67 @@ class AsseticAssetManager implements AssetManagerInterface
/**
* Generates assets from $asset_path in $output_path, using $filters.
*
* @param string $asset_path the full path to the asset file (or file collection, e.g. *.less)
* @param $assetSource
* @param string $webAssetsDirectoryBase the full path to the asset file (or file collection, e.g. *.less)
*
* @param string $web_assets_directory_base the full disk path to the base assets output directory in the web space
* @param string $output_url the URL to the base assets output directory in the web space
* @param string $webAssetsTemplate the full disk path to the base assets output directory in the web space
* @param $webAssetsKey
* @param string $outputUrl the URL to the base assets output directory in the web space
*
* @param string $asset_type the asset type: css, js, ... The generated files will have this extension. Pass an empty string to use the asset source extension.
* @param array $filters a list of filters, as defined below (see switch($filter_name) ...)
* @param string $assetType the asset type: css, js, ... The generated files will have this extension. Pass an empty string to use the asset source extension.
* @param array $filters a list of filters, as defined below (see switch($filter_name) ...)
*
* @param boolean $debug true / false
*
* @param boolean $debug true / false
* @throws \InvalidArgumentException if an invalid filter name is found
* @return string The URL to the generated asset file.
*/
public function processAsset($asset_path, $web_assets_directory_base, $output_url, $asset_type, $filters, $debug)
public function processAsset($assetSource, $webAssetsDirectoryBase, $webAssetsTemplate, $webAssetsKey, $outputUrl, $assetType, $filters, $debug)
{
$asset_name = basename($asset_path);
$input_directory = realpath(dirname($asset_path));
$assetName = basename($assetSource);
$inputDirectory = realpath(dirname($assetSource));
$am = new AssetManager();
$fm = new FilterManager();
// Get the filter list
$filter_list = $this->decodeAsseticFilters($fm, $filters);
$filterList = $this->decodeAsseticFilters($fm, $filters);
// Factory setup
$factory = new AssetFactory($input_directory);
$factory = new AssetFactory($inputDirectory);
$factory->setAssetManager($am);
$factory->setFilterManager($fm);
$factory->setDefaultOutput('*' . (!empty($asset_type) ? '.' : '') . $asset_type);
$factory->setDefaultOutput('*' . (!empty($assetType) ? '.' : '') . $assetType);
$factory->setDebug($debug);
$asset = $factory->createAsset($asset_name, $filter_list);
$asset = $factory->createAsset($assetName, $filterList);
$input_directory = realpath(dirname($asset_path));
$output_directory = $this->getDestinationDirectory($input_directory, $web_assets_directory_base);
$outputDirectory = $this->getDestinationDirectory($webAssetsDirectoryBase, $webAssetsTemplate, $webAssetsKey);
// Get the URL part from the relative path
$output_relative_path = $this->getRelativeDirectoryPath($input_directory, $web_assets_directory_base);
$outputRelativePath = $webAssetsTemplate . DS . $webAssetsKey;
$output_relative_web_path = rtrim(str_replace('\\', '/', $output_relative_path), '/') . '/';
$outputRelativeWebPath = rtrim(str_replace('\\', '/', $outputRelativePath), '/') . '/';
$asset_target_filename = $asset->getTargetPath();
$assetTargetFilename = $asset->getTargetPath();
// This is the final name of the generated asset
$asset_destination_path = $output_directory . DS . $asset_target_filename;
$assetDestinationPath = $outputDirectory . DS . $assetTargetFilename;
Tlog::getInstance()->addDebug("Asset destination full path: $asset_destination_path");
Tlog::getInstance()->addDebug("Asset destination full path: $assetDestinationPath");
// We generate an asset only if it does not exists, or if the asset processing is forced in development mode
if (! file_exists($asset_destination_path) || ($this->debugMode && ConfigQuery::read('process_assets', true)) ) {
if (! file_exists($assetDestinationPath) || ($this->debugMode && ConfigQuery::read('process_assets', true)) ) {
$writer = new AssetWriter($output_directory);
$writer = new AssetWriter($outputDirectory);
Tlog::getInstance()->addDebug("Writing asset to $output_directory");
Tlog::getInstance()->addDebug("Writing asset to $outputDirectory");
$writer->writeAsset($asset);
}
return rtrim($output_url, '/') . '/' . $output_relative_web_path . $asset_target_filename;
return rtrim($outputUrl, '/') . '/' . $outputRelativeWebPath . $assetTargetFilename;
}
}

View File

@@ -24,6 +24,7 @@
namespace Thelia\Core\Template\Smarty\Assets;
use Thelia\Core\Template\Assets\AsseticHelper;
use Thelia\Core\Template\TemplateDefinition;
use Thelia\Tools\URL;
use Thelia\Core\Template\Assets\AssetManagerInterface;
@@ -51,48 +52,140 @@ class SmartyAssetsManager
$this->assetsManager = $assetsManager;
}
public function prepareAssets($assets_directory, \Smarty_Internal_Template $template) {
public function prepareAssets($assets_directory, \Smarty_Internal_Template $template)
{
$smartyParser = $template->smarty;
$templateDefinition = $smartyParser->getTemplateDefinition();
switch($templateDefinition->getType()) {
case TemplateDefinition::FRONT_OFFICE:
$frontOfficeTemplateDirectories = $smartyParser->getFrontOfficeTemplateDirectories();
if(isset($frontOfficeTemplateDirectories[$templateDefinition->getName()])) {
/* create assets foreach directory : main @ modules */
foreach($frontOfficeTemplateDirectories[$templateDefinition->getName()] as $key => $directory) {
$tpl_path = $directory . DS . $assets_directory;
$asset_dir_absolute_path = realpath($tpl_path);
if(false !== $asset_dir_absolute_path) {
$this->assetsManager->prepareAssets(
$asset_dir_absolute_path,
$this->web_root . $this->path_relative_to_web_root,
$templateDefinition->getPath(),
$key
);
}
}
}
break;
$tpl_dir = dirname($template->source->filepath);
case TemplateDefinition::BACK_OFFICE:
$backOfficeTemplateDirectories = $smartyParser->getBackOfficeTemplateDirectories();
if(isset($backOfficeTemplateDirectories[$templateDefinition->getName()])) {
/* create assets foreach directory : main @ modules */
foreach($backOfficeTemplateDirectories[$templateDefinition->getName()] as $key => $directory) {
$tpl_path = $directory . DS . $assets_directory;
$asset_dir_absolute_path = realpath($tpl_path);
if(false !== $asset_dir_absolute_path) {
$this->assetsManager->prepareAssets(
$asset_dir_absolute_path,
$this->web_root . $this->path_relative_to_web_root,
$templateDefinition->getPath(),
$key
);
}
}
}
break;
$asset_dir_absolute_path = realpath($tpl_dir . DS . $assets_directory);
case TemplateDefinition::PDF:
break;
if ($asset_dir_absolute_path === false) throw new \Exception("Failed to get real path of '".$tpl_dir . DS . $assets_directory."'");
default:
break;
}
$this->assetsManager->prepareAssets(
$asset_dir_absolute_path,
$this->web_root . $this->path_relative_to_web_root
);
// $tpl_dir = dirname($template->source->filepath);
//
// $tpl_path = $tpl_dir . DS . $assets_directory;
// $asset_dir_absolute_path = realpath($tpl_path);
// if ($asset_dir_absolute_path === false) {
// /* no assets for current template */
// $tpl_path = THELIA_TEMPLATE_DIR . DS . $template->smarty->getTemplate();
// $asset_dir_absolute_path = realpath($tpl_path);
// }
//
// if ($asset_dir_absolute_path === false) {
// throw new \Exception("Failed to get real path of '" . $tpl_path . "'");
// }
//
// $this->assetsManager->prepareAssets(
// $asset_dir_absolute_path,
// $this->web_root . $this->path_relative_to_web_root
// );
}
public function computeAssetUrl($assetType, $params, \Smarty_Internal_Template $template)
{
$file = $params['file'];
$filters = isset($params['filters']) ? $params['filters'] : '';
$debug = isset($params['debug']) ? trim(strtolower($params['debug'])) == 'true' : false;
$file = $params['file'];
// Get template base path
$tpl_path = $template->source->filepath;
/* we trick here relative thinking for file attribute */
$file = ltrim($file, '/');
while(substr($file, 0, 3) == '../') {
$file = substr($file, 3);
}
// Get basedir
$tpl_dir = dirname($tpl_path);
$assetOrigin = isset($params['origin']) ? $params['origin'] : "0";
$filters = isset($params['filters']) ? $params['filters'] : '';
$debug = isset($params['debug']) ? trim(strtolower($params['debug'])) == 'true' : false;
// Create absolute dir path
$asset_dir = realpath($tpl_dir) . DS . dirname($file);
$asset_file = basename($file);
$smartyParser = $template->smarty;
$templateDefinition = $smartyParser->getTemplateDefinition();
if ($asset_dir === false) throw new \Exception("Failed to get real path of '".$tpl_dir.'/'.dirname($file)."'");
// // Get template base path
// $tpl_path = $template->source->filepath;
//
// // Get basedir
// $tpl_dir = dirname($tpl_path);
//
// // Create absolute dir path
// $assetDir = realpath($tpl_dir) . DS . dirname($file);
// $asset_file = basename($file);
$url = $this->assetsManager->processAsset(
$asset_dir . DS . $asset_file,
$this->web_root . $this->path_relative_to_web_root,
URL::getInstance()->absoluteUrl($this->path_relative_to_web_root, null, URL::PATH_TO_FILE /* path only */),
$assetType,
$filters,
$debug
);
$assetSource = false;
switch($templateDefinition->getType()) {
case TemplateDefinition::FRONT_OFFICE:
$frontOfficeTemplateDirectories = $smartyParser->getFrontOfficeTemplateDirectories();
if(isset($frontOfficeTemplateDirectories[$templateDefinition->getName()][$assetOrigin])) {
$assetSource = $frontOfficeTemplateDirectories[$templateDefinition->getName()][$assetOrigin];
}
break;
return $url;
case TemplateDefinition::BACK_OFFICE:
$backOfficeTemplateDirectories = $smartyParser->getbackOfficeTemplateDirectories();
if(isset($backOfficeTemplateDirectories[$templateDefinition->getName()][$assetOrigin])) {
$assetSource = $backOfficeTemplateDirectories[$templateDefinition->getName()][$assetOrigin];
}
break;
case TemplateDefinition::PDF:
break;
default:
break;
}
if ($assetSource === false) throw new \Exception("Failed to get real path of '/".dirname($file)."'");
$url = $this->assetsManager->processAsset(
$assetSource . DS . $file,
$this->web_root . $this->path_relative_to_web_root,
$templateDefinition->getPath(),
$assetOrigin,
URL::getInstance()->absoluteUrl($this->path_relative_to_web_root, null, URL::PATH_TO_FILE /* path only */),
$assetType,
$filters,
$debug
);
return $url;
}
public function processSmartyPluginCall($assetType, $params, $content, \Smarty_Internal_Template $template, &$repeat)

View File

@@ -33,7 +33,10 @@ class SmartyParser extends Smarty implements ParserInterface
protected $backOfficeTemplateDirectories = array();
protected $frontOfficeTemplateDirectories = array();
protected $template = "";
/**
* @var TemplateDefinition
*/
protected $templateDefinition = "";
protected $status = 200;
@@ -90,6 +93,22 @@ class SmartyParser extends Smarty implements ParserInterface
$this->registerFilter('variable', array(__CLASS__, "theliaEscape"));
}
/**
* @return array
*/
public function getFrontOfficeTemplateDirectories()
{
return $this->frontOfficeTemplateDirectories;
}
/**
* @return array
*/
public function getBackOfficeTemplateDirectories()
{
return $this->backOfficeTemplateDirectories;
}
public function removeBlankLines($tpl_source, \Smarty_Internal_Template $template)
{
return preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $tpl_source);
@@ -104,36 +123,54 @@ class SmartyParser extends Smarty implements ParserInterface
}
}
public function addBackOfficeTemplateDirectory($templateName, $templateDirectory, $key)
public function addBackOfficeTemplateDirectory($templateName, $templateDirectory, $key, $unshift = false)
{
$this->backOfficeTemplateDirectories[$templateName][$key] = $templateDirectory;
if(true === $unshift && isset($this->backOfficeTemplateDirectories[$templateName])) {
$this->backOfficeTemplateDirectories[$templateName] = array_merge(
array(
$key => $templateDirectory,
),
$this->backOfficeTemplateDirectories[$templateName]
);
} else {
$this->backOfficeTemplateDirectories[$templateName][$key] = $templateDirectory;
}
}
public function addFrontOfficeTemplateDirectory($templateName, $templateDirectory, $key)
public function addFrontOfficeTemplateDirectory($templateName, $templateDirectory, $key, $unshift = false)
{
$this->frontOfficeTemplateDirectories[$templateName][$key] = $templateDirectory;
if(true === $unshift && isset($this->frontOfficeTemplateDirectories[$templateName])) {
$this->frontOfficeTemplateDirectories[$templateName] = array_merge(
array(
$key => $templateDirectory,
),
$this->frontOfficeTemplateDirectories[$templateName]
);
} else {
$this->frontOfficeTemplateDirectories[$templateName][$key] = $templateDirectory;
}
}
/**
* @param TemplateDefinition $templateDefinition
*/
public function setTemplate(TemplateDefinition $templateDefinition)
public function setTemplateDefinition(TemplateDefinition $templateDefinition)
{
$this->template = $templateDefinition->getPath();
$this->templateDefinition = $templateDefinition;
/* init template directories */
$this->setTemplateDir(array());
/* add main template directory */
$this->addTemplateDir(THELIA_TEMPLATE_DIR . $this->template, 0);
/* define config directory */
$configDirectory = THELIA_TEMPLATE_DIR . $this->template . '/configs';
$configDirectory = THELIA_TEMPLATE_DIR . $this->getTemplate() . '/configs';
$this->setConfigDir($configDirectory);
/* add modules template directories */
switch($templateDefinition->getType()) {
case TemplateDefinition::FRONT_OFFICE:
/* add main template directory */
$this->addFrontOfficeTemplateDirectory($templateDefinition->getName(), THELIA_TEMPLATE_DIR . $this->getTemplate(), '0', true);
/* do not pass array directly to addTemplateDir since we cant control on keys */
if(isset($this->frontOfficeTemplateDirectories[$templateDefinition->getName()])) {
foreach($this->frontOfficeTemplateDirectories[$templateDefinition->getName()] as $key => $directory) {
@@ -143,6 +180,9 @@ class SmartyParser extends Smarty implements ParserInterface
break;
case TemplateDefinition::BACK_OFFICE:
/* add main template directory */
$this->addBackOfficeTemplateDirectory($templateDefinition->getName(), THELIA_TEMPLATE_DIR . $this->getTemplate(), '0', true);
/* do not pass array directly to addTemplateDir since we cant control on keys */
if(isset($this->backOfficeTemplateDirectories[$templateDefinition->getName()])) {
foreach($this->backOfficeTemplateDirectories[$templateDefinition->getName()] as $key => $directory) {
@@ -152,16 +192,25 @@ class SmartyParser extends Smarty implements ParserInterface
break;
case TemplateDefinition::PDF:
/* add main template directory */
$this->addTemplateDir(THELIA_TEMPLATE_DIR . $this->getTemplate(), 0);
break;
default:
/* add main template directory */
$this->addTemplateDir(THELIA_TEMPLATE_DIR . $this->getTemplate(), 0);
break;
}
}
public function getTemplateDefinition()
{
return $this->templateDefinition;
}
public function getTemplate()
{
return $this->template;
return $this->templateDefinition->getPath();
}
/**

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB