forked from Public/pics
Compare commits
22 Commits
refactor-t
...
improve_th
| Author | SHA1 | Date | |
|---|---|---|---|
| 187a7cd02f | |||
| 8414843bbf | |||
| 474c387786 | |||
| 12407d797d | |||
| 64d7433a56 | |||
| 58b7204fbf | |||
| 36a2779381 | |||
| 44bb501d13 | |||
| 9010123d18 | |||
| e3b67c4022 | |||
| 2bcdc5fe6e | |||
| edfad992cc | |||
| 357d95f6ff | |||
| 0ec0de4414 | |||
| 69417c36ed | |||
| f2d8a32e67 | |||
| 4863561129 | |||
| 8474d3b2b2 | |||
| 3bf69fd21f | |||
| 70e6001c85 | |||
| 4402521051 | |||
| 889302cd36 |
@@ -47,9 +47,13 @@ class ManageErrors extends HTMLController
|
|||||||
'parse' => [
|
'parse' => [
|
||||||
'type' => 'function',
|
'type' => 'function',
|
||||||
'data' => function($row) {
|
'data' => function($row) {
|
||||||
return $row['message'] . '<br><div><a onclick="this.parentNode.childNodes[1].style.display=\'block\';this.style.display=\'none\';">Show debug info</a>' .
|
return $row['message'] . '<br>' .
|
||||||
'<pre style="display: none">' . $row['debug_info'] . '</pre></div>' .
|
'<div><a onclick="this.parentNode.childNodes[1].style.display=\'block\';this.style.display=\'none\';">Show debug info</a>' .
|
||||||
'<small><a href="' . BASEURL . $row['request_uri'] . '">' . $row['request_uri'] . '</a></small>';
|
'<pre style="display: none">' . htmlspecialchars($row['debug_info']) .
|
||||||
|
'</pre></div>' .
|
||||||
|
'<small><a href="' . BASEURL .
|
||||||
|
htmlspecialchars($row['request_uri']) . '">' .
|
||||||
|
htmlspecialchars($row['request_uri']) . '</a></small>';
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
'header' => 'Message / URL',
|
'header' => 'Message / URL',
|
||||||
@@ -85,7 +89,7 @@ class ManageErrors extends HTMLController
|
|||||||
'header' => 'UID',
|
'header' => 'UID',
|
||||||
'is_sortable' => true,
|
'is_sortable' => true,
|
||||||
'parse' => [
|
'parse' => [
|
||||||
'link' => BASEURL . '/member/?id={ID_USER}',
|
'link' => BASEURL . '/edituser/?id={ID_USER}',
|
||||||
'data' => 'id_user',
|
'data' => 'id_user',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -488,7 +488,7 @@ class Database
|
|||||||
/**
|
/**
|
||||||
* This function can be used to insert data into the database in a secure way.
|
* This function can be used to insert data into the database in a secure way.
|
||||||
*/
|
*/
|
||||||
public function insert($method = 'replace', $table, $columns, $data)
|
public function insert($method, $table, $columns, $data)
|
||||||
{
|
{
|
||||||
// With nothing to insert, simply return.
|
// With nothing to insert, simply return.
|
||||||
if (empty($data))
|
if (empty($data))
|
||||||
|
|||||||
@@ -96,7 +96,9 @@ class EXIF
|
|||||||
elseif (!empty($exif['Make']))
|
elseif (!empty($exif['Make']))
|
||||||
$meta['camera'] = trim($exif['Make']);
|
$meta['camera'] = trim($exif['Make']);
|
||||||
|
|
||||||
if (!empty($exif['DateTimeDigitized']))
|
if (!empty($exif['DateTimeOriginal']))
|
||||||
|
$meta['created_timestamp'] = self::toUnixTime($exif['DateTimeOriginal']);
|
||||||
|
elseif (!empty($exif['DateTimeDigitized']))
|
||||||
$meta['created_timestamp'] = self::toUnixTime($exif['DateTimeDigitized']);
|
$meta['created_timestamp'] = self::toUnixTime($exif['DateTimeDigitized']);
|
||||||
|
|
||||||
return new self($meta);
|
return new self($meta);
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class GenericTable
|
|||||||
$this->start = empty($options['start']) || !is_numeric($options['start']) || $options['start'] < 0 || $options['start'] > $this->recordCount ? 0 : $options['start'];
|
$this->start = empty($options['start']) || !is_numeric($options['start']) || $options['start'] < 0 || $options['start'] > $this->recordCount ? 0 : $options['start'];
|
||||||
|
|
||||||
// Figure out where we are on the whole, too.
|
// Figure out where we are on the whole, too.
|
||||||
$numPages = ceil($this->recordCount / $this->items_per_page);
|
$numPages = max(1, ceil($this->recordCount / $this->items_per_page));
|
||||||
$this->currentPage = min(ceil($this->start / $this->items_per_page) + 1, $numPages);
|
$this->currentPage = min(ceil($this->start / $this->items_per_page) + 1, $numPages);
|
||||||
|
|
||||||
// Let's bear a few things in mind...
|
// Let's bear a few things in mind...
|
||||||
@@ -228,13 +228,15 @@ class GenericTable
|
|||||||
// Timestamps get custom treatment.
|
// Timestamps get custom treatment.
|
||||||
case 'timestamp':
|
case 'timestamp':
|
||||||
if (empty($options['data']['pattern']) || $options['data']['pattern'] === 'long')
|
if (empty($options['data']['pattern']) || $options['data']['pattern'] === 'long')
|
||||||
$pattern = '%F %H:%M';
|
$pattern = 'Y-m-d H:i';
|
||||||
elseif ($options['data']['pattern'] === 'short')
|
elseif ($options['data']['pattern'] === 'short')
|
||||||
$pattern = '%F';
|
$pattern = 'Y-m-d';
|
||||||
else
|
else
|
||||||
$pattern = $options['data']['pattern'];
|
$pattern = $options['data']['pattern'];
|
||||||
|
|
||||||
if (!is_numeric($rowData[$options['data']['timestamp']]))
|
if (!isset($rowData[$options['data']['timestamp']]))
|
||||||
|
$timestamp = 0;
|
||||||
|
elseif (!is_numeric($rowData[$options['data']['timestamp']]))
|
||||||
$timestamp = strtotime($rowData[$options['data']['timestamp']]);
|
$timestamp = strtotime($rowData[$options['data']['timestamp']]);
|
||||||
else
|
else
|
||||||
$timestamp = (int) $rowData[$options['data']['timestamp']];
|
$timestamp = (int) $rowData[$options['data']['timestamp']];
|
||||||
@@ -242,7 +244,7 @@ class GenericTable
|
|||||||
if (isset($options['data']['if_null']) && $timestamp == 0)
|
if (isset($options['data']['if_null']) && $timestamp == 0)
|
||||||
$value = $options['data']['if_null'];
|
$value = $options['data']['if_null'];
|
||||||
else
|
else
|
||||||
$value = strftime($pattern, $timestamp);
|
$value = date($pattern, $timestamp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,14 +67,33 @@ class Image extends Asset
|
|||||||
return EXIF::fromFile($this->getPath());
|
return EXIF::fromFile($this->getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPath()
|
public function getImageUrls($width = null, $height = null)
|
||||||
{
|
{
|
||||||
return ASSETSDIR . '/' . $this->subdir . '/' . $this->filename;
|
$image_urls = [];
|
||||||
|
if (isset($width) || isset($height))
|
||||||
|
{
|
||||||
|
$thumbnail = new Thumbnail($this);
|
||||||
|
$image_urls[1] = $this->getThumbnailUrl($width, $height, false);
|
||||||
|
|
||||||
|
// Can we afford to generate double-density thumbnails as well?
|
||||||
|
if ((!isset($width) || $this->image_width >= $width * 2) &&
|
||||||
|
(!isset($height) || $this->image_height >= $height * 2))
|
||||||
|
$image_urls[2] = $this->getThumbnailUrl($width * 2, $height * 2, false);
|
||||||
|
else
|
||||||
|
$image_urls[2] = $this->getThumbnailUrl($this->image_width, $this->image_height, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$image_urls[1] = $this->getUrl();
|
||||||
|
|
||||||
|
return $image_urls;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUrl()
|
public function getInlineImage($width = null, $height = null, $className = 'inline-image')
|
||||||
{
|
{
|
||||||
return ASSETSURL . '/' . $this->subdir . '/' . $this->filename;
|
$image_urls = $this->getImageUrls($width, $height);
|
||||||
|
|
||||||
|
return '<img class="' . $className . '" src="' . $image_urls[1] . '" alt=""' .
|
||||||
|
(isset($image_urls[2]) ? ' srcset="' . $image_urls[2] . ' 2x"' : '') . '>';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -141,7 +160,8 @@ class Image extends Asset
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Registry::get('db')->query('
|
return Registry::get('db')->query('
|
||||||
DELETE FROM assets_thumbs
|
UPDATE assets_thumbs
|
||||||
|
SET filename = NULL
|
||||||
WHERE id_asset = {int:id_asset}',
|
WHERE id_asset = {int:id_asset}',
|
||||||
['id_asset' => $this->id_asset]);
|
['id_asset' => $this->id_asset]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,9 +63,9 @@ class PageIndex
|
|||||||
lower current/cont. pgs. center upper
|
lower current/cont. pgs. center upper
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$this->num_pages = ceil($this->recordCount / $this->items_per_page);
|
$this->num_pages = max(1, ceil($this->recordCount / $this->items_per_page));
|
||||||
$this->current_page = min(ceil($this->start / $this->items_per_page) + 1, $this->num_pages);
|
$this->current_page = min(ceil($this->start / $this->items_per_page) + 1, $this->num_pages);
|
||||||
if ($this->num_pages == 0)
|
if ($this->num_pages <= 1)
|
||||||
{
|
{
|
||||||
$this->needsPageIndex = false;
|
$this->needsPageIndex = false;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
class Thumbnail
|
class Thumbnail
|
||||||
{
|
{
|
||||||
private $image;
|
private $image;
|
||||||
|
private $image_meta;
|
||||||
private $thumbnails;
|
private $thumbnails;
|
||||||
|
|
||||||
private $properly_initialised;
|
private $properly_initialised;
|
||||||
@@ -23,7 +24,7 @@ class Thumbnail
|
|||||||
const CROP_MODE_SLICE_CENTRE = 4;
|
const CROP_MODE_SLICE_CENTRE = 4;
|
||||||
const CROP_MODE_SLICE_BOTTOM = 5;
|
const CROP_MODE_SLICE_BOTTOM = 5;
|
||||||
|
|
||||||
public function __construct($image)
|
public function __construct(Image $image)
|
||||||
{
|
{
|
||||||
$this->image = $image;
|
$this->image = $image;
|
||||||
$this->image_meta = $image->getMeta();
|
$this->image_meta = $image->getMeta();
|
||||||
@@ -45,51 +46,45 @@ class Thumbnail
|
|||||||
$thumb_selector = $this->width . 'x' . $this->height . $this->filename_suffix;
|
$thumb_selector = $this->width . 'x' . $this->height . $this->filename_suffix;
|
||||||
if (!empty($this->thumbnails[$thumb_selector]))
|
if (!empty($this->thumbnails[$thumb_selector]))
|
||||||
{
|
{
|
||||||
$thumb_path = '/' . $this->image->getSubdir() . '/' . $this->thumbnails[$thumb_selector];
|
$thumb_filename = $this->image->getSubdir() . '/' . $this->thumbnails[$thumb_selector];
|
||||||
if (file_exists(THUMBSDIR . $thumb_path))
|
if (file_exists(THUMBSDIR . '/' . $thumb_filename))
|
||||||
return THUMBSURL . $thumb_path;
|
return THUMBSURL . '/' . $thumb_filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we have a custom thumbnail on file?
|
// Do we have a custom thumbnail on file?
|
||||||
$custom_selector = 'custom_' . $this->width . 'x' . $this->height;
|
$custom_selector = 'custom_' . $this->width . 'x' . $this->height;
|
||||||
if (isset($this->image_meta[$custom_selector]))
|
if (isset($this->image_meta[$custom_selector]))
|
||||||
{
|
{
|
||||||
$custom_thumb_path = '/' . $this->image->getSubdir() . '/' . $this->image_meta[$custom_selector];
|
$custom_filename = $this->image->getSubdir() . '/' . $this->image_meta[$custom_selector];
|
||||||
if (file_exists(ASSETSDIR . $custom_thumb_path))
|
if (file_exists(ASSETSDIR . '/' . $custom_filename))
|
||||||
{
|
{
|
||||||
// Ensure destination thumbnail directory exists.
|
|
||||||
if (!file_exists($this->image->getSubdir()))
|
|
||||||
@mkdir(THUMBSDIR . '/' . $this->image->getSubdir(), 0755, true);
|
|
||||||
|
|
||||||
// Copy the custom thumbail to the general thumbnail directory.
|
// Copy the custom thumbail to the general thumbnail directory.
|
||||||
copy(ASSETSDIR . $custom_thumb_path, THUMBSDIR . $custom_thumb_path);
|
copy(ASSETSDIR . '/' . $custom_filename, THUMBSDIR . '/' . $custom_filename);
|
||||||
|
|
||||||
// Let's remember this for future reference.
|
// Let's remember this for future reference.
|
||||||
$this->markAsGenerated($this->image_meta[$custom_selector]);
|
$this->markAsGenerated($this->image_meta[$custom_selector]);
|
||||||
|
|
||||||
return THUMBSURL . $custom_thumb_path;
|
return THUMBSURL . '/' . $custom_filename;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw new UnexpectedValueException('Custom thumbnail expected, but missing in file system!');
|
throw new UnexpectedValueException('Custom thumbnail expected, but missing in file system!');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this the right moment to generate a thumbnail, then?
|
// Is this the right moment to generate a thumbnail, then?
|
||||||
if ($generate && array_key_exists($thumb_selector, $this->thumbnails))
|
if ($generate)
|
||||||
{
|
{
|
||||||
return $this->generate();
|
if (array_key_exists($thumb_selector, $this->thumbnails))
|
||||||
|
return $this->generate();
|
||||||
|
else
|
||||||
|
throw new Exception("Trying to generate a thumbnail not previously queued by the system\n" .
|
||||||
|
print_r(func_get_args(), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not, queue it for generation at another time, and return a URL to generate it with.
|
// If not, queue it for generation at another time, and return a URL to generate it with.
|
||||||
elseif (!$generate)
|
|
||||||
{
|
|
||||||
$this->markAsQueued();
|
|
||||||
return BASEURL . '/thumbnail/' . $this->image->getId() . '/' . $this->width . 'x' . $this->height . $this->filename_suffix . '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Still here..? What are you up to? ..Sneaking?
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new Exception("Trying to generate a thumbnail for selector " . $thumb_selector . ", which does not appear to have been requested by the system.\n" . print_r(func_get_args(), true));
|
$this->markAsQueued();
|
||||||
|
return BASEURL . '/thumbnail/' . $this->image->getId() . '/' . $thumb_selector . '/';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,14 +255,18 @@ class Thumbnail
|
|||||||
'_' . $this->width . 'x' . $this->height . $this->filename_suffix . '.' . $ext;
|
'_' . $this->width . 'x' . $this->height . $this->filename_suffix . '.' . $ext;
|
||||||
|
|
||||||
// Ensure the thumbnail subdirectory exists.
|
// Ensure the thumbnail subdirectory exists.
|
||||||
if (!is_dir(THUMBSDIR . '/' . $this->image->getSubdir()))
|
$target_dir = THUMBSDIR . '/' . $this->image->getSubdir();
|
||||||
mkdir(THUMBSDIR . '/' . $this->image->getSubdir(), 0755, true);
|
if (!is_dir($target_dir))
|
||||||
|
mkdir($target_dir, 0755, true);
|
||||||
|
|
||||||
|
if (!is_writable($target_dir))
|
||||||
|
throw new Exception('Thumbnail directory is not writable!');
|
||||||
|
|
||||||
// No need to preserve every detail.
|
// No need to preserve every detail.
|
||||||
$thumb->setImageCompressionQuality(80);
|
$thumb->setImageCompressionQuality(80);
|
||||||
|
|
||||||
// Save it in a public spot.
|
// Save it in a public spot.
|
||||||
$thumb->writeImage(THUMBSDIR . '/' . $this->image->getSubdir() . '/' . $thumb_filename);
|
$thumb->writeImage($target_dir . '/' . $thumb_filename);
|
||||||
|
|
||||||
// Let's remember this for future reference...
|
// Let's remember this for future reference...
|
||||||
$this->markAsGenerated($thumb_filename);
|
$this->markAsGenerated($thumb_filename);
|
||||||
@@ -278,7 +277,6 @@ class Thumbnail
|
|||||||
// Finally, return the URL for the generated thumbnail image.
|
// Finally, return the URL for the generated thumbnail image.
|
||||||
return THUMBSURL . '/' . $this->image->getSubdir() . '/' . $thumb_filename;
|
return THUMBSURL . '/' . $this->image->getSubdir() . '/' . $thumb_filename;
|
||||||
}
|
}
|
||||||
// Blast! Curse your sudden but inevitable betrayal!
|
|
||||||
catch (ImagickException $e)
|
catch (ImagickException $e)
|
||||||
{
|
{
|
||||||
throw new Exception('ImageMagick error occurred while generating thumbnail. Output: ' . $e->getMessage());
|
throw new Exception('ImageMagick error occurred while generating thumbnail. Output: ' . $e->getMessage());
|
||||||
@@ -336,10 +334,16 @@ class Thumbnail
|
|||||||
if ($success)
|
if ($success)
|
||||||
{
|
{
|
||||||
$thumb_selector = $this->width . 'x' . $this->height . $this->filename_suffix;
|
$thumb_selector = $this->width . 'x' . $this->height . $this->filename_suffix;
|
||||||
$this->thumbnails[$thumb_selector] = $filename !== 'NULL' ? $filename : '';
|
$this->thumbnails[$thumb_selector] = $filename !== 'NULL' ? $filename : null;
|
||||||
}
|
|
||||||
|
|
||||||
return $success;
|
// For consistency, write new thumbnail filename to parent Image object.
|
||||||
|
// TODO: there could still be an inconsistency if multiple objects exists for the same image asset.
|
||||||
|
$this->image->getThumbnails()[$thumb_selector] = $this->thumbnails[$thumb_selector];
|
||||||
|
|
||||||
|
return $success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new UnexpectedValueException('Thumbnail queuing query failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function markAsQueued()
|
private function markAsQueued()
|
||||||
|
|||||||
@@ -13,164 +13,165 @@ provided that the following conditions are met:
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function AutoSuggest(opt) {
|
class AutoSuggest {
|
||||||
if (typeof opt.inputElement === "undefined" || typeof opt.listElement === "undefined" || typeof opt.baseUrl === "undefined" || typeof opt.appendCallback === "undefined") {
|
constructor(opt) {
|
||||||
return;
|
if (typeof opt.inputElement === "undefined" || typeof opt.listElement === "undefined" ||
|
||||||
|
typeof opt.baseUrl === "undefined" || typeof opt.appendCallback === "undefined") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.input = document.getElementById(opt.inputElement);
|
||||||
|
this.input.autocomplete = "off";
|
||||||
|
this.list = document.getElementById(opt.listElement);
|
||||||
|
this.appendCallback = opt.appendCallback;
|
||||||
|
this.baseurl = opt.baseUrl;
|
||||||
|
|
||||||
|
this.input.addEventListener('keydown', event => this.doSelection(event), false);
|
||||||
|
this.input.addEventListener('keyup', event => this.onType(event), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.input = document.getElementById(opt.inputElement);
|
doSelection(event) {
|
||||||
this.input.autocomplete = "off";
|
if (typeof this.container === "undefined" || this.container.children.length === 0) {
|
||||||
this.list = document.getElementById(opt.listElement);
|
return;
|
||||||
this.appendCallback = opt.appendCallback;
|
}
|
||||||
this.baseurl = opt.baseUrl;
|
|
||||||
|
|
||||||
var self = this;
|
switch (event.key) {
|
||||||
this.input.addEventListener('keydown', function(event) {
|
case 'Enter':
|
||||||
self.doSelection(event);
|
event.preventDefault();
|
||||||
}, false);
|
this.container.children[this.selectedIndex].click();
|
||||||
this.input.addEventListener('keyup', function(event) {
|
break;
|
||||||
self.onType(this, event);
|
|
||||||
}, false);
|
case 'ArrowUp':
|
||||||
|
case 'ArrowDown':
|
||||||
|
event.preventDefault();
|
||||||
|
this.findSelectedElement().className = '';
|
||||||
|
this.selectedIndex += event.key === 'ArrowUp' ? -1 : 1;
|
||||||
|
if (this.selectedIndex < 0) {
|
||||||
|
this.selectedIndex = this.container.children.length - 1;
|
||||||
|
} else if (this.selectedIndex === this.container.children.length) {
|
||||||
|
this.selectedIndex = 0;
|
||||||
|
}
|
||||||
|
let new_el = this.findSelectedElement().className = 'selected';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
findSelectedElement() {
|
||||||
|
return this.container.children[this.selectedIndex];
|
||||||
|
};
|
||||||
|
|
||||||
|
onType(event) {
|
||||||
|
if (['Enter', 'ArrowDown', 'ArrowUp'].indexOf(event.key) !== -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokens = event.target.value.split(/\s+/).filter(token => token.length >= 2);
|
||||||
|
|
||||||
|
if (tokens.length === 0) {
|
||||||
|
if (typeof this.container !== "undefined") {
|
||||||
|
this.clearContainer();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let request_uri = this.baseurl + '/suggest/?type=tags&data=' + window.encodeURIComponent(tokens.join(" "));
|
||||||
|
let request = new HttpRequest('get', request_uri, {}, this.onReceive, this);
|
||||||
|
};
|
||||||
|
|
||||||
|
onReceive(response, self) {
|
||||||
|
self.openContainer();
|
||||||
|
self.clearContainer();
|
||||||
|
self.fillContainer(response);
|
||||||
|
};
|
||||||
|
|
||||||
|
openContainer() {
|
||||||
|
if (this.container) {
|
||||||
|
if (!this.container.parentNode) {
|
||||||
|
this.input.parentNode.appendChild(this.container);
|
||||||
|
}
|
||||||
|
return this.container;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.container = document.createElement('ul');
|
||||||
|
this.container.className = 'autosuggest';
|
||||||
|
this.input.parentNode.appendChild(this.container);
|
||||||
|
return this.container;
|
||||||
|
};
|
||||||
|
|
||||||
|
clearContainer() {
|
||||||
|
while (this.container.children.length > 0) {
|
||||||
|
this.container.removeChild(this.container.children[0]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
clearInput() {
|
||||||
|
this.input.value = "";
|
||||||
|
this.input.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
closeContainer() {
|
||||||
|
this.container.parentNode.removeChild(this.container);
|
||||||
|
};
|
||||||
|
|
||||||
|
fillContainer(response) {
|
||||||
|
this.selectedIndex = 0;
|
||||||
|
|
||||||
|
let query = this.input.value.trim().replace(/[\-\[\]{}()*+?.,\\\/^\$|#]/g, ' ');
|
||||||
|
let query_tokens = query.split(/ +/).sort((a,b) => a.length - b.length);
|
||||||
|
|
||||||
|
response.items.forEach((item, i) => {
|
||||||
|
let node = document.createElement('li');
|
||||||
|
node.innerHTML = this.highlightMatches(query_tokens, item.label);
|
||||||
|
node.jsondata = item;
|
||||||
|
node.addEventListener('click', event => {
|
||||||
|
this.appendCallback(event.target.jsondata);
|
||||||
|
this.closeContainer();
|
||||||
|
this.clearInput();
|
||||||
|
});
|
||||||
|
this.container.appendChild(node);
|
||||||
|
if (this.container.children.length === 1) {
|
||||||
|
node.className = 'selected';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
highlightMatches(query_tokens, item) {
|
||||||
|
let itemTokens = item.split(/ +/);
|
||||||
|
let queryTokens = new RegExp('(' + query_tokens.join('\|') + ')', 'i');
|
||||||
|
itemTokens.forEach((token, index) => {
|
||||||
|
item = item.replace(token, token.replace(queryTokens, ($1, match) => '<strong>' + match + '</strong>'));
|
||||||
|
});
|
||||||
|
return item;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoSuggest.prototype.doSelection = function(event) {
|
class TagAutoSuggest extends AutoSuggest {
|
||||||
if (typeof this.container === "undefined" || this.container.children.length === 0) {
|
constructor(opt) {
|
||||||
return;
|
super(opt);
|
||||||
|
this.type = "tags";
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (event.keyCode) {
|
fillContainer(response) {
|
||||||
case 13: // Enter
|
if (response.items.length > 0) {
|
||||||
event.preventDefault();
|
super.fillContainer.call(this, response);
|
||||||
this.container.children[this.selectedIndex].click();
|
} else {
|
||||||
break;
|
let node = document.createElement('li')
|
||||||
|
node.innerHTML = "<em>Tag does not exist yet. Create it?</em>";
|
||||||
|
|
||||||
case 38: // Arrow up
|
node.addEventListener('click', event => {
|
||||||
case 40: // Arrow down
|
this.createNewTag(response => this.appendCallback(response));
|
||||||
event.preventDefault();
|
this.closeContainer();
|
||||||
this.findSelectedElement().className = '';
|
this.clearInput();
|
||||||
this.selectedIndex += event.keyCode === 38 ? -1 : 1;
|
});
|
||||||
if (this.selectedIndex < 0) {
|
|
||||||
this.selectedIndex = this.container.children.length - 1;
|
|
||||||
} else if (this.selectedIndex === this.container.children.length) {
|
|
||||||
this.selectedIndex = 0;
|
|
||||||
}
|
|
||||||
var new_el = this.findSelectedElement().className = 'selected';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
AutoSuggest.prototype.findSelectedElement = function() {
|
this.container.appendChild(node);
|
||||||
return this.container.children[this.selectedIndex];
|
this.selectedIndex = 0;
|
||||||
};
|
|
||||||
|
|
||||||
AutoSuggest.prototype.onType = function(input, event) {
|
|
||||||
if (event.keyCode === 13 || event.keyCode === 38 || event.keyCode === 40) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var tokens = input.value.split(/\s+/).filter(function(token) {
|
|
||||||
return token.length >= 2;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (tokens.length === 0) {
|
|
||||||
if (typeof this.container !== "undefined") {
|
|
||||||
this.clearContainer();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var request_uri = this.baseurl + '/suggest/?type=tags&data=' + window.encodeURIComponent(tokens.join(" "));
|
|
||||||
var request = new HttpRequest('get', request_uri, {}, this.onReceive, this);
|
|
||||||
};
|
|
||||||
|
|
||||||
AutoSuggest.prototype.onReceive = function(response, self) {
|
|
||||||
self.openContainer();
|
|
||||||
self.clearContainer();
|
|
||||||
self.fillContainer(response);
|
|
||||||
};
|
|
||||||
|
|
||||||
AutoSuggest.prototype.openContainer = function() {
|
|
||||||
if (this.container) {
|
|
||||||
if (!this.container.parentNode) {
|
|
||||||
this.input.parentNode.appendChild(this.container);
|
|
||||||
}
|
|
||||||
return this.container;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.container = document.createElement('ul');
|
|
||||||
this.container.className = 'autosuggest';
|
|
||||||
this.input.parentNode.appendChild(this.container);
|
|
||||||
return this.container;
|
|
||||||
};
|
|
||||||
|
|
||||||
AutoSuggest.prototype.clearContainer = function() {
|
|
||||||
while (this.container.children.length > 0) {
|
|
||||||
this.container.removeChild(this.container.children[0]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
AutoSuggest.prototype.clearInput = function() {
|
|
||||||
this.input.value = "";
|
|
||||||
this.input.focus();
|
|
||||||
};
|
|
||||||
|
|
||||||
AutoSuggest.prototype.closeContainer = function() {
|
|
||||||
this.container.parentNode.removeChild(this.container);
|
|
||||||
};
|
|
||||||
|
|
||||||
AutoSuggest.prototype.fillContainer = function(response) {
|
|
||||||
var self = this;
|
|
||||||
this.selectedIndex = 0;
|
|
||||||
response.items.forEach(function(item, i) {
|
|
||||||
var node = document.createElement('li');
|
|
||||||
var text = document.createTextNode(item.label);
|
|
||||||
node.jsondata = item;
|
|
||||||
node.addEventListener('click', function(event) {
|
|
||||||
self.appendCallback(this.jsondata);
|
|
||||||
self.closeContainer();
|
|
||||||
self.clearInput();
|
|
||||||
});
|
|
||||||
node.appendChild(text);
|
|
||||||
self.container.appendChild(node);
|
|
||||||
if (self.container.children.length === 1) {
|
|
||||||
node.className = 'selected';
|
node.className = 'selected';
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
};
|
|
||||||
|
|
||||||
|
createNewTag(callback) {
|
||||||
function TagAutoSuggest(opt) {
|
let request_uri = this.baseurl + '/suggest/?type=createtag';
|
||||||
AutoSuggest.prototype.constructor.call(this, opt);
|
let request = new HttpRequest('post', request_uri, 'tag=' + encodeURIComponent(this.input.value), callback, this);
|
||||||
this.type = "tags";
|
|
||||||
}
|
|
||||||
|
|
||||||
TagAutoSuggest.prototype = Object.create(AutoSuggest.prototype);
|
|
||||||
|
|
||||||
TagAutoSuggest.prototype.constructor = TagAutoSuggest;
|
|
||||||
|
|
||||||
TagAutoSuggest.prototype.fillContainer = function(response) {
|
|
||||||
if (response.items.length > 0) {
|
|
||||||
AutoSuggest.prototype.fillContainer.call(this, response);
|
|
||||||
} else {
|
|
||||||
var node = document.createElement('li')
|
|
||||||
node.innerHTML = "<em>Tag does not exist yet. Create it?</em>";
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
node.addEventListener('click', function(event) {
|
|
||||||
self.createNewTag(function(response) {
|
|
||||||
self.appendCallback(response);
|
|
||||||
});
|
|
||||||
self.closeContainer();
|
|
||||||
self.clearInput();
|
|
||||||
});
|
|
||||||
|
|
||||||
self.container.appendChild(node);
|
|
||||||
this.selectedIndex = 0;
|
|
||||||
node.className = 'selected';
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
TagAutoSuggest.prototype.createNewTag = function(callback) {
|
|
||||||
var request_uri = this.baseurl + '/suggest/?type=createtag';
|
|
||||||
var request = new HttpRequest('post', request_uri, 'tag=' + encodeURIComponent(this.input.value), callback, this);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,11 +65,9 @@ class PhotoPage extends SubTemplate
|
|||||||
<a href="', $this->photo->getUrl(), '">';
|
<a href="', $this->photo->getUrl(), '">';
|
||||||
|
|
||||||
if ($this->photo->isPortrait())
|
if ($this->photo->isPortrait())
|
||||||
echo '
|
echo $this->photo->getInlineImage(null, 960);
|
||||||
<img src="', $this->photo->getThumbnailUrl(null, 960), '" alt="">';
|
|
||||||
else
|
else
|
||||||
echo '
|
echo $this->photo->getInlineImage(1280, null);
|
||||||
<img src="', $this->photo->getThumbnailUrl(1280, null), '" alt="">';
|
|
||||||
|
|
||||||
echo '
|
echo '
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -90,8 +90,14 @@ class PhotosIndex extends SubTemplate
|
|||||||
<a class="edit" href="', BASEURL, '/editasset/?id=', $image->getId(), '">Edit</a>';
|
<a class="edit" href="', BASEURL, '/editasset/?id=', $image->getId(), '">Edit</a>';
|
||||||
|
|
||||||
echo '
|
echo '
|
||||||
<a href="', $image->getPageUrl(), $this->url_suffix, '">
|
<a href="', $image->getPageUrl(), $this->url_suffix, '#photo_frame">
|
||||||
<img src="', $image->getThumbnailUrl($width, $height, $crop, $fit), '" alt="" title="', $image->getTitle(), '">';
|
<img src="', $image->getThumbnailUrl($width, $height, $crop, $fit), '"';
|
||||||
|
|
||||||
|
// Can we offer double-density thumbs?
|
||||||
|
if ($image->width() >= $width * 2 && $image->height() >= $height * 2)
|
||||||
|
echo ' srcset="', $image->getThumbnailUrl($width * 2, $height * 2, $crop, $fit), ' 2x"';
|
||||||
|
|
||||||
|
echo ' alt="" title="', $image->getTitle(), '">';
|
||||||
|
|
||||||
if ($this->show_labels)
|
if ($this->show_labels)
|
||||||
echo '
|
echo '
|
||||||
|
|||||||
Reference in New Issue
Block a user