From 359baa14dc7b87a07ad981f8cb24aa33da8eefe5 Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Fri, 10 Apr 2020 12:45:00 -0400 Subject: [PATCH] Update FieldtypeFile along with Pagefile and Pagefiles classes to support additional DB schema for filesize, created_users_id and modified_users_id. Also adds new related API methods. --- wire/core/Pagefile.php | 252 ++++++- wire/core/Pagefiles.php | 7 +- wire/modules/Fieldtype/FieldtypeFile.module | 647 +++++++++++++----- .../InputfieldFile/InputfieldFile.module | 3 + .../InputfieldImage/InputfieldImage.js | 1 + .../InputfieldImage/InputfieldImage.min.js | 2 +- 6 files changed, 720 insertions(+), 192 deletions(-) diff --git a/wire/core/Pagefile.php b/wire/core/Pagefile.php index a655f3d5..30d3f482 100644 --- a/wire/core/Pagefile.php +++ b/wire/core/Pagefile.php @@ -12,7 +12,7 @@ * Pagefile objects are contained by a `Pagefiles` object. * #pw-body * - * ProcessWire 3.x, Copyright 2019 by Ryan Cramer + * ProcessWire 3.x, Copyright 2020 by Ryan Cramer * https://processwire.com * * @property-read string $url URL to the file on the server. @@ -40,6 +40,10 @@ * @property Page $page The Page object that this file is part of. #pw-group-other * @property Field $field The Field object that this file is part of. #pw-group-other * @property array $filedata + * @property int $created_users_id ID of user that added/uploaded the file or 0 if not known (3.0.154+). #pw-group-other + * @property int $modified_users_id ID of user that last modified the file or 0 if not known (3.0.154+). #pw-group-other + * @property User|NullPage $createdUser User that added/uploaded the file or NullPage if not known (3.0.154)+. #pw-group-other + * @property User|NullPage $modifiedUser User that last modified the file or NullPage if not known (3.0.154)+. #pw-group-other * * @method void install($filename) * @method string httpUrl() @@ -88,6 +92,30 @@ class Pagefile extends WireData { */ protected $fieldValues = array(); + /** + * Created user (populated only on rquest) + * + * @var User|null + * + */ + protected $_createdUser = null; + + /** + * Modifed user (populated only on request) + * + * @var User|null + * + */ + protected $_modifiedUser = null; + + /** + * Is this a brand new Pagefile rather than one loaded from DB? + * + * @var bool + * + */ + protected $_isNew = true; + /** * Construct a new Pagefile * @@ -108,7 +136,10 @@ class Pagefile extends WireData { $this->set('tags', ''); $this->set('formatted', false); // has an output formatter been run on this Pagefile? $this->set('modified', 0); - $this->set('created', 0); + $this->set('created', 0); + $this->set('filesize', 0); + $this->set('created_users_id', 0); + $this->set('modified_users_id', 0); } /** @@ -119,11 +150,17 @@ class Pagefile extends WireData { */ public function __clone() { $this->extras = array(); + $this->set('filesize', 0); + $this->set('created_users_id', 0); + $this->set('modified_users_id', 0); + $this->_createdUser = null; + $this->_modifiedUser = null; + $this->isNew(true); parent::__clone(); } /** - * Set the filename associated with this Pagefile. + * Set the filename associated with this Pagefile * * No need to call this as it's already called from the constructor. * This exists so that Pagefile/Pageimage descendents can create cloned variations, if applicable. @@ -137,7 +174,10 @@ class Pagefile extends WireData { $basename = basename($filename); - if(DIRECTORY_SEPARATOR != '/') $filename = str_replace('\\' . $basename, '/' . $basename, $filename); // To correct issue with XAMPP in Windows + if(DIRECTORY_SEPARATOR != '/') { + // To correct issue with XAMPP in Windows + $filename = str_replace('\\' . $basename, '/' . $basename, $filename); + } if($basename != $filename && strpos($filename, $this->pagefiles->path()) !== 0) { $this->install($filename); @@ -198,6 +238,7 @@ class Pagefile extends WireData { $this->wire('files')->chmod($destination); $this->changed('file'); + $this->isNew(true); parent::set('basename', $basename); } @@ -215,20 +256,32 @@ class Pagefile extends WireData { */ public function set($key, $value) { - if($key == 'basename') { + if($key === 'basename') { $value = $this->pagefiles->cleanBasename($value, false); - } else if($key == 'description') { + } else if($key === 'description') { return $this->setDescription($value); - } else if($key == 'modified') { + } else if($key === 'modified') { $value = ctype_digit("$value") ? (int) $value : strtotime($value); - } else if($key == 'created') { + } else if($key === 'created') { $value = ctype_digit("$value") ? (int) $value : strtotime($value); - } else if($key == 'tags') { + } else if($key === 'created_users_id' || $key === 'createdUser') { + $this->setUser($value, 'created'); + return $this; + } else if($key === 'modified_users_id' || $key === 'modifiedUser') { + $this->setUser($value, 'modified'); + return $this; + } else if($key === 'tags') { $this->tags($value); return $this; - } else if($key == 'filedata') { + } else if($key === 'filedata') { if(is_array($value)) $this->filedata($value); return $this; + } else if($key === 'filesize') { + $value = (int) $value; + if(empty($this->data['filesize'])) { + $this->data['filesize'] = $value; + return $this; + } } if(strpos($key, 'description') === 0 && preg_match('/^description(\d+)$/', $value, $matches)) { @@ -245,6 +298,64 @@ class Pagefile extends WireData { return parent::set($key, $value); } + /** + * Set user that created or modified this file + * + * #pw-internal + * + * @param User|int|string|true $user Specify user object, name, ID, or boolean true for current user + * @param $type 'created' or 'modified' + * @since 3.0.154 + * + */ + protected function setUser($user, $type) { + $id = 0; + if($user === true) $user = $this->wire('user'); + if(is_object($user)) { + if($user instanceof NullPage) { + $id = 0; + } else if($user instanceof User) { + $id = $user->isGuest() ? 0 : $user->id; + } + } else if(is_int($user)) { + $id = $user; + } else if(ctype_digit($user)) { + $id = (int) $user; + } else if(is_string($user)) { + $name = $this->wire('sanitizer')->pageName($user); + $user = $name ? $this->wire('users')->get("name=$name") : null; + $id = $user && $user->id ? $user->id : 0; + } + if($id < 0) $id = 0; + if(strpos($type, 'created') === 0) { + $this->_createdUser = ($id && $user instanceof User ? $user : null); + parent::set('created_users_id', $id); + } else if(strpos($type, 'modified') === 0) { + $this->_modifiedUser = ($id && $user instanceof User ? $user : null); + parent::set('modified_users_id', $id); + } + } + + /** + * Get created/modified user + * + * #pw-internal + * + * @param string $type One of 'created' or 'modified' + * @return User|NullPage + * @since 3.0.154 + * + */ + protected function getUser($type) { + $type = strpos($type, 'created') === 0 ? 'created' : 'modified'; + $key = $type === 'created' ? '_createdUser' : '_modifiedUser'; + if(!$this->$key) { + $id = (int) parent::get($type . '_users_id'); + $this->$key = $id ? $this->wire('users')->get($id) : new NullPage(); + } + return $this->$key; + } + /** * Get or set filedata * @@ -550,7 +661,7 @@ class Pagefile extends WireData { case 'created': $value = parent::get($key); if(empty($value)) { - $value = filemtime($this->filename()); + $value = $this->filemtime(); parent::set($key, $value); } break; @@ -559,16 +670,25 @@ class Pagefile extends WireData { $value = parent::get(str_replace('Str', '', $key)); $value = wireDate($this->wire('config')->dateFormat, $value); break; + case 'created_users_id': + case 'modified_users_id': + $value = (int) parent::get($key); + break; + case 'createdUser': + case 'modifiedUser': + $value = $this->getUser($key); + break; case 'fileData': case 'filedata': $value = $this->filedata(); break; case 'mtime': - case 'mtimeStr': case 'filemtime': - case 'filemtimeStr': - $value = filemtime($this->filename()); - if(strpos($key, 'Str')) $value = wireDate($this->wire('config')->dateFormat, $value); + $value = $this->filemtime(); + break; + case 'mtimeStr': + case 'filemtimeStr': + $value = wireDate($this->wire('config')->dateFormat, $this->filemtime()); break; case 'fieldValues': return $this->fieldValues; @@ -694,7 +814,7 @@ class Pagefile extends WireData { * */ public function ___noCacheURL($http = false) { - return ($http ? $this->httpUrl() : $this->url()) . '?nc=' . @filemtime($this->filename()); + return ($http ? $this->httpUrl() : $this->url()) . '?nc=' . $this->filemtime(); } /** @@ -1025,14 +1145,30 @@ class Pagefile extends WireData { return parent::get('formatted') ? true : false; } + /** + * Get last modified time of file + * + * @param bool $reset + * @return int Unix timestamp + * @since 3.0.154 + * + */ + public function filemtime($reset = false) { + if($reset) {} // @todo + return (int) @filemtime($this->filename()); + } + /** * Returns the filesize in number of bytes. * + * @param bool $reset * @return int * */ - public function filesize() { - return @filesize($this->filename()); + public function filesize($reset = false) { + if($reset) {} // @todo + $filesize = (int) @filesize($this->filename()); + return $filesize; } /** @@ -1158,7 +1294,8 @@ class Pagefile extends WireData { * */ public function ___changed($what, $old = null, $new = null) { - if(in_array($what, array('description', 'tags', 'file'))) { + if(in_array($what, array('description', 'tags', 'file', 'filedata'))) { + $this->setUser(true, 'modified'); $this->set('modified', time()); $this->pagefiles->trackChange('item'); } @@ -1194,6 +1331,20 @@ class Pagefile extends WireData { return $this->pagefiles->isTemp($this, $set); } + /** + * Get or set “new” status of the Pagefile + * + * This is true with a Pagefile that was created during this request and not loaded from DB. + * + * @param bool|null $set + * @return bool + * + */ + public function isNew($set = null) { + if(is_bool($set)) $this->_isNew = $set; + return $this->_isNew; + } + /** * Get all extras, add an extra, or get an extra * @@ -1213,6 +1364,67 @@ class Pagefile extends WireData { return isset($this->extras[$name]) ? $this->extras[$name] : null; } + /** + * Save this Pagefile independently of the Page it lives on + * + * @return bool + * @throws WireException + * @since 3.0.154 + * + */ + public function save() { + /** @var FieldtypeFile $fieldtype */ + $fieldtype = $this->field->type; + return $fieldtype->saveFile($this->page, $this->field, $this); + } + + /** + * Replace file with another + * + * Should be followed up with a save() to ensure related properties are also committed to DB. + * + * #pw-internal + * + * @param string $filename File to replace current one with + * @param bool $move Move given $filename rather than copy? (default=true) + * @return bool + * @throws WireException + * @since 3.0.154 + * + */ + public function replaceFile($filename, $move = true) { + + /** @var WireFileTools $files */ + $files = $this->wire('files'); + if(!is_file($filename) || !is_readable($filename)) return false; + if($move && !is_writable($filename)) $move = false; + + $srcFile = $filename; + $dstFile = $this->filename(); + $tmpFile = dirname($dstFile) . '/.' . basename($dstFile) . '.tmp'; + + if(file_exists($tmpFile)) $files->unlink($tmpFile); + + $files->rename($dstFile, $tmpFile); + + if($move) { + $result = $files->rename($srcFile, $dstFile); + } else { + $result = $files->copy($srcFile, $dstFile); + } + + if(!$result) { + $files->rename($tmpFile, $dstFile); + return false; + } + + $files->unlink($tmpFile); + $this->filesize(true); + $this->filemtime(true); + + return true; + } + /** * Ensures that isset() and empty() work for dynamic class properties * @@ -1242,6 +1454,8 @@ class Pagefile extends WireData { 'tags' => $this->tags, 'created' => $this->createdStr, 'modified' => $this->modifiedStr, + 'created_users_id' => $this->created_users_id, + 'modified_users_id' => $this->modified_users_id, 'filemtime' => $this->mtimeStr, 'filedata' => $filedata, ); diff --git a/wire/core/Pagefiles.php b/wire/core/Pagefiles.php index e9feab36..d3f3d67b 100644 --- a/wire/core/Pagefiles.php +++ b/wire/core/Pagefiles.php @@ -771,7 +771,8 @@ class Pagefiles extends WireArray implements PageFieldValueInterface { if(!$isTemp) return false; // if not a temp file, we can exit now if(!$checkDeletable) return $isTemp; // if not checking deletable, we can exit now } - + + $user = $this->wire('user'); $now = time(); $session = $this->wire('session'); $pageID = $this->page ? $this->page->id : 0; @@ -803,6 +804,8 @@ class Pagefiles extends WireArray implements PageFieldValueInterface { // set temporary status to true $pagefile->created = Pagefile::createdTemp; $pagefile->modified = $now; + $pagefile->createdUser = $user; + $pagefile->modifiedUser = $user; // mtime atime @touch($pagefile->filename, Pagefile::createdTemp, $now); $isTemp = true; @@ -815,6 +818,8 @@ class Pagefiles extends WireArray implements PageFieldValueInterface { // set temporary status to false $pagefile->created = $now; $pagefile->modified = $now; + $pagefile->createdUser = $user; + $pagefile->modifiedUser = $user; @touch($pagefile->filename, $now); $isTemp = false; diff --git a/wire/modules/Fieldtype/FieldtypeFile.module b/wire/modules/Fieldtype/FieldtypeFile.module index 4db9c050..0d131d85 100644 --- a/wire/modules/Fieldtype/FieldtypeFile.module +++ b/wire/modules/Fieldtype/FieldtypeFile.module @@ -59,7 +59,7 @@ class FieldtypeFile extends FieldtypeMulti implements ConfigurableModule { const fileSchemaTags = 1; /** - * File schema is configured to support 'created' date (flag) + * File schema is configured to support 'created' and 'modified' dates (flag) * */ const fileSchemaDate = 2; @@ -70,6 +70,12 @@ class FieldtypeFile extends FieldtypeMulti implements ConfigurableModule { */ const fileSchemaFiledata = 4; + /** + * File schema is configured to store 'filesize' and created/modified users (flag) + * + */ + const fileSchemaFilesize = 8; + /** * Flag for useTags: tags off/disabled * @@ -87,6 +93,14 @@ class FieldtypeFile extends FieldtypeMulti implements ConfigurableModule { * */ const useTagsPredefined = 8; + + /** + * Auto-update non-present settings in DB during wakeup? + * + * @since 3.0.154 for enabling true when in development + * + */ + const autoUpdateOnWakeup = false; /** * Default class for Inputfield object used, auto-generated at construct @@ -220,6 +234,35 @@ class FieldtypeFile extends FieldtypeMulti implements ConfigurableModule { $inputfield->destinationPath = $pagefiles->path(); } + /** + * @param Page $page + * @param Field $field + * @return array|null + * + */ + public function ___loadPageField(Page $page, Field $field) { + + $n = 0; + $retry = false; + $result = null; + + do { + try { + $result = parent::___loadPageField($page, $field); + } catch(\PDOException $e) { + // retry to apply new schema (this can eventually be removed) + $fileSchema = (int) $field->get('fileSchema'); + // 42S22=Column not found + if($e->getCode() !== '42S22' || $n > 0 || !($fileSchema & self::fileSchemaFilesize)) throw $e; + $field->set('fileSchema', $fileSchema & ~self::fileSchemaFilesize); + $this->getDatabaseSchema($field); + $retry = true; + } + } while($retry && ++$n < 2); + + return $result; + } + /** * Given a raw value (value as stored in DB), return the value as it would appear in a Page object * @@ -236,26 +279,61 @@ class FieldtypeFile extends FieldtypeMulti implements ConfigurableModule { if(empty($value)) return $pagefiles; if(!is_array($value) || array_key_exists('data', $value)) $value = array($value); - foreach($value as $v) { - if(empty($v['data'])) continue; - $pagefile = $this->getBlankPagefile($pagefiles, $v['data']); - $pagefile->description(true, $v['description']); - if(isset($v['modified'])) $pagefile->modified = $v['modified']; - if(isset($v['created'])) $pagefile->created = $v['created']; - if(isset($v['tags'])) $pagefile->tags = $v['tags']; - if(!empty($v['filedata'])) { - $filedata = json_decode($v['filedata'], true); - unset($filedata['ix']); - $pagefile->filedata = $filedata; - } - $pagefile->setTrackChanges(true); - $pagefiles->add($pagefile); + + foreach($value as $a) { + if(empty($a['data'])) continue; + $this->wakeupFile($page, $field, $pagefiles, $a); } $pagefiles->resetTrackChanges(true); + return $pagefiles; } + /** + * Wakeup individual file converting array of data to Pagefile and adding it to Pagefiles + * + * @param Page $page + * @param Field $field + * @param Pagefiles $pagefiles + * @param array $a Data from DB to create Pagefile from + * @return Pagefile The Pagefile object that was added + * + */ + protected function wakeupFile(Page $page, Field $field, Pagefiles $pagefiles, array $a) { + + $pagefile = $this->getBlankPagefile($pagefiles, $a['data']); + $pagefile->description(true, $a['description']); + + $columns = array('modified', 'created', 'tags', 'modified_users_id', 'created_users_id'); + + foreach($columns as $column) { + if(isset($a[$column])) $pagefile->set($column, $a[$column]); + } + + if(!empty($a['filedata'])) { + $filedata = json_decode($a['filedata'], true); + unset($filedata['ix']); + $pagefile->filedata = $filedata; + } + + $pagefile->isNew(false); + $pagefile->setTrackChanges(true); + $pagefiles->add($pagefile); + + if(!empty($a['filesize'])) { + // @todo + // $pagefile->setQuietly('filesize', (int) $a['filesize']); + } else if(((int) $field->get('fileSchema')) & self::fileSchemaFilesize) { + // populate file size into DB row + if(self::autoUpdateOnWakeup) { + $this->saveFileCols($page, $field, $pagefile, array('filesize' => $pagefile->filesize())); + } + } + + return $pagefile; + } + /** * Given an 'awake' value, as set by wakeupValue, convert the value back to a basic type for storage in DB. * @@ -272,37 +350,76 @@ class FieldtypeFile extends FieldtypeMulti implements ConfigurableModule { foreach($value as $pagefile) { /** @var Pagefile $pagefile */ - - $item = array( - 'data' => $pagefile->basename, - 'description' => $pagefile->description(true), - ); - - $fileSchema = $field->get('fileSchema'); - - if($fileSchema & self::fileSchemaDate) { - $item['modified'] = date('Y-m-d H:i:s', $pagefile->modified); - $item['created'] = date('Y-m-d H:i:s', $pagefile->created); - } - - if($fileSchema & self::fileSchemaTags) { - $item['tags'] = $pagefile->tags; - } - - if($fileSchema & self::fileSchemaFiledata) { - $filedata = $this->sleepFiledata($field, $pagefile); - if(empty($filedata)) { - $item['filedata'] = null; - } else { - $item['filedata'] = json_encode($filedata); - } - } - + $item = $this->sleepFile($page, $field, $pagefile); $sleepValue[] = $item; } return $sleepValue; } + /** + * Convert individual Pagefile to array for storage in DB + * + * @param Page $page + * @param Field $field + * @param Pagefile $pagefile + * @return array + * + */ + protected function sleepFile(Page $page, Field $field, Pagefile $pagefile) { + + if($page) {} // ignore + + $isNew = $pagefile->isNew(); + $isChanged = $pagefile->isChanged(); + + if($isNew) { + $pagefile->createdUser = $this->wire('user'); + $pagefile->created = time(); + } + + if($isChanged || $isNew) { + $changes = array_flip($pagefile->getChanges()); + unset($changes['hash'], $changes['sort'], $changes['modified'], $changes['modified_users_id']); + if($isNew || count($changes)) { + $pagefile->modifiedUser = $this->wire('user'); + $pagefile->modified = time(); + } + } + + $item = array( + 'data' => $pagefile->basename, + 'description' => $pagefile->description(true), + ); + + $fileSchema = (int) $field->get('fileSchema'); + + if($fileSchema & self::fileSchemaDate) { + $item['modified'] = date('Y-m-d H:i:s', $pagefile->modified); + $item['created'] = date('Y-m-d H:i:s', $pagefile->created); + } + + if($fileSchema & self::fileSchemaFilesize) { + $item['filesize'] = $pagefile->filesize(true); + $item['modified_users_id'] = (int) $pagefile->modified_users_id; + $item['created_users_id'] = (int) $pagefile->created_users_id; + } + + if($fileSchema & self::fileSchemaTags) { + $item['tags'] = $pagefile->tags; + } + + if($fileSchema & self::fileSchemaFiledata) { + $filedata = $this->sleepFiledata($field, $pagefile); + if(empty($filedata)) { + $item['filedata'] = null; + } else { + $item['filedata'] = json_encode($filedata); + } + } + + return $item; + } + /** * Get the filedata from given $pagefile ready for placement in a sleep value * @@ -665,20 +782,71 @@ class FieldtypeFile extends FieldtypeMulti implements ConfigurableModule { * */ public function getMatchQuery($query, $table, $subfield, $operator, $value) { + + $field = $query->field; + $schema = $this->getDatabaseSchema($field); + $compareType = Selectors::getOperators(array('valueType' => 'compareType', 'operator' => $operator)); + $isFindOperator = $compareType & Selector::compareTypeFind; + $isInvalidOperator = false; + $isInvalidSubfield = false; - if($subfield && !in_array($subfield, array('data', 'count', 'filedata', 'created', 'modified', 'description', 'tags'))) { - // match custom fields - if($this->getMatchQuerySubfield($query, $subfield, $operator, $value)) { - // successfully handled + unset($schema['keys'], $schema['xtra']); + + if($subfield) { + if($subfield === 'created' || $subfield === 'modified') { + // created or modified date + if($isFindOperator) { + $isInvalidOperator = true; + } else if(ctype_digit(ltrim("$value", "-"))) { + $value = date('Y-m-d H:i:s', (int) $value); + } else { + $value = new \DateTime($value); + $value = $value->format('Y-m-d H:i:s'); + } + + } else if($subfield === 'modified_users_id' || $subfield === 'created_users_id') { + if($isFindOperator) { + $isInvalidOperator = true; + } else if(ctype_digit("$value")) { + $value = (int) $value; + } else { + $value = $this->wire('users')->get('name=' . $this->wire('sanitizer')->pageName($value))->id; + if(!$value) { + $operator = '='; + $value = -1; + } + } + + } else if($subfield === 'count') { + // count match + if($isFindOperator) $isInvalidOperator = true; + $value = (int) $value; + + } else if(isset($schema[$subfield])) { + // subfield is a column native to table + $useInt = stripos($schema[$subfield], 'int') === 0; + $useFloat = !$useInt && stripos($schema[$subfield], 'float') === 0; + if(($useInt || $useFloat) && $isFindOperator) $isInvalidOperator = true; + if($useInt) $value = (int) $value; + if($useFloat) $value = (float) $value; + + } else if($this->getMatchQuerySubfield($query, $subfield, $operator, $value)) { + // match custom fields, successfully handled + } else { - // requested subfield does not match what's available - throw new PageFinderSyntaxException("Property '$subfield' not recognized in selector: $query->selector"); + // requested subfield does not match what’s available + $isInvalidSubfield = true; } } + + if($isInvalidSubfield) { + throw new PageFinderSyntaxException("Property '$subfield' not recognized in: $query->selector"); + + } else if($isInvalidOperator) { + throw new PageFinderSyntaxException("Invalid operator '$operator' for: $field->name.$subfield"); - if(in_array($operator, array("*=", "~=", "!~=", "%=", "^=", "$="))) { - // fulltext match filename or description - /** @var DatabaseQuerySelectFulltext $ft */ + } else if($isFindOperator) { + /** @var DatabaseQuerySelectFulltext $ft Fulltext match filename or description */ $ft = $this->wire(new DatabaseQuerySelectFulltext($query)); $ft->match($table, $subfield, $operator, $value); @@ -800,7 +968,6 @@ class FieldtypeFile extends FieldtypeMulti implements ConfigurableModule { $database = $this->wire('database'); $schema = parent::getDatabaseSchema($field); - $table = $database->escapeTable($field->table); $maxLen = $database->getMaxIndexLength(); $schema['data'] = "varchar($maxLen) NOT NULL"; @@ -808,159 +975,175 @@ class FieldtypeFile extends FieldtypeMulti implements ConfigurableModule { $schema['modified'] = "datetime"; $schema['created'] = "datetime"; $schema['filedata'] = "mediumtext"; + $schema['filesize'] = "int"; // 3.0.154+ + $schema['created_users_id'] = 'int unsigned not null default 0'; // 3.0.154+ + $schema['modified_users_id'] = 'int unsigned not null default 0'; // 3.0.154+ $schema['keys']['description'] = 'FULLTEXT KEY description (description)'; $schema['keys']['filedata'] = 'FULLTEXT KEY filedata (filedata)'; $schema['keys']['modified'] = 'index (modified)'; $schema['keys']['created'] = 'index (created)'; + $schema['keys']['filesize'] = 'index (filesize)'; // 3.0.154+ - if($field->id && !($field->get('fileSchema') & self::fileSchemaFiledata)) { - // permanently add new 'filedata' column to schema - $addFiledata = false; - if($field->flags & Field::flagFieldgroupContext) $field = $this->wire('fields')->get($field->name); - - try { - $query = $database->prepare("SHOW COLUMNS FROM `$table` WHERE Field='filedata'"); - $query->execute(); - $numRows = (int) $query->rowCount(); - $query->closeCursor(); - - if($numRows) { - $field->set('fileSchema', $field->get('fileSchema') | self::fileSchemaFiledata); - $field->save(); - } else { - $addFiledata = true; - } - } catch(\Exception $e) { - // intentionally blank + if($field->id) { + if($field->flags & Field::flagFieldgroupContext) { + $field = $this->wire('fields')->get($field->name); } - - if($addFiledata) try { - $database->exec("ALTER TABLE `{$table}` ADD `filedata` $schema[filedata]"); - $database->exec("ALTER TABLE `{$table}` ADD " . $schema['keys']['filedata']); - $field->set('fileSchema', $field->get('fileSchema') | self::fileSchemaFiledata); + $fileSchema1 = (int) $field->get('fileSchema'); + $fileSchema2 = $this->updateDatabaseSchema($field, $schema, $fileSchema1); + if($fileSchema1 !== $fileSchema2) { + // update fileSchema flags + $field->set('fileSchema', $fileSchema2); $field->save(); - $this->message("Added filedata to DB schema for '{$field->name}'", Notice::log); + } + } + + return $schema; + } + + /** + * Check and update database schema according to current version and features + * + * @param Field $field + * @param array $schema Updated directly + * @param int $fileSchema The fileSchema version flags integer + * @return int Updated fileSchema flags integer + * @since 3.0.154 + * + */ + protected function updateDatabaseSchema(Field $field, array &$schema, $fileSchema) { - } catch(\Exception $e) { + $contextField = $field; + if($field->flags & Field::flagFieldgroupContext) $field = $this->wire('fields')->get($field->name); - $query = $database->prepare("SHOW COLUMNS FROM `$table` WHERE Field='filedata'"); - $query->execute(); - $numRows = (int) $query->rowCount(); - $query->closeCursor(); + /** @var WireDatabasePDO $database */ + $database = $this->wire('database'); + $table = $database->escapeTable($field->table); - if($numRows) { - $field->set('fileSchema', $field->get('fileSchema') | self::fileSchemaFiledata); - $field->save(); - } else { - $this->error("Error adding created/modified to '{$field->name}' schema", Notice::log); - unset($schema['filedata'], $schema['keys']['filedata']); - } + $hasFilesize = $fileSchema & self::fileSchemaFilesize; + $hasFiledata = $fileSchema & self::fileSchemaFiledata; + $hasDate = $fileSchema & self::fileSchemaDate; + $hasTags = $fileSchema & self::fileSchemaTags; + $useTags = $field->get('useTags') || $contextField->get('useTags'); + + // Filesize update (3.0.154): Adds 'filesize', 'created_users_id', 'modified_users_id' columns + if(!$hasFilesize) { + $columns = array('filesize', 'created_users_id', 'modified_users_id'); + $numErrors = 0; + foreach($columns as $column) { + if(!$this->addColumn($field, $column, $schema)) $numErrors++; + if($numErrors) break; + } + if(!$numErrors) { + $fileSchema = $fileSchema | self::fileSchemaFilesize; } } - if($field->id && !($field->get('fileSchema') & self::fileSchemaDate)) { - // permanently add new 'modified' and 'created' column to file schema - $addDates = false; - if($field->flags & Field::flagFieldgroupContext) $field = $this->wire('fields')->get($field->name); + // Filedata update: adds 'filedata' column + if(!$hasFiledata && $this->addColumn($field, 'filedata', $schema)) { + $fileSchema = $fileSchema | self::fileSchemaFiledata; + } - try { - $query = $database->prepare("SHOW COLUMNS FROM `$table` WHERE Field='modified'"); - $query->execute(); - $numRows = (int) $query->rowCount(); - $query->closeCursor(); - - if($numRows) { - $field->set('fileSchema', $field->get('fileSchema') | self::fileSchemaDate); - $field->save(); - } else { - $addDates = true; - } - } catch(\Exception $e) { - // intentionally blank - } - - if($addDates) try { - $database->exec("ALTER TABLE `{$table}` ADD `modified` $schema[modified]"); - $database->exec("ALTER TABLE `{$table}` ADD `created` $schema[created]"); - $database->exec("ALTER TABLE `{$table}` ADD " . $schema['keys']['modified']); - $database->exec("ALTER TABLE `{$table}` ADD " . $schema['keys']['created']); - $field->set('fileSchema', $field->get('fileSchema') | self::fileSchemaDate); - $field->save(); - $date = date('Y-m-d H:i:s'); - $query = $database->prepare("UPDATE `$table` SET created=:created, modified=:modified"); + // Date update: Adds 'modified', 'created' columns + if(!$hasDate) { + $numErrors = 0; + if(!$this->addColumn($field, 'created', $schema)) $numErrors++; + if(!$numErrors && !$this->addColumn($field, 'modified', $schema)) $numErrors++; + if(!$numErrors) { + $fileSchema = $fileSchema | self::fileSchemaDate; + // now populate initial dates + $date = date('Y-m-d H:i:s'); + $query = $database->prepare("UPDATE `{$table}` SET created=:created, modified=:modified"); $query->bindValue(":created", $date); - $query->bindValue(":modified", $date); - $query->execute(); - $this->message("Added created/modified to DB schema for '{$field->name}'", Notice::log); - - } catch(\Exception $e) { - - $query = $database->prepare("SHOW COLUMNS FROM `$table` WHERE Field='modified'"); - $query->execute(); - $numRows = (int) $query->rowCount(); - $query->closeCursor(); - - if($numRows) { - $field->set('fileSchema', $field->get('fileSchema') | self::fileSchemaDate); - $field->save(); - } else { - $this->error("Error adding created/modified to '{$field->name}' schema", Notice::log); + $query->bindValue(":modified", $date); + try { + $query->execute(); + $this->message("Populated initial created/modified dates for '{$field->name}'", Notice::log); + } catch(\Exception $e) { + $this->error("Error populating created/modified dates to '{$field->name}'", Notice::log); } - } } - - $tagsAction = null; // null=no change; 1=add tags, 0=remove tags - $schemaTags = 'varchar(250) NOT NULL'; - $schemaTagsIndex = 'FULLTEXT KEY tags (tags)'; - - if($field->get('useTags') && !($field->get('fileSchema') & self::fileSchemaTags)) { - $tagsAction = 'add'; - } else if(!$field->get('useTags') && ($field->get('fileSchema') & self::fileSchemaTags)) { - $tagsAction = 'remove'; - } - - if($tagsAction === 'add') { + + // Tags update: adds 'tags' column + $schemaTags = 'varchar(250) NOT NULL'; + $schemaTagsIndex = 'FULLTEXT KEY tags (tags)'; + if($useTags && !$hasTags) { // add tags field - try { - $query = $database->prepare("SHOW COLUMNS FROM `$table` WHERE Field='tags'"); - $query->execute(); - $numRows = (int) $query->rowCount(); - $query->closeCursor(); - } catch(\Exception $e) { - // probably in a clone, we can ignore and skip over any further changes - $numRows = 1; - } - - if(!$numRows) try { + if($database->columnExists($table, 'tags')) { + // congrats + $fileSchema = $fileSchema | self::fileSchemaTags; + } else try { $database->exec("ALTER TABLE `{$table}` ADD tags $schemaTags"); - $database->exec("ALTER TABLE `{$table}` ADD $schemaTagsIndex"); - $field->set('fileSchema', $field->get('fileSchema') | self::fileSchemaTags); - $field->save(); - $this->message("Added tags to DB schema for '{$field->name}'", Notice::log); + $database->exec("ALTER TABLE `{$table}` ADD $schemaTagsIndex"); + $this->message("Added tags to DB schema for '{$field->name}'", Notice::log); + $fileSchema = $fileSchema | self::fileSchemaTags; } catch(\Exception $e) { - $this->error("Error adding tags to '{$field->name}' schema", Notice::log); + $this->error("Error adding tags to '{$field->name}' schema", Notice::log); } - } else if($tagsAction === 'remove') { + } else if(!$useTags && $hasTags) { // remove tags field + $fileSchema = $fileSchema & ~self::fileSchemaTags; + /* try { - $database->exec("ALTER TABLE `{$table}` DROP INDEX tags"); - $database->exec("ALTER TABLE `{$table}` DROP tags"); - $field->set('fileSchema', $field->get('fileSchema') & ~self::fileSchemaTags); - $field->save(); - $this->message("Dropped tags from DB schema for '{$field->name}'", Notice::log); + $database->exec("ALTER TABLE `{$table}` DROP INDEX tags"); + $database->exec("ALTER TABLE `{$table}` DROP tags"); + $this->message("Dropped tags from DB schema for '{$field->name}'", Notice::log); + $fileSchema = $fileSchema & ~self::fileSchemaTags; } catch(\Exception $e) { - $this->error("Error dropping tags from '{$field->name}' schema", Notice::log); + $this->error("Error dropping tags from '{$field->name}' schema", Notice::log); } + */ } - if($field->get('fileSchema') & self::fileSchemaTags) { + if($fileSchema & self::fileSchemaTags) { $schema['tags'] = $schemaTags; $schema['keys']['tags'] = $schemaTagsIndex; } + + return $fileSchema; + } - return $schema; + /** + * Adds a column + * + * @param Field $field + * @param string $column + * @param array $schema Schema array + * @return bool + * @throws WireException + * + */ + protected function addColumn(Field $field, $column, array &$schema) { + + /** @var WireDatabasePDO $database */ + $database = $this->wire('database'); + + if($database->columnExists($field->table, $column)) return true; + + if(!isset($schema[$column])) throw new WireException("Missing schema for $field->name.$column"); + + $table = $database->escapeTable($field->table); + + try { + $result = $database->exec("ALTER TABLE `{$table}` ADD `$column` $schema[$column]"); + if($result) { + if(isset($schema['keys'][$column])) { + $database->exec("ALTER TABLE `{$table}` ADD " . $schema['keys'][$column]); + } + $this->message("Added '$column' to DB schema for '$field->name'", Notice::log | Notice::debug); + } + } catch(\Exception $e) { + if($database->columnExists($table, $column)) { + $result = true; + } else { + $this->error("Error adding '$column' to '{$field->name}' schema", Notice::log); + unset($schema[$column], $schema['keys'][$column]); + $result = false; + } + } + + return $result; } @@ -1069,6 +1252,128 @@ class FieldtypeFile extends FieldtypeMulti implements ConfigurableModule { return $page; } + /** + * Save a single Pagefile to DB + * + * #pw-internal + * + * @param Page $page + * @param Field $field + * @param Pagefile $pagefile + * @param array $columns Update only these column names (for existing Pagefile only) + * @return bool + * @throws WireException + * @since 3.0.154 + * + */ + public function saveFile(Page $page, Field $field, Pagefile $pagefile, array $columns = array()) { + + $item = $this->sleepFile($page, $field, $pagefile); + $database = $this->wire('database'); /** @var WireDatabasePDO $database */ + $table = $database->escapeTable($field->getTable()); + $sets = array(); + $binds = array(':pages_id' => $page->id); + $isNew = $pagefile->isNew(); + $columns = !$isNew && count($columns) ? array_flip($columns) : null; + + if($pagefile->formatted()) { + if(!$columns || isset($columns['filedata']) || isset($columns['description'])) { + throw new WireException("Cannot save formatted Pagefile: $pagefile"); + } + } + + foreach($item as $key => $value) { + if($columns && !isset($columns[$key])) continue; + $sets[] = "$key=:$key" ; + $binds[":$key"] = $value; + } + + $sets = implode(', ', $sets); + + if($isNew) { + $sql = "INSERT INTO $table SET pages_id=:pages_id, sort=:sort, $sets"; + $binds[":sort"] = $this->getMaxColumnValue($page, $field, 'sort'); + } else { + $sql = "UPDATE $table SET $sets WHERE pages_id=:pages_id AND data=:name"; + $binds[':name'] = $pagefile->name; + } + + $query = $database->prepare($sql); + + foreach($binds as $key => $value) { + if($value === null) { + $type = \PDO::PARAM_NULL; + } else if(is_int($value)) { + $type = \PDO::PARAM_INT; + } else { + $type = \PDO::PARAM_STR; + } + $query->bindValue($key, $value, $type); + } + + $result = $query->execute() ? $query->rowCount() : 0; + if($isNew && $result) $pagefile->isNew(false); + + return (bool) $result; + } + + /** + * Specify specific columns and values to update for Pagefile + * + * This update is performed quietly, not updating 'modified' or 'modified_users_id' + * unless specified in given data array $a. + * + * This method cannot be used to update 'description' or 'filedata' properties, + * and it will not save for Pagefile entries not already in the DB. + * + * #pw-internal + * + * @param Page $page + * @param Field $field + * @param Pagefile $pagefile + * @param array $a Associative array of column names and values + * @return bool + * @throws WireException + * @since 3.0.154 + * + */ + public function saveFileCols(Page $page, Field $field, Pagefile $pagefile, array $a) { + + $database = $this->wire('database'); /** @var WireDatabasePDO $database */ + $table = $database->escapeTable($field->getTable()); + $schema = $this->getDatabaseSchema($field); + $binds = array(':pid' => $page->id, ':name' => $pagefile->name); + $sets = array(); + + if($pagefile->isNew()) { + throw new WireException("Cannot saveFileCols() on “new” Pagefile $pagefile"); + } + + if(empty($a) || !$page->id || !($field->type instanceof FieldtypeFile)) return false; + + foreach($a as $col => $value) { + if(!isset($schema[$col])) { + throw new WireException("Unknown column '$col' for field $field->name"); + } else if($col === 'filedata' || strpos($col, 'description') === 0) { + throw new WireException("Column '$col' cannot be saved with $this::saveFileCols()"); + } + $sets[] = "$col=:$col"; + $binds[":$col"] = $value; + } + + $sql = "UPDATE $table SET " . implode(', ', $sets) . " where pages_id=:pid AND data=:name"; + $query = $database->prepare($sql); + + foreach($binds as $key => $value) { + $query->bindValue($key, $value); + } + + $result = $query->execute() ? $query->rowCount() : 0; + + return (bool) $result; + + } + /** * Field config * diff --git a/wire/modules/Inputfield/InputfieldFile/InputfieldFile.module b/wire/modules/Inputfield/InputfieldFile/InputfieldFile.module index 8ebc9385..464884db 100644 --- a/wire/modules/Inputfield/InputfieldFile/InputfieldFile.module +++ b/wire/modules/Inputfield/InputfieldFile/InputfieldFile.module @@ -740,6 +740,9 @@ class InputfieldFile extends Inputfield implements InputfieldItemList, Inputfiel } else { $this->message($message); } + + $pagefile->createdUser = $this->wire('user'); + $pagefile->modifiedUser = $this->wire('user'); } protected function fileAddedGetMarkup(Pagefile $pagefile, $n) { diff --git a/wire/modules/Inputfield/InputfieldImage/InputfieldImage.js b/wire/modules/Inputfield/InputfieldImage/InputfieldImage.js index 29321ef5..3178b2f0 100755 --- a/wire/modules/Inputfield/InputfieldImage/InputfieldImage.js +++ b/wire/modules/Inputfield/InputfieldImage/InputfieldImage.js @@ -2315,6 +2315,7 @@ function InputfieldImage($) { initInputfield($inputfield); initUploadHTML5($inputfield); //console.log('InputfieldImage reloaded'); + Inputfields.init($inputfield); }).on('wiretabclick', function(e, $newTab, $oldTab) { $newTab.find(".InputfieldImage").each(function() { initInputfield($(this)); diff --git a/wire/modules/Inputfield/InputfieldImage/InputfieldImage.min.js b/wire/modules/Inputfield/InputfieldImage/InputfieldImage.min.js index b51f6ace..9dcb5526 100644 --- a/wire/modules/Inputfield/InputfieldImage/InputfieldImage.min.js +++ b/wire/modules/Inputfield/InputfieldImage/InputfieldImage.min.js @@ -1 +1 @@ -function InputfieldImage($){var $uploadBeforeItem=null;var uploadReplace={file:"",item:null,edit:null};var magnificOptions={type:"image",closeOnContentClick:true,closeBtnInside:true};var cookieData=null;var retryGridItems=[];var gridSliding=false;function useAjaxUpload(){var isFileReaderSupport=window.File&&window.FileList&&window.FileReader;var isAjaxUpload=$(".InputfieldAllowAjaxUpload").length>0;var isPageIDIndicator=$("#PageIDIndicator").length>0;return isFileReaderSupport&&(isPageIDIndicator||isAjaxUpload)}function throttle(fn,threshhold,scope){threshhold||(threshhold=250);var last,deferTimer;return function(){var context=scope||this;var now=+new Date,args=arguments;if(last&&now .gridImage",start:function(e,ui){var size=getCookieData($el.closest(".Inputfield"),"size");ui.placeholder.append($("
").css({display:"block",height:size+"px",width:size+"px"}));timer=window.setTimeout(function(){closeEdit($el,null)},100);$el.addClass("InputfieldImageSorting")},stop:function(e,ui){var $this=$(this);if(timer!==null){ui.item.find(".InputfieldImageEdit__edit").click();clearTimeout(timer)}$this.children("li").each(function(n){var $sort=$(this).find(".InputfieldFileSort");if($sort.val()!=n)$sort.val(n).change()});$el.removeClass("InputfieldImageSorting")},cancel:".InputfieldImageEdit,.focusArea,input,textarea,button,select,option"};$el.sortable(sortableOptions)}function setupMagnificForRenderValue($el){var options=$.extend(true,{},magnificOptions);options.callbacks={elementParse:function(item){var src=$(item.el).attr("data-original");if(typeof src=="undefined"||!src)src=$(item.el).attr("src");item.src=src}};options.gallery={enabled:true};$el.find("img").magnificPopup(options)}function setupMagnificForSingle($el){var options=$.extend(true,{},magnificOptions);options.callbacks={elementParse:function(item){item.src=$(item.el).attr("src")}};options.gallery={enabled:false};$el.find("img").magnificPopup(options)}function findEditedElement($parent){return $parent.find(".InputfieldImageEdit--active")}function findEditMarkup($edit){return $("#"+$edit.find(".InputfieldImageEdit__edit").attr("data-current"))}function setDeleteStateOnAllItems($input){var checked=$input.is(":checked");var $items=$input.parents(".gridImages").find(".gridImage__deletebox");if(checked){$items.prop("checked","checked").change()}else{$items.removeAttr("checked").change()}}function updateGrid($inputfield){if(typeof $inputfield=="undefined"){var $gridImages=$(".gridImages")}else{var $gridImages=$inputfield.find(".gridImages")}$gridImages.each(function(){var $grid=$(this),$edit=findEditedElement($grid);if($edit.length){moveEdit(findEditMarkup($edit),$edit)}})}function checkInputfieldWidth($inputfield){var narrowItems=[];var mediumItems=[];var wideItems=[];var ni=0,mi=0,wi=0;var $inputfields;if(typeof $inputfield=="undefined"){$inputfields=$(".InputfieldImage.Inputfield")}else{$inputfields=$inputfield}$inputfields.removeClass("InputfieldImageNarrow InputfieldImageMedium InputfieldImageWide");$inputfields.each(function(){var $item=$(this);var width=$item.width();if(width<1)return;if(width<=500){narrowItems[ni]=$item;ni++}else if(width<=900){mediumItems[mi]=$item;mi++}else{wideItems[wi]=$item;wi++}});for(var n=0;n100?100:top,left:left>100?100:left,zoom:zoom>100?0:zoom};return focusData}function getFocusStr(focusObj){if(typeof focusObj=="undefined")focusObj=getFocus();return focusObj.top+" "+focusObj.left+" "+focusObj.zoom}function getFocusProperty(property){var focus=getFocus();return focus[property]}function setFocus(focusObj){focusData=focusObj;var focusStr=focusObj.top+" "+focusObj.left+" "+focusObj.zoom;$thumb.attr("data-focus",focusStr);$input=$edit.find(".InputfieldImageFocus");if(focusStr!=$input.val()){$input.val(focusStr).trigger("change")}}function setFocusProperty(property,value){var focus=getFocus();focus[property]=value;setFocus(focus)}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);var y=Math.round(focus.top/100*h);if(x<0)x=0;if(y<0)y=0;if(x>w)x=w;if(y>h)y=h;$focusCircle.css({top:y+"px",left:x+"px"})}$focusArea=$img.siblings(".focusArea");if(!$focusArea.length){$focusArea=$("
").addClass("focusArea");$img.after($focusArea)}$focusArea.css({height:$img.height()+"px",width:$img.width()+"px","background-color":"rgba(0,0,0,0.7)"}).addClass("focusActive");$focusCircle=$focusArea.find(".focusCircle");if(!$focusCircle.length){$focusCircle=$("
").addClass("focusCircle");$focusArea.append($focusCircle)}$img.parent().addClass("focusWrap");setFocusDragPosition();var zoomSlide=function(zoomPercent){var zoomBoxSize,focus,faWidth,faHeight;if(typeof zoomPercent=="undefined")zoomPercent=lastZoomPercent;lastZoomPercent=zoomPercent;faWidth=$focusArea.width();faHeight=$focusArea.height();if(faWidth>faHeight){$zoomBox.height(100-zoomPercent+"%");zoomBoxSize=$zoomBox.height();$zoomBox.width(zoomBoxSize)}else{$zoomBox.width(100-zoomPercent+"%");zoomBoxSize=$zoomBox.width();$zoomBox.height(zoomBoxSize)}focus=getFocus();var crop=getFocusZoomCropDimensions(focus.left,focus.top,zoomPercent,faWidth,faHeight,zoomBoxSize);$zoomBox.css({top:crop.top+"px",left:crop.left+"px","background-position":"-"+crop.left+"px -"+crop.top+"px","background-size":faWidth+"px "+faHeight+"px"});focus.zoom=zoomPercent;setFocusProperty("zoom",focus.zoom);if(mode=="grid")setGridSizeItem($thumb.parent(),gridSize,false,focus)};var dragEvent=function(event,ui){var $this=$(this);var circleSize=$this.outerHeight();var w=$this.parent().width();var h=$this.parent().height();var top=ui.position.top>0?ui.position.top:0;var left=ui.position.left>0?ui.position.left:0;top=top>0?top/h*100:0;left=left>0?left/w*100:0;var newFocus={top:top,left:left,zoom:getFocusProperty("zoom")};setFocus(newFocus);if(useZoomFocus){zoomSlide(newFocus.zoom)}else if(mode=="grid"){setGridSizeItem($thumb.parent(),gridSize,false,newFocus)}};$focusCircle.draggable({containment:"parent",drag:dragEvent,stop:dragEvent});if(useZoomFocus){var zoom=getFocusProperty("zoom");$zoomSlider=$("
").addClass("focusZoomSlider").css({"margin-top":"5px"});$zoomBox=$("
").addClass("focusZoomBox").css({position:"absolute",background:"transparent","background-image":"url("+$img.attr("src")+")"});$focusArea.prepend($zoomBox);$img.after($zoomSlider);$thumb.attr("src",$img.attr("src"));$zoomSlider.slider({min:0,max:50,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"))}}}function getFocusZoomPosition(focusPercent,sourceDimension,cropDimension){var focusPX=parseInt(sourceDimension*focusPercent/100);var position=parseInt(focusPX-cropDimension/2);var maxPosition=parseInt(sourceDimension-cropDimension);if(0>position)position=0;if(maxPosition=percentH?faWidth:faHeight;var cropDimension=maxDimension-maxDimension*zoomPercent/100;var posLeft=getFocusZoomPosition(focusLeft,faWidth,cropDimension);var posTop=getFocusZoomPosition(focusTop,faHeight,cropDimension);return{left:posLeft,top:posTop,width:cropDimension,height:cropDimension}}function getFocusZoomPosition4GridviewSquare(focusPercent,sourceDimPX,gridViewPX,zoomPercent,scale,smallestSidePX){var sourceDimPX=sourceDimPX*scale;var gridViewPercent=gridViewPX/sourceDimPX*100;var adjustPercent=gridViewPercent/2;var posPercent=focusPercent-adjustPercent;var posMinVal=0;var posMaxVal=100-gridViewPercent;if(posPercent<=posMinVal)posPercent=0;if(posPercent>=posMaxVal)posPercent=posMaxVal;var posPX=sourceDimPX/100*posPercent/scale;posPX=-1*parseInt(posPX);return posPX}function getFocusZoomCropDimensions4GridviewSquare(focusLeft,focusTop,zoomPercent,w,h,gridViewSize,scale){var smallestSidePX=w>=h?h:w;var posLeft=getFocusZoomPosition4GridviewSquare(focusLeft,w,gridViewSize,zoomPercent,scale,smallestSidePX);var posTop=getFocusZoomPosition4GridviewSquare(focusTop,h,gridViewSize,zoomPercent,scale,smallestSidePX);var transformLeft=parseInt(posLeft);var transformTop=parseInt(posTop);return{transformLeft:transformLeft,transformTop:transformTop,scale:scale}}function tearDownEdit($edit){stopFocus($edit);$edit.off("click",".InputfieldImageButtonFocus");$inputArea=$edit.find(".InputfieldImageEdit__edit");if($inputArea.children().not(".InputfieldFileSort").length){var $items=$inputArea.children();$("#"+$inputArea.attr("data-current")).find(".ImageData").append($items)}}function closeEdit($parent,$not){var $edit;if($parent){$edit=$parent.find(".InputfieldImageEdit--active")}else if($not){$edit=$(".InputfieldImageEdit--active").not($not.find(".InputfieldImageEdit--active"))}else{$edit=$(".InputfieldImageEdit--active")}if($edit.length){tearDownEdit($edit);$edit.removeClass("InputfieldImageEdit--active").removeAttr("id");$("#"+$edit.attr("data-for")).removeClass("gridImageEditing")}$(".InputfieldImageEdit__replace").removeClass("InputfieldImageEdit__replace")}function moveEdit($el,$edit){if(!$el||!$el.length)return;var $children=$el.parent().children().not(".InputfieldImageEdit");var lastTop=0;var found=false;var $insertBeforeItem=null;$children.each(function(){if($insertBeforeItem)return;var $item=$(this);var top=$item.offset().top;if(found&&top!=lastTop){$insertBeforeItem=$item}else if($item.attr("id")==$el.attr("id")){found=true}lastTop=top});if($insertBeforeItem){$edit.insertBefore($insertBeforeItem)}else{$edit.insertAfter($children.eq($children.length-1))}var $arrow=$edit.find(".InputfieldImageEdit__arrow");if($arrow.length)$arrow.css("left",$el.position().left+$el.outerWidth()/2+"px")}function initGridEvents(){$(window).resize(throttle(windowResize,200));$(document).on("click dblclick",".gridImage__trash",function(e){var $input=$(this).find("input");$input.prop("checked",inverseState).change();if(e.type=="dblclick"){setDeleteStateOnAllItems($input);e.preventDefault();e.stopPropagation()}});$(document).on("change",".gridImage__deletebox",function(){updateDeleteClass($(this))});$(document).on("click",".gridImage__edit",function(e){var $el=$(this).closest(".gridImage");if(!$el.length)return;if($el.closest(".InputfieldImageEditAll").length)return false;var $all=$el.closest(".gridImages");var $edit=$all.find(".InputfieldImageEdit");if($el.hasClass("gridImageEditing")){$edit.find(".InputfieldImageEdit__close").click()}else{moveEdit($el,$edit);tearDownEdit($edit);setupEdit($el,$edit);$edit.addClass("InputfieldImageEdit--active").attr("data-for",$el.attr("id"));$all.find(".gridImageEditing").removeClass("gridImageEditing");$el.addClass("gridImageEditing")}}).on("click",".InputfieldImageEditAll img",function(e){e.stopPropagation();e.preventDefault();$.magnificPopup.close();var options=$.extend(true,{},magnificOptions);var $img=$(this);options["items"]={src:$img.attr("data-original"),title:$img.attr("alt")};$.magnificPopup.open(options);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){stopFocus($edit)}else{startFocus($edit);$icon.addClass("focusIconActive");$icon.toggleClass($icon.attr("data-toggle"))}});$(document).on("click",function(e){var $el=$(e.target);if(typeof clickLanguageTabActive!="undefined"&&clickLanguageTabActive){return}if($el.closest(".InputfieldImageEdit").length){closeEdit(null,$el.parents(".gridImages"))}else if($el.is("input, textarea")&&$el.closest(".InputfieldImageEditAll").length){$el.focus().one("blur",function(){$el.closest(".gridImages").sortable("enable")});$el.closest(".gridImages").sortable("disable")}else if($el.closest(".gridImage__inner").length){closeEdit(null,$el.parents(".gridImages"))}else if($el.closest(".mfp-container").length){return}else if($el.closest(".ui-dialog").length){return}else if($el.is(".mfp-close")){return}else if($el.is("a.remove")){return}else{closeEdit(null,null)}});$(document).on("click",".InputfieldImageEdit__close",function(e){closeEdit($(this).parents(".gridImages"),null)});$(document).on("change",".InputfieldImage",function(){$(this).find(".InputfieldImageButtonCrop:not(.pw-modal-dblclick)").addClass("pw-modal-dblclick ui-state-disabled")}).on("click",".InputfieldImageButtonCrop.ui-state-disabled",function(e){var $button=$(this);var $list=$button.closest(".gridImages");if(!$list.hasClass("gridImagesAlerted")){ProcessWire.alert(ProcessWire.config.InputfieldImage.labels.changes);$list.addClass("gridImagesAlerted")}setTimeout(function(){$button.removeClass("ui-state-active")},500);return false});$(".ImagesGrid").on("click","button.pw-modal",function(e){e.preventDefault()});setupEditableFilename();checkInputfieldWidth()}function setupEditableFilename(){$(document).on("click",".InputfieldImageEdit__name",function(e){var $span=$(this).children("span");var $input=$span.closest(".gridImage, .InputfieldImageEdit").find(".InputfieldFileRename");var $list=$span.closest(".gridImages");$list.sortable("disable");$input.val($span.text());$span.on("keypress",function(e){if(e.which==13){$span.blur();return false}return true});$span.attr("autocomplete","off").attr("autocorrect","off").attr("autocapitalize","off").attr("spellcheck","false");$span.focus().on("blur",function(){var val=$(this).text();if($.trim(val).length<1){$span.text($input.val())}else if(val!=$input.val()){$input.val(val).change();$list.closest(".Inputfield").trigger("change")}$span.off("keypress");$list.sortable("enable")})})}function setListSize($inputfield,pct){pct=Math.floor(pct);$inputfield.find(".gridImage__overflow").each(function(){var dataPct=100-pct;var $this=$(this);$this.css("width",pct+"%");$this.siblings(".ImageData").css("width",dataPct+"%");$this.find("img").css({top:0,left:0,transform:"none"})});setCookieData($inputfield,"listSize",pct)}function setGridSize($inputfield,gridSize,ragged){if(!gridSize)return;var size=gridSize+"px";var $gridImages=$inputfield.find(".gridImages");if(typeof ragged=="undefined"||ragged==null)ragged=$gridImages.attr("data-ragged")?true:false;if(ragged){$gridImages.attr("data-ragged",1)}else{$gridImages.removeAttr("data-ragged")}$gridImages.find(".gridImage__overflow").each(function(){setGridSizeItem($(this),gridSize,ragged)});$gridImages.find(".gridImage__edit, .gridImage__resize").css("line-height",size);$gridImages.attr("data-size",gridSize);setCookieData($inputfield,"size",gridSize);if(retryGridItems.length)setTimeout(function(){while(retryGridItems.length){var item=retryGridItems.pop();setGridSizeItem(item.item,item.gridSize,ragged)}},150)}function setGridSizeItem($item,gridSize,ragged,focus){if($item.hasClass("gridImage__overflow")){var $img=$item.children("img")}else if($item.is("img")){var $img=$item;$item=$img.closest(".gridImage__overflow")}else{return}if(!gridSize){$img.removeAttr("width").removeAttr("height");$item.width("auto").height("auto");return}var zoom=0;var w=$img.width();var h=$img.height();var dataW=parseInt($img.attr("data-w"));var dataH=parseInt($img.attr("data-h"));if(!w)w=dataW;if(!h)h=dataH;if(!ragged&&typeof focus=="undefined"){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)zoom=focus.zoom;if(ragged){$img.attr("height",gridSize).removeAttr("width");$img.css({"max-height":"100%","max-width":"none",top:"50%",left:"50%",transform:"translate3d(-50%, -50%, 0)"})}else if(zoom>0&&$item.closest(".InputfieldImageFocusZoom").length&&!gridSliding){if(w>=h){var maxHeight="100%";var maxWidth="none";if(w==dataW){h=gridSize;w=h/dataH*dataW}}else{var maxHeight="none";var maxWidth="100%";if(h==dataH){w=gridSize;h=w/dataW*dataH}}var scale=1+zoom/100*2;var crop=getFocusZoomCropDimensions4GridviewSquare(focus.left,focus.top,zoom,w,h,gridSize,scale);$img.css({left:"0px",top:"0px","transform-origin":"0px 0px",transform:"scale("+crop.scale+") translate3d("+crop.transformLeft+"px, "+crop.transformTop+"px, 0)","max-width":maxWidth,"max-height":maxHeight})}else if(w>=h){$img.attr("height",gridSize).removeAttr("width");if(focus.left<1)focus.left=.001;$img.css({"max-height":"100%","max-width":"none",top:"50%",left:focus.left+"%",transform:"translate3d(-"+focus.left+"%, -50%, 0)"})}else if(h>w){$img.attr("width",gridSize).removeAttr("height");if(focus.top<1)focus.top=.001;$img.css({"max-height":"none","max-width":"100%",top:focus.top+"%",left:"50%",transform:"translate3d(-50%, -"+focus.top+"%, 0)"})}else{$img.css({"max-height":"100%","max-width":"none",top:"50%",left:"50%",transform:"translate3d(-50%, -50%, 0)"});$img.removeAttr("width").attr("height",gridSize)}var w=$img.width();if(w){$item.css({width:ragged?w+"px":gridSize+"px",height:gridSize+"px"})}else{var tries=$item.attr("data-tries");if(!tries)tries=0;if(typeof tries=="undefined")tries=0;tries=parseInt(tries);if(tries>3){$item.css({width:gridSize+"px",height:gridSize+"px"})}else{retryGridItems.push({item:$item,gridSize:gridSize});$item.attr("data-tries",tries+1)}}}function setupImageListToggle($target){if($target.find(".InputfieldImageListToggle").length)return;var $list=$("").append("");var $left=$("").append("");var $grid=$("").append("");var activeClass="InputfieldImageListToggle--active";var defaultMode="";var toggleClick=function(e){var $a=$(this);var $inputfield=$a.closest(".Inputfield");var href=$a.attr("href");var size;var $aPrev=$a.parent().children("."+activeClass);var hrefPrev=$aPrev.attr("href");$aPrev.removeClass(activeClass);$a.addClass(activeClass);stopFocus($inputfield);if(href=="list"){if(!$inputfield.hasClass("InputfieldImageEditAll")){$inputfield.find(".InputfieldImageEdit--active .InputfieldImageEdit__close").click();$inputfield.addClass("InputfieldImageEditAll")}size=getCookieData($inputfield,"listSize");setListSize($inputfield,size);setCookieData($inputfield,"mode","list")}else if(href=="left"){$inputfield.removeClass("InputfieldImageEditAll");size=getCookieData($inputfield,"size");setGridSize($inputfield,size,true);setCookieData($inputfield,"mode","left");updateGrid()}else if(href=="grid"){$inputfield.removeClass("InputfieldImageEditAll");size=getCookieData($inputfield,"size");setGridSize($inputfield,size,false);setCookieData($inputfield,"mode","grid");if(hrefPrev=="left")setTimeout(function(){setGridSize($inputfield,size,false)},100)}setupSortable($inputfield.find(".gridImages"));$a.blur();return false};$list.click(toggleClick);$left.click(toggleClick);$grid.click(toggleClick);if($target.hasClass("InputfieldImage")){$target.children(".InputfieldHeader").append($list).append($left).append($grid);defaultMode=getCookieData($target,"mode")}else{$(".InputfieldImage > .InputfieldHeader",$target).append($list).append($left).append($grid)}if(defaultMode=="list"){$list.click()}else if(defaultMode=="left"){$left.click()}else{}}function setupSizeSlider($inputfield){var $header=$inputfield.children(".InputfieldHeader");if($header.children(".InputfieldImageSizeSlider").length)return;var $gridImages=$inputfield.find(".gridImages");var gridSize=$gridImages.attr("data-gridsize");var min=gridSize/2;var max=gridSize*2;var $slider=$('');$header.append($slider);var sizeSlide=function(event,ui){var value=ui.value;var minPct=15;var divisor=Math.floor(gridSize/minPct);var v=value-min;var listSize=Math.floor(minPct+v/divisor);if($inputfield.hasClass("InputfieldImageEditAll")){setCookieData($inputfield,"size",value);setListSize($inputfield,listSize)}else{setCookieData($inputfield,"listSize",listSize);setGridSize($inputfield,value)}};$slider.slider({min:min,max:max,value:getCookieData($inputfield,"size"),range:"min",slide:sizeSlide,start:function(event,ui){gridSliding=true;if($inputfield.find(".InputfieldImageEdit:visible").length){$inputfield.find(".InputfieldImageEdit__close").click()}},stop:function(event,ui){gridSliding=false;sizeSlide(event,ui);updateGrid($inputfield)}})}function setCookieData($inputfield,property,value){var data=getCookieData($inputfield);var id=$inputfield.attr("id");var name=id?id.replace("wrap_Inputfield_",""):"";if(!name.length||typeof value=="undefined")return;if(data[name][property]==value)return;data[name][property]=value;$.cookie("InputfieldImage",data,{secure:window.location.protocol.indexOf("https:")===0});cookieData=data}function getCookieData($inputfield,property){if(cookieData&&typeof property=="undefined")return cookieData;var id=$inputfield.attr("id");var name=id?id.replace("wrap_Inputfield_",""):"na";var data=cookieData?cookieData:$.cookie("InputfieldImage");var value=null;if(!data)var data={};if(typeof data[name]=="undefined")data[name]={};if(typeof data[name].size=="undefined"||!data[name].size){data[name].size=parseInt($inputfield.find(".gridImages").attr("data-size"));if(!data[name].size)data[name].size=130}if(typeof data[name].listSize=="undefined"||!data[name].listSize){data[name].listSize=23}if(typeof data[name].mode=="undefined"||!data[name].mode){data[name].mode=$inputfield.find(".gridImages").attr("data-gridMode");if(!data[name].mode)data[name].mode="list"}if(cookieData==null)cookieData=data;if(typeof property=="undefined"){value=data}else if(property===true){value=data[name]}else if(typeof data[name][property]!="undefined"){value=data[name][property]}return value}function initInputfield($inputfield){if($inputfield.hasClass("InputfieldStateCollapsed"))return;var maxFiles=parseInt($inputfield.find(".InputfieldImageMaxFiles").val());var $gridImages=$inputfield.find(".gridImages");var size=getCookieData($inputfield,"size");var mode=getCookieData($inputfield,"mode");var ragged=mode=="left"?true:false;var renderValueMode=$inputfield.hasClass("InputfieldRenderValueMode");if(!size)size=$gridImages.attr("data-gridsize");size=parseInt(size);if(!renderValueMode&&($inputfield.hasClass("InputfieldImageEditAll")||mode=="list")){var listSize=getCookieData($inputfield,"listSize");setListSize($inputfield,listSize)}else{setGridSize($inputfield,size,ragged)}if(!$inputfield.hasClass("InputfieldImageInit")){$inputfield.addClass("InputfieldImageInit");if(renderValueMode){return setupMagnificForRenderValue($inputfield)}else if(maxFiles==1){$inputfield.addClass("InputfieldImageMax1");setupMagnificForSingle($inputfield)}else{setupSortable($gridImages)}setupImageListToggle($inputfield);setupSizeSlider($inputfield)}checkInputfieldWidth($inputfield);$inputfield.on("change",".InputfieldFileActionSelect",function(){var $note=$(this).next(".InputfieldFileActionNote");if($(this).val().length){$note.fadeIn()}else{$note.hide()}})}function initUploadOldSchool(){$("body").addClass("ie-no-drop");$(".InputfieldImage.InputfieldFileMultiple").each(function(){var $field=$(this),maxFiles=parseInt($field.find(".InputfieldFileMaxFiles").val()),$list=$field.find(".gridImages"),$uploadArea=$field.find(".InputfieldImageUpload");$uploadArea.on("change","input[type=file]",function(){var $t=$(this),$mask=$t.parent(".InputMask");if($t.val().length>1)$mask.addClass("ui-state-disabled");else $mask.removeClass("ui-state-disabled");if($t.next("input.InputfieldFile").length>0)return;var numFiles=$list.children("li").length+$uploadArea.find("input[type=file]").length+1;if(maxFiles>0&&numFiles>=maxFiles)return;$uploadArea.find(".InputMask").not(":last").each(function(){var $m=$(this);if($m.find("input[type=file]").val()<1)$m.remove()});var $i=$mask.clone().removeClass("ui-state-disabled");$i.children("input[type=file]").val("");$i.insertAfter($mask)})})}function initUploadHTML5($inputfield){var $target;if($inputfield.length>0){$target=$inputfield.find(".InputfieldImageUpload")}else{$target=$(".InputfieldImageUpload")}$target.each(function(i){var $this=$(this);var $content=$this.closest(".InputfieldContent");if($this.hasClass("InputfieldImageInitUpload"))return;initHTML5Item($content,i);$this.addClass("InputfieldImageInitUpload")});function initHTML5Item($this,i){var $form=$this.parents("form");var $repeaterItem=$this.closest(".InputfieldRepeaterItem");var postUrl=$repeaterItem.length?$repeaterItem.attr("data-editUrl"):$form.attr("action");postUrl+=(postUrl.indexOf("?")>-1?"&":"?")+"InputfieldFileAjax=1";var $postToken=$form.find("input._post_token");var postTokenName=$postToken.attr("name");var postTokenValue=$postToken.val();var $errorParent=$this.find(".InputfieldImageErrors").first();var fieldName=$this.find(".InputfieldImageUpload").data("fieldname");fieldName=fieldName.slice(0,-2);var $inputfield=$this.closest(".Inputfield.InputfieldImage");var extensions=$this.find(".InputfieldImageUpload").data("extensions").toLowerCase();var maxFilesize=$this.find(".InputfieldImageUpload").data("maxfilesize");var filesUpload=$this.find("input[type=file]").get(0);var $fileList=$this.find(".gridImages");var fileList=$fileList.get(0);var gridSize=$fileList.data("gridsize");var doneTimer=null;var maxFiles=parseInt($this.find(".InputfieldImageMaxFiles").val());var resizeSettings=getClientResizeSettings($inputfield);var useClientResize=resizeSettings.maxWidth>0||resizeSettings.maxHeight>0||resizeSettings.maxSize>0;setupDropzone($this);if(maxFiles!=1)setupDropInPlace($fileList);$fileList.children().addClass("InputfieldFileItemExisting");$inputfield.on("pwimageupload",function(event,data){traverseFiles([data.file],data.xhr)});function errorItem(message,filename){if(typeof filename!=="undefined")message=""+filename+": "+message;var icon=" ";return"
  • "+icon+message+"
  • "}function basename(str){var base=new String(str).substring(str.lastIndexOf("/")+1);if(base.lastIndexOf(".")!=-1)base=base.substring(0,base.lastIndexOf("."));return base}function setupDropzone($el){if($el.hasClass("InputfieldImageDropzoneInit"))return;var el=$el.get(0);var $inputfield=$el.closest(".Inputfield");function dragStart(){if($inputfield.hasClass("pw-drag-in-file"))return;$el.addClass("ui-state-hover");$inputfield.addClass("pw-drag-in-file")}function dragStop(){if(!$inputfield.hasClass("pw-drag-in-file"))return;$el.removeClass("ui-state-hover");$inputfield.removeClass("pw-drag-in-file")}el.addEventListener("dragleave",function(){dragStop()},false);el.addEventListener("dragenter",function(evt){evt.preventDefault();dragStart()},false);el.addEventListener("dragover",function(evt){if(!$el.is("ui-state-hover"))dragStart();evt.preventDefault();evt.stopPropagation();return false},false);el.addEventListener("drop",function(evt){traverseFiles(evt.dataTransfer.files);dragStop();evt.preventDefault();evt.stopPropagation();return false},false);$el.addClass("InputfieldImageDropzoneInit")}function setupDropInPlace($gridImages){var $i=null;var haltDrag=false;var timer=null;var $inputfield=$gridImages.closest(".Inputfield");function addInputfieldClass(){$inputfield.addClass("pw-drag-in-file")}function removeInputfieldClass(){$inputfield.removeClass("pw-drag-in-file")}function getCenterCoordinates($el){var offset=$el.offset();var width=$el.width();var height=$el.height();var centerX=offset.left+width/2;var centerY=offset.top+height/2;return{clientX:centerX,clientY:centerY}}function noDropInPlace(){return $gridImages.find(".InputfieldImageEdit--active").length>0}function dragEnter(evt){if(noDropInPlace())return;evt.preventDefault();evt.stopPropagation();addInputfieldClass();haltDrag=false;if($i==null){var gridSize=$gridImages.attr("data-size")+"px";var $o=$("
    ").addClass("gridImage__overflow");if($gridImages.closest(".InputfieldImageEditAll").length){$o.css({width:"100%",height:gridSize})}else{$o.css({width:gridSize,height:gridSize})}$i=$("
  • ").addClass("ImageOuter gridImage gridImagePlaceholder").append($o);$gridImages.append($i)}var coords=getCenterCoordinates($i);$i.simulate("mousedown",coords)}function dragOver(evt){if(noDropInPlace())return;evt.preventDefault();evt.stopPropagation();addInputfieldClass();haltDrag=false;if($i==null)return;var coords={clientX:evt.originalEvent.clientX,clientY:evt.originalEvent.clientY};$i.simulate("mousemove",coords)}function dragEnd(evt){if(noDropInPlace())return;evt.preventDefault();evt.stopPropagation();if($i==null)return false;haltDrag=true;if(timer)clearTimeout(timer);timer=setTimeout(function(){if(!haltDrag||$i==null)return;$i.remove();$i=null;removeInputfieldClass()},1e3)}function drop(evt){if(noDropInPlace())return;removeInputfieldClass();haltDrag=false;var coords={clientX:evt.clientX,clientY:evt.clientY};$i.simulate("mouseup",coords);$uploadBeforeItem=$i.next(".gridImage");$i.remove();$i=null}if($gridImages.length&&!$gridImages.hasClass("gridImagesInitDropInPlace")){$gridImages.on("dragenter",dragEnter);$gridImages.on("dragover",dragOver);$gridImages.on("dragleave",dragEnd);$gridImages.on("drop",drop);$gridImages.addClass("gridImagesInitDropInPlace")}}function uploadFile(file,extension,xhrsub){var labels=ProcessWire.config.InputfieldImage.labels;var filesizeStr=parseInt(file.size/1024,10)+" kB";var tooltipMarkup=""+'
    '+""+""+'"+""+""+""+""+""+""+"
    "+labels.dimensions+"'+labels.na+"
    "+labels.filesize+""+filesizeStr+"
    "+labels.variations+"0
    "+"
    ";var $progressItem=$('
  • '),$tooltip=$(tooltipMarkup),$imgWrapper=$('
    '),$imageData=$('
    '),$hover=$("
    "),$progressBar=$(""),$edit=$(' '),$spinner=$('
    '),reader,xhr,fileData,fileUrl=URL.createObjectURL(file),$fileList=$inputfield.find(".gridImages"),singleMode=maxFiles==1,size=getCookieData($inputfield,"size"),listSize=getCookieData($inputfield,"listSize"),listMode=$inputfield.hasClass("InputfieldImageEditAll"),$img=$('');$imgWrapper.append($img);$hover.find(".gridImage__inner").append($edit);$hover.find(".gridImage__inner").append($spinner.css("display","none"));$hover.find(".gridImage__inner").append($progressBar);$imageData.append($(""+'

    '+file.name+"

    "+''+filesizeStr+""));if(listMode){$imgWrapper.css("width",listSize+"%");$imageData.css("width",100-listSize+"%")}else{$imgWrapper.css({width:size+"px",height:size+"px"})}$progressItem.append($tooltip).append($imgWrapper).append($hover).append($imageData);$img.attr({src:fileUrl,"data-original":fileUrl});img=new Image;img.addEventListener("load",function(){$tooltip.find(".dimensions").html(this.width+" × "+this.height);var factor=Math.min(this.width,this.height)/size;$img.attr({width:this.width/factor,height:this.height/factor})},false);img.src=fileUrl;if(typeof xhrsub!="undefined"){xhr=xhrsub}else{xhr=new XMLHttpRequest}function updateProgress(evt){if(typeof evt!="undefined"){if(!evt.lengthComputable)return;$progressBar.attr("value",parseInt(evt.loaded/evt.total*100))}$("body").addClass("pw-uploading");$spinner.css("display","block")}xhr.upload.addEventListener("progress",updateProgress,false);xhr.addEventListener("load",function(){xhr.getAllResponseHeaders();var response=$.parseJSON(xhr.responseText);if(typeof response.ajaxResponse!="undefined")response=response.ajaxResponse;var wasZipFile=response.length>1;if(response.error!==undefined)response=[response];for(var n=0;n-1){uploadReplaceName=uploadReplaceName.substring(0,uploadReplaceName.indexOf("?"))}var uploadReplaceExt=uploadReplaceName.substring(uploadReplaceName.lastIndexOf(".")+1).toLowerCase();uploadReplaceName=uploadReplaceName.substring(0,uploadReplaceName.lastIndexOf("."));if(uploadReplaceExt==uploadNewExt){$imageEditName.children("span").text(uploadReplaceName).removeAttr("contenteditable")}$markup.find(".gridImage__edit").click()}uploadReplace.file="";uploadReplace.item=null;uploadReplace.edit=null}if(doneTimer)clearTimeout(doneTimer);$uploadBeforeItem=null;doneTimer=setTimeout(function(){if(maxFiles!=1){setupSortable($fileList)}else{setupMagnificForSingle($inputfield)}$("body").removeClass("pw-uploading");$fileList.trigger("AjaxUploadDone")},500);$inputfield.trigger("change").removeClass("InputfieldFileEmpty")},false);if(uploadReplace.edit){uploadReplace.edit.find(".InputfieldImageEdit__close").click()}else if($inputfield.find(".InputfieldImageEdit:visible").length){$inputfield.find(".InputfieldImageEdit__close").click()}if(uploadReplace.item){uploadReplace.item.replaceWith($progressItem);uploadReplace.item=$progressItem}else if($uploadBeforeItem&&$uploadBeforeItem.length){$uploadBeforeItem.before($progressItem)}else{$fileList.append($progressItem)}function sendUpload(file,imageData){if(typeof xhrsub=="undefined"){xhr.open("POST",postUrl,true)}xhr.setRequestHeader("X-FILENAME",encodeURIComponent(file.name));xhr.setRequestHeader("X-FIELDNAME",fieldName);if(uploadReplace.item)xhr.setRequestHeader("X-REPLACENAME",uploadReplace.file);xhr.setRequestHeader("Content-Type","application/octet-stream");xhr.setRequestHeader("X-"+postTokenName,postTokenValue);xhr.setRequestHeader("X-REQUESTED-WITH","XMLHttpRequest");if(typeof imageData!="undefined"&&imageData!=false){xhr.send(imageData)}else{xhr.send(file)}updateGrid();$inputfield.trigger("change");var numFiles=$inputfield.find(".InputfieldFileItem").length;if(numFiles==1){$inputfield.removeClass("InputfieldFileEmpty").removeClass("InputfieldFileMultiple").addClass("InputfieldFileSingle")}else if(numFiles>1){$inputfield.removeClass("InputfieldFileEmpty").removeClass("InputfieldFileSingle").addClass("InputfieldFileMultiple")}}updateProgress();var ext=file.name.substring(file.name.lastIndexOf(".")+1).toLowerCase();if(useClientResize&&(ext=="jpg"||ext=="jpeg"||ext=="png"||ext=="gif")){var resizer=new PWImageResizer(resizeSettings);$spinner.addClass("pw-resizing");resizer.resize(file,function(imageData){$spinner.removeClass("pw-resizing");sendUpload(file,imageData)})}else{sendUpload(file)}}function traverseFiles(files,xhr){var toKilobyte=function(i){return parseInt(i/1024,10)};if(typeof files==="undefined"){fileList.innerHTML="No support for the File API in this web browser";return}for(var i=0,l=files.length;imaxFilesize&&maxFilesize>2e6){var filesizeKB=toKilobyte(files[i].size),maxFilesizeKB=toKilobyte(maxFilesize);if(typeof ProcessWire.config.InputfieldFile.labels["too-big"]!="undefined"){message=ProcessWire.config.InputfieldFile.labels["too-big"];message=message.replace("MAX_KB",maxFilesizeKB)}else{message="Filesize "+filesizeKB+" kb is too big. Maximum allowed is "+maxFilesizeKB+" kb"}$errorParent.append(errorItem(message,files[i].name))}else if(typeof xhr!="undefined"){uploadFile(files[i],extension,xhr)}else{uploadFile(files[i],extension)}if(maxFiles==1)break}}filesUpload.addEventListener("change",function(evt){traverseFiles(this.files);evt.preventDefault();evt.stopPropagation();this.value=""},false)}function setupEnlargementDropzones(){var sel=".InputfieldImageEdit__imagewrapper img";$(document).on("dragenter",sel,function(){var $this=$(this);if($this.closest(".InputfieldImageMax1").length)return;var src=$this.attr("src");var $edit=$this.closest(".InputfieldImageEdit");var $parent=$this.closest(".InputfieldImageEdit__imagewrapper");$parent.addClass("InputfieldImageEdit__replace");uploadReplace.file=new String(src).substring(src.lastIndexOf("/")+1);uploadReplace.item=$("#"+$edit.attr("data-for"));uploadReplace.edit=$edit}).on("dragleave",sel,function(){var $this=$(this);if($this.closest(".InputfieldImageMax1").length)return;var $parent=$this.closest(".InputfieldImageEdit__imagewrapper");$parent.removeClass("InputfieldImageEdit__replace");uploadReplace.file="";uploadReplace.item=null;uploadReplace.edit=null})}setupEnlargementDropzones()}function getClientResizeSettings($inputfield){var settings={maxWidth:0,maxHeight:0,maxSize:0,quality:1,autoRotate:true,debug:ProcessWire.config.debug};var data=$inputfield.attr("data-resize");if(typeof data!="undefined"&&data.length){data=data.split(";");settings.maxWidth=data[0].length?parseInt(data[0]):0;settings.maxHeight=data[1].length?parseInt(data[1]):0;settings.maxSize=data[2].length?parseFloat(data[2]):0;settings.quality=parseFloat(data[3])}return settings}function init(){$(".InputfieldImage.Inputfield").each(function(){initInputfield($(this))});initGridEvents();if(useAjaxUpload()){initUploadHTML5("")}else{initUploadOldSchool()}$(document).on("reloaded",".InputfieldImage",function(){var $inputfield=$(this);initInputfield($inputfield);initUploadHTML5($inputfield)}).on("wiretabclick",function(e,$newTab,$oldTab){$newTab.find(".InputfieldImage").each(function(){initInputfield($(this))})}).on("opened",".InputfieldImage",function(){initInputfield($(this))})}init()}jQuery(document).ready(function($){InputfieldImage($)}); \ No newline at end of file +function InputfieldImage($){var $uploadBeforeItem=null;var uploadReplace={file:"",item:null,edit:null};var magnificOptions={type:"image",closeOnContentClick:true,closeBtnInside:true};var cookieData=null;var retryGridItems=[];var gridSliding=false;function useAjaxUpload(){var isFileReaderSupport=window.File&&window.FileList&&window.FileReader;var isAjaxUpload=$(".InputfieldAllowAjaxUpload").length>0;var isPageIDIndicator=$("#PageIDIndicator").length>0;return isFileReaderSupport&&(isPageIDIndicator||isAjaxUpload)}function throttle(fn,threshhold,scope){threshhold||(threshhold=250);var last,deferTimer;return function(){var context=scope||this;var now=+new Date,args=arguments;if(last&&now .gridImage",start:function(e,ui){var size=getCookieData($el.closest(".Inputfield"),"size");ui.placeholder.append($("
    ").css({display:"block",height:size+"px",width:size+"px"}));timer=window.setTimeout(function(){closeEdit($el,null)},100);$el.addClass("InputfieldImageSorting")},stop:function(e,ui){var $this=$(this);if(timer!==null){ui.item.find(".InputfieldImageEdit__edit").click();clearTimeout(timer)}$this.children("li").each(function(n){var $sort=$(this).find(".InputfieldFileSort");if($sort.val()!=n)$sort.val(n).change()});$el.removeClass("InputfieldImageSorting")},cancel:".InputfieldImageEdit,.focusArea,input,textarea,button,select,option"};$el.sortable(sortableOptions)}function setupMagnificForRenderValue($el){var options=$.extend(true,{},magnificOptions);options.callbacks={elementParse:function(item){var src=$(item.el).attr("data-original");if(typeof src=="undefined"||!src)src=$(item.el).attr("src");item.src=src}};options.gallery={enabled:true};$el.find("img").magnificPopup(options)}function setupMagnificForSingle($el){var options=$.extend(true,{},magnificOptions);options.callbacks={elementParse:function(item){item.src=$(item.el).attr("src")}};options.gallery={enabled:false};$el.find("img").magnificPopup(options)}function findEditedElement($parent){return $parent.find(".InputfieldImageEdit--active")}function findEditMarkup($edit){return $("#"+$edit.find(".InputfieldImageEdit__edit").attr("data-current"))}function setDeleteStateOnAllItems($input){var checked=$input.is(":checked");var $items=$input.parents(".gridImages").find(".gridImage__deletebox");if(checked){$items.prop("checked","checked").change()}else{$items.removeAttr("checked").change()}}function updateGrid($inputfield){if(typeof $inputfield=="undefined"){var $gridImages=$(".gridImages")}else{var $gridImages=$inputfield.find(".gridImages")}$gridImages.each(function(){var $grid=$(this),$edit=findEditedElement($grid);if($edit.length){moveEdit(findEditMarkup($edit),$edit)}})}function checkInputfieldWidth($inputfield){var narrowItems=[];var mediumItems=[];var wideItems=[];var ni=0,mi=0,wi=0;var $inputfields;if(typeof $inputfield=="undefined"){$inputfields=$(".InputfieldImage.Inputfield")}else{$inputfields=$inputfield}$inputfields.removeClass("InputfieldImageNarrow InputfieldImageMedium InputfieldImageWide");$inputfields.each(function(){var $item=$(this);var width=$item.width();if(width<1)return;if(width<=500){narrowItems[ni]=$item;ni++}else if(width<=900){mediumItems[mi]=$item;mi++}else{wideItems[wi]=$item;wi++}});for(var n=0;n100?100:top,left:left>100?100:left,zoom:zoom>100?0:zoom};return focusData}function getFocusStr(focusObj){if(typeof focusObj=="undefined")focusObj=getFocus();return focusObj.top+" "+focusObj.left+" "+focusObj.zoom}function getFocusProperty(property){var focus=getFocus();return focus[property]}function setFocus(focusObj){focusData=focusObj;var focusStr=focusObj.top+" "+focusObj.left+" "+focusObj.zoom;$thumb.attr("data-focus",focusStr);$input=$edit.find(".InputfieldImageFocus");if(focusStr!=$input.val()){$input.val(focusStr).trigger("change")}}function setFocusProperty(property,value){var focus=getFocus();focus[property]=value;setFocus(focus)}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);var y=Math.round(focus.top/100*h);if(x<0)x=0;if(y<0)y=0;if(x>w)x=w;if(y>h)y=h;$focusCircle.css({top:y+"px",left:x+"px"})}$focusArea=$img.siblings(".focusArea");if(!$focusArea.length){$focusArea=$("
    ").addClass("focusArea");$img.after($focusArea)}$focusArea.css({height:$img.height()+"px",width:$img.width()+"px","background-color":"rgba(0,0,0,0.7)"}).addClass("focusActive");$focusCircle=$focusArea.find(".focusCircle");if(!$focusCircle.length){$focusCircle=$("
    ").addClass("focusCircle");$focusArea.append($focusCircle)}$img.parent().addClass("focusWrap");setFocusDragPosition();var zoomSlide=function(zoomPercent){var zoomBoxSize,focus,faWidth,faHeight;if(typeof zoomPercent=="undefined")zoomPercent=lastZoomPercent;lastZoomPercent=zoomPercent;faWidth=$focusArea.width();faHeight=$focusArea.height();if(faWidth>faHeight){$zoomBox.height(100-zoomPercent+"%");zoomBoxSize=$zoomBox.height();$zoomBox.width(zoomBoxSize)}else{$zoomBox.width(100-zoomPercent+"%");zoomBoxSize=$zoomBox.width();$zoomBox.height(zoomBoxSize)}focus=getFocus();var crop=getFocusZoomCropDimensions(focus.left,focus.top,zoomPercent,faWidth,faHeight,zoomBoxSize);$zoomBox.css({top:crop.top+"px",left:crop.left+"px","background-position":"-"+crop.left+"px -"+crop.top+"px","background-size":faWidth+"px "+faHeight+"px"});focus.zoom=zoomPercent;setFocusProperty("zoom",focus.zoom);if(mode=="grid")setGridSizeItem($thumb.parent(),gridSize,false,focus)};var dragEvent=function(event,ui){var $this=$(this);var circleSize=$this.outerHeight();var w=$this.parent().width();var h=$this.parent().height();var top=ui.position.top>0?ui.position.top:0;var left=ui.position.left>0?ui.position.left:0;top=top>0?top/h*100:0;left=left>0?left/w*100:0;var newFocus={top:top,left:left,zoom:getFocusProperty("zoom")};setFocus(newFocus);if(useZoomFocus){zoomSlide(newFocus.zoom)}else if(mode=="grid"){setGridSizeItem($thumb.parent(),gridSize,false,newFocus)}};$focusCircle.draggable({containment:"parent",drag:dragEvent,stop:dragEvent});if(useZoomFocus){var zoom=getFocusProperty("zoom");$zoomSlider=$("
    ").addClass("focusZoomSlider").css({"margin-top":"5px"});$zoomBox=$("
    ").addClass("focusZoomBox").css({position:"absolute",background:"transparent","background-image":"url("+$img.attr("src")+")"});$focusArea.prepend($zoomBox);$img.after($zoomSlider);$thumb.attr("src",$img.attr("src"));$zoomSlider.slider({min:0,max:50,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"))}}}function getFocusZoomPosition(focusPercent,sourceDimension,cropDimension){var focusPX=parseInt(sourceDimension*focusPercent/100);var position=parseInt(focusPX-cropDimension/2);var maxPosition=parseInt(sourceDimension-cropDimension);if(0>position)position=0;if(maxPosition=percentH?faWidth:faHeight;var cropDimension=maxDimension-maxDimension*zoomPercent/100;var posLeft=getFocusZoomPosition(focusLeft,faWidth,cropDimension);var posTop=getFocusZoomPosition(focusTop,faHeight,cropDimension);return{left:posLeft,top:posTop,width:cropDimension,height:cropDimension}}function getFocusZoomPosition4GridviewSquare(focusPercent,sourceDimPX,gridViewPX,zoomPercent,scale,smallestSidePX){var sourceDimPX=sourceDimPX*scale;var gridViewPercent=gridViewPX/sourceDimPX*100;var adjustPercent=gridViewPercent/2;var posPercent=focusPercent-adjustPercent;var posMinVal=0;var posMaxVal=100-gridViewPercent;if(posPercent<=posMinVal)posPercent=0;if(posPercent>=posMaxVal)posPercent=posMaxVal;var posPX=sourceDimPX/100*posPercent/scale;posPX=-1*parseInt(posPX);return posPX}function getFocusZoomCropDimensions4GridviewSquare(focusLeft,focusTop,zoomPercent,w,h,gridViewSize,scale){var smallestSidePX=w>=h?h:w;var posLeft=getFocusZoomPosition4GridviewSquare(focusLeft,w,gridViewSize,zoomPercent,scale,smallestSidePX);var posTop=getFocusZoomPosition4GridviewSquare(focusTop,h,gridViewSize,zoomPercent,scale,smallestSidePX);var transformLeft=parseInt(posLeft);var transformTop=parseInt(posTop);return{transformLeft:transformLeft,transformTop:transformTop,scale:scale}}function tearDownEdit($edit){stopFocus($edit);$edit.off("click",".InputfieldImageButtonFocus");$inputArea=$edit.find(".InputfieldImageEdit__edit");if($inputArea.children().not(".InputfieldFileSort").length){var $items=$inputArea.children();$("#"+$inputArea.attr("data-current")).find(".ImageData").append($items)}}function closeEdit($parent,$not){var $edit;if($parent){$edit=$parent.find(".InputfieldImageEdit--active")}else if($not){$edit=$(".InputfieldImageEdit--active").not($not.find(".InputfieldImageEdit--active"))}else{$edit=$(".InputfieldImageEdit--active")}if($edit.length){tearDownEdit($edit);$edit.removeClass("InputfieldImageEdit--active").removeAttr("id");$("#"+$edit.attr("data-for")).removeClass("gridImageEditing")}$(".InputfieldImageEdit__replace").removeClass("InputfieldImageEdit__replace")}function moveEdit($el,$edit){if(!$el||!$el.length)return;var $children=$el.parent().children().not(".InputfieldImageEdit");var lastTop=0;var found=false;var $insertBeforeItem=null;$children.each(function(){if($insertBeforeItem)return;var $item=$(this);var top=$item.offset().top;if(found&&top!=lastTop){$insertBeforeItem=$item}else if($item.attr("id")==$el.attr("id")){found=true}lastTop=top});if($insertBeforeItem){$edit.insertBefore($insertBeforeItem)}else{$edit.insertAfter($children.eq($children.length-1))}var $arrow=$edit.find(".InputfieldImageEdit__arrow");if($arrow.length)$arrow.css("left",$el.position().left+$el.outerWidth()/2+"px")}function initGridEvents(){$(window).resize(throttle(windowResize,200));$(document).on("click dblclick",".gridImage__trash",function(e){var $input=$(this).find("input");$input.prop("checked",inverseState).change();if(e.type=="dblclick"){setDeleteStateOnAllItems($input);e.preventDefault();e.stopPropagation()}});$(document).on("change",".gridImage__deletebox",function(){updateDeleteClass($(this))});$(document).on("click",".gridImage__edit",function(e){var $el=$(this).closest(".gridImage");if(!$el.length)return;if($el.closest(".InputfieldImageEditAll").length)return false;var $all=$el.closest(".gridImages");var $edit=$all.find(".InputfieldImageEdit");if($el.hasClass("gridImageEditing")){$edit.find(".InputfieldImageEdit__close").click()}else{moveEdit($el,$edit);tearDownEdit($edit);setupEdit($el,$edit);$edit.addClass("InputfieldImageEdit--active").attr("data-for",$el.attr("id"));$all.find(".gridImageEditing").removeClass("gridImageEditing");$el.addClass("gridImageEditing")}}).on("click",".InputfieldImageEditAll img",function(e){e.stopPropagation();e.preventDefault();$.magnificPopup.close();var options=$.extend(true,{},magnificOptions);var $img=$(this);options["items"]={src:$img.attr("data-original"),title:$img.attr("alt")};$.magnificPopup.open(options);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){stopFocus($edit)}else{startFocus($edit);$icon.addClass("focusIconActive");$icon.toggleClass($icon.attr("data-toggle"))}});$(document).on("click",function(e){var $el=$(e.target);if(typeof clickLanguageTabActive!="undefined"&&clickLanguageTabActive){return}if($el.closest(".InputfieldImageEdit").length){closeEdit(null,$el.parents(".gridImages"))}else if($el.is("input, textarea")&&$el.closest(".InputfieldImageEditAll").length){$el.focus().one("blur",function(){$el.closest(".gridImages").sortable("enable")});$el.closest(".gridImages").sortable("disable")}else if($el.closest(".gridImage__inner").length){closeEdit(null,$el.parents(".gridImages"))}else if($el.closest(".mfp-container").length){return}else if($el.closest(".ui-dialog").length){return}else if($el.is(".mfp-close")){return}else if($el.is("a.remove")){return}else{closeEdit(null,null)}});$(document).on("click",".InputfieldImageEdit__close",function(e){closeEdit($(this).parents(".gridImages"),null)});$(document).on("change",".InputfieldImage",function(){$(this).find(".InputfieldImageButtonCrop:not(.pw-modal-dblclick)").addClass("pw-modal-dblclick ui-state-disabled")}).on("click",".InputfieldImageButtonCrop.ui-state-disabled",function(e){var $button=$(this);var $list=$button.closest(".gridImages");if(!$list.hasClass("gridImagesAlerted")){ProcessWire.alert(ProcessWire.config.InputfieldImage.labels.changes);$list.addClass("gridImagesAlerted")}setTimeout(function(){$button.removeClass("ui-state-active")},500);return false});$(".ImagesGrid").on("click","button.pw-modal",function(e){e.preventDefault()});setupEditableFilename();checkInputfieldWidth()}function setupEditableFilename(){$(document).on("click",".InputfieldImageEdit__name",function(e){var $span=$(this).children("span");var $input=$span.closest(".gridImage, .InputfieldImageEdit").find(".InputfieldFileRename");var $list=$span.closest(".gridImages");$list.sortable("disable");$input.val($span.text());$span.on("keypress",function(e){if(e.which==13){$span.blur();return false}return true});$span.attr("autocomplete","off").attr("autocorrect","off").attr("autocapitalize","off").attr("spellcheck","false");$span.focus().on("blur",function(){var val=$(this).text();if($.trim(val).length<1){$span.text($input.val())}else if(val!=$input.val()){$input.val(val).change();$list.closest(".Inputfield").trigger("change")}$span.off("keypress");$list.sortable("enable")})})}function setListSize($inputfield,pct){pct=Math.floor(pct);$inputfield.find(".gridImage__overflow").each(function(){var dataPct=100-pct;var $this=$(this);$this.css("width",pct+"%");$this.siblings(".ImageData").css("width",dataPct+"%");$this.find("img").css({top:0,left:0,transform:"none"})});setCookieData($inputfield,"listSize",pct)}function setGridSize($inputfield,gridSize,ragged){if(!gridSize)return;var size=gridSize+"px";var $gridImages=$inputfield.find(".gridImages");if(typeof ragged=="undefined"||ragged==null)ragged=$gridImages.attr("data-ragged")?true:false;if(ragged){$gridImages.attr("data-ragged",1)}else{$gridImages.removeAttr("data-ragged")}$gridImages.find(".gridImage__overflow").each(function(){setGridSizeItem($(this),gridSize,ragged)});$gridImages.find(".gridImage__edit, .gridImage__resize").css("line-height",size);$gridImages.attr("data-size",gridSize);setCookieData($inputfield,"size",gridSize);if(retryGridItems.length)setTimeout(function(){while(retryGridItems.length){var item=retryGridItems.pop();setGridSizeItem(item.item,item.gridSize,ragged)}},150)}function setGridSizeItem($item,gridSize,ragged,focus){if($item.hasClass("gridImage__overflow")){var $img=$item.children("img")}else if($item.is("img")){var $img=$item;$item=$img.closest(".gridImage__overflow")}else{return}if(!gridSize){$img.removeAttr("width").removeAttr("height");$item.width("auto").height("auto");return}var zoom=0;var w=$img.width();var h=$img.height();var dataW=parseInt($img.attr("data-w"));var dataH=parseInt($img.attr("data-h"));if(!w)w=dataW;if(!h)h=dataH;if(!ragged&&typeof focus=="undefined"){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)zoom=focus.zoom;if(ragged){$img.attr("height",gridSize).removeAttr("width");$img.css({"max-height":"100%","max-width":"none",top:"50%",left:"50%",transform:"translate3d(-50%, -50%, 0)"})}else if(zoom>0&&$item.closest(".InputfieldImageFocusZoom").length&&!gridSliding){if(w>=h){var maxHeight="100%";var maxWidth="none";if(w==dataW){h=gridSize;w=h/dataH*dataW}}else{var maxHeight="none";var maxWidth="100%";if(h==dataH){w=gridSize;h=w/dataW*dataH}}var scale=1+zoom/100*2;var crop=getFocusZoomCropDimensions4GridviewSquare(focus.left,focus.top,zoom,w,h,gridSize,scale);$img.css({left:"0px",top:"0px","transform-origin":"0px 0px",transform:"scale("+crop.scale+") translate3d("+crop.transformLeft+"px, "+crop.transformTop+"px, 0)","max-width":maxWidth,"max-height":maxHeight})}else if(w>=h){$img.attr("height",gridSize).removeAttr("width");if(focus.left<1)focus.left=.001;$img.css({"max-height":"100%","max-width":"none",top:"50%",left:focus.left+"%",transform:"translate3d(-"+focus.left+"%, -50%, 0)"})}else if(h>w){$img.attr("width",gridSize).removeAttr("height");if(focus.top<1)focus.top=.001;$img.css({"max-height":"none","max-width":"100%",top:focus.top+"%",left:"50%",transform:"translate3d(-50%, -"+focus.top+"%, 0)"})}else{$img.css({"max-height":"100%","max-width":"none",top:"50%",left:"50%",transform:"translate3d(-50%, -50%, 0)"});$img.removeAttr("width").attr("height",gridSize)}var w=$img.width();if(w){$item.css({width:ragged?w+"px":gridSize+"px",height:gridSize+"px"})}else{var tries=$item.attr("data-tries");if(!tries)tries=0;if(typeof tries=="undefined")tries=0;tries=parseInt(tries);if(tries>3){$item.css({width:gridSize+"px",height:gridSize+"px"})}else{retryGridItems.push({item:$item,gridSize:gridSize});$item.attr("data-tries",tries+1)}}}function setupImageListToggle($target){if($target.find(".InputfieldImageListToggle").length)return;var $list=$("").append("");var $left=$("").append("");var $grid=$("").append("");var activeClass="InputfieldImageListToggle--active";var defaultMode="";var toggleClick=function(e){var $a=$(this);var $inputfield=$a.closest(".Inputfield");var href=$a.attr("href");var size;var $aPrev=$a.parent().children("."+activeClass);var hrefPrev=$aPrev.attr("href");$aPrev.removeClass(activeClass);$a.addClass(activeClass);stopFocus($inputfield);if(href=="list"){if(!$inputfield.hasClass("InputfieldImageEditAll")){$inputfield.find(".InputfieldImageEdit--active .InputfieldImageEdit__close").click();$inputfield.addClass("InputfieldImageEditAll")}size=getCookieData($inputfield,"listSize");setListSize($inputfield,size);setCookieData($inputfield,"mode","list")}else if(href=="left"){$inputfield.removeClass("InputfieldImageEditAll");size=getCookieData($inputfield,"size");setGridSize($inputfield,size,true);setCookieData($inputfield,"mode","left");updateGrid()}else if(href=="grid"){$inputfield.removeClass("InputfieldImageEditAll");size=getCookieData($inputfield,"size");setGridSize($inputfield,size,false);setCookieData($inputfield,"mode","grid");if(hrefPrev=="left")setTimeout(function(){setGridSize($inputfield,size,false)},100)}setupSortable($inputfield.find(".gridImages"));$a.blur();return false};$list.click(toggleClick);$left.click(toggleClick);$grid.click(toggleClick);if($target.hasClass("InputfieldImage")){$target.children(".InputfieldHeader").append($list).append($left).append($grid);defaultMode=getCookieData($target,"mode")}else{$(".InputfieldImage > .InputfieldHeader",$target).append($list).append($left).append($grid)}if(defaultMode=="list"){$list.click()}else if(defaultMode=="left"){$left.click()}else{}}function setupSizeSlider($inputfield){var $header=$inputfield.children(".InputfieldHeader");if($header.children(".InputfieldImageSizeSlider").length)return;var $gridImages=$inputfield.find(".gridImages");var gridSize=$gridImages.attr("data-gridsize");var min=gridSize/2;var max=gridSize*2;var $slider=$('');$header.append($slider);var sizeSlide=function(event,ui){var value=ui.value;var minPct=15;var divisor=Math.floor(gridSize/minPct);var v=value-min;var listSize=Math.floor(minPct+v/divisor);if($inputfield.hasClass("InputfieldImageEditAll")){setCookieData($inputfield,"size",value);setListSize($inputfield,listSize)}else{setCookieData($inputfield,"listSize",listSize);setGridSize($inputfield,value)}};$slider.slider({min:min,max:max,value:getCookieData($inputfield,"size"),range:"min",slide:sizeSlide,start:function(event,ui){gridSliding=true;if($inputfield.find(".InputfieldImageEdit:visible").length){$inputfield.find(".InputfieldImageEdit__close").click()}},stop:function(event,ui){gridSliding=false;sizeSlide(event,ui);updateGrid($inputfield)}})}function setCookieData($inputfield,property,value){var data=getCookieData($inputfield);var id=$inputfield.attr("id");var name=id?id.replace("wrap_Inputfield_",""):"";if(!name.length||typeof value=="undefined")return;if(data[name][property]==value)return;data[name][property]=value;$.cookie("InputfieldImage",data,{secure:window.location.protocol.indexOf("https:")===0});cookieData=data}function getCookieData($inputfield,property){if(cookieData&&typeof property=="undefined")return cookieData;var id=$inputfield.attr("id");var name=id?id.replace("wrap_Inputfield_",""):"na";var data=cookieData?cookieData:$.cookie("InputfieldImage");var value=null;if(!data)var data={};if(typeof data[name]=="undefined")data[name]={};if(typeof data[name].size=="undefined"||!data[name].size){data[name].size=parseInt($inputfield.find(".gridImages").attr("data-size"));if(!data[name].size)data[name].size=130}if(typeof data[name].listSize=="undefined"||!data[name].listSize){data[name].listSize=23}if(typeof data[name].mode=="undefined"||!data[name].mode){data[name].mode=$inputfield.find(".gridImages").attr("data-gridMode");if(!data[name].mode)data[name].mode="list"}if(cookieData==null)cookieData=data;if(typeof property=="undefined"){value=data}else if(property===true){value=data[name]}else if(typeof data[name][property]!="undefined"){value=data[name][property]}return value}function initInputfield($inputfield){if($inputfield.hasClass("InputfieldStateCollapsed"))return;var maxFiles=parseInt($inputfield.find(".InputfieldImageMaxFiles").val());var $gridImages=$inputfield.find(".gridImages");var size=getCookieData($inputfield,"size");var mode=getCookieData($inputfield,"mode");var ragged=mode=="left"?true:false;var renderValueMode=$inputfield.hasClass("InputfieldRenderValueMode");if(!size)size=$gridImages.attr("data-gridsize");size=parseInt(size);if(!renderValueMode&&($inputfield.hasClass("InputfieldImageEditAll")||mode=="list")){var listSize=getCookieData($inputfield,"listSize");setListSize($inputfield,listSize)}else{setGridSize($inputfield,size,ragged)}if(!$inputfield.hasClass("InputfieldImageInit")){$inputfield.addClass("InputfieldImageInit");if(renderValueMode){return setupMagnificForRenderValue($inputfield)}else if(maxFiles==1){$inputfield.addClass("InputfieldImageMax1");setupMagnificForSingle($inputfield)}else{setupSortable($gridImages)}setupImageListToggle($inputfield);setupSizeSlider($inputfield)}checkInputfieldWidth($inputfield);$inputfield.on("change",".InputfieldFileActionSelect",function(){var $note=$(this).next(".InputfieldFileActionNote");if($(this).val().length){$note.fadeIn()}else{$note.hide()}})}function initUploadOldSchool(){$("body").addClass("ie-no-drop");$(".InputfieldImage.InputfieldFileMultiple").each(function(){var $field=$(this),maxFiles=parseInt($field.find(".InputfieldFileMaxFiles").val()),$list=$field.find(".gridImages"),$uploadArea=$field.find(".InputfieldImageUpload");$uploadArea.on("change","input[type=file]",function(){var $t=$(this),$mask=$t.parent(".InputMask");if($t.val().length>1)$mask.addClass("ui-state-disabled");else $mask.removeClass("ui-state-disabled");if($t.next("input.InputfieldFile").length>0)return;var numFiles=$list.children("li").length+$uploadArea.find("input[type=file]").length+1;if(maxFiles>0&&numFiles>=maxFiles)return;$uploadArea.find(".InputMask").not(":last").each(function(){var $m=$(this);if($m.find("input[type=file]").val()<1)$m.remove()});var $i=$mask.clone().removeClass("ui-state-disabled");$i.children("input[type=file]").val("");$i.insertAfter($mask)})})}function initUploadHTML5($inputfield){var $target;if($inputfield.length>0){$target=$inputfield.find(".InputfieldImageUpload")}else{$target=$(".InputfieldImageUpload")}$target.each(function(i){var $this=$(this);var $content=$this.closest(".InputfieldContent");if($this.hasClass("InputfieldImageInitUpload"))return;initHTML5Item($content,i);$this.addClass("InputfieldImageInitUpload")});function initHTML5Item($this,i){var $form=$this.parents("form");var $repeaterItem=$this.closest(".InputfieldRepeaterItem");var postUrl=$repeaterItem.length?$repeaterItem.attr("data-editUrl"):$form.attr("action");postUrl+=(postUrl.indexOf("?")>-1?"&":"?")+"InputfieldFileAjax=1";var $postToken=$form.find("input._post_token");var postTokenName=$postToken.attr("name");var postTokenValue=$postToken.val();var $errorParent=$this.find(".InputfieldImageErrors").first();var fieldName=$this.find(".InputfieldImageUpload").data("fieldname");fieldName=fieldName.slice(0,-2);var $inputfield=$this.closest(".Inputfield.InputfieldImage");var extensions=$this.find(".InputfieldImageUpload").data("extensions").toLowerCase();var maxFilesize=$this.find(".InputfieldImageUpload").data("maxfilesize");var filesUpload=$this.find("input[type=file]").get(0);var $fileList=$this.find(".gridImages");var fileList=$fileList.get(0);var gridSize=$fileList.data("gridsize");var doneTimer=null;var maxFiles=parseInt($this.find(".InputfieldImageMaxFiles").val());var resizeSettings=getClientResizeSettings($inputfield);var useClientResize=resizeSettings.maxWidth>0||resizeSettings.maxHeight>0||resizeSettings.maxSize>0;setupDropzone($this);if(maxFiles!=1)setupDropInPlace($fileList);$fileList.children().addClass("InputfieldFileItemExisting");$inputfield.on("pwimageupload",function(event,data){traverseFiles([data.file],data.xhr)});function errorItem(message,filename){if(typeof filename!=="undefined")message=""+filename+": "+message;var icon=" ";return"
  • "+icon+message+"
  • "}function basename(str){var base=new String(str).substring(str.lastIndexOf("/")+1);if(base.lastIndexOf(".")!=-1)base=base.substring(0,base.lastIndexOf("."));return base}function setupDropzone($el){if($el.hasClass("InputfieldImageDropzoneInit"))return;var el=$el.get(0);var $inputfield=$el.closest(".Inputfield");function dragStart(){if($inputfield.hasClass("pw-drag-in-file"))return;$el.addClass("ui-state-hover");$inputfield.addClass("pw-drag-in-file")}function dragStop(){if(!$inputfield.hasClass("pw-drag-in-file"))return;$el.removeClass("ui-state-hover");$inputfield.removeClass("pw-drag-in-file")}el.addEventListener("dragleave",function(){dragStop()},false);el.addEventListener("dragenter",function(evt){evt.preventDefault();dragStart()},false);el.addEventListener("dragover",function(evt){if(!$el.is("ui-state-hover"))dragStart();evt.preventDefault();evt.stopPropagation();return false},false);el.addEventListener("drop",function(evt){traverseFiles(evt.dataTransfer.files);dragStop();evt.preventDefault();evt.stopPropagation();return false},false);$el.addClass("InputfieldImageDropzoneInit")}function setupDropInPlace($gridImages){var $i=null;var haltDrag=false;var timer=null;var $inputfield=$gridImages.closest(".Inputfield");function addInputfieldClass(){$inputfield.addClass("pw-drag-in-file")}function removeInputfieldClass(){$inputfield.removeClass("pw-drag-in-file")}function getCenterCoordinates($el){var offset=$el.offset();var width=$el.width();var height=$el.height();var centerX=offset.left+width/2;var centerY=offset.top+height/2;return{clientX:centerX,clientY:centerY}}function noDropInPlace(){return $gridImages.find(".InputfieldImageEdit--active").length>0}function dragEnter(evt){if(noDropInPlace())return;evt.preventDefault();evt.stopPropagation();addInputfieldClass();haltDrag=false;if($i==null){var gridSize=$gridImages.attr("data-size")+"px";var $o=$("
    ").addClass("gridImage__overflow");if($gridImages.closest(".InputfieldImageEditAll").length){$o.css({width:"100%",height:gridSize})}else{$o.css({width:gridSize,height:gridSize})}$i=$("
  • ").addClass("ImageOuter gridImage gridImagePlaceholder").append($o);$gridImages.append($i)}var coords=getCenterCoordinates($i);$i.simulate("mousedown",coords)}function dragOver(evt){if(noDropInPlace())return;evt.preventDefault();evt.stopPropagation();addInputfieldClass();haltDrag=false;if($i==null)return;var coords={clientX:evt.originalEvent.clientX,clientY:evt.originalEvent.clientY};$i.simulate("mousemove",coords)}function dragEnd(evt){if(noDropInPlace())return;evt.preventDefault();evt.stopPropagation();if($i==null)return false;haltDrag=true;if(timer)clearTimeout(timer);timer=setTimeout(function(){if(!haltDrag||$i==null)return;$i.remove();$i=null;removeInputfieldClass()},1e3)}function drop(evt){if(noDropInPlace())return;removeInputfieldClass();haltDrag=false;var coords={clientX:evt.clientX,clientY:evt.clientY};$i.simulate("mouseup",coords);$uploadBeforeItem=$i.next(".gridImage");$i.remove();$i=null}if($gridImages.length&&!$gridImages.hasClass("gridImagesInitDropInPlace")){$gridImages.on("dragenter",dragEnter);$gridImages.on("dragover",dragOver);$gridImages.on("dragleave",dragEnd);$gridImages.on("drop",drop);$gridImages.addClass("gridImagesInitDropInPlace")}}function uploadFile(file,extension,xhrsub){var labels=ProcessWire.config.InputfieldImage.labels;var filesizeStr=parseInt(file.size/1024,10)+" kB";var tooltipMarkup=""+'
    '+""+""+'"+""+""+""+""+""+""+"
    "+labels.dimensions+"'+labels.na+"
    "+labels.filesize+""+filesizeStr+"
    "+labels.variations+"0
    "+"
    ";var $progressItem=$('
  • '),$tooltip=$(tooltipMarkup),$imgWrapper=$('
    '),$imageData=$('
    '),$hover=$("
    "),$progressBar=$(""),$edit=$(' '),$spinner=$('
    '),reader,xhr,fileData,fileUrl=URL.createObjectURL(file),$fileList=$inputfield.find(".gridImages"),singleMode=maxFiles==1,size=getCookieData($inputfield,"size"),listSize=getCookieData($inputfield,"listSize"),listMode=$inputfield.hasClass("InputfieldImageEditAll"),$img=$('');$imgWrapper.append($img);$hover.find(".gridImage__inner").append($edit);$hover.find(".gridImage__inner").append($spinner.css("display","none"));$hover.find(".gridImage__inner").append($progressBar);$imageData.append($(""+'

    '+file.name+"

    "+''+filesizeStr+""));if(listMode){$imgWrapper.css("width",listSize+"%");$imageData.css("width",100-listSize+"%")}else{$imgWrapper.css({width:size+"px",height:size+"px"})}$progressItem.append($tooltip).append($imgWrapper).append($hover).append($imageData);$img.attr({src:fileUrl,"data-original":fileUrl});img=new Image;img.addEventListener("load",function(){$tooltip.find(".dimensions").html(this.width+" × "+this.height);var factor=Math.min(this.width,this.height)/size;$img.attr({width:this.width/factor,height:this.height/factor})},false);img.src=fileUrl;if(typeof xhrsub!="undefined"){xhr=xhrsub}else{xhr=new XMLHttpRequest}function updateProgress(evt){if(typeof evt!="undefined"){if(!evt.lengthComputable)return;$progressBar.attr("value",parseInt(evt.loaded/evt.total*100))}$("body").addClass("pw-uploading");$spinner.css("display","block")}xhr.upload.addEventListener("progress",updateProgress,false);xhr.addEventListener("load",function(){xhr.getAllResponseHeaders();var response=$.parseJSON(xhr.responseText);if(typeof response.ajaxResponse!="undefined")response=response.ajaxResponse;var wasZipFile=response.length>1;if(response.error!==undefined)response=[response];for(var n=0;n-1){uploadReplaceName=uploadReplaceName.substring(0,uploadReplaceName.indexOf("?"))}var uploadReplaceExt=uploadReplaceName.substring(uploadReplaceName.lastIndexOf(".")+1).toLowerCase();uploadReplaceName=uploadReplaceName.substring(0,uploadReplaceName.lastIndexOf("."));if(uploadReplaceExt==uploadNewExt){$imageEditName.children("span").text(uploadReplaceName).removeAttr("contenteditable")}$markup.find(".gridImage__edit").click()}uploadReplace.file="";uploadReplace.item=null;uploadReplace.edit=null}if(doneTimer)clearTimeout(doneTimer);$uploadBeforeItem=null;doneTimer=setTimeout(function(){if(maxFiles!=1){setupSortable($fileList)}else{setupMagnificForSingle($inputfield)}$("body").removeClass("pw-uploading");$fileList.trigger("AjaxUploadDone")},500);$inputfield.trigger("change").removeClass("InputfieldFileEmpty")},false);if(uploadReplace.edit){uploadReplace.edit.find(".InputfieldImageEdit__close").click()}else if($inputfield.find(".InputfieldImageEdit:visible").length){$inputfield.find(".InputfieldImageEdit__close").click()}if(uploadReplace.item){uploadReplace.item.replaceWith($progressItem);uploadReplace.item=$progressItem}else if($uploadBeforeItem&&$uploadBeforeItem.length){$uploadBeforeItem.before($progressItem)}else{$fileList.append($progressItem)}function sendUpload(file,imageData){if(typeof xhrsub=="undefined"){xhr.open("POST",postUrl,true)}xhr.setRequestHeader("X-FILENAME",encodeURIComponent(file.name));xhr.setRequestHeader("X-FIELDNAME",fieldName);if(uploadReplace.item)xhr.setRequestHeader("X-REPLACENAME",uploadReplace.file);xhr.setRequestHeader("Content-Type","application/octet-stream");xhr.setRequestHeader("X-"+postTokenName,postTokenValue);xhr.setRequestHeader("X-REQUESTED-WITH","XMLHttpRequest");if(typeof imageData!="undefined"&&imageData!=false){xhr.send(imageData)}else{xhr.send(file)}updateGrid();$inputfield.trigger("change");var numFiles=$inputfield.find(".InputfieldFileItem").length;if(numFiles==1){$inputfield.removeClass("InputfieldFileEmpty").removeClass("InputfieldFileMultiple").addClass("InputfieldFileSingle")}else if(numFiles>1){$inputfield.removeClass("InputfieldFileEmpty").removeClass("InputfieldFileSingle").addClass("InputfieldFileMultiple")}}updateProgress();var ext=file.name.substring(file.name.lastIndexOf(".")+1).toLowerCase();if(useClientResize&&(ext=="jpg"||ext=="jpeg"||ext=="png"||ext=="gif")){var resizer=new PWImageResizer(resizeSettings);$spinner.addClass("pw-resizing");resizer.resize(file,function(imageData){$spinner.removeClass("pw-resizing");sendUpload(file,imageData)})}else{sendUpload(file)}}function traverseFiles(files,xhr){var toKilobyte=function(i){return parseInt(i/1024,10)};if(typeof files==="undefined"){fileList.innerHTML="No support for the File API in this web browser";return}for(var i=0,l=files.length;imaxFilesize&&maxFilesize>2e6){var filesizeKB=toKilobyte(files[i].size),maxFilesizeKB=toKilobyte(maxFilesize);if(typeof ProcessWire.config.InputfieldFile.labels["too-big"]!="undefined"){message=ProcessWire.config.InputfieldFile.labels["too-big"];message=message.replace("MAX_KB",maxFilesizeKB)}else{message="Filesize "+filesizeKB+" kb is too big. Maximum allowed is "+maxFilesizeKB+" kb"}$errorParent.append(errorItem(message,files[i].name))}else if(typeof xhr!="undefined"){uploadFile(files[i],extension,xhr)}else{uploadFile(files[i],extension)}if(maxFiles==1)break}}filesUpload.addEventListener("change",function(evt){traverseFiles(this.files);evt.preventDefault();evt.stopPropagation();this.value=""},false)}function setupEnlargementDropzones(){var sel=".InputfieldImageEdit__imagewrapper img";$(document).on("dragenter",sel,function(){var $this=$(this);if($this.closest(".InputfieldImageMax1").length)return;var src=$this.attr("src");var $edit=$this.closest(".InputfieldImageEdit");var $parent=$this.closest(".InputfieldImageEdit__imagewrapper");$parent.addClass("InputfieldImageEdit__replace");uploadReplace.file=new String(src).substring(src.lastIndexOf("/")+1);uploadReplace.item=$("#"+$edit.attr("data-for"));uploadReplace.edit=$edit}).on("dragleave",sel,function(){var $this=$(this);if($this.closest(".InputfieldImageMax1").length)return;var $parent=$this.closest(".InputfieldImageEdit__imagewrapper");$parent.removeClass("InputfieldImageEdit__replace");uploadReplace.file="";uploadReplace.item=null;uploadReplace.edit=null})}setupEnlargementDropzones()}function getClientResizeSettings($inputfield){var settings={maxWidth:0,maxHeight:0,maxSize:0,quality:1,autoRotate:true,debug:ProcessWire.config.debug};var data=$inputfield.attr("data-resize");if(typeof data!="undefined"&&data.length){data=data.split(";");settings.maxWidth=data[0].length?parseInt(data[0]):0;settings.maxHeight=data[1].length?parseInt(data[1]):0;settings.maxSize=data[2].length?parseFloat(data[2]):0;settings.quality=parseFloat(data[3])}return settings}function init(){$(".InputfieldImage.Inputfield").each(function(){initInputfield($(this))});initGridEvents();if(useAjaxUpload()){initUploadHTML5("")}else{initUploadOldSchool()}$(document).on("reloaded",".InputfieldImage",function(){var $inputfield=$(this);initInputfield($inputfield);initUploadHTML5($inputfield);Inputfields.init($inputfield)}).on("wiretabclick",function(e,$newTab,$oldTab){$newTab.find(".InputfieldImage").each(function(){initInputfield($(this))})}).on("opened",".InputfieldImage",function(){initInputfield($(this))})}init()}jQuery(document).ready(function($){InputfieldImage($)}); \ No newline at end of file