diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..cf1f2a1e
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,5 @@
+# These are supported funding model platforms
+
+github: Jako
+open_collective: treehillstudio
+ko_fi: treehillstudio
diff --git a/.gitignore b/.gitignore
index 90c14fcb..eec222b3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,13 @@
-config.core.php
\ No newline at end of file
+_build/build.config.php
+config.core.php
+
+_build/site
+
+icon/*.svg
+
+_packages/*.*
+!_packages/.gitignore
+!_packages/*.zip
+
+node_modules/
+core/components/*/vendor/
diff --git a/core/components/tvimageplus/docs/license.txt b/LICENSE.md
similarity index 99%
rename from core/components/tvimageplus/docs/license.txt
rename to LICENSE.md
index f116d9e9..17ff2f8a 100644
--- a/core/components/tvimageplus/docs/license.txt
+++ b/LICENSE.md
@@ -58,7 +58,7 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
-
+
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
@@ -115,7 +115,7 @@ above, provided that you also meet all of these conditions:
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
-
+
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
@@ -173,7 +173,7 @@ access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
-
+
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
@@ -230,7 +230,7 @@ impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
-
+
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
diff --git a/README.md b/README.md
index 1a3d8a8e..2e20a64d 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,37 @@
-# Image+ v2.0 #
-## Advanced Image Template Variable
+[](https://hosted.weblate.org/projects/modx-extras/imageplus/)
-**v2.1 now available for [download](https://github.com/downloads/alanpich/tvImagePlus/tvimageplus-2.1-pl.transport.zip) `v2.1-pl`**
+# Image+
-**Dependant on PhpThumbOf component**
+Advanced image custom template variable in MODX Revolution.
-Advanced image TV input type for MODx Revolution.
-The required dimensions for the image can (optionally)
-be configured on the TV, restricting one or both
-dimensions. When the editor uploads an image to the TV,
-they can then use a graphical tool to crop the image
-to the required dimensions/proportions.
+## Features
-This extra is a complete replacement for previous versions.
-All previous versions (1.x) should be removed before attempting
-to install v2.x
+With this MODX custom template variable an image could be cropped while
+maintaining the original image. The dimensions for the image can (optionally) be
+configured to constrain a minimal width and/or height. The image crop could be
+forced to remain at a pre-set ratio. A graphical tool could be used to crop the
+image to the required dimensions/proportions.
+
+## Installation
+
+MODX Package Management
+
+## Usage
+
+Install via package manager, create a TV and change the input & output type to 'Image+'.
+
+## Documentation
+
+For more information please read the [documentation](https://jako.github.io/ImagePlus/).
+
+## Dependencies
+
+MODX Cropping Engine i.e. [pthumb](https://modx.com/extras/package/pthumb)
+
+## License
+
+The project is licensed under the [GPLv2 license](https://github.com/Jako/ImagePlus/blob/master/core/components/imageplus/docs/license.md).
+
+## Translations
+
+Translations of the package can be made for the [Default Lexicon](https://hosted.weblate.org/projects/modx-extras/imageplus/standard/), the [Properties Lexicon](https://hosted.weblate.org/projects/modx-extras/imageplus/properties/) and the [System Setting Lexicon](https://hosted.weblate.org/projects/modx-extras/imageplus/system-settings/)
diff --git a/_build/build.config.php b/_build/build.config.php
deleted file mode 100644
index cc6e507b..00000000
--- a/_build/build.config.php
+++ /dev/null
@@ -1,12 +0,0 @@
-'),'',$o));
- return $o;
-}
-
-
-/**
- * Parse the smarty readme tpl for packaging
- * @param string $path Path to tpl
- * @return string
- */
-function getReadmeFile( $path ){
- global $modx;
- $modx->getService('smarty','smarty.modSmarty');
-
- $modx->smarty->assign('date',date("jS M Y g:ia"));
- $modx->smarty->assign('version',PKG_VERSION.' '.PKG_RELEASE);
- $modx->smarty->assign('commit',PKG_COMMIT);
- $readme = $modx->smarty->fetch($path);
- return $readme;
-}//
-
-
-/**
- * Get currect git commit id
- * @param string repoRoot Path to repository root
- * @return string commit hash
- */
-function getGitCommitId( $repoRoot ){
- // Check git exists
- $whichGit = `which git`;
- if(empty($whichGit)){ return ''; };
-
- // Check we're in a git repo
- $gitFolder = str_replace('//','/',$repoRoot.'/.git');
- if( ! is_dir($gitFolder) ){ return ''; };
-
- //
- $test = shell_exec("cd $repoRoot; git rev-parse HEAD;");
- return trim($test);
-
-
-}//
\ No newline at end of file
diff --git a/_build/build.transport.php b/_build/build.transport.php
deleted file mode 100644
index 70a7a718..00000000
--- a/_build/build.transport.php
+++ /dev/null
@@ -1,120 +0,0 @@
- $root,
- 'build' => $root . '_build/',
- 'data' => $root . '_build/data/',
- 'resolvers' => $root . '_build/resolvers/',
- 'plugins' => $root.'core/components/'.PKG_NAME_LOWER.'/elements/plugins/',
- 'lexicon' => $root . 'core/components/'.PKG_NAME_LOWER.'/lexicon/',
- 'docs' => $root.'core/components/'.PKG_NAME_LOWER.'/docs/',
- 'elements' => $root.'core/components/'.PKG_NAME_LOWER.'/elements/',
- 'source_assets' => $root.'assets/components/'.PKG_NAME_LOWER,
- 'source_core' => $root.'core/components/'.PKG_NAME_LOWER,
-);
-unset($root);
-
-/* override with your own defines here (see build.config.sample.php) */
-require_once $sources['build'] . 'build.config.php';
-require_once MODX_CORE_PATH . 'model/modx/modx.class.php';
-
-$modx= new modX();
-$modx->initialize('mgr');
-echo '
Thanks for installing Image+. This open source extra was
+ developed by Treehill Studio - MODX development in Münsterland.
+
+
During the installation, we will collect some statistical data (the
+ hostname, the MODX UUID, the PHP version and the MODX version of your
+ MODX installation). Your data will be kept confidential and under no
+ circumstances be used for promotional purposes or disclosed to third
+ parties. We only like to know the usage count of this package.
+
+
If you install this package, you are giving us your permission to
+ collect, process and use that data for statistical purposes.
Image+ will be upgraded. This open source extra was developed by
+ Treehill Studio - MODX development in Münsterland.
+
+
During the installation, we will collect some statistical data (the
+ hostname, the MODX UUID, the PHP version and the MODX version of your
+ MODX installation). Your data will be kept confidential and under no
+ circumstances be used for promotional purposes or disclosed to third
+ parties. We only like to know the usage count of this package.
+
+
If you upgrade this package, you are giving us your permission to
+ collect, process and use that data for statistical purposes.
';
+
+ break;
+ case xPDOTransport::ACTION_UNINSTALL:
+ break;
+}
+
+return $output;
diff --git a/_packages/imageplus-2.2.2-pl.transport.zip b/_packages/imageplus-2.2.2-pl.transport.zip
new file mode 100644
index 00000000..8b3bed33
Binary files /dev/null and b/_packages/imageplus-2.2.2-pl.transport.zip differ
diff --git a/_packages/imageplus-2.3.4-pl.transport.zip b/_packages/imageplus-2.3.4-pl.transport.zip
new file mode 100644
index 00000000..5edae024
Binary files /dev/null and b/_packages/imageplus-2.3.4-pl.transport.zip differ
diff --git a/_packages/imageplus-2.4.5-pl.transport.zip b/_packages/imageplus-2.4.5-pl.transport.zip
new file mode 100644
index 00000000..264ab56d
Binary files /dev/null and b/_packages/imageplus-2.4.5-pl.transport.zip differ
diff --git a/_packages/imageplus-2.5.0-pl.transport.zip b/_packages/imageplus-2.5.0-pl.transport.zip
new file mode 100644
index 00000000..248531dd
Binary files /dev/null and b/_packages/imageplus-2.5.0-pl.transport.zip differ
diff --git a/_packages/imageplus-2.6.3-pl.transport.zip b/_packages/imageplus-2.6.3-pl.transport.zip
new file mode 100644
index 00000000..c1e28a94
Binary files /dev/null and b/_packages/imageplus-2.6.3-pl.transport.zip differ
diff --git a/_packages/imageplus-2.7.0-pl.transport.zip b/_packages/imageplus-2.7.0-pl.transport.zip
new file mode 100644
index 00000000..470cdd24
Binary files /dev/null and b/_packages/imageplus-2.7.0-pl.transport.zip differ
diff --git a/_packages/imageplus-2.8.9-pl.transport.zip b/_packages/imageplus-2.8.9-pl.transport.zip
new file mode 100644
index 00000000..1f1b733e
Binary files /dev/null and b/_packages/imageplus-2.8.9-pl.transport.zip differ
diff --git a/_packages/imageplus-2.9.3-pl.transport.zip b/_packages/imageplus-2.9.3-pl.transport.zip
new file mode 100644
index 00000000..01299549
Binary files /dev/null and b/_packages/imageplus-2.9.3-pl.transport.zip differ
diff --git a/_packages/imageplus-2.9.3-pl2.transport.zip b/_packages/imageplus-2.9.3-pl2.transport.zip
new file mode 100644
index 00000000..d5142911
Binary files /dev/null and b/_packages/imageplus-2.9.3-pl2.transport.zip differ
diff --git a/_packages/imageplus-2.9.4-pl.transport.zip b/_packages/imageplus-2.9.4-pl.transport.zip
new file mode 100644
index 00000000..275b20f3
Binary files /dev/null and b/_packages/imageplus-2.9.4-pl.transport.zip differ
diff --git a/_packages/imageplus-2.9.5-pl.transport.zip b/_packages/imageplus-2.9.5-pl.transport.zip
new file mode 100644
index 00000000..37456b39
Binary files /dev/null and b/_packages/imageplus-2.9.5-pl.transport.zip differ
diff --git a/assets/components/imageplus/connector.php b/assets/components/imageplus/connector.php
new file mode 100644
index 00000000..5710ca39
--- /dev/null
+++ b/assets/components/imageplus/connector.php
@@ -0,0 +1,37 @@
+getOption('imageplus.core_path', null, $modx->getOption('core_path') . 'components/imageplus/');
+/** @var ImagePlus $imageplus */
+$imageplus = $modx->getService('imageplus', 'ImagePlus', $corePath . 'model/imageplus/', [
+ 'core_path' => $corePath
+]);
+
+// Set HTTP_MODAUTH for web processors
+if (defined('MODX_REQP') && MODX_REQP === false) {
+ $_SERVER['HTTP_MODAUTH'] = $modx->user->getUserToken($modx->context->get('key'));
+}
+
+// Handle request
+$modx->request->handleRequest([
+ 'processors_path' => $imageplus->getOption('processorsPath'),
+ 'location' => ''
+]);
diff --git a/assets/components/imageplus/css/mgr/imageplus.min.css b/assets/components/imageplus/css/mgr/imageplus.min.css
new file mode 100644
index 00000000..cad25322
--- /dev/null
+++ b/assets/components/imageplus/css/mgr/imageplus.min.css
@@ -0,0 +1,6 @@
+#modx-input-props.imageplus-props,#modx-widget-props.imageplus-props{padding-top:0}.modx-grid .x-grid-cell-icons .x-grid3-cell-inner{padding-right:5px}.modx-grid-small .x-grid3-cell-inner{padding:8px 5px}.modx-grid-small .x-grid3-hd-inner{padding:8px 18px 8px 5px}.modx-grid-small .x-grid3-td-checker{padding:6px 0 0}.modx-grid-small .x-grid3-td-checker.x-grid3-hd{padding-top:0}.modx-grid-small .x-panel-tbar .x-toolbar{padding-top:2px}.modx-grid-small .x-panel-tbar .x-toolbar.x-small-editor .x-form-text{margin-top:0;padding:3px 8px 2px}.modx-grid-small .x-panel-tbar .x-toolbar .x-btn{padding:4px 10px}.modx-grid-small .x-panel-bbar .x-toolbar .x-btn{padding:2px}.modx-grid-small .x-panel-bbar .x-toolbar .x-form-text{padding:2px 3px}.modx-grid-small .x-tbar-loading:before{line-height:16px}.modx-grid-small .x-tbar-page-first:before,.modx-grid-small .x-tbar-page-last:before,.modx-grid-small .x-tbar-page-next:before,.modx-grid-small .x-tbar-page-prev:before{line-height:16px}.modx-grid-small .x-btn-icon.arrow_down button:before,.modx-grid-small .x-btn-icon.arrow_up button:before,.modx-grid-small .x-btn-icon.refresh button:before{line-height:16px}.modx-grid-small .x-small-editor .x-form-field-wrap,.modx-grid-small .x-small-editor .x-form-text{margin-top:2px}.x-form-field-wrap .x-form-triple-triggers{border:0;border-radius:0 3px 3px 0;box-shadow:none;height:100%!important;padding:0;position:absolute;right:0;top:0;width:90px}.x-form-field-wrap .x-form-triple-triggers .x-form-trigger{display:inline-block;position:relative;top:auto;vertical-align:top}.x-form-field-wrap .x-form-triple-triggers .x-form-trigger.x-form-clear-trigger:before{content:"\f00d"}.x-form-field-wrap .x-form-triple-triggers .x-form-trigger.x-form-crop-trigger:before{content:"\f125"}.imageplus-hidden-textarea{display:none}.imageplus-sectiontitle .desc-under,.imageplus-sectiontitle .x-form-item-label{padding-top:0}.treehillstudio_about{cursor:pointer;height:40px;margin-top:20px;opacity:.25;transition:opacity .5s}.treehillstudio_about:hover{opacity:1}.treehillstudio_about .x-panel-body{text-align:right}.treehillstudio_window a{color:#b2bf28;text-decoration:none}.treehillstudio_window a:hover{color:#77801a}.jcrop-holder{direction:ltr;text-align:left;-ms-touch-action:none}.jcrop-hline,.jcrop-vline{background:#fff url(Jcrop.gif);font-size:0;position:absolute}.jcrop-vline{height:100%;width:1px!important}.jcrop-vline.right{right:0}.jcrop-hline{height:1px!important;width:100%}.jcrop-hline.bottom{bottom:0}.jcrop-tracker{-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none;height:100%;-webkit-user-select:none;width:100%}.jcrop-handle{background-color:#333;border:1px solid #eee;font-size:1px;height:7px;width:7px}.jcrop-handle.ord-n{left:50%;margin-left:-4px;margin-top:-4px;top:0}.jcrop-handle.ord-s{bottom:0;left:50%;margin-bottom:-4px;margin-left:-4px}.jcrop-handle.ord-e{margin-right:-4px;margin-top:-4px;right:0;top:50%}.jcrop-handle.ord-w{left:0;margin-left:-4px;margin-top:-4px;top:50%}.jcrop-handle.ord-nw{left:0;margin-left:-4px;margin-top:-4px;top:0}.jcrop-handle.ord-ne{margin-right:-4px;margin-top:-4px;right:0;top:0}.jcrop-handle.ord-se{bottom:0;margin-bottom:-4px;margin-right:-4px;right:0}.jcrop-handle.ord-sw{bottom:0;left:0;margin-bottom:-4px;margin-left:-4px}.jcrop-dragbar.ord-n,.jcrop-dragbar.ord-s{height:7px;width:100%}.jcrop-dragbar.ord-e,.jcrop-dragbar.ord-w{height:100%;width:7px}.jcrop-dragbar.ord-n{margin-top:-4px}.jcrop-dragbar.ord-s{bottom:0;margin-bottom:-4px}.jcrop-dragbar.ord-e{margin-right:-4px;right:0}.jcrop-dragbar.ord-w{margin-left:-4px}.jcrop-light .jcrop-hline,.jcrop-light .jcrop-vline{background:#fff;filter:alpha(opacity=70)!important;opacity:.7!important}.jcrop-light .jcrop-handle{background-color:#000;border-color:#fff;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}.jcrop-dark .jcrop-hline,.jcrop-dark .jcrop-vline{background:#000;filter:alpha(opacity=70)!important;opacity:.7!important}.jcrop-dark .jcrop-handle{background-color:#fff;border-color:#000;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}.solid-line .jcrop-hline,.solid-line .jcrop-vline{background:#fff}.jcrop-holder img,img.jcrop-preview{max-width:none}.jcrop-hline,.jcrop-vline{background:#fff url(../../img/mgr/jcrop.gif)}
+/*!
+ * ImagePlus - Advanced Image Template Variable
+ * Version: 2.9.5
+ * Build date: 2024-02-21
+ */
\ No newline at end of file
diff --git a/assets/components/imageplus/img/mgr/jcrop.gif b/assets/components/imageplus/img/mgr/jcrop.gif
new file mode 100644
index 00000000..cc55e8c0
Binary files /dev/null and b/assets/components/imageplus/img/mgr/jcrop.gif differ
diff --git a/assets/components/imageplus/img/mgr/treehill-studio-small.png b/assets/components/imageplus/img/mgr/treehill-studio-small.png
new file mode 100644
index 00000000..d4947faf
Binary files /dev/null and b/assets/components/imageplus/img/mgr/treehill-studio-small.png differ
diff --git a/assets/components/imageplus/img/mgr/treehill-studio-small@2x.png b/assets/components/imageplus/img/mgr/treehill-studio-small@2x.png
new file mode 100644
index 00000000..49415abb
Binary files /dev/null and b/assets/components/imageplus/img/mgr/treehill-studio-small@2x.png differ
diff --git a/assets/components/imageplus/img/mgr/treehill-studio.png b/assets/components/imageplus/img/mgr/treehill-studio.png
new file mode 100644
index 00000000..c13691fc
Binary files /dev/null and b/assets/components/imageplus/img/mgr/treehill-studio.png differ
diff --git a/assets/components/imageplus/img/mgr/treehill-studio@2x.png b/assets/components/imageplus/img/mgr/treehill-studio@2x.png
new file mode 100644
index 00000000..dd7868ba
Binary files /dev/null and b/assets/components/imageplus/img/mgr/treehill-studio@2x.png differ
diff --git a/assets/components/imageplus/js/mgr/imageplus.min.js b/assets/components/imageplus/js/mgr/imageplus.min.js
new file mode 100644
index 00000000..235ec5dc
--- /dev/null
+++ b/assets/components/imageplus/js/mgr/imageplus.min.js
@@ -0,0 +1,6 @@
+/*!
+ * ImagePlus - Advanced Image Template Variable
+ * Version: 2.9.5
+ * Build date: 2024-02-21
+ */
+var imagePlus=function(e){imagePlus.superclass.constructor.call(this,e=e||{})},JSON,$jqIP=(Ext.extend(imagePlus,Ext.Component,{page:{},window:{},grid:{},tree:{},panel:{},combo:{},config:{},jquery:{},form:{},generateThumbUrl:function(e){return this.generatePhpThumbUrl(e)},generatePhpThumbUrl:function(e){var t,n=MODx.config.connectors_url+"system/phpthumb.php?",i={wctx:"mgr",w:150,source:1};for(t in e)i[t]=e[t];var r="";for(t in i)r+=encodeURIComponent(t)+"="+encodeURIComponent(i[t])+"&";return n=0You don't have any crop engines!
Before you can use Image+, you need at least one Crop Engine installed to handle image manipulation.
A quick fix is to install either pThumb, phpThumbOf, phpThumbsUp or phpThumbOn from the MODX Package Repository
"&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/
+{/literal}
diff --git a/core/components/imageplus/elements/tv/input/tpl/imageplus.render.tpl b/core/components/imageplus/elements/tv/input/tpl/imageplus.render.tpl
new file mode 100644
index 00000000..3a72115b
--- /dev/null
+++ b/core/components/imageplus/elements/tv/input/tpl/imageplus.render.tpl
@@ -0,0 +1,15 @@
+
+
+
+
diff --git a/core/components/imageplus/elements/tv/output/imageplus.class.php b/core/components/imageplus/elements/tv/output/imageplus.class.php
new file mode 100644
index 00000000..b7f45076
--- /dev/null
+++ b/core/components/imageplus/elements/tv/output/imageplus.class.php
@@ -0,0 +1,34 @@
+modx->getOption('imageplus.core_path', null, $this->modx->getOption('core_path') . 'components/imageplus/');
+ $imageplus = $this->modx->getService('imageplus', 'ImagePlus', $corePath . 'model/imageplus/', [
+ 'core_path' => $corePath
+ ]);
+
+ $params = array_merge([
+ 'docid' => ($this->modx->resource) ? $this->modx->resource->get('id') : 0
+ ], $params);
+
+ return $imageplus->getImageURL($value, $params, $this->tv);
+ }
+}
+
+return 'ImagePlusOutputRender';
diff --git a/core/components/imageplus/elements/tv/output/options/imageplus.php b/core/components/imageplus/elements/tv/output/options/imageplus.php
new file mode 100644
index 00000000..91afdc57
--- /dev/null
+++ b/core/components/imageplus/elements/tv/output/options/imageplus.php
@@ -0,0 +1,16 @@
+getOption('imageplus.core_path', null, $modx->getOption('core_path') . 'components/imageplus/');
+/** @var ImagePlus $imageplus */
+$imageplus = $modx->getService('imageplus', 'ImagePlus', $corePath . 'model/imageplus/', [
+ 'core_path' => $corePath
+]);
+
+return $modx->smarty->fetch($corePath . 'elements/tv/output/tpl/imageplus.options.tpl');
diff --git a/core/components/imageplus/elements/tv/output/tpl/imageplus.options.tpl b/core/components/imageplus/elements/tv/output/tpl/imageplus.options.tpl
new file mode 100644
index 00000000..c4cb1222
--- /dev/null
+++ b/core/components/imageplus/elements/tv/output/tpl/imageplus.options.tpl
@@ -0,0 +1,123 @@
+
+{/literal}
diff --git a/core/components/imageplus/lexicon/cs/default.inc.php b/core/components/imageplus/lexicon/cs/default.inc.php
new file mode 100644
index 00000000..a470763d
--- /dev/null
+++ b/core/components/imageplus/lexicon/cs/default.inc.php
@@ -0,0 +1,59 @@
+dokumentaci.';
+$_lang['imageplus.selectConfig'] = 'Předdefinované velikosti cílů/poměry stran';
+$_lang['imageplus.selectConfig_desc'] = 'Zvolte předem definovaný cílový poměr velikostí a stran. Definice lze vytvořit v nastavení systému.';
+$_lang['imageplus.selectConfigForce'] = 'Vynucené předdefinované velikosti cílů/poměr stran';
+$_lang['imageplus.selectConfigForce_desc'] = 'Vynucený výběr předem definované velikosti ořezu/poměru stran. Definice lze vytvořit v nastavení systému.';
+$_lang['imageplus.targetwidth'] = 'Cílová šířka (minimální)';
+$_lang['imageplus.targetwidth_desc'] = '(Volitelné, celé číslo) Cílová šířka pro výstupní obrázek. Nahraný obrázek by neměl být menší.';
+$_lang['imageplus.targetheight'] = 'Cílová výška (minimální)';
+$_lang['imageplus.targetheight_desc'] = '(Volitelné, celé číslo) Cílová výška pro výstupní obrázek. Nahraný obrázek by neměl být menší.';
+$_lang['imageplus.targetRatio'] = 'Cílový poměr stran';
+$_lang['imageplus.targetRatio_desc'] = '(Volitelné, plovoucí) Cílový poměr stran výstupního obrázku. Pokud je nastavena cílová výška a šířka, tato hodnota je ignorována.';
+$_lang['imageplus.thumbnailWidth'] = 'Šířka miniatury';
+$_lang['imageplus.thumbnailWidth_desc'] = '(Volitelné, celé číslo) Šířka miniatury v panelu Template variable.';
+$_lang['imageplus.allowAltTag'] = 'Zobrazit pole "alt" tag';
+$_lang['imageplus.allowAltTag_desc'] = 'Povolit uživatelům zadat title/alt.';
+$_lang['imageplus.allowCaption'] = 'Zobrazit pole titulků';
+$_lang['imageplus.allowCaption_desc'] = 'Umožňuje uživateli zadat popisek k obrázku.';
+$_lang['imageplus.allowCredits'] = 'Zobrazit pole Kredity';
+$_lang['imageplus.allowCredits_desc'] = 'Umožnit uživateli zadat kredit obrázku.';
+/** Output options render **/
+$_lang['imageplus.phpThumbParams'] = 'Další phpThumb parametry';
+$_lang['imageplus.phpThumbParams_desc'] = 'Přidání dalších filtrů atd. pro phpThumb. Dokumentaci najdete zde.';
+$_lang['imageplus.outputChunk'] = 'Výstupní chunk';
+$_lang['imageplus.outputChunk_desc'] = 'Vyberte chunk pro výstup TV. Nechte prázdé pro výstup čisté URL.';
+$_lang['imageplus.generateUrl'] = 'Generovat URL miniatury';
+$_lang['imageplus.generateUrl_desc'] = '(Volitelné) URL miniatury možná není třeba, pokud miniaturu generujete v chunku (například pomocí pthumb).';
+$_lang['imageplus.generateUrl_desc_warning'] = 'Tuto možnost musíte aktivovat, pokud ve výstupních možnostech nezadáte výstupní oddíl nebo pokud v zadaném výstupním oddílu použijete zástupný znak [[+url]]. Jinak se obrázek neořízne/nezmění velikost a vrátí se původní cesta k obrázku.';
+/** Placeholder descriptions */
+$_lang['imageplus.placeholder.url'] = 'URL miniatury';
+$_lang['imageplus.placeholder.alt'] = 'Alt text';
+$_lang['imageplus.placeholder.width'] = 'Šířka miniatury (ignorováno pokud je 0)';
+$_lang['imageplus.placeholder.height'] = 'Výška miniatury (ignorováno pokud je 0)';
+$_lang['imageplus.placeholder.source.src'] = 'Cesta ke zdrojovému obrázku';
+$_lang['imageplus.placeholder.source.width'] = 'Šířka zdrojového obrázku';
+$_lang['imageplus.placeholder.source.height'] = 'Výška zdrojového obrázku';
+$_lang['imageplus.placeholder.crop.width'] = 'Oříznout šířku zdrojového obrázku';
+$_lang['imageplus.placeholder.crop.height'] = 'Oříznout výšku zdrojového obrázku';
+$_lang['imageplus.placeholder.crop.x'] = 'Oříznout podle X osy zdrojového obrázku';
+$_lang['imageplus.placeholder.crop.y'] = 'Oříznout podle Y osy zdrojového obrázku';
+$_lang['imageplus.placeholder.options'] = 'phpThumb řetězec pro generování miniatur';
+$_lang['imageplus.placeholder.crop.options'] = 'phpThumb řetězec ořezu pro generování miniatur';
+$_lang['imageplus.error.image_too_small.title'] = 'Obrázek je příliš malý';
+$_lang['imageplus.error.image_too_small.msg'] = 'Vybraný obrázek je moc malý pro použití. Prosím vyberte jiný obrázek.';
+$_lang['imageplus.error.image_not_found.title'] = 'Obrázek nebyl nalezen';
+$_lang['imageplus.error.image_not_found.msg'] = 'Obrázek nebyl nalezen a nelze ho oříznout. Prosím vyberte jiný obrázek.';
diff --git a/core/components/imageplus/lexicon/cs/properties.inc.php b/core/components/imageplus/lexicon/cs/properties.inc.php
new file mode 100644
index 00000000..881d6db4
--- /dev/null
+++ b/core/components/imageplus/lexicon/cs/properties.inc.php
@@ -0,0 +1,12 @@
+check tpl a thumb.';
diff --git a/core/components/imageplus/lexicon/cs/setting.inc.php b/core/components/imageplus/lexicon/cs/setting.inc.php
new file mode 100644
index 00000000..d4c8ec59
--- /dev/null
+++ b/core/components/imageplus/lexicon/cs/setting.inc.php
@@ -0,0 +1,33 @@
+dokumentationen for at finde de relevante taster i kontekst-/systemindstillingerne.';
+$_lang['imageplus.selectConfig'] = 'Foruddefinerede målstørrelser/aspektforhold';
+$_lang['imageplus.selectConfig_desc'] = 'Vælg et foruddefineret målstørrelse/aspektforhold. Definitionerne kan oprettes i systemindstillingerne.';
+$_lang['imageplus.selectConfigForce'] = 'Forcerede foruddefinerede målstørrelser/aspektforhold';
+$_lang['imageplus.selectConfigForce_desc'] = 'Tvangsvalg af en foruddefineret afgrødestørrelse/aspektforhold. Definitionerne kan oprettes i systemindstillingerne.';
+$_lang['imageplus.targetwidth'] = 'Ønsket (minimums) bredde';
+$_lang['imageplus.targetwidth_desc'] = '(Valgfrit, heltal) Målbredden for outputbilledet. Det uploadede billede skal have denne minimumsbredde.';
+$_lang['imageplus.targetheight'] = 'Højde';
+$_lang['imageplus.targetheight_desc'] = '(Valgfrit, heltal) Målhøjden for outputbilledet. Det uploadede billede skal have denne minimumshøjde.';
+$_lang['imageplus.targetRatio'] = 'Ønsket højde- breddeforhold';
+$_lang['imageplus.targetRatio_desc'] = '(Valgfri, float) Det ønskede højde-breddeforhold for billedet som en float værdi. Hvis den ønskede højde og den ønskede bredde er sat bliver denne værdi ignoreret.';
+$_lang['imageplus.thumbnailWidth'] = 'Miniature bredde';
+$_lang['imageplus.thumbnailWidth_desc'] = '(Valgfrit, heltal) Bredden på miniaturebilledet i TV-fanen.';
+$_lang['imageplus.allowAltTag'] = 'Alternativ tekst';
+$_lang['imageplus.allowAltTag_desc'] = 'Tillader brugeren at indtaste en titel/alt-tag til billedet.';
+$_lang['imageplus.allowCaption'] = 'Vis feltet med billedtekst';
+$_lang['imageplus.allowCaption_desc'] = 'Tillader brugeren at indtaste en billedtekst til billedet.';
+$_lang['imageplus.allowCredits'] = 'Vis feltet Credits';
+$_lang['imageplus.allowCredits_desc'] = 'Tillader brugeren at angive en kredit for billedet.';
+/** Output options render **/
+$_lang['imageplus.phpThumbParams'] = 'Ekstra phpThumb parametre';
+$_lang['imageplus.phpThumbParams_desc'] = 'Tilføj ekstra filtre osv. til phpThumb. Dokumentationen kan findes her.';
+$_lang['imageplus.outputChunk'] = 'Output chunk';
+$_lang['imageplus.outputChunk_desc'] = 'Vælg en chunk til tv-udgang. Lad det være tomt for rå url-udgang.';
+$_lang['imageplus.generateUrl'] = 'Generer URL til miniature';
+$_lang['imageplus.generateUrl_desc'] = '(Valgfrit) URL til miniature er måske ikke nødvendigt hvis du generer miniaturen i output chunk\'en f.eks. ved brug af pthumb output filter.';
+$_lang['imageplus.generateUrl_desc_warning'] = 'Du skal aktivere denne indstilling, når du ikke angiver en output chunk i outputindstillingerne, eller når du bruger [[+url]]-pladsholderen [[+url]] i den angivne output chunk. Ellers beskæres/formindskes billedet ikke, og den originale billedsti returneres.';
+/** Placeholder descriptions */
+$_lang['imageplus.placeholder.url'] = 'URL til miniaturebilledet';
+$_lang['imageplus.placeholder.alt'] = 'Alt-tekst';
+$_lang['imageplus.placeholder.width'] = 'Miniaturebredde (ignoreret ved 0)';
+$_lang['imageplus.placeholder.height'] = 'Miniaturehøjde (ignoreret ved 0)';
+$_lang['imageplus.placeholder.source.src'] = 'Stien til kildebilledet';
+$_lang['imageplus.placeholder.source.width'] = 'Bredden på kildebilledet';
+$_lang['imageplus.placeholder.source.height'] = 'Højden på kildebilledet';
+$_lang['imageplus.placeholder.crop.width'] = 'Beskær kildebilledets bredde';
+$_lang['imageplus.placeholder.crop.height'] = 'Beskær kildebilledets højde';
+$_lang['imageplus.placeholder.crop.x'] = 'Beskær x placering af kildebilledet';
+$_lang['imageplus.placeholder.crop.y'] = 'Beskær y placering af kildebilledet';
+$_lang['imageplus.placeholder.options'] = 'phpThumb indstillingsstreng til at generere miniaturebilledet';
+$_lang['imageplus.placeholder.crop.options'] = 'phpThumb beskæringsindstillingsstreng til at generere miniaturebilledet';
+$_lang['imageplus.error.image_too_small.title'] = 'Billedet er for lille';
+$_lang['imageplus.error.image_too_small.msg'] = 'Det valgte billede er for småt til at blive brugt her. Vælg venligst et andet billede.';
+$_lang['imageplus.error.image_not_found.title'] = 'Billedet blev ikke fundet';
+$_lang['imageplus.error.image_not_found.msg'] = 'Billedet blev ikke fundet og kan dermed ikke blive beskåret. Vælg venligst et andet billede.';
diff --git a/core/components/imageplus/lexicon/da/properties.inc.php b/core/components/imageplus/lexicon/da/properties.inc.php
new file mode 100644
index 00000000..0bc38fe1
--- /dev/null
+++ b/core/components/imageplus/lexicon/da/properties.inc.php
@@ -0,0 +1,12 @@
+check tpl eller thumb.';
diff --git a/core/components/imageplus/lexicon/da/setting.inc.php b/core/components/imageplus/lexicon/da/setting.inc.php
new file mode 100644
index 00000000..a16a5c6b
--- /dev/null
+++ b/core/components/imageplus/lexicon/da/setting.inc.php
@@ -0,0 +1,33 @@
+Dokumentation für die entsprechenden Einträge in den System-/Kontexteinstellungen.';
+$_lang['imageplus.selectConfig'] = 'Vordefinierte Ausgabegrößen/Ausgabe-Seitenverältnisse';
+$_lang['imageplus.selectConfig_desc'] = 'Wählen Sie eine vordefinierte Ausgabegröße/ein Seitenverhältnis. Die Vorgaben können in den Systemeinstellungen eingestellt werden.';
+$_lang['imageplus.selectConfigForce'] = 'Erzwungene vordefinierte Ausgabegrößen/Ausgabe-Seitenverältnisse';
+$_lang['imageplus.selectConfigForce_desc'] = 'Wählen Sie eine erzwungene vordefinierte Ausgabegröße/ein Seitenverhältnis. Die Vorgaben können in den Systemeinstellungen eingestellt werden.';
+$_lang['imageplus.targetwidth'] = '(Minimale) Ausgabebreite';
+$_lang['imageplus.targetwidth_desc'] = '(Optional, Integer) Gibt die Ausgabebreite des Bildes an. Das hochgeladene Bild muss mindestens diese Breite haben.';
+$_lang['imageplus.targetheight'] = '(Minimale) Ausgabehöhe';
+$_lang['imageplus.targetheight_desc'] = '(Optional, Integer) Gibt die Ausgabehöhe des Bildes an. Das hochgeladene Bild muss mindestens diese Höhe haben.';
+$_lang['imageplus.targetRatio'] = 'Ausgabe-Seitenverhältnis';
+$_lang['imageplus.targetRatio_desc'] = '(Optional, Float) Gibt das Seitenverhältnis des Bildes an. Wenn die Ausgabebreite und die Ausgabehöhe des Bildes angegeben sind, wird dieser Wert ignoriert.';
+$_lang['imageplus.thumbnailWidth'] = 'Breite des Thumbnails';
+$_lang['imageplus.thumbnailWidth_desc'] = '(Optional, Integer) Breite des Thumbnails im Template Variablen Bereich.';
+$_lang['imageplus.allowAltTag'] = 'Alternatives Textfeld anzeigen';
+$_lang['imageplus.allowAltTag_desc'] = 'Ermöglicht die Eingabe eines Alt oder Title-Attributes für das Bild.';
+$_lang['imageplus.allowCaption'] = 'Titel Feld anzeigen';
+$_lang['imageplus.allowCaption_desc'] = 'Ermöglicht die Eingabe eines Titels für das Bild.';
+$_lang['imageplus.allowCredits'] = 'Bildnachweis Feld anzeigen';
+$_lang['imageplus.allowCredits_desc'] = 'Ermöglicht die Eingabe eines Bildnachweises für das Bild.';
+/** Output options render **/
+$_lang['imageplus.phpThumbParams'] = 'Zursätzliche phpThumb Parameter';
+$_lang['imageplus.phpThumbParams_desc'] = '(Optional) Geben Sie zusätzliche phpThumb Parameter an. Mehr Informationen zu phpThumb Parametern erhalten Sie hier.';
+$_lang['imageplus.outputChunk'] = 'Ausgabe Chunk';
+$_lang['imageplus.outputChunk_desc'] = '(Optional) Wählen Sie einen Ausgabe Chunk aus. Wenn kein Wert angebeben ist wird der Bildpfad ausgegeben.';
+$_lang['imageplus.generateUrl'] = 'Thumbnail URL generieren';
+$_lang['imageplus.generateUrl_desc'] = '(Optional) Die Thumbnail URL eventuell wird nicht benötigt, wenn das Thumbnail im Ausgabe Chunk z.B. mit einem pThumb Output Filter generiert wird.';
+$_lang['imageplus.generateUrl_desc_warning'] = 'Sie müssen diese Option aktivieren, wenn Sie keinen Ausgabe Chunk benutzen oder wenn Sie den [[+url]] Platzhalter im angegebenen Ausgabe Chunk einsetzen. Andernfalls wird das Bild nicht beschnitten/skaliert und der original Bildpfad ausgegeben.';
+/** Placeholder descriptions */
+$_lang['imageplus.placeholder.url'] = 'Thumbnail URL';
+$_lang['imageplus.placeholder.alt'] = 'Alt-Text';
+$_lang['imageplus.placeholder.width'] = 'Breite des Thumbnails (wird ignoriert, wenn 0)';
+$_lang['imageplus.placeholder.height'] = 'Höhe des Thumbnails (wird ignoriert, wenn 0)';
+$_lang['imageplus.placeholder.source.src'] = 'Server-Pfad zum Originalbild';
+$_lang['imageplus.placeholder.source.width'] = 'Minimale Breite des Originalbilds';
+$_lang['imageplus.placeholder.source.height'] = 'Minimale Höhe des Originalbilds';
+$_lang['imageplus.placeholder.crop.width'] = 'Crop-Breite des Originalbilds';
+$_lang['imageplus.placeholder.crop.height'] = 'Crop-Höhe des Originalbilds';
+$_lang['imageplus.placeholder.crop.x'] = 'Crop-X-Startposition des Originalbilds';
+$_lang['imageplus.placeholder.crop.y'] = 'Crop-Y-Startposition des Originalbilds';
+$_lang['imageplus.placeholder.options'] = 'phpThumb Optionen für das Thumbnail';
+$_lang['imageplus.placeholder.crop.options'] = 'phpThumb Crop Optionen für das Thumbnail';
+$_lang['imageplus.error.image_too_small.title'] = 'Bild ist zu klein';
+$_lang['imageplus.error.image_too_small.msg'] = 'Das gewählte Bild ist zu klein um benutzt zu werden. Bitte wählen Sie ein anderes Bild.';
+$_lang['imageplus.error.image_not_found.title'] = 'Bild nicht gefunden';
+$_lang['imageplus.error.image_not_found.msg'] = 'Das Bild wurde nicht gefunden und kann nicht zugeschnitten werden. Bitte wählen Sie ein anderes Bild.';
diff --git a/core/components/imageplus/lexicon/de/properties.inc.php b/core/components/imageplus/lexicon/de/properties.inc.php
new file mode 100644
index 00000000..214a5d11
--- /dev/null
+++ b/core/components/imageplus/lexicon/de/properties.inc.php
@@ -0,0 +1,12 @@
+check tpl oder thumb enthalten.';
diff --git a/core/components/imageplus/lexicon/de/setting.inc.php b/core/components/imageplus/lexicon/de/setting.inc.php
new file mode 100644
index 00000000..9bf7b781
--- /dev/null
+++ b/core/components/imageplus/lexicon/de/setting.inc.php
@@ -0,0 +1,33 @@
+documentation for the appropriate keys in the context/system settings.';
+$_lang['imageplus.selectConfig'] = 'Predefined target sizes/aspect ratios';
+$_lang['imageplus.selectConfig_desc'] = 'Select a predefined target size/aspect ratio. The definitions could be created in the system settings.';
+$_lang['imageplus.selectConfigForce'] = 'Forced predefined target sizes/aspect ratios';
+$_lang['imageplus.selectConfigForce_desc'] = 'Forced select a predefined crop size/aspect ratio. The definitions could be created in the system settings.';
+$_lang['imageplus.targetwidth'] = 'Target (Minimal) Width';
+$_lang['imageplus.targetwidth_desc'] = '(Optional, Integer) The target width for the output image. The uploaded image should have this minimal width.';
+$_lang['imageplus.targetheight'] = 'Target (Minimal) Height';
+$_lang['imageplus.targetheight_desc'] = '(Optional, Integer) The target height for the output image. The uploaded image should have this minimal height.';
+$_lang['imageplus.targetRatio'] = 'Target Aspect Ratio';
+$_lang['imageplus.targetRatio_desc'] = '(Optional, Float) The target aspect ratio for the output image as float value. If the target height and the targed width are set, this value is ignored.';
+$_lang['imageplus.thumbnailWidth'] = 'Thumbnail Width';
+$_lang['imageplus.thumbnailWidth_desc'] = '(Optional, Integer) The thumbnail width of the image in the template variable panel.';
+$_lang['imageplus.allowAltTag'] = 'Show Alt Tag Field';
+$_lang['imageplus.allowAltTag_desc'] = 'Allow user to enter a title/alt-tag for the image.';
+$_lang['imageplus.allowCaption'] = 'Show Caption Field';
+$_lang['imageplus.allowCaption_desc'] = 'Allow user to enter a caption for the image.';
+$_lang['imageplus.allowCredits'] = 'Show Credits Field';
+$_lang['imageplus.allowCredits_desc'] = 'Allow user to enter a credit for the image.';
+/** Output options render **/
+$_lang['imageplus.phpThumbParams'] = 'Additional phpThumb Parameters';
+$_lang['imageplus.phpThumbParams_desc'] = 'Add additional filters etc for phpThumb. Documentation can be found here.';
+$_lang['imageplus.outputChunk'] = 'Output Chunk';
+$_lang['imageplus.outputChunk_desc'] = 'Select a chunk for tv output. Leave blank for raw url output.';
+$_lang['imageplus.generateUrl'] = 'Generate Thumb URL';
+$_lang['imageplus.generateUrl_desc'] = '(Optional) The thumb url is maybe not necessary, if you generate the thumbnail in output chunk i.e. by a pthumb output filter.';
+$_lang['imageplus.generateUrl_desc_warning'] = 'You have to activate this option, when you don’t specify an output chunk in the output options or when you use the [[+url]] placeholder in the specified output chunk. Otherwise the image is not cropped/resized and the original image path is returned.';
+/** Placeholder descriptions */
+$_lang['imageplus.placeholder.url'] = 'URL of the thumbnail image';
+$_lang['imageplus.placeholder.alt'] = 'Alt text';
+$_lang['imageplus.placeholder.width'] = 'Width of the thumbnail image (ignored when 0)';
+$_lang['imageplus.placeholder.height'] = 'Height of the thumbnail image (ignored when 0)';
+$_lang['imageplus.placeholder.source.src'] = 'Path to the source image';
+$_lang['imageplus.placeholder.source.width'] = 'Width of the source image';
+$_lang['imageplus.placeholder.source.height'] = 'Height of the source image';
+$_lang['imageplus.placeholder.crop.width'] = 'Crop width of the source image';
+$_lang['imageplus.placeholder.crop.height'] = 'Crop height of the source image';
+$_lang['imageplus.placeholder.crop.x'] = 'Crop x position of the source image';
+$_lang['imageplus.placeholder.crop.y'] = 'Crop y position of the source image';
+$_lang['imageplus.placeholder.options'] = 'phpThumb option string to generate the thumbnail image';
+$_lang['imageplus.placeholder.crop.options'] = 'phpThumb crop option string to generate the thumbnail image';
+$_lang['imageplus.error.image_too_small.title'] = 'Image too small';
+$_lang['imageplus.error.image_too_small.msg'] = 'The selected image is too small to be used here. Please select a different image.';
+$_lang['imageplus.error.image_not_found.title'] = 'Image not found';
+$_lang['imageplus.error.image_not_found.msg'] = 'The image was not found and can’t be cropped. Please select a different image.';
diff --git a/core/components/imageplus/lexicon/en/properties.inc.php b/core/components/imageplus/lexicon/en/properties.inc.php
new file mode 100644
index 00000000..2a495cd9
--- /dev/null
+++ b/core/components/imageplus/lexicon/en/properties.inc.php
@@ -0,0 +1,12 @@
+check tpl and thumb.';
diff --git a/core/components/imageplus/lexicon/en/setting.inc.php b/core/components/imageplus/lexicon/en/setting.inc.php
new file mode 100644
index 00000000..4748fe4a
--- /dev/null
+++ b/core/components/imageplus/lexicon/en/setting.inc.php
@@ -0,0 +1,33 @@
+documentación para conocer las claves apropiadas en la configuración del contexto/sistema.';
+$_lang['imageplus.selectConfig'] = 'Tamaños predefinidos de objetivos/relación de aspectos';
+$_lang['imageplus.selectConfig_desc'] = 'Seleccione una proporción predefinida de tamaño y aspecto del objetivo. Las definiciones podrían crearse en los ajustes del sistema.';
+$_lang['imageplus.selectConfigForce'] = 'Tamaños de objetivos predefinidos y relaciones de aspecto forzadas';
+$_lang['imageplus.selectConfigForce_desc'] = 'Seleccionar forzosamente un tamaño de cultivo/relación de aspecto predefinido. Las definiciones podrían crearse en los ajustes del sistema.';
+$_lang['imageplus.targetwidth'] = 'Ancho de la imagen';
+$_lang['imageplus.targetwidth_desc'] = '(Opcional, Entero) El ancho objetivo para la imagen de salida. La imagen cargada debe tener esta anchura mínima.';
+$_lang['imageplus.targetheight'] = 'Alto de la imagen';
+$_lang['imageplus.targetheight_desc'] = '(Opcional, Entero) La altura objetivo para la imagen de salida. La imagen cargada debe tener esta altura mínima.';
+$_lang['imageplus.targetRatio'] = 'Relación de aspecto objetivo';
+$_lang['imageplus.targetRatio_desc'] = '(Opcional, Float) La relación de aspecto objetivo para la imagen de salida como valor float. Si se establecen la altura y la anchura objetivo, este valor se ignora.';
+$_lang['imageplus.thumbnailWidth'] = 'Anchura de las miniaturas';
+$_lang['imageplus.thumbnailWidth_desc'] = '(Opcional, Entero) El ancho de la miniatura de la imagen en el panel de variables de la plantilla.';
+$_lang['imageplus.allowAltTag'] = 'Etiqueta Alt';
+$_lang['imageplus.allowAltTag_desc'] = 'Permitir que el usuario introduzca un título/etiqueta de alto para la imagen.';
+$_lang['imageplus.allowCaption'] = 'Mostrar campo de subtítulos';
+$_lang['imageplus.allowCaption_desc'] = 'Permite al usuario introducir un título para la imagen.';
+$_lang['imageplus.allowCredits'] = 'Mostrar campo de créditos';
+$_lang['imageplus.allowCredits_desc'] = 'Permitir al usuario introducir un crédito para la imagen.';
+/** Output options render **/
+$_lang['imageplus.phpThumbParams'] = 'Parámetros phpThumb adicionales';
+$_lang['imageplus.phpThumbParams_desc'] = 'Añade filtros adicionales etc a phpThumb. La documentación se puede encontrar aquí.';
+$_lang['imageplus.outputChunk'] = 'Resultado chunk';
+$_lang['imageplus.outputChunk_desc'] = 'Selecciona un chunk para obtener un resultado del tv. Dejar en vacío para obtener un resultado de la url cruda.';
+$_lang['imageplus.generateUrl'] = 'Generar la URL del pulgar';
+$_lang['imageplus.generateUrl_desc'] = '(Opcional) La url de la miniatura puede no ser necesaria si se genera la miniatura en el chunk de salida, es decir, mediante un filtro de salida pthumb.';
+$_lang['imageplus.generateUrl_desc_warning'] = 'Tiene que activar esta opción, cuando no especifica un chunk de salida en las opciones de salida o cuando utiliza el marcador de posición [[+url]] en el chunk de salida especificado. En caso contrario, la imagen no se recortará/redimensionará y se devolverá la ruta original de la imagen.';
+/** Placeholder descriptions */
+$_lang['imageplus.placeholder.url'] = 'URL de la imagen en miniatura';
+$_lang['imageplus.placeholder.alt'] = 'Alt text';
+$_lang['imageplus.placeholder.width'] = 'Ancho de la imagen en miniatura (se ignora si es 0)';
+$_lang['imageplus.placeholder.height'] = 'Altura de la imagen en miniatura (se ignora si es 0)';
+$_lang['imageplus.placeholder.source.src'] = 'Ruta de acceso a la imagen de origen';
+$_lang['imageplus.placeholder.source.width'] = 'Anchura de la imagen de origen';
+$_lang['imageplus.placeholder.source.height'] = 'Altura de la imagen de origen';
+$_lang['imageplus.placeholder.crop.width'] = 'Ancho de recorte de la imagen de origen';
+$_lang['imageplus.placeholder.crop.height'] = 'Altura de recorte de la imagen de origen';
+$_lang['imageplus.placeholder.crop.x'] = 'Recorte de la posición x de la imagen de origen';
+$_lang['imageplus.placeholder.crop.y'] = 'Posición de recorte y de la imagen de origen';
+$_lang['imageplus.placeholder.options'] = 'Cadena de opciones phpThumb para generar la imagen en miniatura';
+$_lang['imageplus.placeholder.crop.options'] = 'Cadena de opciones phpThumb para generar la imagen en miniatura';
+$_lang['imageplus.error.image_too_small.title'] = 'Imagen demasiado pequeña';
+$_lang['imageplus.error.image_too_small.msg'] = 'La imagen seleccionada es demasiado pequeña para ser utilizada aquí. Por favor, seleccione una imagen diferente.';
+$_lang['imageplus.error.image_not_found.title'] = 'Imagen no encontrada';
+$_lang['imageplus.error.image_not_found.msg'] = 'La imagen no fue encontrada y no puede ser recortada. Por favor, seleccione una imagen diferente.';
diff --git a/core/components/imageplus/lexicon/es/properties.inc.php b/core/components/imageplus/lexicon/es/properties.inc.php
new file mode 100644
index 00000000..98a34c41
--- /dev/null
+++ b/core/components/imageplus/lexicon/es/properties.inc.php
@@ -0,0 +1,12 @@
+check tpl y thumb.';
diff --git a/core/components/imageplus/lexicon/es/setting.inc.php b/core/components/imageplus/lexicon/es/setting.inc.php
new file mode 100644
index 00000000..d0dbac3b
--- /dev/null
+++ b/core/components/imageplus/lexicon/es/setting.inc.php
@@ -0,0 +1,33 @@
+ documentation pour connaître les clés appropriées dans les paramètres du contexte/système.';
+$_lang['imageplus.selectConfig'] = 'Tailles cibles/ratios d’aspect prédéfinis';
+$_lang['imageplus.selectConfig_desc'] = 'Sélectionnez un rapport taille/aspect cible prédéfini. Les définitions peuvent être créées dans les paramètres du système.';
+$_lang['imageplus.selectConfigForce'] = 'Obligation de fixer des tailles cibles/ratios d’aspect prédéfinis';
+$_lang['imageplus.selectConfigForce_desc'] = 'Forcé de sélectionner un rapport taille/aspect de la culture prédéfini. Les définitions peuvent être créées dans les paramètres du système.';
+$_lang['imageplus.targetwidth'] = 'Largeur (minimale) de la cible';
+$_lang['imageplus.targetwidth_desc'] = '(Facultatif, entier) La largeur ciblée de l’image en sortie. L’image téléchargée devrait avoir cette largeur minimale.';
+$_lang['imageplus.targetheight'] = 'Hauteur (minimale) de la cible';
+$_lang['imageplus.targetheight_desc'] = '(Facultatif, entier) La hauteur ciblée de l’image en sortie. L’image téléchargée devrait avoir cette hauteur minimale.';
+$_lang['imageplus.targetRatio'] = 'Aspect ratio cible';
+$_lang['imageplus.targetRatio_desc'] = '(Facultatif, Float) Les proportions ciblée de l’image en sortie, valeur de type float. Si la hauteur et la largeur de la cible sont définies, cette valeur est ignorée.';
+$_lang['imageplus.thumbnailWidth'] = 'Largeur miniature';
+$_lang['imageplus.thumbnailWidth_desc'] = '(Optionnel, entier) Largeur de la miniature dans le panneau de variable de modèle (TV).';
+$_lang['imageplus.allowAltTag'] = 'Afficher le champ tag alt';
+$_lang['imageplus.allowAltTag_desc'] = 'Permettre à l’utilisateur à entrer un titre/tag alt pour l’image.';
+$_lang['imageplus.allowCaption'] = 'Afficher le champ de légende';
+$_lang['imageplus.allowCaption_desc'] = 'Permettre à l’utilisateur d’entrer une légende pour l’image.';
+$_lang['imageplus.allowCredits'] = 'Afficher le champ de crédits';
+$_lang['imageplus.allowCredits_desc'] = 'Permettre à l’utilisateur d’entrer un crédit pour l’image.';
+/** Output options render **/
+$_lang['imageplus.phpThumbParams'] = 'Paramètres phpThumb additionnels';
+$_lang['imageplus.phpThumbParams_desc'] = 'Ajoutez des filtres additionnels pour phpThumb. La documentation est disponible ici.';
+$_lang['imageplus.outputChunk'] = 'Chunk d’affichage';
+$_lang['imageplus.outputChunk_desc'] = 'Sélectionnez le chunk utilisé pour afficher le résultat de la TV. Laissez vide pour obtenir l’URL brute du résultat.';
+$_lang['imageplus.generateUrl'] = 'Génère l’URL de la miniature';
+$_lang['imageplus.generateUrl_desc'] = '(Optionnel) L’URL de la miniature n’est peut-être pas nécessaire, si vous générez la miniature dans le chunk de sortie (exemple par un filtre pthumb).';
+$_lang['imageplus.generateUrl_desc_warning'] = 'Vous devez activer cette option lorsque vous ne spécifiez pas de morceau de sortie dans les options de sortie ou lorsque vous utilisez le caractère de remplacement [[+url]] dans le morceau de sortie spécifié. Sinon, l’image n’est pas recadrée/redimensionnée et le chemin d’accès à l’image originale est renvoyé.';
+/** Placeholder descriptions */
+$_lang['imageplus.placeholder.url'] = 'URL de l’image de miniature';
+$_lang['imageplus.placeholder.alt'] = 'Texte de l’attribut Alt';
+$_lang['imageplus.placeholder.width'] = 'Largeur de l’image miniature (ignorée quand 0)';
+$_lang['imageplus.placeholder.height'] = 'Hauteur de l’image miniature (ignorée quand 0)';
+$_lang['imageplus.placeholder.source.src'] = 'Chemin de l’image source';
+$_lang['imageplus.placeholder.source.width'] = 'Largeur de l’image source';
+$_lang['imageplus.placeholder.source.height'] = 'Hauteur de l’image source';
+$_lang['imageplus.placeholder.crop.width'] = 'Largeur de l’image source recadrée';
+$_lang['imageplus.placeholder.crop.height'] = 'Hauteur de l’image source recadrée';
+$_lang['imageplus.placeholder.crop.x'] = 'Position recadrage en x de l’image source';
+$_lang['imageplus.placeholder.crop.y'] = 'Position recadrage en y de l’image source';
+$_lang['imageplus.placeholder.options'] = 'chaîne d’option phpThumb pour générer l’image miniature';
+$_lang['imageplus.placeholder.crop.options'] = 'chaîne d’option de recadrage phpThumb pour générer l’image miniature';
+$_lang['imageplus.error.image_too_small.title'] = 'Image trop petite';
+$_lang['imageplus.error.image_too_small.msg'] = 'L’image sélectionnée est trop petite pour être utilisé ici. Veuillez sélectionner une image différente.';
+$_lang['imageplus.error.image_not_found.title'] = 'Image non trouvée';
+$_lang['imageplus.error.image_not_found.msg'] = 'L’image est introuvable et ne peut être recadrée. Veuillez sélectionner une image différente.';
diff --git a/core/components/imageplus/lexicon/fr/properties.inc.php b/core/components/imageplus/lexicon/fr/properties.inc.php
new file mode 100644
index 00000000..90e28009
--- /dev/null
+++ b/core/components/imageplus/lexicon/fr/properties.inc.php
@@ -0,0 +1,12 @@
+vérifier tpl ou thumb.';
diff --git a/core/components/imageplus/lexicon/fr/setting.inc.php b/core/components/imageplus/lexicon/fr/setting.inc.php
new file mode 100644
index 00000000..23659831
--- /dev/null
+++ b/core/components/imageplus/lexicon/fr/setting.inc.php
@@ -0,0 +1,33 @@
+dokumentációt a megfelelő kulcsokért a kontextus/rendszerbeállításokban.';
+$_lang['imageplus.selectConfig'] = 'Előre meghatározott célméretek/aspektusarányok';
+$_lang['imageplus.selectConfig_desc'] = 'Válasszon ki egy előre meghatározott célméret/szögarányt. A definíciókat a rendszerbeállításokban lehet létrehozni.';
+$_lang['imageplus.selectConfigForce'] = 'Kényszerített előre meghatározott célméretek/aspektusarányok';
+$_lang['imageplus.selectConfigForce_desc'] = 'Kényszerített választás egy előre meghatározott termésméret/szögarány. A definíciókat a rendszerbeállításokban lehet létrehozni.';
+$_lang['imageplus.targetwidth'] = 'Új szélesség';
+$_lang['imageplus.targetwidth_desc'] = '(Választható, egész szám) A kimeneti kép célszélessége. A feltöltött képnek ezzel a minimális szélességgel kell rendelkeznie.';
+$_lang['imageplus.targetheight'] = 'Új magasság';
+$_lang['imageplus.targetheight_desc'] = '(Választható, egész szám) A kimeneti kép célmagassága. A feltöltött képnek ezzel a minimális magassággal kell rendelkeznie.';
+$_lang['imageplus.targetRatio'] = 'A cél képarány';
+$_lang['imageplus.targetRatio_desc'] = '(Választható, Float) A kimeneti kép céloldali képaránya float értékként. Ha a célmagasság és a célszélesség be van állítva, ez az érték figyelmen kívül marad.';
+$_lang['imageplus.thumbnailWidth'] = 'Miniatűr szélessége';
+$_lang['imageplus.thumbnailWidth_desc'] = '(Választható, egész szám) A kép miniatűr szélessége a sablon változó panelen.';
+$_lang['imageplus.allowAltTag'] = 'Alt tag megengedése';
+$_lang['imageplus.allowAltTag_desc'] = 'Lehetővé teszi a felhasználó számára a kép címének/alt-tagjének megadását.';
+$_lang['imageplus.allowCaption'] = 'Felirat mező megjelenítése';
+$_lang['imageplus.allowCaption_desc'] = 'Lehetővé teszi a felhasználó számára a kép feliratának megadását.';
+$_lang['imageplus.allowCredits'] = 'Mutasd a kreditek mezőt';
+$_lang['imageplus.allowCredits_desc'] = 'Lehetővé teszi a felhasználó számára, hogy megadja a kép kreditpontját.';
+/** Output options render **/
+$_lang['imageplus.phpThumbParams'] = 'További phpThumb paraméterek';
+$_lang['imageplus.phpThumbParams_desc'] = 'További szűrők stb. hozzáadása a phpThumbhoz. A dokumentáció megtalálható itt.';
+$_lang['imageplus.outputChunk'] = 'Kimeneti chunk';
+$_lang['imageplus.outputChunk_desc'] = 'Válasszon ki egy darabot a tv-kimenethez. Hagyja üresen a nyers url kimenethez.';
+$_lang['imageplus.generateUrl'] = 'Hüvelykujj URL generálása';
+$_lang['imageplus.generateUrl_desc'] = '(Választható) A thumb url-re talán nincs szükség, ha a kimeneti csomagban, azaz egy pthumb kimeneti szűrővel generálod a miniatűr képet.';
+$_lang['imageplus.generateUrl_desc_warning'] = 'Ezt az opciót akkor kell aktiválnod, ha nem adsz meg kimeneti egységet a kimeneti beállítások között, vagy ha a [[+url]] helyőrzőt használod a megadott kimeneti egységben. Ellenkező esetben a képet nem vágja le/méretezi, és az eredeti kép elérési útvonalát adja vissza.';
+/** Placeholder descriptions */
+$_lang['imageplus.placeholder.url'] = 'A miniatűr kép URL címe';
+$_lang['imageplus.placeholder.alt'] = 'Alt text';
+$_lang['imageplus.placeholder.width'] = 'A miniatűr kép szélessége (0 esetén figyelmen kívül hagyva)';
+$_lang['imageplus.placeholder.height'] = 'A miniatűr kép magassága (0 esetén figyelmen kívül hagyva)';
+$_lang['imageplus.placeholder.source.src'] = 'A forráskép elérési útvonala';
+$_lang['imageplus.placeholder.source.width'] = 'A forráskép szélessége';
+$_lang['imageplus.placeholder.source.height'] = 'A forráskép magassága';
+$_lang['imageplus.placeholder.crop.width'] = 'A forráskép vágási szélessége';
+$_lang['imageplus.placeholder.crop.height'] = 'A forráskép vágási magassága';
+$_lang['imageplus.placeholder.crop.x'] = 'A forráskép x pozíciójának kivágása';
+$_lang['imageplus.placeholder.crop.y'] = 'A forráskép y pozíciójának vágása';
+$_lang['imageplus.placeholder.options'] = 'phpThumb crop option string to generate the thumbnail image';
+$_lang['imageplus.placeholder.crop.options'] = 'phpThumb crop opció string a miniatűr kép létrehozásához';
+$_lang['imageplus.error.image_too_small.title'] = 'Túl kicsi kép';
+$_lang['imageplus.error.image_too_small.msg'] = 'A kiválasztott kép túl kicsi ahhoz, hogy itt felhasználható legyen. Kérjük, válasszon másik képet.';
+$_lang['imageplus.error.image_not_found.title'] = 'Kép nem található';
+$_lang['imageplus.error.image_not_found.msg'] = 'A képet nem találtuk, és nem lehet levágni. Kérjük, válasszon másik képet.';
diff --git a/core/components/imageplus/lexicon/hu/properties.inc.php b/core/components/imageplus/lexicon/hu/properties.inc.php
new file mode 100644
index 00000000..c7fe8e3c
--- /dev/null
+++ b/core/components/imageplus/lexicon/hu/properties.inc.php
@@ -0,0 +1,12 @@
+check tpl és thumb.';
diff --git a/core/components/imageplus/lexicon/hu/setting.inc.php b/core/components/imageplus/lexicon/hu/setting.inc.php
new file mode 100644
index 00000000..09c163f1
--- /dev/null
+++ b/core/components/imageplus/lexicon/hu/setting.inc.php
@@ -0,0 +1,33 @@
+documentazione per conoscere le chiavi da utilizzare nelle impostazioni di sistema/contesto.';
+$_lang['imageplus.selectConfig'] = 'Destinazione con dimensioni/proporzioni predefinite';
+$_lang['imageplus.selectConfig_desc'] = 'Seleziona un’impostazione di dimensioni/proporzioni predefinita. Le definizioni possono essere create nelle impostazioni del sistema.';
+$_lang['imageplus.selectConfigForce'] = 'Forza destinazione con dimensioni/proporzioni predefinite';
+$_lang['imageplus.selectConfigForce_desc'] = 'Selezione forzata di un ritaglio con dimensioni/proporzioni predefinite. Le definizioni possono essere create nelle impostazioni di sistema.';
+$_lang['imageplus.targetwidth'] = 'Larghezza (minima)';
+$_lang['imageplus.targetwidth_desc'] = '(Facoltativo, Intero) La larghezza desiderata per l’immagine finale. L’immagine caricata dovrebbe avere almeno questa larghezza.';
+$_lang['imageplus.targetheight'] = 'Altezza (minima)';
+$_lang['imageplus.targetheight_desc'] = '(Facoltativo, Intero) L’altezza desiderata per l’immagine finale. L’immagine caricata dovrebbe avere almeno questa altezza.';
+$_lang['imageplus.targetRatio'] = 'Proporzioni di destinazione';
+$_lang['imageplus.targetRatio_desc'] = '(Opzionale, Float [numero intero/frazionale]) Le proporzioni con valore di tipo Float per l’immagine finale. Se l’altezza e la larghezza hanno il valore impostato, il valore delle proporzioni viene ignorato.';
+$_lang['imageplus.thumbnailWidth'] = 'Larghezza delle anteprime';
+$_lang['imageplus.thumbnailWidth_desc'] = '(Opzionale, Intero) La larghezza della miniatura dell’immagine nella pannello della Template Variable.';
+$_lang['imageplus.allowAltTag'] = 'Visualizza campo tag Alt';
+$_lang['imageplus.allowAltTag_desc'] = 'Consente all’utente di inserire un titolo/tag-alt per l’immagine.';
+$_lang['imageplus.allowCaption'] = 'Visualizza campo didascalia';
+$_lang['imageplus.allowCaption_desc'] = 'Consente all’utente di inserire una didascalia per l’immagine.';
+$_lang['imageplus.allowCredits'] = 'Mostra il campo Credits';
+$_lang['imageplus.allowCredits_desc'] = 'Consente all’utente di inserire un credito per l’immagine.';
+/** Output options render **/
+$_lang['imageplus.phpThumbParams'] = 'Parametri aggiuntivi di phpThumb';
+$_lang['imageplus.phpThumbParams_desc'] = 'Aggiungi filtri aggiuntivi ecc. per phpThumb. La documentazione può essere trovata qui.';
+$_lang['imageplus.outputChunk'] = 'Chunk Output';
+$_lang['imageplus.outputChunk_desc'] = 'Seleziona un chunk per l’output della variabile tv. Lascia vuoto per avere un semplice url come output.';
+$_lang['imageplus.generateUrl'] = 'Generare l’URL dell’anteprima';
+$_lang['imageplus.generateUrl_desc'] = '(Facoltativo) L’URL della miniatura dell’immagine potrebbe non essere necessario se questa viene generata nel chunk di output, per esempio con un filtro di output pThumb.';
+$_lang['imageplus.generateUrl_desc_warning'] = 'Devi attivare questa opzione quando non specifichi un chunk di output nelle opzioni di output o quando usi il segnaposto [[+url]] nel chunk di output specificato. Altrimenti l’immagine non viene ritagliata/ridimensionata e viene restituito il percorso originale dell’immagine.';
+/** Placeholder descriptions */
+$_lang['imageplus.placeholder.url'] = 'URL dell’immagine di anteprima';
+$_lang['imageplus.placeholder.alt'] = 'Testo per l’attributo Alt';
+$_lang['imageplus.placeholder.width'] = 'Larghezza dell’anteprima dell’immagine (ignorata quando 0)';
+$_lang['imageplus.placeholder.height'] = 'Altezza dell’anteprima dell’immagine (ignorata quando 0)';
+$_lang['imageplus.placeholder.source.src'] = 'Percorso dell’immagine di origine';
+$_lang['imageplus.placeholder.source.width'] = 'Larghezza dell’immagine di origine';
+$_lang['imageplus.placeholder.source.height'] = 'Altezza dell’immagine di origine';
+$_lang['imageplus.placeholder.crop.width'] = 'Larghezza del ritaglio dell’immagine di origine';
+$_lang['imageplus.placeholder.crop.height'] = 'Altezza del ritaglio dell’immagine di origine';
+$_lang['imageplus.placeholder.crop.x'] = 'Posizione orizzontale (asse x) del ritaglio dell’immagine di origine';
+$_lang['imageplus.placeholder.crop.y'] = 'Posizione verticale (asse y) del ritaglio dell’immagine di origine';
+$_lang['imageplus.placeholder.options'] = 'L’opzione su phpThumb per generare l’anteprima dell’immagine';
+$_lang['imageplus.placeholder.crop.options'] = 'L’opzione di ritaglio su phpthumb per generare l’anteprima dell’immagine';
+$_lang['imageplus.error.image_too_small.title'] = 'Immagine troppo piccola';
+$_lang['imageplus.error.image_too_small.msg'] = 'L’immagine selezionata è troppo piccola per essere usata qui. Si prega di selezionare un’altra immagine.';
+$_lang['imageplus.error.image_not_found.title'] = 'L’immagine non è stata trovata';
+$_lang['imageplus.error.image_not_found.msg'] = 'L’immagine non è stata trovata e perciò non può essere tagliata. Si prega di selezionare un’altra immagine.';
diff --git a/core/components/imageplus/lexicon/it/properties.inc.php b/core/components/imageplus/lexicon/it/properties.inc.php
new file mode 100644
index 00000000..4cd1aed8
--- /dev/null
+++ b/core/components/imageplus/lexicon/it/properties.inc.php
@@ -0,0 +1,12 @@
+check tpl e thumb.';
diff --git a/core/components/imageplus/lexicon/it/setting.inc.php b/core/components/imageplus/lexicon/it/setting.inc.php
new file mode 100644
index 00000000..dab4de22
--- /dev/null
+++ b/core/components/imageplus/lexicon/it/setting.inc.php
@@ -0,0 +1,33 @@
+documentatie voor de juiste keys.';
+$_lang['imageplus.selectConfig'] = 'Voorgedefinieerde doel maten/aspect ratios';
+$_lang['imageplus.selectConfig_desc'] = 'Selecteer een vooraf gedefinieerde doel grootte/hoogte-breedteverhouding. Deze kunnen aangemaakt worden in de systeeminstellingen.';
+$_lang['imageplus.selectConfigForce'] = 'Forceer vooraf gedefinieerde doel maten/hoogte-breedteverhoudingen';
+$_lang['imageplus.selectConfigForce_desc'] = 'Forceer een vooraf gedefinieerde uitsnede grootte/hoogte-breedteverhouding. Deze kunnen aangemaakt worden in de systeeminstellingen.';
+$_lang['imageplus.targetwidth'] = 'Doel (minimale) breedte';
+$_lang['imageplus.targetwidth_desc'] = '(Optioneel, geheel getal) De breedte van de uiteindelijke afbeelding.';
+$_lang['imageplus.targetheight'] = 'Doel (minimale) hoogte';
+$_lang['imageplus.targetheight_desc'] = '(Optioneel, geheel getal) De hoogte van de uiteindelijke afbeelding.';
+$_lang['imageplus.targetRatio'] = 'Hoogte/breedteverhouding voor het doelbestand';
+$_lang['imageplus.targetRatio_desc'] = '(Optioneel, drijvend) De hoogte/breedteverhouding van de afbeelding. Als de hoogte en breedte voor de output zijn ingesteld wordt deze waarde genegeerd.';
+$_lang['imageplus.thumbnailWidth'] = 'Thumbnail breedte';
+$_lang['imageplus.thumbnailWidth_desc'] = '(Optioneel, geheel getal) De breedte van de thumbnail in het Template Variabele paneel.';
+$_lang['imageplus.allowAltTag'] = 'Alt-Tag veld weergeven';
+$_lang['imageplus.allowAltTag_desc'] = 'Laat de gebruiker een titel/alt-tag voor de afbeelding invoeren.';
+$_lang['imageplus.allowCaption'] = 'Toon bijschriftveld';
+$_lang['imageplus.allowCaption_desc'] = 'Gebruiker toestaan aan een bijschrift voor de afbeelding in te voeren.';
+$_lang['imageplus.allowCredits'] = 'Credits-veld weergeven';
+$_lang['imageplus.allowCredits_desc'] = 'Mogelijk maken dat de gebruiker een naamsvermelding (credits) voor de afbeelding kan invoeren.';
+/** Output options render **/
+$_lang['imageplus.phpThumbParams'] = 'Extra phpThumb opties';
+$_lang['imageplus.phpThumbParams_desc'] = 'Voeg extra phpThumb opties zoals filters toe. Documentatie is hier te vinden.';
+$_lang['imageplus.outputChunk'] = 'Output Chunk';
+$_lang['imageplus.outputChunk_desc'] = 'Selecteer een chunk voor de TV output. Laat leeg om alleen een link naar de afbeelding terug te krijgen.';
+$_lang['imageplus.generateUrl'] = 'Genereer Thumbnail URL';
+$_lang['imageplus.generateUrl_desc'] = '(Optioneel) De thumbnail URL is mogelijk niet nodig, als de thumbnail gegenereerd wordt in de output chunk met bijvoorbeeld een pthumb output filter.';
+$_lang['imageplus.generateUrl_desc_warning'] = 'Deze optie moet ingeschakeld zijn wanneer je geen "output chunk" hebt gedefineerd; of wanneer je de "output chunk" wel hebt gedefineerd en de [[+url]] placeholder hierin gebruikt. Als deze optie in voorgaande situaties niet is ingeschakeld dan worden afbeeldingen niet bijgesneden en/of vergroot en verkleind en wordt het pad naar de originele afbeelding weergegeven.';
+/** Placeholder descriptions */
+$_lang['imageplus.placeholder.url'] = 'URL van de thumbnail';
+$_lang['imageplus.placeholder.alt'] = 'Alternatieve tekst';
+$_lang['imageplus.placeholder.width'] = 'Breedte van de thumbnail (genegeerd indien 0)';
+$_lang['imageplus.placeholder.height'] = 'Hoogte van de thumbnail (genegeerd indien 0)';
+$_lang['imageplus.placeholder.source.src'] = 'Pad naar de bronafbeelding';
+$_lang['imageplus.placeholder.source.width'] = 'Breedte van de bronafbeelding';
+$_lang['imageplus.placeholder.source.height'] = 'Hoogte van de bronafbeelding';
+$_lang['imageplus.placeholder.crop.width'] = 'Cropbreedte van de bronafbeelding';
+$_lang['imageplus.placeholder.crop.height'] = 'Crophoogte van de bronafbeelding';
+$_lang['imageplus.placeholder.crop.x'] = 'Crop X positie van de bronafbeelding';
+$_lang['imageplus.placeholder.crop.y'] = 'Crop Y positie van de bronafbeelding';
+$_lang['imageplus.placeholder.options'] = 'phpThumb waarde om de thumbnail te genereren';
+$_lang['imageplus.placeholder.crop.options'] = 'phpThumb crop waarde om de thumbnail te genereren';
+$_lang['imageplus.error.image_too_small.title'] = 'Afbeelding is te klein';
+$_lang['imageplus.error.image_too_small.msg'] = 'De geselecteerde afbeelding is te klein om hier gebruikt te worden. Selecteer een andere afbeelding.';
+$_lang['imageplus.error.image_not_found.title'] = 'Afbeelding niet gevonden';
+$_lang['imageplus.error.image_not_found.msg'] = 'De afbeelding kan niet worden bijgesneden omdat hij niet gevonden kon worden. Selecteer een andere afbeelding.';
diff --git a/core/components/imageplus/lexicon/nl/properties.inc.php b/core/components/imageplus/lexicon/nl/properties.inc.php
new file mode 100644
index 00000000..1b6936d3
--- /dev/null
+++ b/core/components/imageplus/lexicon/nl/properties.inc.php
@@ -0,0 +1,12 @@
+check, tpl of thumb.';
diff --git a/core/components/imageplus/lexicon/nl/setting.inc.php b/core/components/imageplus/lexicon/nl/setting.inc.php
new file mode 100644
index 00000000..8bdbed99
--- /dev/null
+++ b/core/components/imageplus/lexicon/nl/setting.inc.php
@@ -0,0 +1,33 @@
+documentația pentru tastele corespunzătoare din setările de context/sistem.';
+$_lang['imageplus.selectConfig'] = 'Dimensiuni țintă predefinite/raporturi de aspect';
+$_lang['imageplus.selectConfig_desc'] = 'Selectați un raport predefinit de dimensiune/aspect țintă. Definițiile ar putea fi create în setările sistemului.';
+$_lang['imageplus.selectConfigForce'] = 'Dimensiuni țintă predefinite forțate/raporturi de aspect';
+$_lang['imageplus.selectConfigForce_desc'] = 'Selectare forțată a unei dimensiuni predefinite a culturii/raport de aspect. Definițiile ar putea fi create în setările sistemului.';
+$_lang['imageplus.targetwidth'] = 'Lățimea țintă (minimă)';
+$_lang['imageplus.targetwidth_desc'] = '(Opțional, număr întreg) Lățimea țintă pentru imaginea de ieșire. Imaginea încărcată trebuie să aibă această lățime minimă.';
+$_lang['imageplus.targetheight'] = 'Înălțimea țintă (minimă)';
+$_lang['imageplus.targetheight_desc'] = '(Opțional, număr întreg) Înălțimea țintă pentru imaginea de ieșire. Imaginea încărcată trebuie să aibă această înălțime minimă.';
+$_lang['imageplus.targetRatio'] = 'Raportul de aspect țintă';
+$_lang['imageplus.targetRatio_desc'] = '(Opțional, float) Raportul de aspect țintă pentru imaginea de ieșire ca valoare float. În cazul în care înălțimea țintă și lățimea țintă sunt stabilite, această valoare este ignorată.';
+$_lang['imageplus.thumbnailWidth'] = 'Lățimea miniaturii';
+$_lang['imageplus.thumbnailWidth_desc'] = '(Opțional, număr întreg) Lățimea miniaturii imaginii din panoul de variabile al șablonului.';
+$_lang['imageplus.allowAltTag'] = 'Afișați câmpul Alt Tag';
+$_lang['imageplus.allowAltTag_desc'] = 'Permiteți utilizatorului să introducă un titlu/un etichet pentru imagine.';
+$_lang['imageplus.allowCaption'] = 'Afișați câmpul de legendă';
+$_lang['imageplus.allowCaption_desc'] = 'Permiteți utilizatorului să introducă o legendă pentru imagine.';
+$_lang['imageplus.allowCredits'] = 'Afișați câmpul Credite';
+$_lang['imageplus.allowCredits_desc'] = 'Permite utilizatorului să introducă un credit pentru imagine.';
+/** Output options render **/
+$_lang['imageplus.phpThumbParams'] = 'Parametrii suplimentari phpThumb';
+$_lang['imageplus.phpThumbParams_desc'] = 'Adăugați filtre suplimentare etc. pentru phpThumb. Documentația poate fi găsită aici.';
+$_lang['imageplus.outputChunk'] = 'Output Chunk';
+$_lang['imageplus.outputChunk_desc'] = 'Selectați o bucată pentru ieșirea TV. Lăsați gol pentru ieșirea în format raw url.';
+$_lang['imageplus.generateUrl'] = 'Generarea URL-ului degetului mare';
+$_lang['imageplus.generateUrl_desc'] = '(Opțional) Adresa URL a thumbnail-ului poate să nu fie necesară dacă generați thumbnail-ul în fișierul de ieșire, de exemplu, printr-un filtru de ieșire pthumb.';
+$_lang['imageplus.generateUrl_desc_warning'] = 'Trebuie să activați această opțiune atunci când nu specificați o bucată de ieșire în opțiunile de ieșire sau atunci când folosiți simbolul de poziție [[+url]] în bucată de ieșire specificată. În caz contrar, imaginea nu este decupată/redimensionată și se returnează calea originală a imaginii.';
+/** Placeholder descriptions */
+$_lang['imageplus.placeholder.url'] = 'URL-ul imaginii în miniatură';
+$_lang['imageplus.placeholder.alt'] = 'Alt text';
+$_lang['imageplus.placeholder.width'] = 'Lățimea imaginii miniaturale (ignorată când este 0)';
+$_lang['imageplus.placeholder.height'] = 'Înălțimea imaginii miniaturale (ignorată dacă este 0)';
+$_lang['imageplus.placeholder.source.src'] = 'Cale de acces la imaginea sursă';
+$_lang['imageplus.placeholder.source.width'] = 'Lățimea imaginii sursă';
+$_lang['imageplus.placeholder.source.height'] = 'Înălțimea imaginii sursă';
+$_lang['imageplus.placeholder.crop.width'] = 'Lățimea de tăiere a imaginii sursă';
+$_lang['imageplus.placeholder.crop.height'] = 'Înălțimea de tăiere a imaginii sursă';
+$_lang['imageplus.placeholder.crop.x'] = 'Poziția x de tăiere a imaginii sursă';
+$_lang['imageplus.placeholder.crop.y'] = 'Poziția y de tăiere a imaginii sursă';
+$_lang['imageplus.placeholder.options'] = 'phpThumb șir de opțiuni pentru a genera imaginea miniaturală';
+$_lang['imageplus.placeholder.crop.options'] = 'phpThumb crop șir de opțiuni pentru generarea imaginii thumbnail';
+$_lang['imageplus.error.image_too_small.title'] = 'Imagine prea mică';
+$_lang['imageplus.error.image_too_small.msg'] = 'Imaginea selectată este prea mică pentru a fi utilizată aici. Vă rugăm să selectați o altă imagine.';
+$_lang['imageplus.error.image_not_found.title'] = 'Imaginea nu a fost găsită';
+$_lang['imageplus.error.image_not_found.msg'] = 'Imaginea nu a fost găsită și nu poate fi decupată. Vă rugăm să selectați o altă imagine.';
diff --git a/core/components/imageplus/lexicon/ru/default.inc.php b/core/components/imageplus/lexicon/ru/default.inc.php
new file mode 100644
index 00000000..763cd42d
--- /dev/null
+++ b/core/components/imageplus/lexicon/ru/default.inc.php
@@ -0,0 +1,59 @@
+документацию для соответствующих ключей в контексте/системных настройках.';
+$_lang['imageplus.selectConfig'] = 'Предопределенные размеры/соотношения сторон';
+$_lang['imageplus.selectConfig_desc'] = 'Выберите заранее заданное соотношение размера/аспекта. Определения могут быть созданы в настройках системы.';
+$_lang['imageplus.selectConfigForce'] = 'Принудительные предопределенные размеры/пропорции';
+$_lang['imageplus.selectConfigForce_desc'] = 'Выберите заранее заданное соотношение размера/аспекта. Определения могут быть созданы в настройках системы.';
+$_lang['imageplus.targetwidth'] = 'Необходимая (минимальная) ширина';
+$_lang['imageplus.targetwidth_desc'] = '(Необязательно, Integer) Необходимая ширина для отображаемого изображения. Загружаемое изображение должно иметь эту минимальную ширину.';
+$_lang['imageplus.targetheight'] = 'Необходимая (минимальная) высота';
+$_lang['imageplus.targetheight_desc'] = '(Необязательно, Integer) Необходимая высота для отображаемого изображения. Загружаемое изображение должно иметь эту минимальную высоту.';
+$_lang['imageplus.targetRatio'] = 'Необходимые пропорции';
+$_lang['imageplus.targetRatio_desc'] = '(Необязательно, Float) Необходимые пропорции для отображаемого изображения. Если заданы минимальная высота и ширина, это значение игнорируется.';
+$_lang['imageplus.thumbnailWidth'] = 'Ширина иконки';
+$_lang['imageplus.thumbnailWidth_desc'] = '(Необязательно) Ширина изображения в панели управления TV.';
+$_lang['imageplus.allowAltTag'] = 'Показывать поле для атрибута Аlt';
+$_lang['imageplus.allowAltTag_desc'] = 'Позволяет пользователю ввести заголовок/альтернативный текст для изображения.';
+$_lang['imageplus.allowCaption'] = 'Показать поле заголовка';
+$_lang['imageplus.allowCaption_desc'] = 'Разрешить пользователю ввести подпись для изображения.';
+$_lang['imageplus.allowCredits'] = 'Показать поле «Авторы»';
+$_lang['imageplus.allowCredits_desc'] = 'Разрешить пользователю ввести Автора для изображения.';
+/** Output options render **/
+$_lang['imageplus.phpThumbParams'] = 'Дополнительные параметры phpThumb';
+$_lang['imageplus.phpThumbParams_desc'] = 'Добавляет дополнительные фильтры и другие параметры для phpThumb. Документацию можно найти здесь.';
+$_lang['imageplus.outputChunk'] = 'Чанк для вывода';
+$_lang['imageplus.outputChunk_desc'] = 'Выберите чанк для вывода TV. Оставьте пустым для обычного вывода текстом.';
+$_lang['imageplus.generateUrl'] = 'Генерировать Thumb URL';
+$_lang['imageplus.generateUrl_desc'] = '(Необязательно) URL иконки может быть необязательным, если вы генерируете иконку в чанке, т.е. когда phpThumb используется как output filter.';
+$_lang['imageplus.generateUrl_desc_warning'] = 'Вы должны активировать эту опцию, когда вы не указываете выходной блок в опциях вывода или когда вы используете заполнитель [[+url]] в указанном выходном блоке. В противном случае изображение не обрезается/не изменяется, и возвращается исходный путь к изображению.';
+/** Placeholder descriptions */
+$_lang['imageplus.placeholder.url'] = 'URL иконки изображения';
+$_lang['imageplus.placeholder.alt'] = 'Альтернатиный текст (alt)';
+$_lang['imageplus.placeholder.width'] = 'Ширина иконки изображения (игнорируется когда 0)';
+$_lang['imageplus.placeholder.height'] = 'Высота иконки изображения (игнорируется когда 0)';
+$_lang['imageplus.placeholder.source.src'] = 'Путь к исходному изображению';
+$_lang['imageplus.placeholder.source.width'] = 'Ширина исходного изображения';
+$_lang['imageplus.placeholder.source.height'] = 'Высота исходного изображения';
+$_lang['imageplus.placeholder.crop.width'] = 'Ширина обрезки исходного изображения';
+$_lang['imageplus.placeholder.crop.height'] = 'Высота обрезки исходного изображения';
+$_lang['imageplus.placeholder.crop.x'] = 'Позиция обрезки исходного изображения по оси X (горизонталь)';
+$_lang['imageplus.placeholder.crop.y'] = 'Позиция обрезки исходного изображения по оси Y (вертикаль)';
+$_lang['imageplus.placeholder.options'] = 'Строка параметров phpThumb для генерации иконки изображения';
+$_lang['imageplus.placeholder.crop.options'] = 'Строка опций обрезки phpThumb для генерации иконки изображения';
+$_lang['imageplus.error.image_too_small.title'] = 'Изображение слишком маленькое';
+$_lang['imageplus.error.image_too_small.msg'] = 'Выбранное изображение слишком маленькое для использования здесь. Выберите другое изображение, пожалуйста.';
+$_lang['imageplus.error.image_not_found.title'] = 'Изображение не найдено';
+$_lang['imageplus.error.image_not_found.msg'] = 'Изображение не найдено и не может быть обрезано. Выберите другое изображение, пожалуйста.';
diff --git a/core/components/imageplus/lexicon/ru/properties.inc.php b/core/components/imageplus/lexicon/ru/properties.inc.php
new file mode 100644
index 00000000..8b6ec769
--- /dev/null
+++ b/core/components/imageplus/lexicon/ru/properties.inc.php
@@ -0,0 +1,12 @@
+check, tpl и thumb.';
diff --git a/core/components/imageplus/lexicon/ru/setting.inc.php b/core/components/imageplus/lexicon/ru/setting.inc.php
new file mode 100644
index 00000000..28b61cc2
--- /dev/null
+++ b/core/components/imageplus/lexicon/ru/setting.inc.php
@@ -0,0 +1,33 @@
+
+ * Copyright 2015-2023 by Thomas Jakobi
+ *
+ * @package imageplus
+ * @subpackage classfile
+ */
+
+require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
+
+/**
+ * Class DaterangeTV
+ */
+class ImagePlus extends \TreehillStudio\ImagePlus\ImagePlus
+{
+}
diff --git a/core/components/imageplus/src/CropEngines/AbstractCropEngine.php b/core/components/imageplus/src/CropEngines/AbstractCropEngine.php
new file mode 100644
index 00000000..178fa38f
--- /dev/null
+++ b/core/components/imageplus/src/CropEngines/AbstractCropEngine.php
@@ -0,0 +1,60 @@
+modx =& $modx;
+
+ $corePath = $this->modx->getOption('imageplus.core_path', null, $this->modx->getOption('core_path') . 'components/imageplus/');
+ $this->imageplus = $this->modx->getService('imageplus', 'ImagePlus', $corePath . 'model/imageplus/', [
+ 'core_path' => $corePath
+ ]);
+ }
+
+ /**
+ * Checks that all requirements are met for using this engine
+ *
+ * @param modX $modx
+ * @return bool True if engine is usable
+ */
+ public static function engineRequirementsMet(modX $modx)
+ {
+ return true;
+ }
+
+ /**
+ * Parse image+ data and return an url for the cropped
+ * version of the image
+ *
+ * @param $json
+ * @param array $opts
+ * @param modTemplateVar $tv
+ * @return string
+ */
+ abstract public function getImageUrl($json, $opts = [], $tv = null);
+}
diff --git a/core/components/imageplus/src/CropEngines/PhpThumbOf.php b/core/components/imageplus/src/CropEngines/PhpThumbOf.php
new file mode 100644
index 00000000..9069e407
--- /dev/null
+++ b/core/components/imageplus/src/CropEngines/PhpThumbOf.php
@@ -0,0 +1,176 @@
+getObject('modSnippet', ['name' => 'phpthumbof']);
+ return $pto instanceof modSnippet;
+ }
+
+ /**
+ * Parse image+ data and return an url for the cropped
+ * version of the image
+ *
+ * @param $json
+ * @param array $opts
+ * @param modTemplateVar $tv
+ * @return string
+ */
+ public function getImageUrl($json, $opts = [], $tv = null)
+ {
+ if ($json == '') {
+ if ($this->imageplus->getOption('debug')) {
+ $this->modx->log(xPDO::LOG_LEVEL_ERROR, 'The value is empty. Could not prepare the output.', '', 'Image+');
+ }
+ return ($tv) ? $tv->default_text : '';
+ }
+
+ // Parse json to object
+ $data = json_decode($json);
+
+ // If data is null, json was invalid or empty.
+ // This is almost certainly because the TV is empty
+ if (is_null($data)) {
+ if ($this->imageplus->getOption('debug')) {
+ $this->modx->log(xPDO::LOG_LEVEL_ERROR, 'The JSON value is invalid. Could not prepare the output.', '', 'Image+');
+ }
+ return ($tv) ? $tv->default_text : '';
+ }
+
+ // Load up the mediaSource
+ /** @var modMediaSource $source */
+ $source = $this->modx->getObject('sources.modMediaSource', $data->sourceImg->source);
+ if (!$source instanceof modMediaSource) {
+ if ($this->imageplus->getOption('debug')) {
+ $this->modx->log(xPDO::LOG_LEVEL_ERROR, 'Invalid Media Source', '', 'Image+');
+ }
+ return 'Image+ Error: Invalid Media Source';
+ }
+ $this->modx->setPlaceholder('docid', $this->modx->getOption('docid', $opts, 0));
+ $source->initialize();
+
+ // Grab absolute system path to image
+ $imgPath = realpath($source->getBasePath() . $data->sourceImg->src);
+
+ if ($this->imageplus->getOption('debug') && !$imgPath) {
+ $this->modx->log(xPDO::LOG_LEVEL_ERROR, 'The realpath of the image ' . $source->getBasePath() . $data->sourceImg->src . 'is not valid. Please check the media source path setting of the Image+ image.', '', 'Image+');
+ }
+
+ // Prepare arguments for phpthumbof snippet call
+ $cropParams = [
+ 'sx' => $data->crop->x,
+ 'sy' => $data->crop->y,
+ 'sw' => $data->crop->width,
+ 'sh' => $data->crop->height,
+ ];
+ $params = [];
+ if ($data->targetWidth) {
+ $params = array_merge($params, [
+ 'w' => $data->targetWidth
+ ]);
+ }
+ if ($data->targetHeight) {
+ $params = array_merge($params, [
+ 'h' => $data->targetHeight
+ ]);
+ }
+ if ($data->targetWidth && $data->targetHeight) {
+ $params = array_merge($params, [
+ 'far' => true
+ ]);
+ }
+ $params = array_merge($cropParams, $params);
+
+ // Add phpThumbParams to phpthumbof snippet call arguments
+ $phpThumbParams = $this->modx->getOption('phpThumbParams', $opts, '');
+ $optParams = [];
+ if ($phpThumbParams) {
+ parse_str($phpThumbParams, $optParams);
+ foreach ($optParams as $key => $val) {
+ if (empty($val)) {
+ unset($optParams[$key]);
+ }
+ }
+ }
+ $optParams = ($optParams) ? array_merge($cropParams, $optParams) : array_merge($params, $optParams);
+ $options = http_build_query($optParams);
+ $options = rawurldecode(preg_replace('/%5B[0-9]+%5D/simU', '%5B%5D', $options));
+ $cropOptions = http_build_query($cropParams);
+
+ $data->targetWidth = $optParams['w'] ?? 0;
+ $data->targetHeight = $optParams['h'] ?? 0;
+
+ // Call phpthumbof for url
+ $generateUrl = $this->modx->getOption('generateUrl', $opts, 1);
+ if (file_exists($imgPath)) {
+ if ($generateUrl) {
+ $url = $this->modx->runSnippet(
+ 'phpthumbof',
+ [
+ 'options' => $options,
+ 'input' => $imgPath
+ ]
+ );
+ } else {
+ $url = $source->getBaseUrl() . $data->sourceImg->src;
+ }
+ } else {
+ $url = $data->sourceImg->src;
+ }
+
+ // If an output chunk is selected, parse that
+ $outputChunk = $this->modx->getOption('outputChunk', $opts, '');
+ if ($outputChunk) {
+ $chunkParams = array_merge($opts, [
+ 'url' => $url,
+ 'alt' => $data->altTag ?? '',
+ 'width' => $data->targetWidth,
+ 'height' => $data->targetHeight,
+ 'source.src' => $imgPath,
+ 'source.width' => $data->sourceImg->width,
+ 'source.height' => $data->sourceImg->height,
+ 'crop.width' => $data->crop->width,
+ 'crop.height' => $data->crop->height,
+ 'crop.x' => $data->crop->x,
+ 'crop.y' => $data->crop->y,
+ 'options' => $options,
+ 'crop.options' => $cropOptions,
+ 'caption' => $data->caption ?? '',
+ 'credits' => $data->credits ?? ''
+ ]);
+ return $this->modx->getChunk($outputChunk, $chunkParams);
+ } else {
+ // Otherwise return raw url
+ return $url;
+ }
+ }
+}
diff --git a/core/components/imageplus/src/CropEngines/PhpThumbOn.php b/core/components/imageplus/src/CropEngines/PhpThumbOn.php
new file mode 100644
index 00000000..13453026
--- /dev/null
+++ b/core/components/imageplus/src/CropEngines/PhpThumbOn.php
@@ -0,0 +1,176 @@
+getObject('modSnippet', ['name' => 'phpthumbon']);
+ return $pto instanceof modSnippet;
+ }
+
+ /**
+ * Parse image+ data and return an url for the cropped
+ * version of the image
+ *
+ * @param $json
+ * @param array $opts
+ * @param modTemplateVar $tv
+ * @return string
+ */
+ public function getImageUrl($json, $opts = [], $tv = null)
+ {
+ if ($json == '') {
+ if ($this->imageplus->getOption('debug')) {
+ $this->modx->log(xPDO::LOG_LEVEL_ERROR, 'The value is empty. Could not prepare the output.', '', 'Image+');
+ }
+ return ($tv) ? $tv->default_text : '';
+ }
+
+ // Parse json to object
+ $data = json_decode($json);
+
+ // If data is null, json was invalid or empty.
+ // This is almost certainly because the TV is empty
+ if (is_null($data)) {
+ if ($this->imageplus->getOption('debug')) {
+ $this->modx->log(xPDO::LOG_LEVEL_ERROR, 'The JSON value is invalid. Could not prepare the output.', '', 'Image+');
+ }
+ return ($tv) ? $tv->default_text : '';
+ }
+
+ // Load up the mediaSource
+ /** @var modMediaSource $source */
+ $source = $this->modx->getObject('sources.modMediaSource', $data->sourceImg->source);
+ if (!$source instanceof modMediaSource) {
+ if ($this->imageplus->getOption('debug')) {
+ $this->modx->log(xPDO::LOG_LEVEL_ERROR, 'Invalid Media Source', '', 'Image+');
+ }
+ return 'Image+ Error: Invalid Media Source';
+ }
+ $this->modx->setPlaceholder('docid', $this->modx->getOption('docid', $opts, 0));
+ $source->initialize();
+
+ // Grab absolute system path to image
+ $imgPath = realpath($source->getBasePath() . $data->sourceImg->src);
+
+ if ($this->imageplus->getOption('debug') && !$imgPath) {
+ $this->modx->log(xPDO::LOG_LEVEL_ERROR, 'The realpath of the image ' . $source->getBasePath() . $data->sourceImg->src . 'is not valid. Please check the media source path setting of the Image+ image.', '', 'Image+');
+ }
+
+ // Prepare arguments for phpthumbon snippet call
+ $cropParams = [
+ 'sx' => $data->crop->x,
+ 'sy' => $data->crop->y,
+ 'sw' => $data->crop->width,
+ 'sh' => $data->crop->height,
+ ];
+ $params = [];
+ if ($data->targetWidth) {
+ $params = array_merge($params, [
+ 'w' => $data->targetWidth
+ ]);
+ }
+ if ($data->targetHeight) {
+ $params = array_merge($params, [
+ 'h' => $data->targetHeight
+ ]);
+ }
+ if ($data->targetWidth && $data->targetHeight) {
+ $params = array_merge($params, [
+ 'far' => true
+ ]);
+ }
+ $params = array_merge($cropParams, $params);
+
+ // Add phpThumbParams to phpthumbon snippet call arguments
+ $phpThumbParams = $this->modx->getOption('phpThumbParams', $opts, '');
+ $optParams = [];
+ if ($phpThumbParams) {
+ parse_str($phpThumbParams, $optParams);
+ foreach ($optParams as $key => $val) {
+ if (empty($val)) {
+ unset($optParams[$key]);
+ }
+ }
+ }
+ $optParams = ($optParams) ? array_merge($cropParams, $optParams) : array_merge($params, $optParams);
+ $options = http_build_query($optParams);
+ $options = rawurldecode(preg_replace('/%5B[0-9]+%5D/simU', '%5B%5D', $options));
+ $cropOptions = http_build_query($cropParams);
+
+ $data->targetWidth = $optParams['w'] ?? 0;
+ $data->targetHeight = $optParams['h'] ?? 0;
+
+ // Call phpthumbon for url
+ $generateUrl = $this->modx->getOption('generateUrl', $opts, 1);
+ if (file_exists($imgPath)) {
+ if ($generateUrl) {
+ $url = $this->modx->runSnippet(
+ 'phpthumbon',
+ [
+ 'options' => $options,
+ 'input' => $imgPath
+ ]
+ );
+ } else {
+ $url = $source->getBaseUrl() . $data->sourceImg->src;
+ }
+ } else {
+ $url = $data->sourceImg->src;
+ }
+
+ // If an output chunk is selected, parse that
+ $outputChunk = $this->modx->getOption('outputChunk', $opts, '');
+ if ($outputChunk) {
+ $chunkParams = array_merge($opts, [
+ 'url' => $url,
+ 'alt' => $data->altTag ?? '',
+ 'width' => $data->targetWidth,
+ 'height' => $data->targetHeight,
+ 'source.src' => $imgPath,
+ 'source.width' => $data->sourceImg->width,
+ 'source.height' => $data->sourceImg->height,
+ 'crop.width' => $data->crop->width,
+ 'crop.height' => $data->crop->height,
+ 'crop.x' => $data->crop->x,
+ 'crop.y' => $data->crop->y,
+ 'options' => $options,
+ 'crop.options' => $cropOptions,
+ 'caption' => $data->caption ?? '',
+ 'credits' => $data->credits ?? ''
+ ]);
+ return $this->modx->getChunk($outputChunk, $chunkParams);
+ } else {
+ // Otherwise return raw url
+ return $url;
+ }
+ }
+}
diff --git a/core/components/imageplus/src/CropEngines/PhpThumbsUp.php b/core/components/imageplus/src/CropEngines/PhpThumbsUp.php
new file mode 100755
index 00000000..7f2177aa
--- /dev/null
+++ b/core/components/imageplus/src/CropEngines/PhpThumbsUp.php
@@ -0,0 +1,176 @@
+getObject('modSnippet', ['name' => 'phpthumbsup']);
+ return $pto instanceof modSnippet;
+ }
+
+ /**
+ * Parse image+ data and return an url for the cropped
+ * version of the image
+ *
+ * @param $json
+ * @param array $opts
+ * @param modTemplateVar $tv
+ * @return string
+ */
+ public function getImageUrl($json, $opts = [], $tv = null)
+ {
+ if ($json == '') {
+ if ($this->imageplus->getOption('debug')) {
+ $this->modx->log(xPDO::LOG_LEVEL_ERROR, 'The value is empty. Could not prepare the output.', '', 'Image+');
+ }
+ return ($tv) ? $tv->default_text : '';
+ }
+
+ // Parse json to object
+ $data = json_decode($json);
+
+ // If data is null, json was invalid or empty.
+ // This is almost certainly because the TV is empty
+ if (is_null($data)) {
+ if ($this->imageplus->getOption('debug')) {
+ $this->modx->log(xPDO::LOG_LEVEL_ERROR, 'The JSON value is invalid. Could not prepare the output.', '', 'Image+');
+ }
+ return ($tv) ? $tv->default_text : '';
+ }
+
+ // Load up the mediaSource
+ /** @var modMediaSource $source */
+ $source = $this->modx->getObject('sources.modMediaSource', $data->sourceImg->source);
+ if (!$source instanceof modMediaSource) {
+ if ($this->imageplus->getOption('debug')) {
+ $this->modx->log(xPDO::LOG_LEVEL_ERROR, 'Invalid Media Source', '', 'Image+');
+ }
+ return 'Image+ Error: Invalid Media Source';
+ }
+ $this->modx->setPlaceholder('docid', $this->modx->getOption('docid', $opts, 0));
+ $source->initialize();
+
+ // Grab absolute system path to image
+ $imgPath = realpath($source->getBasePath() . $data->sourceImg->src);
+
+ if ($this->imageplus->getOption('debug') && !$imgPath) {
+ $this->modx->log(xPDO::LOG_LEVEL_ERROR, 'The realpath of the image ' . $source->getBasePath() . $data->sourceImg->src . 'is not valid. Please check the media source path setting of the Image+ image.', '', 'Image+');
+ }
+
+ // Prepare arguments for phpthumbof snippet call
+ $cropParams = [
+ 'sx' => $data->crop->x,
+ 'sy' => $data->crop->y,
+ 'sw' => $data->crop->width,
+ 'sh' => $data->crop->height,
+ ];
+ $params = [];
+ if ($data->targetWidth) {
+ $params = array_merge($params, [
+ 'w' => $data->targetWidth
+ ]);
+ }
+ if ($data->targetHeight) {
+ $params = array_merge($params, [
+ 'h' => $data->targetHeight
+ ]);
+ }
+ if ($data->targetWidth && $data->targetHeight) {
+ $params = array_merge($params, [
+ 'far' => true
+ ]);
+ }
+ $params = array_merge($cropParams, $params);
+
+ // Add phpThumbParams to phpthumbsup snippet call arguments
+ $phpThumbParams = $this->modx->getOption('phpThumbParams', $opts, '');
+ $optParams = [];
+ if ($phpThumbParams) {
+ parse_str($phpThumbParams, $optParams);
+ foreach ($optParams as $key => $val) {
+ if (empty($val)) {
+ unset($optParams[$key]);
+ }
+ }
+ }
+ $optParams = ($optParams) ? array_merge($cropParams, $optParams) : array_merge($params, $optParams);
+ $options = http_build_query($optParams);
+ $options = rawurldecode(preg_replace('/%5B[0-9]+%5D/simU', '%5B%5D', $options));
+ $cropOptions = http_build_query($cropParams);
+
+ $data->targetWidth = $optParams['w'] ?? 0;
+ $data->targetHeight = $optParams['h'] ?? 0;
+
+ // Call phpthumbsup for url
+ $generateUrl = $this->modx->getOption('generateUrl', $opts, 1);
+ if (file_exists($imgPath)) {
+ if ($generateUrl) {
+ $url = $this->modx->runSnippet(
+ 'phpthumbsup',
+ [
+ 'options' => $options,
+ 'input' => $imgPath
+ ]
+ );
+ } else {
+ $url = $source->getBaseUrl() . $data->sourceImg->src;
+ }
+ } else {
+ $url = $data->sourceImg->src;
+ }
+
+ // If an output chunk is selected, parse that
+ $outputChunk = $this->modx->getOption('outputChunk', $opts, '');
+ if ($outputChunk) {
+ $chunkParams = array_merge($opts, [
+ 'url' => $url,
+ 'alt' => $data->altTag ?? '',
+ 'width' => $data->targetWidth,
+ 'height' => $data->targetHeight,
+ 'source.src' => $imgPath,
+ 'source.width' => $data->sourceImg->width,
+ 'source.height' => $data->sourceImg->height,
+ 'crop.width' => $data->crop->width,
+ 'crop.height' => $data->crop->height,
+ 'crop.x' => $data->crop->x,
+ 'crop.y' => $data->crop->y,
+ 'options' => $options,
+ 'crop.options' => $cropOptions,
+ 'caption' => $data->caption ?? '',
+ 'credits' => $data->credits ?? ''
+ ]);
+ return $this->modx->getChunk($outputChunk, $chunkParams);
+ } else {
+ // Otherwise return raw url
+ return $url;
+ }
+ }
+}
diff --git a/core/components/imageplus/src/ImagePlus.php b/core/components/imageplus/src/ImagePlus.php
new file mode 100644
index 00000000..915785f3
--- /dev/null
+++ b/core/components/imageplus/src/ImagePlus.php
@@ -0,0 +1,299 @@
+
+ * Copyright 2015-2023 by Thomas Jakobi
+ *
+ * @package imageplus
+ * @subpackage classfile
+ */
+
+namespace TreehillStudio\ImagePlus;
+
+use modTemplateVar;
+use TreehillStudio\ImagePlus\CropEngines\AbstractCropEngine;
+use modMediaSource;
+use modX;
+use stdClass;
+use xPDO;
+
+/**
+ * Class ImagePlus
+ */
+class ImagePlus
+{
+ /**
+ * A reference to the modX instance
+ * @var modX $modx
+ */
+ public $modx;
+
+ /**
+ * The namespace
+ * @var string $namespace
+ */
+ public $namespace = 'imageplus';
+
+ /**
+ * The package name
+ * @var string $packageName
+ */
+ public $packageName = 'ImagePlus';
+
+ /**
+ * The version
+ * @var string $version
+ */
+ public $version = '2.9.5';
+
+ /**
+ * The class options
+ * @var array $options
+ */
+ public $options = [];
+
+ /**
+ * ImagePlus constructor
+ *
+ * @param modX $modx A reference to the modX instance.
+ * @param array $options An array of options. Optional.
+ */
+ public function __construct(modX &$modx, $options = [])
+ {
+ $this->modx =& $modx;
+
+ $corePath = $this->getOption('core_path', $options, $this->modx->getOption('core_path', null, MODX_CORE_PATH) . 'components/' . $this->namespace . '/');
+ $assetsPath = $this->getOption('assets_path', $options, $this->modx->getOption('assets_path', null, MODX_ASSETS_PATH) . 'components/' . $this->namespace . '/');
+ $assetsUrl = $this->getOption('assets_url', $options, $this->modx->getOption('assets_url', null, MODX_ASSETS_URL) . 'components/' . $this->namespace . '/');
+ $modxversion = $this->modx->getVersionData();
+
+ // Load some default paths for easier management
+ $this->options = array_merge([
+ 'namespace' => $this->namespace,
+ 'version' => $this->version,
+ 'corePath' => $corePath,
+ 'modelPath' => $corePath . 'model/',
+ 'vendorPath' => $corePath . 'vendor/',
+ 'chunksPath' => $corePath . 'elements/chunks/',
+ 'pagesPath' => $corePath . 'elements/pages/',
+ 'snippetsPath' => $corePath . 'elements/snippets/',
+ 'pluginsPath' => $corePath . 'elements/plugins/',
+ 'controllersPath' => $corePath . 'controllers/',
+ 'processorsPath' => $corePath . 'processors/',
+ 'templatesPath' => $corePath . 'templates/',
+ 'assetsPath' => $assetsPath,
+ 'assetsUrl' => $assetsUrl,
+ 'jsUrl' => $assetsUrl . 'js/',
+ 'cssUrl' => $assetsUrl . 'css/',
+ 'imagesUrl' => $assetsUrl . 'images/',
+ 'connectorUrl' => $assetsUrl . 'connector.php'
+ ], $options);
+
+ // Add default options
+ $this->options = array_merge($this->options, [
+ 'debug' => (bool)$this->modx->getOption($this->namespace . '.debug', null, '0') == 1,
+ 'modxversion' => $modxversion['version'],
+ 'sources' => $this->loadSourceMap()
+ ]);
+
+ $this->checkDependencies();
+
+ $lexicon = $this->modx->getService('lexicon', 'modLexicon');
+ $lexicon->load($this->namespace . ':default');
+ }
+
+ /**
+ * Get a local configuration option or a namespaced system setting by key.
+ *
+ * @param string $key The option key to search for.
+ * @param array $options An array of options that override local options.
+ * @param mixed $default The default value returned if the option is not found locally or as a
+ * namespaced system setting; by default this value is null.
+ * @return mixed The option value or the default value specified.
+ */
+ public function getOption($key, $options = [], $default = null)
+ {
+ $option = $default;
+ if (!empty($key) && is_string($key)) {
+ if ($options != null && array_key_exists($key, $options)) {
+ $option = $options[$key];
+ } elseif (array_key_exists($key, $this->options)) {
+ $option = $this->options[$key];
+ } elseif (array_key_exists("$this->namespace.$key", $this->modx->config)) {
+ $option = $this->modx->getOption("$this->namespace.$key");
+ }
+ }
+ return $option;
+ }
+
+ /**
+ * Check dependencies and raise warnings if not met
+ */
+ private function checkDependencies()
+ {
+ // Do some basic intelligent sniffing
+ if (!$this->getOption('cropEngineClass')) {
+ if (CropEngines\PhpThumbsUp::engineRequirementsMet($this->modx)) {
+ $this->options['cropEngineClass'] = '\TreehillStudio\ImagePlus\CropEngines\PhpThumbsUp';
+ } elseif (CropEngines\PhpThumbOf::engineRequirementsMet($this->modx)) {
+ $this->options['cropEngineClass'] = '\TreehillStudio\ImagePlus\CropEngines\PhpThumbOf';
+ } elseif (CropEngines\PhpThumbOn::engineRequirementsMet($this->modx)) {
+ $this->options['cropEngineClass'] = '\TreehillStudio\ImagePlus\CropEngines\PhpThumbOn';
+ } else {
+ $this->options['cropEngineClass'] = '';
+ }
+ if (!$this->getOption('cropEngineClass')) {
+ // Handle unmet dependencies
+ $this->options['hasUnmetDependencies'] = true;
+ return;
+ }
+ }
+ $this->options['hasUnmetDependencies'] = false;
+ }
+
+ /**
+ * Get a map of MediaSource id => baseUrl
+ *
+ * @return array
+ */
+ private function loadSourceMap()
+ {
+ $sources = $this->modx->getCollection('sources.modMediaSource');
+ $sourceMap = [];
+ foreach ($sources as $source) {
+ /** @var modMediaSource $source */
+ $source->initialize();
+ $sourceMap[$source->get('id')] = new stdClass();
+ $sourceMap[$source->get('id')]->url = $source->getBaseUrl();
+ }
+ return $sourceMap;
+ }
+
+ /**
+ * Register javascripts in the controller
+ */
+ public function includeScriptAssets()
+ {
+ $assetsUrl = $this->getOption('assetsUrl');
+ $jsUrl = $this->getOption('jsUrl') . 'mgr/';
+ $jsSourceUrl = $assetsUrl . '../../../source/js/mgr/';
+ $jsVendorUrl = $assetsUrl . '../../../source/vendor/';
+ $cssUrl = $this->getOption('cssUrl') . 'mgr/';
+ $cssSourceUrl = $assetsUrl . '../../../source/css/mgr/';
+ $nodeUrl = $assetsUrl . '../../../node_modules/';
+
+ if ($this->getOption('debug') && $assetsUrl != MODX_ASSETS_URL . 'components/imageplus/') {
+ $this->modx->controller->addJavascript($jsSourceUrl . 'imageplus.js?v=v' . $this->version);
+ $this->modx->controller->addJavascript($jsSourceUrl . 'imageplus.panel.input.js?v=v' . $this->version);
+ $this->modx->controller->addJavascript($jsSourceUrl . 'imageplus.window.editor.js?v=v' . $this->version);
+ $this->modx->controller->addJavascript($jsSourceUrl . 'imageplus.migx_renderer.js?v=v' . $this->version);
+ $this->modx->controller->addJavascript($jsSourceUrl . 'tools/JSON2.js?v=v' . $this->version);
+ $this->modx->controller->addJavascript($nodeUrl . 'jquery/dist/jquery.slim.min.js?v=v' . $this->version);
+ $this->modx->controller->addJavascript($jsVendorUrl . 'jcrop/js/jquery.Jcrop.min.js?v=v' . $this->version);
+ $this->modx->controller->addJavascript($jsSourceUrl . 'imageplus.jquery.imagecrop.js?v=v' . $this->version);
+ $this->modx->controller->addJavascript($jsSourceUrl . 'imageplus.grid.js?v=v' . $this->version);
+ $this->modx->controller->addCss($cssSourceUrl . 'imageplus.css?v=v' . $this->version);
+ } else {
+ $this->modx->controller->addJavascript($jsUrl . 'imageplus.min.js?v=v' . $this->version);
+ $this->modx->controller->addCss($cssUrl . 'imageplus.min.css?v=v' . $this->version);
+ }
+ $this->modx->controller->addHtml('');
+ }
+
+ /**
+ * Return a scaled, cached version of the source image for front-end use
+ *
+ * @param string $json
+ * @param array $opts
+ * @param modTemplateVar $tv
+ * @return string
+ * @internal param array $params
+ */
+ public function getImageURL($json, $opts = [], $tv = null)
+ {
+ // Check system settings for crop engine override
+ $engineClass = $this->getOption('cropEngineClass');
+
+ /**
+ * @var AbstractCropEngine $cropEngine
+ */
+ $cropEngine = new $engineClass($this->modx, [
+ 'core_path' => $this->getOption('corePath')
+ ]);
+
+ // Check crop engine is usable
+ if ($this->getOption('hasUnmetDependencies')) {
+ $this->modx->log(xPDO::LOG_LEVEL_ERROR, 'Requirements not met for crop engine.', '', 'Image+');
+ return 'Image+ error - requirements not met for crop engine.';
+ }
+
+ $json = $this->prepareTvValue($json, $opts, $tv);
+ return $cropEngine->getImageUrl($json, $opts, $tv);
+ }
+
+ /**
+ * Prepare a JSON encoded object and return a valid JSON encoded Image+ object
+ *
+ * @param string $json JSON value to prepare
+ * @param array $opts
+ * @param modTemplateVar $tv
+ * @return string
+ */
+ public function prepareTvValue($json, $opts = [], $tv = null)
+ {
+ // Prepare value
+ $decoded = json_decode($json);
+ if (!$decoded) {
+ // The variable does not contain an Image+ image object
+ if ($json != '') {
+ // Get Media Source
+ /** @var modMediaSource $source */
+ if ($tv) {
+ $source = $tv->getSource(($this->modx->resource) ? $this->modx->resource->get('context_key') : 'mgr');
+ } else {
+ $source = $this->modx->getObject('sources.modMediaSource', $this->modx->getOption('default_media_source'));
+ }
+ if (!$source) {
+ $this->modx->log(xPDO::LOG_LEVEL_ERROR, 'Invalid Media Source', '', 'Image+');
+ return '';
+ }
+ $source->setRequestProperties($_REQUEST);
+ $source->initialize();
+
+ // The variable contains a value and has to be converted to an Image+ image object
+ $imgPath = $source->getBasePath() . $json;
+ if (file_exists($imgPath)) {
+ $size = getimagesize($imgPath);
+ } else {
+ if ($this->getOption('debug')) {
+ $this->modx->log(xPDO::LOG_LEVEL_ERROR, 'The template variabe value does not contain an existing image', '', 'Image+');
+ }
+ $size = false;
+ }
+ $json = json_encode([
+ 'altTag' => '',
+ 'crop' => [
+ 'height' => ($size) ? $size[1] : 0,
+ 'width' => ($size) ? $size[0] : 0,
+ 'x' => 0,
+ 'y' => 0
+ ],
+ 'sourceImg' => [
+ 'height' => ($size) ? $size[1] : 0,
+ 'width' => ($size) ? $size[0] : 0,
+ 'source' => $source->get('id'),
+ 'src' => $json
+ ],
+ 'targetHeight' => (int)($opts['targetHeight'] ?? 0),
+ 'targetWidth' => (int)($opts['targetWidth'] ?? 0)
+ ]);
+ }
+ }
+ return $json;
+ }
+}
+
+define('imageplus', true);
diff --git a/core/components/imageplus/src/Plugins/Events/OnManagerPageBeforeRender.php b/core/components/imageplus/src/Plugins/Events/OnManagerPageBeforeRender.php
new file mode 100644
index 00000000..1608afd0
--- /dev/null
+++ b/core/components/imageplus/src/Plugins/Events/OnManagerPageBeforeRender.php
@@ -0,0 +1,19 @@
+modx->controller->addLexiconTopic('imageplus:default');
+ $this->modx->controller->addLexiconTopic('imageplus:setting');
+ $this->imageplus->includeScriptAssets();
+ }
+}
diff --git a/core/components/imageplus/src/Plugins/Events/OnTVInputPropertiesList.php b/core/components/imageplus/src/Plugins/Events/OnTVInputPropertiesList.php
new file mode 100644
index 00000000..efa758bc
--- /dev/null
+++ b/core/components/imageplus/src/Plugins/Events/OnTVInputPropertiesList.php
@@ -0,0 +1,17 @@
+modx->event->output($this->imageplus->getOption('corePath') . 'elements/tv/input/options/');
+ }
+}
diff --git a/core/components/imageplus/src/Plugins/Events/OnTVInputRenderList.php b/core/components/imageplus/src/Plugins/Events/OnTVInputRenderList.php
new file mode 100644
index 00000000..92750884
--- /dev/null
+++ b/core/components/imageplus/src/Plugins/Events/OnTVInputRenderList.php
@@ -0,0 +1,17 @@
+modx->event->output($this->imageplus->getOption('corePath') . 'elements/tv/input/');
+ }
+}
diff --git a/core/components/imageplus/src/Plugins/Events/OnTVOutputRenderList.php b/core/components/imageplus/src/Plugins/Events/OnTVOutputRenderList.php
new file mode 100644
index 00000000..d4e4113c
--- /dev/null
+++ b/core/components/imageplus/src/Plugins/Events/OnTVOutputRenderList.php
@@ -0,0 +1,17 @@
+modx->event->output($this->imageplus->getOption('corePath') . 'elements/tv/output/');
+ }
+}
diff --git a/core/components/imageplus/src/Plugins/Events/OnTVOutputRenderPropertiesList.php b/core/components/imageplus/src/Plugins/Events/OnTVOutputRenderPropertiesList.php
new file mode 100644
index 00000000..78115ae9
--- /dev/null
+++ b/core/components/imageplus/src/Plugins/Events/OnTVOutputRenderPropertiesList.php
@@ -0,0 +1,17 @@
+modx->event->output($this->imageplus->getOption('corePath') . 'elements/tv/output/options/');
+ }
+}
diff --git a/core/components/imageplus/src/Plugins/Plugin.php b/core/components/imageplus/src/Plugins/Plugin.php
new file mode 100644
index 00000000..74af3d21
--- /dev/null
+++ b/core/components/imageplus/src/Plugins/Plugin.php
@@ -0,0 +1,71 @@
+scriptProperties = &$scriptProperties;
+ $this->modx =& $modx;
+ $corePath = $this->modx->getOption('imageplus.core_path', null, $this->modx->getOption('core_path') . 'components/imageplus/');
+ $this->imageplus = $this->modx->getService('imageplus', 'ImagePlus', $corePath . 'model/imageplus/', [
+ 'core_path' => $corePath
+ ]);
+ }
+
+ /**
+ * Run the plugin event.
+ */
+ public function run()
+ {
+ $init = $this->init();
+ if ($init !== true) {
+ return;
+ }
+
+ $this->process();
+ }
+
+ /**
+ * Initialize the plugin event.
+ *
+ * @return bool
+ */
+ public function init()
+ {
+ return true;
+ }
+
+ /**
+ * Process the plugin event code.
+ *
+ * @return mixed
+ */
+ abstract public function process();
+}
\ No newline at end of file
diff --git a/core/components/imageplus/src/Snippets/ImagePlus.php b/core/components/imageplus/src/Snippets/ImagePlus.php
new file mode 100644
index 00000000..6a5f3aa5
--- /dev/null
+++ b/core/components/imageplus/src/Snippets/ImagePlus.php
@@ -0,0 +1,121 @@
+ '',
+ 'tvname' => '',
+ 'docid::int' => (isset($this->modx->resource)) ? $this->modx->resource->get('id') : 0,
+ 'type' => '',
+ 'options' => '',
+ 'tpl' => 'ImagePlus.image',
+ 'input::bool' => $this->imageplus->getOption('debug')
+ ];
+ }
+
+ /**
+ * Execute the snippet and return the result.
+ *
+ * @return string
+ */
+ public function execute()
+ {
+ $value = $this->getProperty('value');
+ $tvname = $this->getProperty('tvname');
+ $docid = $this->getProperty('docid');
+ $type = $this->getProperty('type');
+ $options = $this->getProperty('options');
+ $tpl = $this->getProperty('tpl');
+ $debug = $this->getProperty('debug');
+
+ if ($value) {
+ // Value is set by snippet property
+ $data = json_decode($value);
+ if (!$data) {
+ if ($debug) {
+ $this->modx->log(xPDO::LOG_LEVEL_ERROR, 'Unable to decode JSON in snippet property', '', 'Image+');
+ return 'Unable to decode JSON in snippet property';
+ }
+ }
+ // No TV is used
+ $tv = null;
+ $tvOutputProperties = [];
+ } else {
+ // Value is retreived from template variable
+ /** @var modTemplateVar $tv */
+ $tv = $this->modx->getObject('modTemplateVar', ['name' => $tvname]);
+ if ($tv) {
+ // Get the raw content of the TV
+ $value = $tv->getValue($docid);
+ $value = $tv->processBindings($value, $docid);
+ $tvOutputProperties = $tv->get('output_properties');
+ foreach ($tvOutputProperties as &$tvOutputProperty) {
+ switch ($tvOutputProperty) {
+ case 'true' :
+ $tvOutputProperty = true;
+ break;
+ case 'false' :
+ $tvOutputProperty = false;
+ break;
+ }
+ }
+ } else {
+ if ($debug) {
+ $this->modx->log(xPDO::LOG_LEVEL_ERROR, "Template Variable '$tvname' not found.", '', 'Image+');
+ return "Template Variable '$tvname' not found.";
+ }
+ $tvOutputProperties = [];
+ }
+ }
+
+ // Render output
+ switch ($type) {
+ case 'check':
+ $data = json_decode($value);
+ $output = ($data && $data->sourceImg->src) ? 'image' : 'noimage';
+ break;
+ case 'tpl':
+ $data = json_decode($value);
+ $output = ($value) ? $this->imageplus->getImageURL($value, array_merge($tvOutputProperties, $this->getProperties(), [
+ 'docid' => $docid,
+ 'phpThumbParams' => $options,
+ 'outputChunk' => $tpl,
+ 'caption' => ($data && isset($data->caption)) ? $data->caption : '',
+ 'credits' => ($data && isset($data->credits)) ? $data->credits : ''
+ ]), $tv) : '';
+ break;
+ case 'thumb':
+ $output = ($value) ? $this->imageplus->getImageURL($value, array_merge($tvOutputProperties, $this->getProperties(), [
+ 'docid' => $docid,
+ 'phpThumbParams' => $options,
+ 'outputChunk' => '',
+ ]), $tv) : '';
+ break;
+ default:
+ $output = ($value) ? $this->imageplus->getImageURL($value, array_merge($tvOutputProperties, $this->getProperties(), [
+ 'docid' => $docid,
+ 'phpThumbParams' => $options,
+ ]), $tv) : '';
+ break;
+ }
+ return $output;
+ }
+}
diff --git a/core/components/imageplus/src/Snippets/Snippet.php b/core/components/imageplus/src/Snippets/Snippet.php
new file mode 100644
index 00000000..e15277e0
--- /dev/null
+++ b/core/components/imageplus/src/Snippets/Snippet.php
@@ -0,0 +1,163 @@
+modx =& $modx;
+
+ $corePath = $this->modx->getOption('imageplus.core_path', null, $this->modx->getOption('core_path') . 'components/imageplus/');
+ $this->imageplus = $this->modx->getService('imageplus', 'ImagePlus', $corePath . 'model/imageplus/', [
+ 'core_path' => $corePath
+ ]);
+
+ $this->properties = $this->initProperties($properties);
+ }
+
+ /**
+ * Get default snippet properties.
+ *
+ * @return array
+ */
+ public function getDefaultProperties()
+ {
+ return [];
+ }
+
+ /**
+ * @param array $properties
+ * @return array
+ */
+ public function initProperties(array $properties = [])
+ {
+ $result = [];
+ foreach ($this->getDefaultProperties() as $key => $value) {
+ $parts = explode('::', $key);
+ $key = ($this->propertyPrefix && !in_array('noPrefix', $parts)) ? $this->propertyPrefix . ucfirst($parts[0]) : $parts[0];
+ if (isset($parts[1]) && method_exists($this, 'get' . ucfirst($parts[1]))) {
+ if (isset($parts[2])) {
+ $result[$parts[0]] = $this->{'get' . ucfirst($parts[1])}($this->modx->getOption($key, $properties, $value, true), $parts[2]);
+ } else {
+ $result[$parts[0]] = $this->{'get' . ucfirst($parts[1])}($this->modx->getOption($key, $properties, $value, true));
+ }
+ } else {
+ $result[$parts[0]] = $this->modx->getOption($key, $properties, $value, true);
+ }
+ if ($this->propertyPrefix) {
+ unset($properties[$key]);
+ }
+ unset($properties[$parts[0]]);
+ }
+ return array_merge($result, $properties);
+ }
+
+ /**
+ * @param $value
+ * @return int
+ */
+ protected function getInt($value)
+ {
+ return (int)$value;
+ }
+
+ /**
+ * @param $value
+ * @return bool
+ */
+ protected function getBool($value)
+ {
+ return ($value == 1 || $value == '1' || $value == true || $value == 'true');
+ }
+
+ /**
+ * @param $value
+ * @return array|null
+ */
+ protected function getAssociativeJson($value)
+ {
+ return (is_string($value)) ? json_decode($value, true) : $value;
+ }
+
+ /**
+ * Explode a separated value to an array.
+ *
+ * @param mixed $value
+ * @param string $separator
+ * @return array
+ */
+ protected function getExplodeSeparated($value, $separator = ',')
+ {
+ return (is_string($value) && $value !== '') ? array_map('trim', explode($separator, $value)) : [];
+ }
+
+ /**
+ * Get the snippet properties.
+ *
+ * @return array
+ */
+ public function getProperties()
+ {
+ return $this->properties;
+ }
+
+ /**
+ * Get a snippet property value or the default value.
+ *
+ * @param string $key
+ * @param null $default
+ * @return mixed
+ */
+ public function getProperty(string $key, $default = null)
+ {
+ if (isset($this->properties[$key])) {
+ return $this->properties[$key];
+ }
+ return $default;
+ }
+
+ abstract public function execute();
+}
diff --git a/core/components/tvimageplus/docs/changelog.txt b/core/components/tvimageplus/docs/changelog.txt
deleted file mode 100644
index b4e5bb67..00000000
--- a/core/components/tvimageplus/docs/changelog.txt
+++ /dev/null
@@ -1,13 +0,0 @@
--------------------------
- Image+ TV type changelog
--------------------------
-
-# v2.1
---------
- :: Fixed bug with non-default media sources
- :: Added field for additional phpThumb parameters to output renderer
- :: Added option to specify a chunk for output formatting (fields: url,alt,width,height)
-
-# v2.0
--------
-Complete rewrite
\ No newline at end of file
diff --git a/core/components/tvimageplus/docs/readme.tpl b/core/components/tvimageplus/docs/readme.tpl
deleted file mode 100644
index ad46adcd..00000000
--- a/core/components/tvimageplus/docs/readme.tpl
+++ /dev/null
@@ -1,27 +0,0 @@
----------------------
- Image+ TV type
----------------------
- Version: {$version}
- Author: Alan Pich
- License: GNU GPLv2
- Date: {$date}
- Build: {$commit}
-
- Dependencies: phpThumbOf
-
-
-
-Advanced image TV input type for MODx Revolution.
-The required dimensions for the image can (optionally)
-be configured on the TV, restricting one or both
-dimensions. When the editor uploads an image to the TV,
-they can then use a graphical tool to crop the image
-to the required dimensions/proportions.
-
-Usage
----------
-Install via package manager
-Create a TV with input & output types of Image+
-
-Full documentation can be found at
-https://github.com/alanpich/tvImagePlus
\ No newline at end of file
diff --git a/core/components/tvimageplus/elements/plugins/plugin.ImagePlusRouter.php b/core/components/tvimageplus/elements/plugins/plugin.ImagePlusRouter.php
deleted file mode 100644
index 357aa5ca..00000000
--- a/core/components/tvimageplus/elements/plugins/plugin.ImagePlusRouter.php
+++ /dev/null
@@ -1,20 +0,0 @@
-getOption('core_path',null,MODX_CORE_PATH).'components/tvimageplus/';
-$assetsUrl = $modx->getOption('assets_url',null,MODX_ASSETS_URL).'components/tvimageplus/js/mgr/';
-
-$modx->lexicon->load('tvimageplus:default');
-
-switch ($modx->event->name) {
- case 'OnTVInputRenderList':
- $modx->event->output($corePath.'elements/tv/input/');
- break;
- case 'OnTVOutputRenderList':
- $modx->event->output($corePath.'elements/tv/output/');
- break;
- case 'OnTVInputPropertiesList':
- $modx->event->output($corePath.'elements/tv/input/options/');
- break;
- case 'OnTVOutputRenderPropertiesList':
- $modx->event->output($corePath.'elements/tv/output/options/');
- break;
-};
\ No newline at end of file
diff --git a/core/components/tvimageplus/elements/tv/input/imageplus.class.php b/core/components/tvimageplus/elements/tv/input/imageplus.class.php
deleted file mode 100644
index a64619dd..00000000
--- a/core/components/tvimageplus/elements/tv/input/imageplus.class.php
+++ /dev/null
@@ -1,111 +0,0 @@
-modx->lexicon->load('tvimageplus:default');
-
- // Load helper class
- if(!class_exists('tvImagePlus')){
- require $this->modx->getOption('core_path').'components/tvimageplus/tvImagePlus.class.php'; };
- $this->helper = new tvImagePlus($this->modx);
-
- // Load required javascripts & register global config
- $this->modx->regClientCSS($this->helper->config['assets_url'].'mgr/css/jquery/jquery.jcrop.min.css');
- $this->modx->regClientStartupScript($this->helper->config['assets_url'].'mgr/js/tvimageplus.js');
- $this->modx->regClientStartupScript($this->helper->config['assets_url'].'mgr/js/tvimageplus.panel.input.js');
- $this->modx->regClientStartupScript($this->helper->config['assets_url'].'mgr/js/tvimageplus.window.editor.js');
- $this->modx->regClientStartupScript($this->helper->config['assets_url'].'mgr/js/tools/JSON2.js');
- $this->modx->regClientStartupScript($this->helper->config['assets_url'].'mgr/js/jquery/jquery.min.js');
- $this->modx->regClientStartupScript($this->helper->config['assets_url'].'mgr/js/jquery/jquery.jcrop.min.js');
- $this->modx->regClientStartupScript($this->helper->config['assets_url'].'mgr/js/tvimageplus.jquery.imagecrop.js');
- $this->modx->regClientStartupHTMLBlock('');
-
- // Prepare tv config for jsonification
- $tvConfig = $this->helper->loadTvConfig($this,$value,$params);
- $this->setPlaceholder('tvimageplusconfig',json_encode($tvConfig));
- $this->setPlaceholder('tvValue',$value);
-
-
- $this->setPlaceholder('mediasource',$this->tv->getSource('web')->get('id'));
- $this->setPlaceholder('tvparams',json_encode($this->getInputOptions()));
-
- $this->setPlaceholder('imgData',$this->getImageDataJSON($value,$params));
-
- }
-
-
-private function getImageDataJSON($value,$params){
- $I = json_decode($value);
- $Opts = $this->getInputOptions();
-
- $data = new stdClass;
-
- // Grab MediaSource info
- $MS = $this->tv->getSource('web')->toArray();
- $data->mediasource = new stdClass;
- $data->mediasource->id = $MS['id'];
- $data->mediasource->path = !isset($MS['properties']['basePath'])? $this->modx->getOption('base_path') : $MS['properties']['basePath']['value'];
- $data->mediasource->url = !isset($MS['properties']['baseUrl'])? $this->modx->getOption('base_url') : $MS['properties']['baseUrl']['value'];
- unset($MS);
-
- // Grab constraint info
- $data->constraint = new stdClass;
- $data->constraint->width = empty($params['targetWidth']) ? 0 : (int) $params['targetWidth'];
- $data->constraint->height = empty($params['targetHeight'])? 0 : (int) $params['targetHeight'];
-
- // Generate ratio value
- if( $data->constraint->width >0 && $data->constraint->height >0 ){
- // If both width/height constraints set, use that for ratio calc
- $data->constraint->ratio = $data->constraint->width/$data->constraint->height;
- } else
- if( isset($I->source->width) && isset($I->source->height) ){
- // Use source image size for ratio
- $data->constraint->ratio = $I->source->width / $I->source->height;
- } else {
- // Fail safe (and square)
- $data->constraint->ratio = false;
- };
-
- // Grab source image info (if it exists yet)
- if( isset($I->source) ){
- $data->source = new stdClass;
- $data->source->height = $I->source->height;
- $data->source->width = $I->source->width;
- $data->source->path = $I->source->path;
- $data->source->filename = $I->source->filename;
- $data->source->size = $I->source->size;
- } else {
- $data->source = false;
- };
-
- // Grab crop params (if they exist yet)
- if( isset($I->crop)){
- $data->crop = new stdClass;
- $data->crop->x = $I->crop->x;
- $data->crop->y = $I->crop->y;
- $data->crop->width = $I->crop->width;
- $data->crop->height = $I->crop->height;
- };
-
- return json_encode($data);
- }//
-
-}
-return 'ImagePlusInputRender';
diff --git a/core/components/tvimageplus/elements/tv/input/options/imageplus.php b/core/components/tvimageplus/elements/tv/input/options/imageplus.php
deleted file mode 100644
index 92e368fd..00000000
--- a/core/components/tvimageplus/elements/tv/input/options/imageplus.php
+++ /dev/null
@@ -1,14 +0,0 @@
-getOption('core_path').'components/tvimageplus/';
-if(!class_exists('tvImagePlus')){ require $root.'tvImagePlus.class.php'; };
-$helper = new tvImagePlus($modx);
-
-$modx->lexicon->load('tvimageplus:default');
-$a = print_r($this->getInputProperties(),1);
-
-$modx->controller->setPlaceholder('t_width',$a);
-$modx->controller->setPlaceholder('tvimagepluslexicon',json_encode($helper->config['lexicon']));
-$modx->controller->addLexiconTopic('tvimageplus:default');
-
-
-return $modx->smarty->fetch($root.'elements/tv/input/tpl/imageplus.options.tpl');
diff --git a/core/components/tvimageplus/elements/tv/input/tpl/imageplus.inputrender.tpl b/core/components/tvimageplus/elements/tv/input/tpl/imageplus.inputrender.tpl
deleted file mode 100644
index 54420b0c..00000000
--- a/core/components/tvimageplus/elements/tv/input/tpl/imageplus.inputrender.tpl
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
diff --git a/core/components/tvimageplus/elements/tv/input/tpl/imageplus.options.tpl b/core/components/tvimageplus/elements/tv/input/tpl/imageplus.options.tpl
deleted file mode 100644
index df458200..00000000
--- a/core/components/tvimageplus/elements/tv/input/tpl/imageplus.options.tpl
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-{/literal}
diff --git a/core/components/tvimageplus/elements/tv/output/imageplus.class.php b/core/components/tvimageplus/elements/tv/output/imageplus.class.php
deleted file mode 100644
index 557c7f68..00000000
--- a/core/components/tvimageplus/elements/tv/output/imageplus.class.php
+++ /dev/null
@@ -1,12 +0,0 @@
-modx->getOption('core_path').'components/tvimageplus/tvImagePlus.class.php'; };
- $this->helper = new tvImagePlus($this->modx);
-
-
- return $this->helper->getImageURL($value,$params);
- }//
-};
-return 'ImagePlusOutputRender';
diff --git a/core/components/tvimageplus/elements/tv/output/options/imageplus.php b/core/components/tvimageplus/elements/tv/output/options/imageplus.php
deleted file mode 100644
index fce2c00b..00000000
--- a/core/components/tvimageplus/elements/tv/output/options/imageplus.php
+++ /dev/null
@@ -1,16 +0,0 @@
-getOption('core_path').'components/tvimageplus/';
-if(!class_exists('tvImagePlus')){ require $root.'tvImagePlus.class.php'; };
-$helper = new tvImagePlus($modx);
-
-$modx->lexicon->load('tvimageplus:default');
-$a = print_r($this->getProperties(),1);
-
-$modx->controller->setPlaceholder('t_width',$a);
-$modx->controller->setPlaceholder('tvimagepluslexicon',json_encode($helper->config['lexicon']));
-$modx->controller->setPlaceholder('tvimageplus',$helper);
-$modx->controller->addLexiconTopic('tvimageplus:default');
-
-
-return $modx->smarty->fetch($root.'elements/tv/output/tpl/imageplus.options.tpl');
diff --git a/core/components/tvimageplus/elements/tv/output/tpl/imageplus.options.tpl b/core/components/tvimageplus/elements/tv/output/tpl/imageplus.options.tpl
deleted file mode 100644
index d5551d62..00000000
--- a/core/components/tvimageplus/elements/tv/output/tpl/imageplus.options.tpl
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-{/literal}
diff --git a/core/components/tvimageplus/lexicon/cs/default.inc.php b/core/components/tvimageplus/lexicon/cs/default.inc.php
deleted file mode 100755
index 51321483..00000000
--- a/core/components/tvimageplus/lexicon/cs/default.inc.php
+++ /dev/null
@@ -1,21 +0,0 @@
-zde.';
-$_lang['tvimageplus.outputChunk'] = 'Výstupní chunk';
-$_lang['tvimageplus.outputChunk_desc'] = 'Vyberte chunk pro výstup TV. Nechte prázdé pro výstup čisté URL.';
\ No newline at end of file
diff --git a/core/components/tvimageplus/lexicon/da/default.inc.php b/core/components/tvimageplus/lexicon/da/default.inc.php
deleted file mode 100644
index 68ab08e0..00000000
--- a/core/components/tvimageplus/lexicon/da/default.inc.php
+++ /dev/null
@@ -1,21 +0,0 @@
-her.';
-$_lang['tvimageplus.outputChunk'] = 'Output chunk';
-$_lang['tvimageplus.outputChunk_desc'] = 'Vælg en chunk til tv output. Lad feltet være tomt for kun at få en URL';
diff --git a/core/components/tvimageplus/lexicon/de/default.inc.php b/core/components/tvimageplus/lexicon/de/default.inc.php
deleted file mode 100644
index b38d0490..00000000
--- a/core/components/tvimageplus/lexicon/de/default.inc.php
+++ /dev/null
@@ -1,21 +0,0 @@
-hier.';
-$_lang['tvimageplus.outputChunk'] = 'Ausgabe Chunk';
-$_lang['tvimageplus.outputChunk_desc'] = '(Optional) Wählen Sie einen Ausgabe Chunk aus. Wenn kein Wert angebeben ist wird der Bildpfad ausgegeben.';
\ No newline at end of file
diff --git a/core/components/tvimageplus/lexicon/en/default.inc.php b/core/components/tvimageplus/lexicon/en/default.inc.php
deleted file mode 100644
index 6468aae0..00000000
--- a/core/components/tvimageplus/lexicon/en/default.inc.php
+++ /dev/null
@@ -1,21 +0,0 @@
-here.';
-$_lang['tvimageplus.outputChunk'] = 'Output chunk';
-$_lang['tvimageplus.outputChunk_desc'] = 'Select a chunk for tv output. Leave blank for raw url output';
\ No newline at end of file
diff --git a/core/components/tvimageplus/lexicon/es/default.inc.php b/core/components/tvimageplus/lexicon/es/default.inc.php
deleted file mode 100644
index a369dbb1..00000000
--- a/core/components/tvimageplus/lexicon/es/default.inc.php
+++ /dev/null
@@ -1,21 +0,0 @@
-aquí.';
-$_lang['tvimageplus.outputChunk'] = 'Resultado chunk';
-$_lang['tvimageplus.outputChunk_desc'] = 'Selecciona un chunk para obtener un resultado del tv. Dejar en vacío para obtener un resultado de la url cruda.';
\ No newline at end of file
diff --git a/core/components/tvimageplus/lexicon/fr/default.inc.php b/core/components/tvimageplus/lexicon/fr/default.inc.php
deleted file mode 100755
index 1f5bc29d..00000000
--- a/core/components/tvimageplus/lexicon/fr/default.inc.php
+++ /dev/null
@@ -1,21 +0,0 @@
-ici.';
-$_lang['tvimageplus.outputChunk'] = 'Chunk d\'affichage';
-$_lang['tvimageplus.outputChunk_desc'] = 'Sélectionnez le chunk utilisé pour afficher le résultat de la TV. Laissez vide pour obtenir l\'URL brute du résultat.';
diff --git a/core/components/tvimageplus/lexicon/hu/default.inc.php b/core/components/tvimageplus/lexicon/hu/default.inc.php
deleted file mode 100644
index a44bcdc2..00000000
--- a/core/components/tvimageplus/lexicon/hu/default.inc.php
+++ /dev/null
@@ -1,21 +0,0 @@
-itt.';
-$_lang['tvimageplus.outputChunk'] = 'Kimeneti chunk';
-$_lang['tvimageplus.outputChunk_desc'] = 'Válassz ki egy chunk-ot amibe a kimeneteli TV-ket használhatod majd. Hagyd üresen a nyers output-ért';
diff --git a/core/components/tvimageplus/lexicon/it/default.inc.php b/core/components/tvimageplus/lexicon/it/default.inc.php
deleted file mode 100644
index 25ca8fc9..00000000
--- a/core/components/tvimageplus/lexicon/it/default.inc.php
+++ /dev/null
@@ -1,21 +0,0 @@
-qui.';
-$_lang['tvimageplus.outputChunk'] = 'Chunk Output';
-$_lang['tvimageplus.outputChunk_desc'] = 'Seleziona un chunk per l\'output della variabile tv. Lascia vuoto per avere un output del semplice url';
\ No newline at end of file
diff --git a/core/components/tvimageplus/lexicon/nl/default.inc.php b/core/components/tvimageplus/lexicon/nl/default.inc.php
deleted file mode 100755
index 52466a51..00000000
--- a/core/components/tvimageplus/lexicon/nl/default.inc.php
+++ /dev/null
@@ -1,21 +0,0 @@
-hier te vinden.';
-$_lang['tvimageplus.outputChunk'] = 'Output chunk';
-$_lang['tvimageplus.outputChunk_desc'] = 'Selecteer een chunk voor de TV output. Laat leeg om alleen een link naar de afbeelding terug te krijgen.';
\ No newline at end of file
diff --git a/core/components/tvimageplus/lexicon/ru/default.inc.php b/core/components/tvimageplus/lexicon/ru/default.inc.php
deleted file mode 100644
index b4311413..00000000
--- a/core/components/tvimageplus/lexicon/ru/default.inc.php
+++ /dev/null
@@ -1,21 +0,0 @@
-здесь.';
-$_lang['tvimageplus.outputChunk'] = 'Чанк для вывода';
-$_lang['tvimageplus.outputChunk_desc'] = 'Выберите чанк для вывода TV. Оствьте пустым для обычного вывода текстом';
\ No newline at end of file
diff --git a/core/components/tvimageplus/tvImagePlus.class.php b/core/components/tvimageplus/tvImagePlus.class.php
deleted file mode 100644
index 2603884b..00000000
--- a/core/components/tvimageplus/tvImagePlus.class.php
+++ /dev/null
@@ -1,209 +0,0 @@
-modx =& $modx;
- $this->loadConfig();
- $this->loadLexicon();
- $this->loadSourceMap();
- }//
-
-
- private function loadConfig(){
- $core = $this->modx->getOption('core_path').'components/tvimageplus/';
- $assets = $this->modx->getOption('assets_url').'components/tvimageplus/';
- $this->config = array(
- 'core_path' => $core,
- 'assets_url' => $assets,
- 'connectorUrl' => $assets.'mgr/connector.php',
- 'sources' => array()
- );
- }//
-
-
- /**
- * Load the lexicon topic
- * @todo Do it properly with MODx.lang _()
- */
- private function loadLexicon(){
- $lexicon = $this->modx->lexicon;
- $modx = $this->modx;
- $mgr_lang = $modx->getOption('manager_language');
-
- $lexicon->load('tvimageplus');
-
- if(in_array($mgr_lang, $lexicon->getLanguageList('tvimageplus'))){
- $lang = $mgr_lang;
- }
- else{
- $lang = 'en';
- }
-
- $this->config['lexicon'] = $lexicon->getFileTopic($lang, 'tvimageplus');
- }//
-
- /**
- * Get a map of MediaSource id => baseUrl
- * @return void
- */
- private function loadSourceMap(){
- $sources = $this->modx->getCollection('sources.modMediaSource');
- foreach($sources as $source){
- $source->initialize();
- $this->config['sources'][$source->get('id')] = new stdClass();
- $this->config['sources'][$source->get('id')]->url = $source->getBaseUrl();
- };
- }//
-
-
- /**
- * Gather info about the TV
- * @param ImagePlusInputRender $render
- * @return object
- */
- public function loadTvConfig(ImagePlusInputRender $render, $value, array $params){
- $data = new stdClass;
- // Grab the ID of the assigned mediasource
- $data->mediaSource = $render->tv->getSource('web')->get('id');
- // Grab TV info
- $data->tv = new stdClass;
- $data->tv->id = $render->tv->get('id');
- $data->tv->params = $render->getInputOptions();
- $data->tv->value = $value;
- // Misc
- $data->allowBlank = (bool)$params['allowBlank'];
- // Dimension constraints
- $data->targetWidth = (int)$params['targetWidth'];
- $data->targetHeight = (int)$params['targetHeight'];
- // Alt-tag options
- $data->altTagOn = (isset($params['allowAltTag']) && $params['allowAltTag']=='Yes');
-
- $saved = empty($value)? null : json_decode($value);
- if(is_null($saved)){
- // Crop data
- $data->crop = new stdClass();
- $data->crop->width = 0;
- $data->crop->height = 0;
- $data->crop->x = 0;
- $data->crop->y = 0;
- // Source image
- $data->sourceImg = new stdClass();
- $data->sourceImg->width = 0;
- $data->sourceImg->height = 0;
- $data->sourceImg->src = '';
- $data->sourceImg->source = 1;
- $data->altTag = ($data->altTagOn? '' : false);
- } else {
- // Crop data
- $data->crop = new stdClass();
- $data->crop->width = $saved->crop->width;
- $data->crop->height = $saved->crop->height;
- $data->crop->x = $saved->crop->x;
- $data->crop->y = $saved->crop->y;
- // Source image
- $data->sourceImg = new stdClass();
- $data->sourceImg->width = $saved->sourceImg->width;
- $data->sourceImg->height = $saved->sourceImg->height;
- $data->sourceImg->src = $saved->sourceImg->src;
- $data->sourceImg->source = $saved->sourceImg->source;
- // die('
'.print_r($saved,1));
- $data->altTag = ($data->altTagOn? (isset($saved->altTag)? $saved->altTag:'') : false);
- }
-
- return $data;
- }//
-
-
- /**
- * Check if phpThumbOf is installed
- * @return bool
- */
- public function hasPhpThumbOf(){
- $pto = $this->modx->getObject('modSnippet',array('name'=>'phpthumbof'));
- return $pto instanceof modSnippet;
- }//
-
- /**
- * Return a scaled, cached version of the source image for front-end use
- * @param string $json
- * @param array $params
- * @return string
- */
- public function getImageURL($json, $opts = array()){
- // Return error message if phpthumbof not found
- if(!$this->hasPhpThumbOf()){
- return "Image+ Error: PhpThumbOf Extra not found";
- }
-
- // Parse json to object
- $data = json_decode($json);
-
- // Load up the mediaSource
- $source = $this->modx->getObject('modMediaSource',$data->sourceImg->source);
- if(!$source instanceof modMediaSource){
- return 'Image+ Error: Invalid Media Source';
- };
- $source->initialize();
-
- // Grab absolute system path to image
- $imgPath = $source->getBasePath().$data->sourceImg->src;
-
- // Prepare arguments for phpthumbof snippet call
- $params = array(
- 'src' => $imgPath,
- 'w' => $data->targetWidth,
- 'h' => $data->targetHeight,
- 'far' => true,
- 'sx' => $data->crop->x,
- 'sy'=> $data->crop->y,
- 'sw'=> $data->crop->width,
- 'sh'=> $data->crop->height
- );
-
- // Add in output render params
- $optParams = explode('&',$opts['phpThumbParams']);
- foreach($optParams as $oP){
- if(empty($oP)){ continue; };
- $bits = explode('=',$oP);
- $params[$bits[0]] = $bits[1];
- }
-
- $options = array();
- foreach($params as $key => $val){
- $options[] = $key.'='.$val;
- };
- $options = implode('&',$options);
-
-
- // Call phpthumbof for url
- $url = $this->modx->runSnippet('phpthumbof',array(
- 'options'=>$options,
- 'input' => $imgPath
- ));
-
- // If an output chunk is selected, parse that
- if(isset($opts['outputChunk']) && !empty($opts['outputChunk']) ){
- $chunkParams = array(
- 'url' => $url,
- 'alt' => $data->altTag,
- 'width' => $data->targetWidth,
- 'height' => $data->targetHeight
- );
- return $this->modx->getChunk($opts['outputChunk'],$chunkParams);
- } else {
- // Otherwise return raw url
- return $url;
- };
- }//
-
-
-
-
-
-
-};// end class tvImagePlus
-define('tvimageplus',true);
diff --git a/docs/contributors.md b/docs/contributors.md
new file mode 100755
index 00000000..28f7dc44
--- /dev/null
+++ b/docs/contributors.md
@@ -0,0 +1,5 @@
+The Image+ project was started in 2012 by [Alan
+Pich](https://github.com/alanpich) and is maintained and developed further since
+2015 by [Thomas Jakobi](https://github.com/jako).
+
+Many thanks to all who contributed, whether by creating pull requests, submitting bug reports, or donating.
\ No newline at end of file
diff --git a/docs/examples.md b/docs/examples.md
new file mode 100755
index 00000000..713d2baf
--- /dev/null
+++ b/docs/examples.md
@@ -0,0 +1,152 @@
+**Image+* can be used for various purposes. On this page you will find some
+*examples of how it can be used:
+
+### Collections
+
+You can display the *Image+* thumbnail in a column of the Collections grid by
+setting the column's renderer to *ImagePlus.MIGX_Renderer*.
+
+### MIGX
+
+If you want to use *Image+* in MIGX, you can configure all *Image+* TV
+properties with a JSON string in the *Configs* textarea in the MIGX formtabs
+field. The following properties are possible:
+
+```json
+{
+ "targetWidth":"",
+ "targetHeight":"",
+ "targetRatio":"",
+ "thumbnailWidth":"",
+ "allowAltTag":"",
+ "allowCaption":"",
+ "allowCredits":""
+}
+```
+
+You also need to change the *Input TV Type* to `imageplus`.
+
+You can display the thumbnail in the grid column by setting the renderer to
+*ImagePlus.MIGX_Renderer*. In earlier versions of MIGX, you need to edit a MIGX
+configuration to do this.
+
+To use this *Image+* MIGX field in the frontend, call the *ImagePlus Snippet*
+where the value parameter is the name of the MIGX field:
+
+```html
+[[ImagePlus?
+&value=`[[+migxImagePlusField]]`
+]]
+```
+
+This will give you the prepared URL for the cropped image instead of the raw
+JSON object.
+
+### getResources/pdoResources
+
+In order for the TV to be parsed using the *getResources/pdoResources snippet*,
+you need to add the following lines to your *getResources/pdoResources* call
+Snippet* call:
+
+```html
+&includeTVs=`name_of_your_tv`
+&processTVs=`name_of_your_tv`
+```
+
+In the template chunk of the *getResources/pdoResources snippet* call, you can
+use the placeholder `[[+tv.name_your_TV]]` if the TV's Output Type is set to
+`Image+`. Without any further changes, the placeholder will contain the URL of
+the cropped image.
+
+### Using the ImagePlus snippet in the getResources/pdoResources template chunk
+
+In your template chunk for calling *getResources/pdoResources Snippet*, you need
+to add a parameter so that the *ImagePlus Snippet* call knows the origin ID from
+which to retrieve data:
+
+```html
+&docid=`[[+id]]`
+```
+
+Here is an example of a call and configuration, where *image* is your Image+ TV:
+
+**Snippet Call**
+```
+