mirror of
https://github.com/processwire/processwire.git
synced 2025-08-12 17:54:44 +02:00
Add support for image focus area / focus point / focal point (or whatever the best term is) to InputfieldImage per processwire/processwire-requests#150
This commit is contained in:
@@ -109,9 +109,11 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
|
|||||||
*
|
*
|
||||||
* Possible values: northwest, north, northeast, west, center, east, southwest, south, southeast
|
* Possible values: northwest, north, northeast, west, center, east, southwest, south, southeast
|
||||||
* or TRUE to crop to center, or FALSE to disable cropping.
|
* or TRUE to crop to center, or FALSE to disable cropping.
|
||||||
|
* Or array where index 0 is % or px from left, and index 1 is % or px from top. Percent is assumed if
|
||||||
|
* values are number strings that end with %. Pixels are assumed of values are just integers.
|
||||||
* Default is: TRUE
|
* Default is: TRUE
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool|array
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
protected $cropping = true;
|
protected $cropping = true;
|
||||||
@@ -836,9 +838,11 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach(array('x', 'y', 'w', 'h') as $k) {
|
foreach(array('x', 'y', 'w', 'h') as $k) {
|
||||||
$v = isset($$k) ? $$k : -1;
|
$v = (int) (isset($$k) ? $$k : -1);
|
||||||
if(!is_int($v) || $v < 0) throw new WireException("Missing or wrong param $k for ImageSizer-cropExtra!");
|
if(!$v && $k == 'w' && $h > 0) $v = $this->getProportionalWidth((int) $h);
|
||||||
if(('w' == $k || 'h' == $k) && 0 == $v) throw new WireException("Wrong param $k for ImageSizer-cropExtra!");
|
if(!$v && $k == 'h' && $w > 0) $v = $this->getProportionalHeight((int) $w);
|
||||||
|
if($v < 0) throw new WireException("Missing or wrong param $k=$v for ImageSizer-cropExtra! " . print_r($value, true));
|
||||||
|
if(('w' == $k || 'h' == $k) && 0 == $v) throw new WireException("Wrong param $k=$v for ImageSizer-cropExtra! " . print_r($value, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->cropExtra = array($x, $y, $w, $h);
|
$this->cropExtra = array($x, $y, $w, $h);
|
||||||
@@ -1384,7 +1388,7 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
|
|||||||
protected function getCropDimensions(&$w1, &$h1, $gdWidth, $targetWidth, $gdHeight, $targetHeight) {
|
protected function getCropDimensions(&$w1, &$h1, $gdWidth, $targetWidth, $gdHeight, $targetHeight) {
|
||||||
|
|
||||||
if(is_string($this->cropping)) {
|
if(is_string($this->cropping)) {
|
||||||
|
// calculate from 8 named cropping points
|
||||||
switch($this->cropping) {
|
switch($this->cropping) {
|
||||||
case 'nw':
|
case 'nw':
|
||||||
$w1 = 0;
|
$w1 = 0;
|
||||||
@@ -1418,20 +1422,45 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if(is_array($this->cropping)) {
|
} else if(is_array($this->cropping)) {
|
||||||
|
// calculate from specific percent or pixels from left and top
|
||||||
|
// $this->cropping is an array with the following:
|
||||||
|
// index 0 represents % or pixels from left
|
||||||
|
// index 1 represents % or pixels from top
|
||||||
// @interrobang + @u-nikos
|
// @interrobang + @u-nikos
|
||||||
if(strpos($this->cropping[0], '%') === false) $pointX = (int) $this->cropping[0];
|
if(strpos($this->cropping[0], '%') === false) {
|
||||||
else $pointX = $gdWidth * ((int) $this->cropping[0] / 100);
|
$pointX = (int) $this->cropping[0];
|
||||||
|
} else {
|
||||||
|
$pointX = $gdWidth * ((int) $this->cropping[0] / 100);
|
||||||
|
}
|
||||||
|
|
||||||
if(strpos($this->cropping[1], '%') === false) $pointY = (int) $this->cropping[1];
|
if(strpos($this->cropping[1], '%') === false) {
|
||||||
else $pointY = $gdHeight * ((int) $this->cropping[1] / 100);
|
$pointY = (int) $this->cropping[1];
|
||||||
|
} else {
|
||||||
|
$pointY = $gdHeight * ((int) $this->cropping[1] / 100);
|
||||||
|
}
|
||||||
|
|
||||||
if($pointX < $targetWidth / 2) $w1 = 0;
|
/*
|
||||||
else if($pointX > ($gdWidth - $targetWidth / 2)) $w1 = $gdWidth - $targetWidth;
|
if(isset($this->cropping[2]) && $this->cropping[2] > 1) {
|
||||||
else $w1 = $pointX - $targetWidth / 2;
|
// zoom percent (2-100)
|
||||||
|
$zoom = (int) $this->cropping[2];
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
if($pointY < $targetHeight / 2) $h1 = 0;
|
if($pointX < $targetWidth / 2) {
|
||||||
else if($pointY > ($gdHeight - $targetHeight / 2)) $h1 = $gdHeight - $targetHeight;
|
$w1 = 0;
|
||||||
else $h1 = $pointY - $targetHeight / 2;
|
} else if($pointX > ($gdWidth - $targetWidth / 2)) {
|
||||||
|
$w1 = $gdWidth - $targetWidth;
|
||||||
|
} else {
|
||||||
|
$w1 = $pointX - $targetWidth / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($pointY < $targetHeight / 2) {
|
||||||
|
$h1 = 0;
|
||||||
|
} else if($pointY > ($gdHeight - $targetHeight / 2)) {
|
||||||
|
$h1 = $gdHeight - $targetHeight;
|
||||||
|
} else {
|
||||||
|
$h1 = $pointY - $targetHeight / 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -102,11 +102,12 @@ class ImageSizerEngineGD extends ImageSizerEngine {
|
|||||||
protected function processResize($srcFilename, $dstFilename, $fullWidth, $fullHeight, $finalWidth, $finalHeight) {
|
protected function processResize($srcFilename, $dstFilename, $fullWidth, $fullHeight, $finalWidth, $finalHeight) {
|
||||||
|
|
||||||
$this->modified = false;
|
$this->modified = false;
|
||||||
|
$isModified = false;
|
||||||
if(isset($this->info['bits'])) $this->imageDepth = $this->info['bits'];
|
if(isset($this->info['bits'])) $this->imageDepth = $this->info['bits'];
|
||||||
$this->imageFormat = strtoupper(str_replace('image/', '', $this->info['mime']));
|
$this->imageFormat = strtoupper(str_replace('image/', '', $this->info['mime']));
|
||||||
|
|
||||||
if(!in_array($this->imageFormat, $this->validSourceImageFormats())) {
|
if(!in_array($this->imageFormat, $this->validSourceImageFormats())) {
|
||||||
throw new WireException(sprintf($this->_("loaded file '%s' is not in the list of valid images", basename($dstFilename))));
|
throw new WireException(sprintf($this->_("loaded file '%s' is not in the list of valid images"), basename($dstFilename)));
|
||||||
}
|
}
|
||||||
|
|
||||||
$image = null;
|
$image = null;
|
||||||
@@ -141,6 +142,7 @@ class ImageSizerEngineGD extends ImageSizerEngine {
|
|||||||
if($this->rotate || $needRotation) { // @horst
|
if($this->rotate || $needRotation) { // @horst
|
||||||
$degrees = $this->rotate ? $this->rotate : $orientations[0];
|
$degrees = $this->rotate ? $this->rotate : $orientations[0];
|
||||||
$image = $this->imRotate($image, $degrees);
|
$image = $this->imRotate($image, $degrees);
|
||||||
|
$isModified = true;
|
||||||
if(abs($degrees) == 90 || abs($degrees) == 270) {
|
if(abs($degrees) == 90 || abs($degrees) == 270) {
|
||||||
// we have to swap width & height now!
|
// we have to swap width & height now!
|
||||||
$tmp = array($this->getWidth(), $this->getHeight());
|
$tmp = array($this->getWidth(), $this->getHeight());
|
||||||
@@ -155,7 +157,10 @@ class ImageSizerEngineGD extends ImageSizerEngine {
|
|||||||
} else if($orientations[1] > 0) {
|
} else if($orientations[1] > 0) {
|
||||||
$vertical = $orientations[1] == 2;
|
$vertical = $orientations[1] == 2;
|
||||||
}
|
}
|
||||||
if(!is_null($vertical)) $image = $this->imFlip($image, $vertical);
|
if(!is_null($vertical)) {
|
||||||
|
$image = $this->imFlip($image, $vertical);
|
||||||
|
$isModified = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there is requested to crop _before_ resize, we do it here @horst
|
// if there is requested to crop _before_ resize, we do it here @horst
|
||||||
@@ -182,6 +187,7 @@ class ImageSizerEngineGD extends ImageSizerEngine {
|
|||||||
$this->prepareImageLayer($image, $imageTemp);
|
$this->prepareImageLayer($image, $imageTemp);
|
||||||
imagecopy($image, $imageTemp, 0, 0, $x, $y, $w, $h);
|
imagecopy($image, $imageTemp, 0, 0, $x, $y, $w, $h);
|
||||||
unset($x, $y, $w, $h);
|
unset($x, $y, $w, $h);
|
||||||
|
$isModified = true;
|
||||||
|
|
||||||
// now release the intermediate image and update settings
|
// now release the intermediate image and update settings
|
||||||
imagedestroy($imageTemp);
|
imagedestroy($imageTemp);
|
||||||
@@ -205,15 +211,12 @@ class ImageSizerEngineGD extends ImageSizerEngine {
|
|||||||
|
|
||||||
// current version is already the desired result, we only may have to compress JPEGs but leave GIF and PNG as is:
|
// current version is already the desired result, we only may have to compress JPEGs but leave GIF and PNG as is:
|
||||||
|
|
||||||
/*
|
if(!$isModified && ($this->imageType == \IMAGETYPE_PNG || $this->imageType == \IMAGETYPE_GIF)) {
|
||||||
* the following commented block of code prevents PNG/GIF cropping from working
|
|
||||||
if($this->imageType == \IMAGETYPE_PNG || $this->imageType == \IMAGETYPE_GIF) {
|
|
||||||
$result = @copy($srcFilename, $dstFilename);
|
$result = @copy($srcFilename, $dstFilename);
|
||||||
if(isset($image) && is_resource($image)) @imagedestroy($image); // clean up
|
if(isset($image) && is_resource($image)) @imagedestroy($image); // clean up
|
||||||
if(isset($image)) $image = null;
|
if(isset($image)) $image = null;
|
||||||
return $result; // early return !
|
return $result; // early return !
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// process JPEGs
|
// process JPEGs
|
||||||
if(self::checkMemoryForImage(array(imagesx($image), imagesy($image), 3)) === false) {
|
if(self::checkMemoryForImage(array(imagesx($image), imagesy($image), 3)) === false) {
|
||||||
@@ -244,9 +247,39 @@ class ImageSizerEngineGD extends ImageSizerEngine {
|
|||||||
throw new WireException(basename($srcFilename) . " - not enough memory to resize to the intermediate image");
|
throw new WireException(basename($srcFilename) . " - not enough memory to resize to the intermediate image");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$sourceX = 0;
|
||||||
|
$sourceY = 0;
|
||||||
|
$sourceWidth = $this->image['width'];
|
||||||
|
$sourceHeight = $this->image['height'];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @todo figure out how to make zoom setting adjust coordinates to imagecopyresampled() calls
|
||||||
|
$zoom = is_array($this->cropping) && isset($this->cropping[2]) ? $this->cropping[2] : 0;
|
||||||
|
if($zoom > 1) {
|
||||||
|
$zoom = $zoom * 0.01;
|
||||||
|
$sourceWidth -= $sourceWidth * $zoom;
|
||||||
|
$sourceHeight -= $sourceHeight * $zoom;
|
||||||
|
$sourceX = $this->image['width'] - ($sourceWidth / 2);
|
||||||
|
$sourceY = $this->image['height'] - ($sourceHeight / 2);
|
||||||
|
$bgX = 0;
|
||||||
|
$bgY = 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
$thumb2 = imagecreatetruecolor($bgWidth, $bgHeight);
|
$thumb2 = imagecreatetruecolor($bgWidth, $bgHeight);
|
||||||
$this->prepareImageLayer($thumb2, $image);
|
$this->prepareImageLayer($thumb2, $image);
|
||||||
imagecopyresampled($thumb2, $image, 0, 0, 0, 0, $bgWidth, $bgHeight, $this->image['width'], $this->image['height']);
|
imagecopyresampled(
|
||||||
|
$thumb2, // destination image
|
||||||
|
$image, // source image
|
||||||
|
0, // destination X
|
||||||
|
0, // destination Y
|
||||||
|
$sourceX, // source X
|
||||||
|
$sourceY, // source Y
|
||||||
|
$bgWidth, // destination width
|
||||||
|
$bgHeight, // destination height
|
||||||
|
$sourceWidth, // source width
|
||||||
|
$sourceHeight // source height
|
||||||
|
);
|
||||||
|
|
||||||
if(self::checkMemoryForImage(array($finalWidth, $finalHeight, 3)) === false) {
|
if(self::checkMemoryForImage(array($finalWidth, $finalHeight, 3)) === false) {
|
||||||
throw new WireException(basename($srcFilename) . " - not enough memory to crop to the final image");
|
throw new WireException(basename($srcFilename) . " - not enough memory to crop to the final image");
|
||||||
@@ -254,7 +287,18 @@ class ImageSizerEngineGD extends ImageSizerEngine {
|
|||||||
|
|
||||||
$thumb = imagecreatetruecolor($finalWidth, $finalHeight);
|
$thumb = imagecreatetruecolor($finalWidth, $finalHeight);
|
||||||
$this->prepareImageLayer($thumb, $image);
|
$this->prepareImageLayer($thumb, $image);
|
||||||
imagecopyresampled($thumb, $thumb2, 0, 0, $bgX, $bgY, $finalWidth, $finalHeight, $finalWidth, $finalHeight);
|
imagecopyresampled(
|
||||||
|
$thumb, // destination image
|
||||||
|
$thumb2, // source image
|
||||||
|
0, // destination X
|
||||||
|
0, // destination Y
|
||||||
|
$bgX, // source X
|
||||||
|
$bgY, // source Y
|
||||||
|
$finalWidth, // destination width
|
||||||
|
$finalHeight, // destination height
|
||||||
|
$finalWidth, // source width
|
||||||
|
$finalHeight // source height
|
||||||
|
);
|
||||||
imagedestroy($thumb2);
|
imagedestroy($thumb2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,7 +462,7 @@ class ImageSizerEngineGD extends ImageSizerEngine {
|
|||||||
$amount = intval($amount / 100 * $this->usmValue);
|
$amount = intval($amount / 100 * $this->usmValue);
|
||||||
|
|
||||||
// apply unsharp mask filter
|
// apply unsharp mask filter
|
||||||
return $this->UnsharpMask($im, $amount, $radius, $threshold);
|
return $this->unsharpMask($im, $amount, $radius, $threshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we do not use USM, we use our default sharpening method,
|
// if we do not use USM, we use our default sharpening method,
|
||||||
@@ -562,12 +606,12 @@ class ImageSizerEngineGD extends ImageSizerEngine {
|
|||||||
for($x = 0; $x < $w - 1; $x++) { // each row
|
for($x = 0; $x < $w - 1; $x++) { // each row
|
||||||
for($y = 0; $y < $h; $y++) { // each pixel
|
for($y = 0; $y < $h; $y++) { // each pixel
|
||||||
|
|
||||||
$rgbOrig = ImageColorAt($img, $x, $y);
|
$rgbOrig = imagecolorat($img, $x, $y);
|
||||||
$rOrig = (($rgbOrig >> 16) & 0xFF);
|
$rOrig = (($rgbOrig >> 16) & 0xFF);
|
||||||
$gOrig = (($rgbOrig >> 8) & 0xFF);
|
$gOrig = (($rgbOrig >> 8) & 0xFF);
|
||||||
$bOrig = ($rgbOrig & 0xFF);
|
$bOrig = ($rgbOrig & 0xFF);
|
||||||
|
|
||||||
$rgbBlur = ImageColorAt($imgBlur, $x, $y);
|
$rgbBlur = imagecolorat($imgBlur, $x, $y);
|
||||||
|
|
||||||
$rBlur = (($rgbBlur >> 16) & 0xFF);
|
$rBlur = (($rgbBlur >> 16) & 0xFF);
|
||||||
$gBlur = (($rgbBlur >> 8) & 0xFF);
|
$gBlur = (($rgbBlur >> 8) & 0xFF);
|
||||||
@@ -586,20 +630,20 @@ class ImageSizerEngineGD extends ImageSizerEngine {
|
|||||||
: $bOrig;
|
: $bOrig;
|
||||||
|
|
||||||
if(($rOrig != $rNew) || ($gOrig != $gNew) || ($bOrig != $bNew)) {
|
if(($rOrig != $rNew) || ($gOrig != $gNew) || ($bOrig != $bNew)) {
|
||||||
$pixCol = ImageColorAllocate($img, $rNew, $gNew, $bNew);
|
$pixCol = imagecolorallocate($img, $rNew, $gNew, $bNew);
|
||||||
ImageSetPixel($img, $x, $y, $pixCol);
|
imagesetpixel($img, $x, $y, $pixCol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for($x = 0; $x < $w; $x++) { // each row
|
for($x = 0; $x < $w; $x++) { // each row
|
||||||
for($y = 0; $y < $h; $y++) { // each pixel
|
for($y = 0; $y < $h; $y++) { // each pixel
|
||||||
$rgbOrig = ImageColorAt($img, $x, $y);
|
$rgbOrig = imagecolorat($img, $x, $y);
|
||||||
$rOrig = (($rgbOrig >> 16) & 0xFF);
|
$rOrig = (($rgbOrig >> 16) & 0xFF);
|
||||||
$gOrig = (($rgbOrig >> 8) & 0xFF);
|
$gOrig = (($rgbOrig >> 8) & 0xFF);
|
||||||
$bOrig = ($rgbOrig & 0xFF);
|
$bOrig = ($rgbOrig & 0xFF);
|
||||||
|
|
||||||
$rgbBlur = ImageColorAt($imgBlur, $x, $y);
|
$rgbBlur = imagecolorat($imgBlur, $x, $y);
|
||||||
|
|
||||||
$rBlur = (($rgbBlur >> 16) & 0xFF);
|
$rBlur = (($rgbBlur >> 16) & 0xFF);
|
||||||
$gBlur = (($rgbBlur >> 8) & 0xFF);
|
$gBlur = (($rgbBlur >> 8) & 0xFF);
|
||||||
@@ -624,7 +668,7 @@ class ImageSizerEngineGD extends ImageSizerEngine {
|
|||||||
$bNew = 0;
|
$bNew = 0;
|
||||||
}
|
}
|
||||||
$rgbNew = ($rNew << 16) + ($gNew << 8) + $bNew;
|
$rgbNew = ($rNew << 16) + ($gNew << 8) + $bNew;
|
||||||
ImageSetPixel($img, $x, $y, $rgbNew);
|
imagesetpixel($img, $x, $y, $rgbNew);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,18 +24,20 @@
|
|||||||
* ~~~~~
|
* ~~~~~
|
||||||
* #pw-body
|
* #pw-body
|
||||||
*
|
*
|
||||||
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
|
* ProcessWire 3.x, Copyright 2018 by Ryan Cramer
|
||||||
* https://processwire.com
|
* https://processwire.com
|
||||||
*
|
*
|
||||||
* @property int $width Width of image, in pixels.
|
* @property-read int $width Width of image, in pixels.
|
||||||
* @property int $height Height of image, in pixels.
|
* @property-read int $height Height of image, in pixels.
|
||||||
* @property int $hidpiWidth HiDPI width of image, in pixels. #pw-internal
|
* @property-read int $hidpiWidth HiDPI width of image, in pixels. #pw-internal
|
||||||
* @property int $hidpiHeight HiDPI heigh of image, in pixels. #pw-internal
|
* @property-read int $hidpiHeight HiDPI heigh of image, in pixels. #pw-internal
|
||||||
* @property string $error Last image resizing error message, when applicable. #pw-group-resize-and-crop
|
* @property-read string $error Last image resizing error message, when applicable. #pw-group-resize-and-crop
|
||||||
* @property Pageimage $original Reference to original $image, if this is a resized version. #pw-group-variations
|
* @property-read Pageimage $original Reference to original $image, if this is a resized version. #pw-group-variations
|
||||||
* @property string $url
|
* @property-read string $url
|
||||||
* @property string $basename
|
* @property-read string $basename
|
||||||
* @property string $filename
|
* @property-read string $filename
|
||||||
|
* @property-read array $focus Focus array contains 'top' (float), 'left' (float), 'zoom' (int), and 'default' (bool) properties.
|
||||||
|
* @property-read bool $hasFocus Does this image have custom focus settings? (i.e. $focus['default'] == true)
|
||||||
*
|
*
|
||||||
* @method bool|array isVariation($basename, $allowSelf = false)
|
* @method bool|array isVariation($basename, $allowSelf = false)
|
||||||
* @method Pageimage crop($x, $y, $width, $height, $options = array())
|
* @method Pageimage crop($x, $y, $width, $height, $options = array())
|
||||||
@@ -177,6 +179,119 @@ class Pageimage extends Pagefile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or set focus area for crops to use
|
||||||
|
*
|
||||||
|
* These settings are used by $this->size() calls that specify BOTH width AND height. Focus helps to
|
||||||
|
* ensure that the important subject of the photo is not cropped out when the requested size proportion
|
||||||
|
* differs from the original image proportion. For example, not chopping off someone’s head in a photo.
|
||||||
|
*
|
||||||
|
* Default behavior is to return an array containing "top" and "left" indexes, representing percentages
|
||||||
|
* from top and left. When arguments are specified, you are either setting the top/left percentages, or
|
||||||
|
* unsetting focus, or getting focus in different ways, described in arguments below.
|
||||||
|
*
|
||||||
|
* A zoom argument/property is also present here for future use, but not currently supported.
|
||||||
|
*
|
||||||
|
* #pw-group-other
|
||||||
|
*
|
||||||
|
* @param null|float|int|array|false $top Omit to get focus array, or specify one of the following:
|
||||||
|
* - GET: Omit all arguments to get focus array (default behavior).
|
||||||
|
* - GET: Specify boolean TRUE to return TRUE if focus data is present or FALSE if not.
|
||||||
|
* - GET: Specify integer 1 to make this method return pixel dimensions rather than percentages.
|
||||||
|
* - SET: Specify both $top and $left arguments to set (values assumed to be percentages).
|
||||||
|
* - SET: Specify array containing "top" and "left" indexes to set (percentages).
|
||||||
|
* - SET: Specify array where index 0 is top and index 1 is left (percentages).
|
||||||
|
* - SET: Specify string in the format "top left", i.e. "25 70" (percentages).
|
||||||
|
* - UNSET: Specify boolean false to remove any focus values.
|
||||||
|
* @param null|float|int $left Set left value (when $top value is float|int)
|
||||||
|
* - This argument is only used when setting focus and should be omitted otherwise.
|
||||||
|
* @param null|int $zoom Zoom percent (not currently supported)
|
||||||
|
* @return array|bool|Pageimage Returns one of the following:
|
||||||
|
* - When getting returns array containing top, left and default properties.
|
||||||
|
* - When TRUE was specified for the $top argument, it returns either TRUE (has focus) or FALSE (does not have).
|
||||||
|
* - When setting or unsetting returns $this.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function focus($top = null, $left = null, $zoom = null) {
|
||||||
|
|
||||||
|
if(is_string($top) && strpos($top, ' ') && $left === null) {
|
||||||
|
// SET string like "25 70 0" (representing "top left zoom")
|
||||||
|
if(strpos($top, ' ') != strrpos($top, ' ')) {
|
||||||
|
// with zoom
|
||||||
|
list($top, $left, $zoom) = explode(' ', $top, 3);
|
||||||
|
} else {
|
||||||
|
// without zoom
|
||||||
|
list($top, $left) = explode(' ', $top, 2);
|
||||||
|
$zoom = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($top === null || $top === true || ($top === 1 && $left === null)) {
|
||||||
|
// GET
|
||||||
|
$focus = $this->filedata('focus');
|
||||||
|
if(!is_array($focus) || empty($focus)) {
|
||||||
|
// use default
|
||||||
|
if($top === true) return false;
|
||||||
|
$focus = array(
|
||||||
|
'top' => 50,
|
||||||
|
'left' => 50,
|
||||||
|
'zoom' => 0,
|
||||||
|
'default' => true,
|
||||||
|
'str' => '50 50 0',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// use custom
|
||||||
|
if($top === true) return true;
|
||||||
|
if(!isset($focus['zoom'])) $focus['zoom'] = 0;
|
||||||
|
$focus['default'] = false;
|
||||||
|
$focus['str'] = "$focus[top] $focus[left] $focus[zoom]";
|
||||||
|
}
|
||||||
|
if($top === 1) {
|
||||||
|
// return pixel dimensions rather than percentages
|
||||||
|
$centerX = ($focus['left'] / 100) * $this->width(); // i.e. (50 / 100) * 500 = 250;
|
||||||
|
$centerY = ($focus['top'] / 100) * $this->height();
|
||||||
|
$focus['left'] = $centerX;
|
||||||
|
$focus['top'] = $centerY;
|
||||||
|
}
|
||||||
|
return $focus;
|
||||||
|
|
||||||
|
} else if($top === false) {
|
||||||
|
// UNSET
|
||||||
|
$this->filedata(false, 'focus');
|
||||||
|
|
||||||
|
} else if($top !== null && $left !== null) {
|
||||||
|
// SET
|
||||||
|
if(is_array($top)) {
|
||||||
|
if(isset($top['left'])) {
|
||||||
|
$left = $top['left'];
|
||||||
|
$top = $top['top'];
|
||||||
|
$zoom = isset($top['zoom']) ? $top['zoom'] : 0;
|
||||||
|
} else {
|
||||||
|
$top = $top[0];
|
||||||
|
$left = $top[1];
|
||||||
|
$zoom = isset($top[2]) ? $top[2] : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$top = (float) $top;
|
||||||
|
$left = (float) $left;
|
||||||
|
$zoom = (int) $zoom;
|
||||||
|
|
||||||
|
if(((int) $top) == 50 && ((int) $left) == 50 && ($zoom < 2)) {
|
||||||
|
// if matches defaults, then no reason to store in filedata
|
||||||
|
$this->filedata(false, 'focus');
|
||||||
|
} else {
|
||||||
|
$this->filedata('focus', array(
|
||||||
|
'top' => round($top, 1),
|
||||||
|
'left' => round($left, 1),
|
||||||
|
'zoom' => $zoom
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a property from this Pageimage
|
* Get a property from this Pageimage
|
||||||
*
|
*
|
||||||
@@ -206,6 +321,12 @@ class Pageimage extends Pagefile {
|
|||||||
case 'error':
|
case 'error':
|
||||||
$value = $this->error;
|
$value = $this->error;
|
||||||
break;
|
break;
|
||||||
|
case 'focus':
|
||||||
|
$value = $this->focus();
|
||||||
|
break;
|
||||||
|
case 'hasFocus':
|
||||||
|
$value = $this->focus(true);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
$value = parent::get($key);
|
$value = parent::get($key);
|
||||||
}
|
}
|
||||||
@@ -217,7 +338,8 @@ class Pageimage extends Pagefile {
|
|||||||
*
|
*
|
||||||
* #pw-internal
|
* #pw-internal
|
||||||
*
|
*
|
||||||
* @param bool $reset
|
* @param bool|string $reset Specify true to retrieve info fresh, or filename to check and return info for.
|
||||||
|
* When specifying a filename, the info is only returned (not populated with this object).
|
||||||
* @return array
|
* @return array
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -227,21 +349,29 @@ class Pageimage extends Pagefile {
|
|||||||
else if($this->imageInfo['width']) $checkImage = false;
|
else if($this->imageInfo['width']) $checkImage = false;
|
||||||
else $checkImage = true;
|
else $checkImage = true;
|
||||||
|
|
||||||
if($checkImage) {
|
$imageInfo = $this->imageInfo;
|
||||||
|
$filename = is_string($reset) && file_exists($reset) ? $reset : '';
|
||||||
|
|
||||||
|
if($checkImage || $filename) {
|
||||||
if($this->ext == 'svg') {
|
if($this->ext == 'svg') {
|
||||||
$info = $this->getImageInfoSVG();
|
$info = $this->getImageInfoSVG($filename);
|
||||||
$this->imageInfo['width'] = $info['width'];
|
$imageInfo['width'] = $info['width'];
|
||||||
$this->imageInfo['height'] = $info['height'];
|
$imageInfo['height'] = $info['height'];
|
||||||
|
} else {
|
||||||
|
if($filename) {
|
||||||
|
$info = @getimagesize($filename);
|
||||||
} else {
|
} else {
|
||||||
$info = @getimagesize($this->filename);
|
$info = @getimagesize($this->filename);
|
||||||
|
}
|
||||||
if($info) {
|
if($info) {
|
||||||
$this->imageInfo['width'] = $info[0];
|
$imageInfo['width'] = $info[0];
|
||||||
$this->imageInfo['height'] = $info[1];
|
$imageInfo['height'] = $info[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(!$filename) $this->imageInfo = $imageInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->imageInfo;
|
return $imageInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -251,13 +381,15 @@ class Pageimage extends Pagefile {
|
|||||||
*
|
*
|
||||||
* #pw-internal
|
* #pw-internal
|
||||||
*
|
*
|
||||||
|
* @param string $filename Optional filename to check
|
||||||
* @return array of width and height
|
* @return array of width and height
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
protected function getImageInfoSVG() {
|
protected function getImageInfoSVG($filename = '') {
|
||||||
$width = 0;
|
$width = 0;
|
||||||
$height = 0;
|
$height = 0;
|
||||||
$xml = @file_get_contents($this->filename);
|
if(!$filename) $filename = $this->filename;
|
||||||
|
$xml = @file_get_contents($filename);
|
||||||
|
|
||||||
if($xml) {
|
if($xml) {
|
||||||
$a = @simplexml_load_string($xml)->attributes();
|
$a = @simplexml_load_string($xml)->attributes();
|
||||||
@@ -268,7 +400,7 @@ class Pageimage extends Pagefile {
|
|||||||
if((!$width || !$height) && (extension_loaded('imagick') || class_exists('\IMagick'))) {
|
if((!$width || !$height) && (extension_loaded('imagick') || class_exists('\IMagick'))) {
|
||||||
try {
|
try {
|
||||||
$imagick = new \Imagick();
|
$imagick = new \Imagick();
|
||||||
$imagick->readImage($this->filename);
|
$imagick->readImage($filename);
|
||||||
$width = $imagick->getImageWidth();
|
$width = $imagick->getImageWidth();
|
||||||
$height = $imagick->getImageHeight();
|
$height = $imagick->getImageHeight();
|
||||||
} catch(\Exception $e) {
|
} catch(\Exception $e) {
|
||||||
@@ -329,7 +461,7 @@ class Pageimage extends Pagefile {
|
|||||||
*
|
*
|
||||||
* - `quality` (int): Quality setting 1-100 (default=90, or as specified in /site/config.php).
|
* - `quality` (int): Quality setting 1-100 (default=90, or as specified in /site/config.php).
|
||||||
* - `upscaling` (bool): Allow image to be upscaled? (default=true).
|
* - `upscaling` (bool): Allow image to be upscaled? (default=true).
|
||||||
* - `cropping` (string|bool): Cropping mode, see possible values in "cropping" section below (default=center).
|
* - `cropping` (string|bool|array): Cropping mode, see possible values in "cropping" section below (default=center).
|
||||||
* - `suffix` (string|array): Suffix word to identify the new image, or use array of words for multiple (default=none).
|
* - `suffix` (string|array): Suffix word to identify the new image, or use array of words for multiple (default=none).
|
||||||
* - `forceNew` (bool): Force re-creation of the image even if it already exists? (default=false).
|
* - `forceNew` (bool): Force re-creation of the image even if it already exists? (default=false).
|
||||||
* - `sharpening` (string): Sharpening mode: "none", "soft", "medium", or "strong" (default=soft).
|
* - `sharpening` (string): Sharpening mode: "none", "soft", "medium", or "strong" (default=soft).
|
||||||
@@ -339,10 +471,16 @@ class Pageimage extends Pagefile {
|
|||||||
* - `hidpi` (bool): Use HiDPI/retina pixel doubling? (default=false).
|
* - `hidpi` (bool): Use HiDPI/retina pixel doubling? (default=false).
|
||||||
* - `hidpiQuality` (bool): Quality setting for HiDPI (default=40, typically lower than regular quality setting).
|
* - `hidpiQuality` (bool): Quality setting for HiDPI (default=40, typically lower than regular quality setting).
|
||||||
* - `cleanFilename` (bool): Clean filename of historical resize information for shorter filenames? (default=false).
|
* - `cleanFilename` (bool): Clean filename of historical resize information for shorter filenames? (default=false).
|
||||||
|
* - `nameWidth` (int): Width to use for filename (default is to use specified $width argument).
|
||||||
|
* - `nameHeight` (int): Height to use for filename (default is to use specified $height argument).
|
||||||
|
* - `focus` (bool): Should resizes that result in crop use focus area if available? (default=true).
|
||||||
|
* In order for focus to be applicable, resize must include both width and height.
|
||||||
*
|
*
|
||||||
* **Possible values for "cropping" option**
|
* **Possible values for "cropping" option**
|
||||||
*
|
*
|
||||||
* - `center` (string): to crop to center of image, default behavior.
|
* - `true` (bool): Auto detect and allow use of focus (default).
|
||||||
|
* - `false` (bool): Disallow cropping.
|
||||||
|
* - `center` (string): to crop to center of image.
|
||||||
* - `x111y222` (string): to crop by pixels, 111px from left and 222px from top (replacing 111 and 222 with your values).
|
* - `x111y222` (string): to crop by pixels, 111px from left and 222px from top (replacing 111 and 222 with your values).
|
||||||
* - `north` (string): Crop North (top), may also be just "n".
|
* - `north` (string): Crop North (top), may also be just "n".
|
||||||
* - `northwest` (string): Crop from Northwest (top left), may also be just "nw".
|
* - `northwest` (string): Crop from Northwest (top left), may also be just "nw".
|
||||||
@@ -353,6 +491,8 @@ class Pageimage extends Pagefile {
|
|||||||
* - `west` (string): Crop West (left), may also be just "w".
|
* - `west` (string): Crop West (left), may also be just "w".
|
||||||
* - `east` (string): Crop East (right), may alos be just "e".
|
* - `east` (string): Crop East (right), may alos be just "e".
|
||||||
* - `blank` (string): Specify a blank string to disallow cropping during resize.
|
* - `blank` (string): Specify a blank string to disallow cropping during resize.
|
||||||
|
* - `array(111,222)` (array): Array of integers index 0 is left pixels and index 1 is top pixels.
|
||||||
|
* - `array('11%','22%')` (array): Array of '%' appended strings where index 0 is left percent and index 1 is top percent.
|
||||||
*
|
*
|
||||||
* **Note about "quality" and "upscaling" options**
|
* **Note about "quality" and "upscaling" options**
|
||||||
*
|
*
|
||||||
@@ -436,6 +576,9 @@ class Pageimage extends Pagefile {
|
|||||||
'cleanFilename' => false, // clean filename of historial resize information
|
'cleanFilename' => false, // clean filename of historial resize information
|
||||||
'rotate' => 0,
|
'rotate' => 0,
|
||||||
'flip' => '',
|
'flip' => '',
|
||||||
|
'nameWidth' => null, // override width to use for filename, int when populated
|
||||||
|
'nameHeight' => null, // override height to use for filename, int when populated
|
||||||
|
'focus' => true, // allow single dimension resizes to use focus area?
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->error = '';
|
$this->error = '';
|
||||||
@@ -443,16 +586,25 @@ class Pageimage extends Pagefile {
|
|||||||
$configOptions = $this->wire('config')->imageSizerOptions;
|
$configOptions = $this->wire('config')->imageSizerOptions;
|
||||||
if(!is_array($configOptions)) $configOptions = array();
|
if(!is_array($configOptions)) $configOptions = array();
|
||||||
$options = array_merge($defaultOptions, $configOptions, $options);
|
$options = array_merge($defaultOptions, $configOptions, $options);
|
||||||
|
if($options['cropping'] === 1) $options['cropping'] = true;
|
||||||
|
|
||||||
$width = (int) $width;
|
$width = (int) $width;
|
||||||
$height = (int) $height;
|
$height = (int) $height;
|
||||||
|
|
||||||
if(is_string($options['cropping'])
|
if($options['cropping'] === true && empty($options['cropExtra']) && $options['focus'] && $this->hasFocus) {
|
||||||
|
// crop to focus area
|
||||||
|
$focus = $this->focus();
|
||||||
|
$focus['zoom'] = 0; // not yet supported
|
||||||
|
$options['cropping'] = array("$focus[left]%", "$focus[top]%", "$focus[zoom]");
|
||||||
|
$crop = ''; // do not add suffix
|
||||||
|
|
||||||
|
} else if(is_string($options['cropping'])
|
||||||
&& strpos($options['cropping'], 'x') === 0
|
&& strpos($options['cropping'], 'x') === 0
|
||||||
&& preg_match('/^x(\d+)[yx](\d+)/', $options['cropping'], $matches)) {
|
&& preg_match('/^x(\d+)[yx](\d+)/', $options['cropping'], $matches)) {
|
||||||
$options['cropping'] = true;
|
$options['cropping'] = true;
|
||||||
$options['cropExtra'] = array((int) $matches[1], (int) $matches[2], $width, $height);
|
$options['cropExtra'] = array((int) $matches[1], (int) $matches[2], $width, $height);
|
||||||
$crop = '';
|
$crop = '';
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$crop = ImageSizer::croppingValueStr($options['cropping']);
|
$crop = ImageSizer::croppingValueStr($options['cropping']);
|
||||||
}
|
}
|
||||||
@@ -462,9 +614,15 @@ class Pageimage extends Pagefile {
|
|||||||
$options['suffix'] = empty($options['suffix']) ? array() : explode(' ', $options['suffix']);
|
$options['suffix'] = empty($options['suffix']) ? array() : explode(' ', $options['suffix']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($options['rotate'] && !in_array(abs((int) $options['rotate']), array(90, 180, 270))) $options['rotate'] = 0;
|
if($options['rotate'] && !in_array(abs((int) $options['rotate']), array(90, 180, 270))) {
|
||||||
if($options['rotate']) $options['suffix'][] = ($options['rotate'] > 0 ? "rot" : "tor") . abs($options['rotate']);
|
$options['rotate'] = 0;
|
||||||
if($options['flip']) $options['suffix'][] = strtolower(substr($options['flip'], 0, 1)) == 'v' ? 'flipv' : 'fliph';
|
}
|
||||||
|
if($options['rotate']) {
|
||||||
|
$options['suffix'][] = ($options['rotate'] > 0 ? "rot" : "tor") . abs($options['rotate']);
|
||||||
|
}
|
||||||
|
if($options['flip']) {
|
||||||
|
$options['suffix'][] = strtolower(substr($options['flip'], 0, 1)) == 'v' ? 'flipv' : 'fliph';
|
||||||
|
}
|
||||||
|
|
||||||
$suffixStr = '';
|
$suffixStr = '';
|
||||||
if(!empty($options['suffix'])) {
|
if(!empty($options['suffix'])) {
|
||||||
@@ -483,20 +641,28 @@ class Pageimage extends Pagefile {
|
|||||||
if($options['hidpiQuality']) $options['quality'] = $options['hidpiQuality'];
|
if($options['hidpiQuality']) $options['quality'] = $options['hidpiQuality'];
|
||||||
}
|
}
|
||||||
|
|
||||||
//$basename = $this->pagefiles->cleanBasename($this->basename(), false, false, false);
|
|
||||||
// cleanBasename($basename, $originalize = false, $allowDots = true, $translate = false)
|
|
||||||
$originalName = $this->basename();
|
$originalName = $this->basename();
|
||||||
$basename = basename($originalName, "." . $this->ext()); // i.e. myfile
|
// determine basename without extension, i.e. myfile
|
||||||
|
$basename = basename($originalName, "." . $this->ext());
|
||||||
$originalSize = $debug ? @filesize($this->filename) : 0;
|
$originalSize = $debug ? @filesize($this->filename) : 0;
|
||||||
|
|
||||||
if($options['cleanFilename'] && strpos($basename, '.') !== false) {
|
if($options['cleanFilename'] && strpos($basename, '.') !== false) {
|
||||||
$basename = substr($basename, 0, strpos($basename, '.'));
|
$basename = substr($basename, 0, strpos($basename, '.'));
|
||||||
}
|
}
|
||||||
$basename .= '.' . $width . 'x' . $height . $crop . $suffixStr . "." . $this->ext(); // i.e. myfile.100x100.jpg or myfile.100x100nw-suffix1-suffix2.jpg
|
|
||||||
|
// filename uses requested width/height unless another specified via nameWidth or nameHeight options
|
||||||
|
$nameWidth = is_int($options['nameWidth']) ? $options['nameWidth'] : $width;
|
||||||
|
$nameHeight = is_int($options['nameHeight']) ? $options['nameHeight'] : $height;
|
||||||
|
|
||||||
|
// i.e. myfile.100x100.jpg or myfile.100x100nw-suffix1-suffix2.jpg
|
||||||
|
$basename .= '.' . $nameWidth . 'x' . $nameHeight . $crop . $suffixStr . "." . $this->ext();
|
||||||
$filenameFinal = $this->pagefiles->path() . $basename;
|
$filenameFinal = $this->pagefiles->path() . $basename;
|
||||||
$filenameUnvalidated = '';
|
$filenameUnvalidated = '';
|
||||||
$exists = file_exists($filenameFinal);
|
$exists = file_exists($filenameFinal);
|
||||||
|
|
||||||
|
// create a new resize if it doesn't already exist or forceNew option is set
|
||||||
if(!$exists || $options['forceNew']) {
|
if(!$exists || $options['forceNew']) {
|
||||||
|
// filenameUnvalidated is temporary filename used for resize
|
||||||
$filenameUnvalidated = $this->pagefiles->page->filesManager()->getTempPath() . $basename;
|
$filenameUnvalidated = $this->pagefiles->page->filesManager()->getTempPath() . $basename;
|
||||||
if($exists && $options['forceNew']) @unlink($filenameFinal);
|
if($exists && $options['forceNew']) @unlink($filenameFinal);
|
||||||
if(file_exists($filenameUnvalidated)) @unlink($filenameUnvalidated);
|
if(file_exists($filenameUnvalidated)) @unlink($filenameUnvalidated);
|
||||||
@@ -913,6 +1079,7 @@ class Pageimage extends Pagefile {
|
|||||||
* - `1` (int): Rebuild all non-suffix variations, and those w/suffix specifed in $suffix argument. ($suffix is INCLUSION list)
|
* - `1` (int): Rebuild all non-suffix variations, and those w/suffix specifed in $suffix argument. ($suffix is INCLUSION list)
|
||||||
* - `2` (int): Rebuild all variations, except those with suffix specified in $suffix argument. ($suffix is EXCLUSION list)
|
* - `2` (int): Rebuild all variations, except those with suffix specified in $suffix argument. ($suffix is EXCLUSION list)
|
||||||
* - `3` (int): Rebuild only variations specified in the $suffix argument. ($suffix is ONLY-INCLUSION list)
|
* - `3` (int): Rebuild only variations specified in the $suffix argument. ($suffix is ONLY-INCLUSION list)
|
||||||
|
* - `4` (int): Rebuild only non-proportional, non-crop variations (variations that specify both width and height)
|
||||||
*
|
*
|
||||||
* Mode 0 is the only truly safe mode, as in any other mode there are possibilities that the resulting
|
* Mode 0 is the only truly safe mode, as in any other mode there are possibilities that the resulting
|
||||||
* rebuild of the variation may not be exactly what was intended. The issues with other modes primarily
|
* rebuild of the variation may not be exactly what was intended. The issues with other modes primarily
|
||||||
@@ -984,6 +1151,11 @@ class Pageimage extends Pagefile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($mode == 4 && ($info['width'] == 0 || $info['height'] == 0)) {
|
||||||
|
// skip images that don't specify both width and height
|
||||||
|
$skip = true;
|
||||||
|
}
|
||||||
|
|
||||||
if($skip) {
|
if($skip) {
|
||||||
$skipped[] = $name;
|
$skipped[] = $name;
|
||||||
continue;
|
continue;
|
||||||
@@ -994,12 +1166,32 @@ class Pageimage extends Pagefile {
|
|||||||
$o['suffix'] = $info['suffix'];
|
$o['suffix'] = $info['suffix'];
|
||||||
if(is_file($info['path'])) unlink($info['path']);
|
if(is_file($info['path'])) unlink($info['path']);
|
||||||
|
|
||||||
|
if(!$info['width'] && $info['actualWidth']) {
|
||||||
|
$info['width'] = $info['actualWidth'];
|
||||||
|
$options['nameWidth'] = 0;
|
||||||
|
}
|
||||||
|
if(!$info['height'] && $info['actualHeight']) {
|
||||||
|
$info['height'] = $info['actualHeight'];
|
||||||
|
$options['nameHeight'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if($info['crop'] && preg_match('/^x(\d+)y(\d+)$/', $info['crop'], $matches)) {
|
if($info['crop'] && preg_match('/^x(\d+)y(\d+)$/', $info['crop'], $matches)) {
|
||||||
|
// dimensional cropping info contained in filename
|
||||||
$cropX = (int) $matches[1];
|
$cropX = (int) $matches[1];
|
||||||
$cropY = (int) $matches[2];
|
$cropY = (int) $matches[2];
|
||||||
$variation = $this->crop($cropX, $cropY, $info['width'], $info['height'], $options);
|
$variation = $this->crop($cropX, $cropY, $info['width'], $info['height'], $options);
|
||||||
|
|
||||||
|
} else if($info['crop']) {
|
||||||
|
// direct cropping info contained in filename
|
||||||
|
$options['cropping'] = $info['crop'];
|
||||||
|
$variation = $this->size($info['width'], $info['height'], $options);
|
||||||
|
|
||||||
|
} else if($this->hasFocus) {
|
||||||
|
// crop to focus area, which the size() method will determine on its own
|
||||||
|
$variation = $this->size($info['width'], $info['height'], $options);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if($info['crop']) $options['cropping'] = $info['crop'];
|
// no crop, no focus, just resize
|
||||||
$variation = $this->size($info['width'], $info['height'], $options);
|
$variation = $this->size($info['width'], $info['height'], $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1027,8 +1219,10 @@ class Pageimage extends Pagefile {
|
|||||||
* - `original` (string): Original basename
|
* - `original` (string): Original basename
|
||||||
* - `url` (string): URL to image
|
* - `url` (string): URL to image
|
||||||
* - `path` (string): Full path + filename to image
|
* - `path` (string): Full path + filename to image
|
||||||
* - `width` (int): Specified width
|
* - `width` (int): Specified width in filename
|
||||||
* - `height` (int): Specified height
|
* - `height` (int): Specified height in filename
|
||||||
|
* - `actualWidth` (int): Actual width when checked manually
|
||||||
|
* - `actualHeight` (int): Acual height when checked manually
|
||||||
* - `crop` (string): Cropping info string or blank if none
|
* - `crop` (string): Cropping info string or blank if none
|
||||||
* - `suffix` (array): Array of suffixes
|
* - `suffix` (array): Array of suffixes
|
||||||
*
|
*
|
||||||
@@ -1153,6 +1347,9 @@ class Pageimage extends Pagefile {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$actualInfo = $this->getImageInfo($info['path']);
|
||||||
|
$info['actualWidth'] = $actualInfo['width'];
|
||||||
|
$info['actualHeight'] = $actualInfo['height'];
|
||||||
$info['hidpiWidth'] = $this->hidpiWidth(0, $info['width']);
|
$info['hidpiWidth'] = $this->hidpiWidth(0, $info['width']);
|
||||||
$info['hidpiHeight'] = $this->hidpiWidth(0, $info['height']);
|
$info['hidpiHeight'] = $this->hidpiWidth(0, $info['height']);
|
||||||
|
|
||||||
|
@@ -61,7 +61,6 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transition: transform ease .3s;
|
|
||||||
-ms-transform: translate3d(-50%, -50%, 0);
|
-ms-transform: translate3d(-50%, -50%, 0);
|
||||||
transform: translate3d(-50%, -50%, 0); }
|
transform: translate3d(-50%, -50%, 0); }
|
||||||
.gridImage.gridImagePlaceholder .gridImage__overflow {
|
.gridImage.gridImagePlaceholder .gridImage__overflow {
|
||||||
@@ -298,13 +297,22 @@
|
|||||||
min-height: 1em;
|
min-height: 1em;
|
||||||
padding: 20px; }
|
padding: 20px; }
|
||||||
.InputfieldImageEdit__imagewrapper > div {
|
.InputfieldImageEdit__imagewrapper > div {
|
||||||
width: 100%; }
|
width: 100%;
|
||||||
|
position: relative; }
|
||||||
.InputfieldImageEdit__imagewrapper .detail {
|
.InputfieldImageEdit__imagewrapper .detail {
|
||||||
display: block;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
opacity: 0;
|
|
||||||
margin-top: 2px; }
|
margin-top: 2px; }
|
||||||
.InputfieldImageEdit__imagewrapper:hover .detail {
|
.InputfieldImageEdit__imagewrapper .detail-upload {
|
||||||
|
display: block;
|
||||||
|
opacity: 0; }
|
||||||
|
.InputfieldImageEdit__imagewrapper .detail-focus {
|
||||||
|
display: none; }
|
||||||
|
.InputfieldImageEdit__imagewrapper:hover .detail-upload {
|
||||||
|
opacity: 0.7; }
|
||||||
|
.InputfieldImageEdit__imagewrapper > div.focusWrap .detail-upload {
|
||||||
|
display: none; }
|
||||||
|
.InputfieldImageEdit__imagewrapper > div.focusWrap .detail-focus {
|
||||||
|
display: block;
|
||||||
opacity: 0.7; }
|
opacity: 0.7; }
|
||||||
.InputfieldImageEdit__replace img {
|
.InputfieldImageEdit__replace img {
|
||||||
opacity: 0.5; }
|
opacity: 0.5; }
|
||||||
@@ -351,6 +359,29 @@
|
|||||||
.InputfieldImageEdit .InputfieldFileDescription + .InputfieldFileTags {
|
.InputfieldImageEdit .InputfieldFileDescription + .InputfieldFileTags {
|
||||||
margin-top: 0.5em; }
|
margin-top: 0.5em; }
|
||||||
|
|
||||||
|
.gridImage__overflow .focusArea,
|
||||||
|
.InputfieldImageEdit .focusArea {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
color: #fff;
|
||||||
|
cursor: default; }
|
||||||
|
.gridImage__overflow .focusArea.focusActive,
|
||||||
|
.InputfieldImageEdit .focusArea.focusActive {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden; }
|
||||||
|
.gridImage__overflow .focusArea .focusCircle,
|
||||||
|
.InputfieldImageEdit .focusArea .focusCircle {
|
||||||
|
cursor: move;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border: 3px solid #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: rgba(255, 255, 255, 0.3); }
|
||||||
|
|
||||||
.InputfieldImage .ImageData {
|
.InputfieldImage .ImageData {
|
||||||
display: none; }
|
display: none; }
|
||||||
|
|
||||||
@@ -431,6 +462,8 @@
|
|||||||
.InputfieldImageEditAll .gridImage__overflow > img {
|
.InputfieldImageEditAll .gridImage__overflow > img {
|
||||||
display: block;
|
display: block;
|
||||||
position: static !important;
|
position: static !important;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
transition: none;
|
transition: none;
|
||||||
-ms-transform: none;
|
-ms-transform: none;
|
||||||
transform: none;
|
transform: none;
|
||||||
@@ -438,6 +471,10 @@
|
|||||||
max-width: 100% important;
|
max-width: 100% important;
|
||||||
height: initial !important;
|
height: initial !important;
|
||||||
cursor: move; }
|
cursor: move; }
|
||||||
|
.InputfieldImageEditAll .gridImage__overflow > .focusArea {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 10px; }
|
||||||
.InputfieldImageEditAll .gridImage .ImageData {
|
.InputfieldImageEditAll .gridImage .ImageData {
|
||||||
position: relative;
|
position: relative;
|
||||||
float: left;
|
float: left;
|
||||||
|
@@ -30,6 +30,9 @@ function InputfieldImage($) {
|
|||||||
// grid items to retry for sizing by setGridSize() methods
|
// grid items to retry for sizing by setGridSize() methods
|
||||||
var retryGridItems = [];
|
var retryGridItems = [];
|
||||||
|
|
||||||
|
// whether or not zoom-focus feature is available
|
||||||
|
var useZoomFocus = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not AJAX drag/drop upload is allowed?
|
* Whether or not AJAX drag/drop upload is allowed?
|
||||||
*
|
*
|
||||||
@@ -125,7 +128,7 @@ function InputfieldImage($) {
|
|||||||
});
|
});
|
||||||
$el.removeClass('InputfieldImageSorting');
|
$el.removeClass('InputfieldImageSorting');
|
||||||
},
|
},
|
||||||
cancel: ".InputfieldImageEdit,input,textarea,button,select,option"
|
cancel: ".InputfieldImageEdit,.focusArea,input,textarea,button,select,option"
|
||||||
};
|
};
|
||||||
|
|
||||||
$el.sortable(sortableOptions);
|
$el.sortable(sortableOptions);
|
||||||
@@ -280,6 +283,10 @@ function InputfieldImage($) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function windowResize() {
|
function windowResize() {
|
||||||
|
$('.focusArea.focusActive').each(function() {
|
||||||
|
var $edit = $(this).closest('.InputfieldImageEdit, .gridImage');
|
||||||
|
if($edit.length) stopFocus($edit);
|
||||||
|
});
|
||||||
updateGrid();
|
updateGrid();
|
||||||
checkInputfieldWidth();
|
checkInputfieldWidth();
|
||||||
}
|
}
|
||||||
@@ -318,12 +325,11 @@ function InputfieldImage($) {
|
|||||||
*/
|
*/
|
||||||
function setupEdit($el, $edit) {
|
function setupEdit($el, $edit) {
|
||||||
|
|
||||||
if($el.closest('.InputfieldImageEditAll').length) return;
|
if($el.closest('.InputfieldImageEditAll').length) return; // edit all mode
|
||||||
|
|
||||||
var $img = $edit.find(".InputfieldImageEdit__image");
|
var $img = $edit.find(".InputfieldImageEdit__image");
|
||||||
var $thumb = $el.find("img");
|
var $thumb = $el.find("img");
|
||||||
|
|
||||||
|
|
||||||
$img.attr({
|
$img.attr({
|
||||||
src: $thumb.attr("data-original"),
|
src: $thumb.attr("data-original"),
|
||||||
"data-original": $thumb.attr("data-original"),
|
"data-original": $thumb.attr("data-original"),
|
||||||
@@ -348,7 +354,6 @@ function InputfieldImage($) {
|
|||||||
.find("img")
|
.find("img")
|
||||||
.add($img)
|
.add($img)
|
||||||
.magnificPopup(options);
|
.magnificPopup(options);
|
||||||
//.addClass('magnificInit');
|
|
||||||
|
|
||||||
// move all of the .ImageData elements to the edit panel
|
// move all of the .ImageData elements to the edit panel
|
||||||
$edit.find(".InputfieldImageEdit__edit")
|
$edit.find(".InputfieldImageEdit__edit")
|
||||||
@@ -356,6 +361,247 @@ function InputfieldImage($) {
|
|||||||
.append($el.find(".ImageData").children().not(".InputfieldFileSort"));
|
.append($el.find(".ImageData").children().not(".InputfieldFileSort"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup image for a draggable focus area and optional zoom slider
|
||||||
|
*
|
||||||
|
* @param $edit Image editor container (.InputfieldImageEdit or .gridImage)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function startFocus($edit) {
|
||||||
|
|
||||||
|
var $img, $el, $thumb, $input, $focusArea, $focusCircle, $inputfield,
|
||||||
|
focusData = null, gridSize, mode,
|
||||||
|
$zoomSlider, $zoomBox, lastZoomPercent = 0,
|
||||||
|
lastZoomVisual = 0, startZoomVisual = -1;
|
||||||
|
|
||||||
|
$inputfield = $edit.closest('.Inputfield');
|
||||||
|
gridSize = getCookieData($inputfield, 'size');
|
||||||
|
mode = getCookieData($inputfield, 'mode');
|
||||||
|
|
||||||
|
if($edit.hasClass('gridImage')) {
|
||||||
|
// list mode
|
||||||
|
$el = $edit;
|
||||||
|
$img = $edit.find('.gridImage__overflow').find('img');
|
||||||
|
$thumb = $img;
|
||||||
|
} else {
|
||||||
|
// thumbnail click for editor mode
|
||||||
|
$el = $('#' + $edit.attr('data-for'));
|
||||||
|
$img = $edit.find('.InputfieldImageEdit__image');
|
||||||
|
$thumb = $el.find('.gridImage__overflow').find('img');
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the focus object, optionally for a specific focusStr
|
||||||
|
function getFocus(focusStr) {
|
||||||
|
if(typeof focusStr == "undefined") {
|
||||||
|
if(focusData !== null) return focusData;
|
||||||
|
var $input = $edit.find('.InputfieldImageFocus');
|
||||||
|
var focusStr = $input.val();
|
||||||
|
}
|
||||||
|
var a = focusStr.split(' ');
|
||||||
|
var data = {
|
||||||
|
'top': (typeof a[0] == "undefined" ? 50.0 : parseFloat(a[0])),
|
||||||
|
'left': (typeof a[1] == "undefined" ? 50.0 : parseFloat(a[1])),
|
||||||
|
'zoom': (typeof a[2] == "undefined" ? 0 : parseInt(a[2]))
|
||||||
|
};
|
||||||
|
focusData = data;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get focus string
|
||||||
|
function getFocusStr(focusObj) {
|
||||||
|
if(typeof focusObj == "undefined") focusObj = getFocus();
|
||||||
|
return focusObj.top + ' ' + focusObj.left + ' ' + focusObj.zoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get single focus property: top left or zoom
|
||||||
|
function getFocusProperty(property) {
|
||||||
|
var focus = getFocus();
|
||||||
|
return focus[property];
|
||||||
|
}
|
||||||
|
|
||||||
|
// set focus for top left and zoom
|
||||||
|
function setFocus(focusObj) {
|
||||||
|
focusData = focusObj;
|
||||||
|
var focusStr = focusObj.top + ' ' + focusObj.left + ' ' + focusObj.zoom;
|
||||||
|
$thumb.attr('data-focus', focusStr); // for consumption outside startFocus()
|
||||||
|
$input = $edit.find('.InputfieldImageFocus');
|
||||||
|
if(focusStr != $input.val()) {
|
||||||
|
$input.val(focusStr).trigger('change');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set just one focus property (top, left or zoom)
|
||||||
|
function setFocusProperty(property, value) {
|
||||||
|
var focus = getFocus();
|
||||||
|
focus[property] = value;
|
||||||
|
setFocus(focus);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the position of the draggable focus item
|
||||||
|
function setFocusDragPosition() {
|
||||||
|
var focus = getFocus();
|
||||||
|
var $overlay = $focusCircle.parent();
|
||||||
|
var w = $overlay.width();
|
||||||
|
var h = $overlay.height();
|
||||||
|
var x = Math.round(((focus.left / 100) * w) - ($focusCircle.width() / 1.7));
|
||||||
|
var y = Math.round(((focus.top / 100) * h) - ($focusCircle.height() / 2.3));
|
||||||
|
if(x < 0) x = 0;
|
||||||
|
if(y < 0) y = 0;
|
||||||
|
$focusCircle.css({
|
||||||
|
'top': y + 'px',
|
||||||
|
'left': x + 'px'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup focus area (div that contains all the focus stuff)
|
||||||
|
$focusArea = $img.siblings('.focusArea');
|
||||||
|
if(!$focusArea.length) {
|
||||||
|
$focusArea = $('<div />').addClass('focusArea');
|
||||||
|
$img.after($focusArea);
|
||||||
|
}
|
||||||
|
$focusArea.css({
|
||||||
|
'height': $img.height() + 'px',
|
||||||
|
'width': $img.width() + 'px'
|
||||||
|
}).addClass('focusActive');
|
||||||
|
|
||||||
|
// set the draggable circle for focus
|
||||||
|
$focusCircle = $focusArea.find('.focusCircle');
|
||||||
|
if(!$focusCircle.length) {
|
||||||
|
$focusCircle = $("<div />").addClass('focusCircle');
|
||||||
|
$focusArea.append($focusCircle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// indicate active state for focusing, used by stopFocus()
|
||||||
|
$img.parent().addClass('focusWrap');
|
||||||
|
|
||||||
|
// set the initial position for the focus circle
|
||||||
|
setFocusDragPosition();
|
||||||
|
|
||||||
|
// function called whenever the slider is moved
|
||||||
|
var zoomSlide = function(zoomPercent) {
|
||||||
|
if(typeof zoomPercent == "undefined") zoomPercent = lastZoomPercent;
|
||||||
|
lastZoomPercent = zoomPercent;
|
||||||
|
var w = (100 - zoomPercent) + '%';
|
||||||
|
$zoomBox.width(w);
|
||||||
|
var zoomBoxSize = $zoomBox.width();
|
||||||
|
var focusCircleSize = $focusCircle.height();
|
||||||
|
$zoomBox.height(zoomBoxSize)
|
||||||
|
|
||||||
|
var zoom = zoomPercent;
|
||||||
|
var top = parseInt($focusCircle.css('top')); // top of drag item
|
||||||
|
top += Math.floor(focusCircleSize / 2); // plus half the height of drag item
|
||||||
|
top -= Math.ceil(zoomBoxSize / 2) - 3; // minus half the height of the zoom box (-3)
|
||||||
|
|
||||||
|
var left = parseInt($focusCircle.css('left'));
|
||||||
|
left += Math.floor(focusCircleSize / 2);
|
||||||
|
left -= Math.ceil(zoomBoxSize / 2) - 3;
|
||||||
|
|
||||||
|
if(top < 0) top = 0;
|
||||||
|
if(left < 0) left = 0;
|
||||||
|
// constrain to corners
|
||||||
|
if(top + zoomBoxSize > $focusArea.height()) top = $focusArea.height() - zoomBoxSize;
|
||||||
|
if(left + zoomBoxSize > $focusArea.width()) left = $focusArea.width() - zoomBoxSize;
|
||||||
|
|
||||||
|
$zoomBox.css({
|
||||||
|
top: top + 'px',
|
||||||
|
left: left + 'px'
|
||||||
|
});
|
||||||
|
|
||||||
|
setFocusProperty('zoom', zoomPercent);
|
||||||
|
|
||||||
|
// determine when to visually start showing zoom (in grid mode)
|
||||||
|
var zoomVisual = zoomPercent;
|
||||||
|
if(zoomBoxSize > $img.height() || zoomBoxSize > $img.width()) {
|
||||||
|
zoomVisual = 0;
|
||||||
|
} else {
|
||||||
|
if(!lastZoomVisual) startZoomVisual = zoomVisual;
|
||||||
|
zoomVisual = (zoomVisual - startZoomVisual)+1;
|
||||||
|
}
|
||||||
|
if(mode == 'grid') setGridSizeItem($thumb.parent(), gridSize, false, zoomVisual);
|
||||||
|
lastZoomVisual = zoomVisual;
|
||||||
|
/*
|
||||||
|
console.log('lastZoomVisual=' + lastZoomVisual + ', startZoomVisual=' + startZoomVisual +
|
||||||
|
', img.height=' + $img.height() + ', img.width=' + $img.width() +
|
||||||
|
', zoomBoxSize=' + zoomBoxSize + ', zoomVisual=' + zoomVisual);
|
||||||
|
*/
|
||||||
|
}; // zoomSlide
|
||||||
|
|
||||||
|
// function called when the focus item is dragged
|
||||||
|
var dragEvent = function(event, ui) {
|
||||||
|
var $this = $(this);
|
||||||
|
var w = $this.parent().width();
|
||||||
|
var h = $this.parent().height();
|
||||||
|
var t = ui.position.top > 0 ? ui.position.top + ($this.width() / 2) : 0;
|
||||||
|
var l = ui.position.left > 0 ? ui.position.left + ($this.height() / 2) : 0;
|
||||||
|
var oldFocus = getFocus();
|
||||||
|
var newFocus = {
|
||||||
|
'top': t > 0 ? ((t / h) * 100) : 0,
|
||||||
|
'left': l > 0 ? ((l / w) * 100) : 0,
|
||||||
|
'zoom': getFocusProperty('zoom')
|
||||||
|
};
|
||||||
|
setFocus(newFocus);
|
||||||
|
if(useZoomFocus) {
|
||||||
|
zoomSlide();
|
||||||
|
} else if(mode == 'grid') {
|
||||||
|
setGridSizeItem($thumb.parent(), gridSize, false);
|
||||||
|
}
|
||||||
|
}; // dragEvent
|
||||||
|
|
||||||
|
// make draggable and attach events
|
||||||
|
$focusCircle.draggable({
|
||||||
|
containment: 'parent',
|
||||||
|
drag: dragEvent,
|
||||||
|
stop: dragEvent
|
||||||
|
});
|
||||||
|
|
||||||
|
if(useZoomFocus) {
|
||||||
|
// setup the focus zoom slider
|
||||||
|
var zoom = getFocusProperty('zoom');
|
||||||
|
$zoomSlider = $("<div />").addClass('focusZoomSlider').css({
|
||||||
|
'margin-top': '5px'
|
||||||
|
});
|
||||||
|
$zoomBox = $("<div />").addClass('focusZoomBox').css({
|
||||||
|
'position': 'absolute',
|
||||||
|
'background': 'rgba(0,0,0,0.5)',
|
||||||
|
'box-shadow': '0 0 20px rgba(0,0,0,.9)'
|
||||||
|
});
|
||||||
|
$focusArea.prepend($zoomBox);
|
||||||
|
$img.after($zoomSlider);
|
||||||
|
$thumb.attr('src', $img.attr('src'));
|
||||||
|
$zoomSlider.slider({
|
||||||
|
min: 0,
|
||||||
|
max: 80,
|
||||||
|
value: zoom,
|
||||||
|
range: 'max',
|
||||||
|
slide: function(event, ui) {
|
||||||
|
zoomSlide(ui.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
zoomSlide(zoom);
|
||||||
|
} else {
|
||||||
|
$focusArea.css('background-color', 'rgba(0,0,0,0.5)');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopFocus($edit) {
|
||||||
|
$focusCircle = $edit.find('.focusCircle');
|
||||||
|
if($focusCircle.length) {
|
||||||
|
var $focusWrap = $focusCircle.closest('.focusWrap');
|
||||||
|
$focusWrap.find('.focusZoomSlider').slider('destroy').remove();
|
||||||
|
$focusWrap.find('.focusZoomBox').remove();
|
||||||
|
$focusWrap.removeClass('focusWrap');
|
||||||
|
$focusCircle.draggable('destroy');
|
||||||
|
$focusCircle.parent().removeClass('focusActive');
|
||||||
|
$focusCircle.remove();
|
||||||
|
var $button = $edit.find('.InputfieldImageButtonFocus');
|
||||||
|
if($button.length) {
|
||||||
|
$icon = $button.find('i');
|
||||||
|
$icon.removeClass('focusIconActive').toggleClass($icon.attr('data-toggle'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tear down the InputfieldImageEdit panel
|
* Tear down the InputfieldImageEdit panel
|
||||||
*
|
*
|
||||||
@@ -363,6 +609,8 @@ function InputfieldImage($) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function tearDownEdit($edit) {
|
function tearDownEdit($edit) {
|
||||||
|
stopFocus($edit);
|
||||||
|
$edit.off('click', '.InputfieldImageButtonFocus');
|
||||||
$inputArea = $edit.find(".InputfieldImageEdit__edit");
|
$inputArea = $edit.find(".InputfieldImageEdit__edit");
|
||||||
if($inputArea.children().not(".InputfieldFileSort").length) {
|
if($inputArea.children().not(".InputfieldFileSort").length) {
|
||||||
var $items = $inputArea.children();
|
var $items = $inputArea.children();
|
||||||
@@ -517,6 +765,23 @@ function InputfieldImage($) {
|
|||||||
};
|
};
|
||||||
$.magnificPopup.open(options);
|
$.magnificPopup.open(options);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
}).on('click', '.InputfieldImageButtonFocus', function() {
|
||||||
|
|
||||||
|
var $button = $(this);
|
||||||
|
var $icon = $button.find('i');
|
||||||
|
var $edit = $button.closest('.InputfieldImageEdit, .gridImage');
|
||||||
|
var $focusCircle = $edit.find('.focusCircle');
|
||||||
|
|
||||||
|
if($focusCircle.length) {
|
||||||
|
// stops focus
|
||||||
|
stopFocus($edit);
|
||||||
|
} else {
|
||||||
|
// starts focus
|
||||||
|
startFocus($edit);
|
||||||
|
$icon.addClass('focusIconActive');
|
||||||
|
$icon.toggleClass($icon.attr('data-toggle'));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on("click", function(e) {
|
$(document).on("click", function(e) {
|
||||||
@@ -662,8 +927,14 @@ function InputfieldImage($) {
|
|||||||
pct = Math.floor(pct);
|
pct = Math.floor(pct);
|
||||||
$inputfield.find(".gridImage__overflow").each(function() {
|
$inputfield.find(".gridImage__overflow").each(function() {
|
||||||
var dataPct = 100 - pct;
|
var dataPct = 100 - pct;
|
||||||
$(this).css('width', pct + '%');
|
var $this = $(this);
|
||||||
$(this).siblings('.ImageData').css('width', dataPct + '%');
|
$this.css('width', pct + '%');
|
||||||
|
$this.siblings('.ImageData').css('width', dataPct + '%');
|
||||||
|
$this.find('img').css({
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
transform: 'none',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
setCookieData($inputfield, 'listSize', pct);
|
setCookieData($inputfield, 'listSize', pct);
|
||||||
}
|
}
|
||||||
@@ -718,9 +989,10 @@ function InputfieldImage($) {
|
|||||||
* @param $item
|
* @param $item
|
||||||
* @param gridSize
|
* @param gridSize
|
||||||
* @param ragged
|
* @param ragged
|
||||||
|
* @param zoom
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function setGridSizeItem($item, gridSize, ragged) {
|
function setGridSizeItem($item, gridSize, ragged, zoom) {
|
||||||
|
|
||||||
if($item.hasClass('gridImage__overflow')) {
|
if($item.hasClass('gridImage__overflow')) {
|
||||||
var $img = $item.children('img');
|
var $img = $item.children('img');
|
||||||
@@ -737,22 +1009,108 @@ function InputfieldImage($) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(typeof zoom == "undefined") zoom = 0;
|
||||||
|
|
||||||
|
var focus = {};
|
||||||
var w = $img.width();
|
var w = $img.width();
|
||||||
var h = $img.height();
|
var h = $img.height();
|
||||||
if(!w) w = parseInt($img.attr('data-w'));
|
var dataW = parseInt($img.attr('data-w'));
|
||||||
if(!h) h = parseInt($img.attr('data-h'));
|
var dataH = parseInt($img.attr('data-h'));
|
||||||
|
if(!w) w = dataW;
|
||||||
|
if(!h) h = dataH;
|
||||||
|
|
||||||
|
if(!ragged) {
|
||||||
|
var focusStr = $img.attr('data-focus');
|
||||||
|
if(typeof focusStr == "undefined") focusStr = '50.0 50.0 0';
|
||||||
|
var focusArray = focusStr.split(' ');
|
||||||
|
focus = {
|
||||||
|
top: parseFloat(focusArray[0]),
|
||||||
|
left: parseFloat(focusArray[1]),
|
||||||
|
zoom: parseInt(focusArray[2])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if(ragged) {
|
if(ragged) {
|
||||||
$img.css('max-height', '100%').css('max-width', 'none');
|
// show full thumbnail (not square)
|
||||||
$img.attr('height', gridSize).removeAttr('width');
|
$img.attr('height', gridSize).removeAttr('width');
|
||||||
} else if(w >= h) {
|
$img.css({
|
||||||
$img.css('max-height', '100%').css('max-width', 'none');
|
'max-height': '100%',
|
||||||
|
'max-width': 'none',
|
||||||
|
'top': '50%',
|
||||||
|
'left': '50%',
|
||||||
|
'transform': 'translate3d(-50%, -50%, 0)'
|
||||||
|
});
|
||||||
|
|
||||||
|
} else if(zoom > 0 && useZoomFocus) {
|
||||||
|
// focus with zoom
|
||||||
|
if(w >= h) {
|
||||||
$img.attr('height', gridSize).removeAttr('width');
|
$img.attr('height', gridSize).removeAttr('width');
|
||||||
} else if(h > w) {
|
var maxHeight = '100%';
|
||||||
$img.css('max-height', 'none').css('max-width', '100%');
|
var maxWidth = 'none';
|
||||||
$img.attr('width', gridSize).removeAttr('height');
|
|
||||||
} else {
|
} else {
|
||||||
$img.css('max-height', '100%').css('max-width', 'none');
|
var maxHeight = 'none';
|
||||||
|
var maxWidth = '100%';
|
||||||
|
$img.attr('width', gridSize).removeAttr('height');
|
||||||
|
}
|
||||||
|
var top = focus.top;
|
||||||
|
var left = focus.left;
|
||||||
|
var scale = 1 + (zoom / 25); //(zoom * 0.037);
|
||||||
|
if(scale < 0) scale = 0;
|
||||||
|
if(left < 1.0) left = 0.001;
|
||||||
|
if(top < 1.0) top = 0.001;
|
||||||
|
if(left >= 55) {
|
||||||
|
left += (left * 0.15);
|
||||||
|
} else if(left <= 45) {
|
||||||
|
left -= (left * 0.15);
|
||||||
|
}
|
||||||
|
if(top > 50) {
|
||||||
|
top += (top * 0.1);
|
||||||
|
} else if(top < 50) {
|
||||||
|
top -= (top * 0.1);
|
||||||
|
}
|
||||||
|
if(left > 100) left = 100;
|
||||||
|
if(top > 100) top = 100;
|
||||||
|
$img.css({
|
||||||
|
'max-height': maxHeight,
|
||||||
|
'max-width': maxWidth,
|
||||||
|
'top': top + '%',
|
||||||
|
'left': left + '%',
|
||||||
|
'transform-origin': 'top left',
|
||||||
|
'transform': 'scale(' + scale + ') translate3d(-' + (left) + '%, -' + (top) + '%, 0)'
|
||||||
|
});
|
||||||
|
// console.log("top=" + top + ", left=" + left + ", scale=" + scale);
|
||||||
|
|
||||||
|
} else if(w >= h) {
|
||||||
|
// image width greater than height
|
||||||
|
$img.attr('height', gridSize).removeAttr('width');
|
||||||
|
if(focus.left < 1) focus.left = 0.001;
|
||||||
|
$img.css({
|
||||||
|
'max-height': '100%',
|
||||||
|
'max-width': 'none',
|
||||||
|
'top': '50%',
|
||||||
|
'left': focus.left + '%',
|
||||||
|
'transform': 'translate3d(-' + focus.left + '%, -50%, 0)'
|
||||||
|
});
|
||||||
|
} else if(h > w) {
|
||||||
|
// image height greater tahn width
|
||||||
|
$img.attr('width', gridSize).removeAttr('height');
|
||||||
|
if(focus.top < 1) focus.top = 0.001;
|
||||||
|
$img.css({
|
||||||
|
'max-height': 'none',
|
||||||
|
'max-width': '100%',
|
||||||
|
'top': focus.top + '%',
|
||||||
|
'left': '50%',
|
||||||
|
'transform': 'translate3d(-50%, -' + focus.top + '%, 0)'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// perfectly square image
|
||||||
|
$img.css({
|
||||||
|
'max-height': '100%',
|
||||||
|
'max-width': 'none',
|
||||||
|
'top': '50%',
|
||||||
|
'left': '50%',
|
||||||
|
'transform': 'translate3d(-50%, -50%, 0)'
|
||||||
|
});
|
||||||
$img.removeAttr('width').attr('height', gridSize);
|
$img.removeAttr('width').attr('height', gridSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -808,8 +1166,10 @@ function InputfieldImage($) {
|
|||||||
var $inputfield = $a.closest('.Inputfield');
|
var $inputfield = $a.closest('.Inputfield');
|
||||||
var href = $a.attr('href');
|
var href = $a.attr('href');
|
||||||
var size;
|
var size;
|
||||||
|
var $aPrev = $a.parent().children('.' + activeClass);
|
||||||
|
var hrefPrev = $aPrev.attr('href');
|
||||||
|
|
||||||
$a.parent().children('.' + activeClass).removeClass(activeClass);
|
$aPrev.removeClass(activeClass);
|
||||||
$a.addClass(activeClass);
|
$a.addClass(activeClass);
|
||||||
|
|
||||||
if(href == 'list') {
|
if(href == 'list') {
|
||||||
@@ -831,6 +1191,10 @@ function InputfieldImage($) {
|
|||||||
size = getCookieData($inputfield, 'size');
|
size = getCookieData($inputfield, 'size');
|
||||||
setGridSize($inputfield, size, false);
|
setGridSize($inputfield, size, false);
|
||||||
setCookieData($inputfield, 'mode', 'grid');
|
setCookieData($inputfield, 'mode', 'grid');
|
||||||
|
if(hrefPrev == 'left') setTimeout(function() {
|
||||||
|
// because width/height aren't immediately available for img, so run again in this case
|
||||||
|
setGridSize($inputfield, size, false);
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
//hrefPrev = href; //hrefPrev == href && href != 'left' && href != 'list' ? '' : href;
|
//hrefPrev = href; //hrefPrev == href && href != 'left' && href != 'list' ? '' : href;
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -52,7 +52,7 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
|||||||
return array(
|
return array(
|
||||||
'title' => __('Images', __FILE__), // Module Title
|
'title' => __('Images', __FILE__), // Module Title
|
||||||
'summary' => __('One or more image uploads (sortable)', __FILE__), // Module Summary
|
'summary' => __('One or more image uploads (sortable)', __FILE__), // Module Summary
|
||||||
'version' => 121,
|
'version' => 122,
|
||||||
'permanent' => true,
|
'permanent' => true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -125,6 +125,7 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
|||||||
|
|
||||||
$this->labels = array_merge($this->labels, array(
|
$this->labels = array_merge($this->labels, array(
|
||||||
'crop' => $this->_('Crop'),
|
'crop' => $this->_('Crop'),
|
||||||
|
'focus' => $this->_('Focus'),
|
||||||
'variations' => $this->_('Variations'),
|
'variations' => $this->_('Variations'),
|
||||||
'dimensions' => $this->_('Dimensions'),
|
'dimensions' => $this->_('Dimensions'),
|
||||||
'filesize' => $this->_('Filesize'),
|
'filesize' => $this->_('Filesize'),
|
||||||
@@ -145,6 +146,11 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
|||||||
$this->themeSettings = array_merge($this->themeSettings, $themeSettings);
|
$this->themeSettings = array_merge($this->themeSettings, $themeSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function get($key) {
|
||||||
|
if($key == 'themeSettings') return $this->themeSettings;
|
||||||
|
return parent::get($key);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called right before Inputfield render
|
* Called right before Inputfield render
|
||||||
*
|
*
|
||||||
@@ -186,12 +192,15 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!$renderValueMode && $this->value instanceof Pageimages) {
|
if(!$renderValueMode && $this->value instanceof Pageimages) {
|
||||||
|
$page = $this->hasPage;
|
||||||
|
if(!$page || !$page->id) {
|
||||||
$process = $this->wire('process');
|
$process = $this->wire('process');
|
||||||
if($process instanceof WirePageEditor) {
|
if($process instanceof WirePageEditor) {
|
||||||
$page = $process->getPage();
|
$page = $process->getPage();
|
||||||
} else {
|
} else {
|
||||||
$page = new NullPage();
|
$page = new NullPage();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if($page->id && $this->wire('user')->hasPermission('page-edit-images', $page)) {
|
if($page->id && $this->wire('user')->hasPermission('page-edit-images', $page)) {
|
||||||
$modules->get('JqueryUI')->use('modal');
|
$modules->get('JqueryUI')->use('modal');
|
||||||
} else {
|
} else {
|
||||||
@@ -250,6 +259,7 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
|||||||
|
|
||||||
if(!$this->renderValueMode) {
|
if(!$this->renderValueMode) {
|
||||||
$dropNew = $this->wire('sanitizer')->entities1($this->_('drop in new image file to replace'));
|
$dropNew = $this->wire('sanitizer')->entities1($this->_('drop in new image file to replace'));
|
||||||
|
$focus = $this->wire('sanitizer')->entities1($this->_('drag circle to center of focus'));
|
||||||
$out .= "
|
$out .= "
|
||||||
<div class='InputfieldImageEdit'>
|
<div class='InputfieldImageEdit'>
|
||||||
<div class='InputfieldImageEdit__inner'>
|
<div class='InputfieldImageEdit__inner'>
|
||||||
@@ -258,7 +268,8 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
|||||||
<div class='InputfieldImageEdit__imagewrapper'>
|
<div class='InputfieldImageEdit__imagewrapper'>
|
||||||
<div>
|
<div>
|
||||||
<img class='InputfieldImageEdit__image' src='' alt=''>
|
<img class='InputfieldImageEdit__image' src='' alt=''>
|
||||||
<small class='detail'><i class='fa fa-upload'></i> $dropNew</small>
|
<small class='detail detail-upload'>$dropNew</small>
|
||||||
|
<small class='detail detail-focus'>$focus</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class='InputfieldImageEdit__edit'></div>
|
<div class='InputfieldImageEdit__edit'></div>
|
||||||
@@ -474,6 +485,7 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
|||||||
|
|
||||||
$imageSizerOptions = $this->imageSizerOptions;
|
$imageSizerOptions = $this->imageSizerOptions;
|
||||||
$imageSizerOptions['upscaling'] = true;
|
$imageSizerOptions['upscaling'] = true;
|
||||||
|
$imageSizerOptions['focus'] = false; // disable focus since we show focus from JS/CSS in admin thumbs
|
||||||
$adminThumbOptions = $this->wire('config')->adminThumbOptions;
|
$adminThumbOptions = $this->wire('config')->adminThumbOptions;
|
||||||
$gridSize2x = $this->gridSize * 2;
|
$gridSize2x = $this->gridSize * 2;
|
||||||
// if($adminThumbOptions['scale'] === 1.0) $gridSize2x = $this->gridSize; // force non-HiDPI
|
// if($adminThumbOptions['scale'] === 1.0) $gridSize2x = $this->gridSize; // force non-HiDPI
|
||||||
@@ -542,6 +554,10 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
|||||||
$attr['data-w'] = $_thumbWidth;
|
$attr['data-w'] = $_thumbWidth;
|
||||||
$attr['data-h'] = $_thumbHeight;
|
$attr['data-h'] = $_thumbHeight;
|
||||||
$attr["data-original"] = $img->URL;
|
$attr["data-original"] = $img->URL;
|
||||||
|
|
||||||
|
$focus = $img->focus();
|
||||||
|
$attr['data-focus'] = $focus['str'];
|
||||||
|
|
||||||
$markup = "<img ";
|
$markup = "<img ";
|
||||||
foreach($attr as $key => $value) $markup .= "$key=\"$value\" ";
|
foreach($attr as $key => $value) $markup .= "$key=\"$value\" ";
|
||||||
$markup .= " />";
|
$markup .= " />";
|
||||||
@@ -559,6 +575,7 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
|||||||
'error' => $error,
|
'error' => $error,
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
);
|
);
|
||||||
|
|
||||||
return $a;
|
return $a;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -639,6 +656,7 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
|||||||
// @todo move the following to separate method shared by the renderSingle
|
// @todo move the following to separate method shared by the renderSingle
|
||||||
$ext = $pagefile->ext();
|
$ext = $pagefile->ext();
|
||||||
$basename = $pagefile->basename(false);
|
$basename = $pagefile->basename(false);
|
||||||
|
$focus = $pagefile->focus();
|
||||||
|
|
||||||
$out .= "
|
$out .= "
|
||||||
<div class='ImageData'>
|
<div class='ImageData'>
|
||||||
@@ -651,6 +669,7 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
|||||||
<input class='InputfieldFileSort' type='text' name='sort_$id' value='$n' />
|
<input class='InputfieldFileSort' type='text' name='sort_$id' value='$n' />
|
||||||
<input class='InputfieldFileReplace' type='hidden' name='replace_$id' />
|
<input class='InputfieldFileReplace' type='hidden' name='replace_$id' />
|
||||||
<input class='InputfieldFileRename' type='hidden' name='rename_$id' />
|
<input class='InputfieldFileRename' type='hidden' name='rename_$id' />
|
||||||
|
<input class='InputfieldImageFocus' type='hidden' name='focus_$id' value='$focus[str]' />
|
||||||
</div>
|
</div>
|
||||||
";
|
";
|
||||||
}
|
}
|
||||||
@@ -745,15 +764,27 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
|||||||
$variationCount = count($variations);
|
$variationCount = count($variations);
|
||||||
$editUrl = $this->getEditUrl($pagefile, $pageID);
|
$editUrl = $this->getEditUrl($pagefile, $pageID);
|
||||||
$variationUrl = $this->getVariationUrl($pagefile, $id);
|
$variationUrl = $this->getVariationUrl($pagefile, $id);
|
||||||
$buttonClass = $this->themeSettings['buttonClass'] . " $this->modalClass pw-modal";
|
$buttonClass = $this->themeSettings['buttonClass'];
|
||||||
|
$modalButtonClass = trim("$buttonClass $this->modalClass pw-modal");
|
||||||
$modalAttrs = "data-buttons='#non_rte_dialog_buttons button' data-autoclose='1' data-close='#non_rte_cancel'";
|
$modalAttrs = "data-buttons='#non_rte_dialog_buttons button' data-autoclose='1' data-close='#non_rte_cancel'";
|
||||||
$labels = $this->labels;
|
$labels = $this->labels;
|
||||||
|
$out = '';
|
||||||
|
|
||||||
$buttonText = str_replace('{out}', "<span class='fa fa-crop'></span> $labels[crop]", $this->themeSettings['buttonText']);
|
|
||||||
$out = "<button type='button' data-href='$editUrl' class='InputfieldImageButtonCrop $buttonClass' $modalAttrs>$buttonText</button>";
|
// Crop
|
||||||
$buttonText = "<span class='fa fa-files-o'></span> $labels[variations] <span class='ui-priority-secondary'>($variationCount)</span>";
|
$buttonText = str_replace('{out}', "<i class='fa fa-crop'></i> $labels[crop]", $this->themeSettings['buttonText']);
|
||||||
|
$out .= "<button type='button' data-href='$editUrl' class='InputfieldImageButtonCrop $modalButtonClass' $modalAttrs>$buttonText</button>";
|
||||||
|
|
||||||
|
// Focus
|
||||||
|
$iconA = $pagefile->hasFocus ? 'fa-check-circle-o' : 'fa-circle-o';
|
||||||
|
$iconB = $pagefile->hasFocus ? 'fa-check-circle' : 'fa-dot-circle-o';
|
||||||
|
$buttonText = str_replace('{out}', "<i class='fa $iconA' data-toggle='$iconA $iconB'></i> $labels[focus]", $this->themeSettings['buttonText']);
|
||||||
|
$out .= "<button type='button' class='InputfieldImageButtonFocus $buttonClass'>$buttonText</button>";
|
||||||
|
|
||||||
|
// Variations
|
||||||
|
$buttonText = "<i class='fa fa-files-o'></i> $labels[variations] <span class='ui-priority-secondary'>($variationCount)</span>";
|
||||||
$buttonText = str_replace('{out}', $buttonText, $this->themeSettings['buttonText']);
|
$buttonText = str_replace('{out}', $buttonText, $this->themeSettings['buttonText']);
|
||||||
$out .= "<button type='button' data-href='$variationUrl' class='$buttonClass' data-buttons='button'>$buttonText</button>";
|
$out .= "<button type='button' data-href='$variationUrl' class='$modalButtonClass' data-buttons='button'>$buttonText</button>";
|
||||||
|
|
||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
@@ -1262,6 +1293,45 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process input for a given Pageimage
|
||||||
|
*
|
||||||
|
* @param WireInputData $input
|
||||||
|
* @param Pagefile|Pageimage $pagefile
|
||||||
|
* @param int $n
|
||||||
|
* @return bool
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function ___processInputFile(WireInputData $input, Pagefile $pagefile, $n) {
|
||||||
|
|
||||||
|
$changed = false;
|
||||||
|
|
||||||
|
$id = $this->name . '_' . $pagefile->hash;
|
||||||
|
$key = "focus_$id";
|
||||||
|
$val = $input->$key;
|
||||||
|
|
||||||
|
if($val !== null) {
|
||||||
|
if(!strlen($val)) $val = '50 50 0';
|
||||||
|
$focus = $pagefile->focus();
|
||||||
|
if($focus['str'] !== $val) {
|
||||||
|
$pagefile->focus($val);
|
||||||
|
$changed = true;
|
||||||
|
$focus = $pagefile->focus();
|
||||||
|
$rebuild = $pagefile->rebuildVariations();
|
||||||
|
// @todo rebuild variations only for images that specify both width and height
|
||||||
|
$this->message(
|
||||||
|
"Updated focus for $pagefile to: top=$focus[top]%, left=$focus[left]%, zoom=$focus[zoom] " .
|
||||||
|
"and rebuilt " . count($rebuild['rebuilt']) . " variations",
|
||||||
|
Notice::debug
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(parent::___processInputFile($input, $pagefile, $n)) $changed = true;
|
||||||
|
|
||||||
|
return $changed;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process an action on a Pagefile/Pageimage
|
* Process an action on a Pagefile/Pageimage
|
||||||
*
|
*
|
||||||
|
@@ -102,7 +102,7 @@ $itemPadding: 0.4em;
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transition: transform ease .3s;
|
//transition: transform ease .3s;
|
||||||
-ms-transform: translate3d(-50%, -50%, 0);
|
-ms-transform: translate3d(-50%, -50%, 0);
|
||||||
transform: translate3d(-50%, -50%, 0);
|
transform: translate3d(-50%, -50%, 0);
|
||||||
}
|
}
|
||||||
@@ -466,17 +466,30 @@ $itemPadding: 0.4em;
|
|||||||
& > div {
|
& > div {
|
||||||
// Fix for Firefox not respecting aspect ratio on images
|
// Fix for Firefox not respecting aspect ratio on images
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail {
|
.detail {
|
||||||
display: block;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
opacity: 0;
|
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
&:hover .detail {
|
.detail-upload {
|
||||||
|
display: block;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.detail-focus {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&:hover .detail-upload {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
& > div.focusWrap .detail-upload {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
& > div.focusWrap .detail-focus {
|
||||||
|
display: block;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__replace img {
|
&__replace img {
|
||||||
@@ -546,6 +559,33 @@ $itemPadding: 0.4em;
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gridImage__overflow .focusArea,
|
||||||
|
.InputfieldImageEdit .focusArea {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
color: #fff;
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
&.focusActive {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.focusCircle {
|
||||||
|
cursor: move;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border: 3px solid #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: rgba(255,255,255,0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.InputfieldImage .ImageData {
|
.InputfieldImage .ImageData {
|
||||||
// element in a .gridImage item that contains the details and editable content
|
// element in a .gridImage item that contains the details and editable content
|
||||||
display: none;
|
display: none;
|
||||||
@@ -670,6 +710,8 @@ $itemPadding: 0.4em;
|
|||||||
& > img {
|
& > img {
|
||||||
display: block;
|
display: block;
|
||||||
position: static !important;
|
position: static !important;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
transition: none;
|
transition: none;
|
||||||
-ms-transform: none;
|
-ms-transform: none;
|
||||||
transform: none;
|
transform: none;
|
||||||
@@ -678,6 +720,12 @@ $itemPadding: 0.4em;
|
|||||||
height: initial !important;
|
height: initial !important;
|
||||||
cursor: move;
|
cursor: move;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& > .focusArea {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px; // to match padding of __overflow
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ImageData {
|
.ImageData {
|
||||||
|
Reference in New Issue
Block a user