diff --git a/emhttp/plugins/dynamix/Browse.page b/emhttp/plugins/dynamix/Browse.page index c8de8292f..f5b0510cb 100644 --- a/emhttp/plugins/dynamix/Browse.page +++ b/emhttp/plugins/dynamix/Browse.page @@ -1,6 +1,6 @@ -Title="_(Index of)_ $dir" -Tag="navicon" -Markdown="false" +Title="" +Tag="home" +Nchan="file_manager" --- '); + +if (!file_exists($editor)) file_put_contents($editor,implode("\n",['','txt','js','php','page','plg','xml','old','bak','log','css'])); ?> +"> +"> + + + + - - -
_(Type)__(Name)__(Size)__(Last Modified)__(Location)_
+
+
_(Type)__(Name)__(Owner)__(Permission)__(Size)__(Last Modified)__(Location)__(Action)_
+
+
+> +> + + + + + + + + disabled> + disabled> +
diff --git a/emhttp/plugins/dynamix/BrowseButton.page b/emhttp/plugins/dynamix/BrowseButton.page new file mode 100644 index 000000000..fa87cbe68 --- /dev/null +++ b/emhttp/plugins/dynamix/BrowseButton.page @@ -0,0 +1,343 @@ +Menu="Buttons:3a" +Title="File Manager" +Icon="icon-u-duplicate" +Code="e963" +--- + + $myPage['name']=='Browse', + 'running' => file_exists('/var/tmp/file.manager.active'), + 'jobs' => file_exists('/var/tmp/file.manager.jobs'), + 'zfs' => is_executable('/usr/sbin/zfs') +]; +if ($dfm['running'] || $dfm['browser']) eval('?>'.parse_file("$docroot/webGui/include/Templates.php")); +?> + diff --git a/emhttp/plugins/dynamix/include/Browse.php b/emhttp/plugins/dynamix/include/Browse.php index 79096ddd5..4ff3e553f 100644 --- a/emhttp/plugins/dynamix/include/Browse.php +++ b/emhttp/plugins/dynamix/include/Browse.php @@ -12,25 +12,46 @@ ?> ',implode(array_map(function($row){echo gzinflate($row);},$rows)),''; $rows = $size; } +function validdir($dir) { + $path = realpath($dir); + return in_array(explode('/',$path)[1]??'',['mnt','boot']) ? $path : ''; +} +function escapeQuote($data) { + return str_replace('"','"',$data); +} function add($number, $name, $single='', $plural='s') { return $number.' '._($name.($number==1 ? $single : $plural)); } +function age($number,$time) { + return sprintf(_('%s '.($number==1 ? $time : $time.'s').' ago'),$number); +} +function my_age($time) { + if (!is_numeric($time)) $time = time(); + $age = new DateTime('@'.$time); + $age = date_create('now')->diff($age); + if ($age->y > 0) return age($age->y,'year'); + if ($age->m > 0) return age($age->m,'month'); + if ($age->d > 0) return age($age->d,'day'); + if ($age->h > 0) return age($age->h,'hour'); + if ($age->i > 0) return age($age->i,'minute'); + return age($age->s,'second'); +} function parent_link() { global $dir,$path; $parent = dirname($dir); return $parent=='/' ? false : ''._('Parent Directory').''; } -function my_devs(&$devs) { +function my_devs(&$devs,$name,$menu) { global $disks,$lock; $text = []; $i = 0; foreach ($devs as $dev) { @@ -38,66 +59,75 @@ function my_devs(&$devs) { $text[$i] = ' ---'; } else { switch ($disks[$dev]['luksState']??0) { - case 0: $text[$i] = ""._('Not encrypted').""; break; - case 1: $text[$i] = ""._('Encrypted and unlocked').""; break; - case 2: $text[$i] = ""._('Locked: missing encryption key').""; break; - case 3: $text[$i] = ""._('Locked: wrong encryption key').""; break; - default: $text[$i] = ""._('Locked: unknown error').""; break; + case 0: $text[$i] = ''._('Not encrypted').''; break; + case 1: $text[$i] = ''._('Encrypted and unlocked').''; break; + case 2: $text[$i] = ''._('Locked: missing encryption key').''; break; + case 3: $text[$i] = ''._('Locked: wrong encryption key').''; break; + default: $text[$i] = ''._('Locked: unknown error').''; break; } - $text[$i] .= compress($dev,12,0); + $root = $dev=='flash' ? "/boot/$name" : "/mnt/$dev/$name"; + $text[$i] .= ''.compress($dev,11,0).''; } $i++; } return implode($text); } +$dir = validdir(htmlspecialchars_decode(rawurldecode($_GET['dir']))); +if (!$dir) {echo '',_('Invalid path'),''; exit;} + extract(parse_plugin_cfg('dynamix',true)); $disks = parse_ini_file('state/disks.ini',true); $shares = parse_ini_file('state/shares.ini',true); -$dir = realpath(htmlspecialchars_decode(rawurldecode($_GET['dir']))); $path = unscript($_GET['path']); $fmt = "%F {$display['time']}"; $dirs = $files = []; $total = $objs = 0; -[$null,$root,$main,$rest] = my_explode('/',$dir,4); +[$null,$root,$main,$next,$rest] = my_explode('/',$dir,5); $user = $root=='mnt' && in_array($main,['user','user0']); $lock = $root=='mnt' ? ($main ?: '---') : ($root=='boot' ? _('flash') : '---'); +$ishare = $root=='mnt' && (!$main || !$next || ($main=='rootshare' && !$rest)); +$folder = $lock=='---' ? _('DEVICE') : ($ishare ? _('SHARE') : _('FOLDER')); -if ($user) { - exec("shopt -s dotglob; getfattr --no-dereference --absolute-names -n system.LOCATIONS ".escapeshellarg($dir)."/* 2>/dev/null",$tmp); +if ($user ) { + exec("shopt -s dotglob;getfattr --no-dereference --absolute-names -n system.LOCATIONS ".escapeshellarg($dir)."/* 2>/dev/null",$tmp); for ($i = 0; $i < count($tmp); $i+=3) $set[basename($tmp[$i])] = explode('"',$tmp[$i+1])[1]; unset($tmp); } -$stat = popen("shopt -s dotglob; stat -L -c'%F|%s|%Y|%n' ".escapeshellarg($dir)."/* 2>/dev/null",'r'); +$stat = popen("shopt -s dotglob;stat -L -c'%F|%U|%A|%s|%Y|%n' ".escapeshellarg($dir)."/* 2>/dev/null",'r'); while (($row = fgets($stat))!==false) { - [$type,$size,$time,$name] = explode('|',rtrim($row,"\n"),4); + [$type,$owner,$perm,$size,$time,$name] = explode('|',rtrim($row,"\n"),6); $dev = explode('/',$name,5); $devs = explode(',',$user ? $set[basename($name)]??$shares[$dev[3]]['cachePool']??'' : $lock); $objs++; $text = []; if ($type[0]=='d') { - $text[] = ""; - $text[] = "
"; - $text[] = "".htmlspecialchars(basename($name)).""; - $text[] = "<"._('FOLDER').">"; - $text[] = "".my_time($time,$fmt).""; - $text[] = "".my_devs($devs).""; - $text[] = ""; + $text[] = ''; + $text[] = '
'; + $text[] = ''.htmlspecialchars(basename($name)).''; + $text[] = ''.$owner.''; + $text[] = ''.$perm.''; + $text[] = '<'.$folder.'>'; + $text[] = ''.my_time($time,$fmt).''; + $text[] = ''.my_devs($devs,$dev[3]??$dev[2],'deviceFolderContextMenu').''; + $text[] = '...'; $dirs[] = gzdeflate(implode($text)); } else { $ext = strtolower(pathinfo($name,PATHINFO_EXTENSION)); $tag = count($devs)>1 ? 'warning' : ''; - $text[] = ""; - $text[] = "
"; - $text[] = "".htmlspecialchars(basename($name)).""; - $text[] = "".my_scale($size,$unit)." $unit"; - $text[] = "".my_time($time,$fmt).""; - $text[] = "".my_devs($devs).""; - $text[] = ""; + $text[] = ''; + $text[] = '
'; + $text[] = ''.htmlspecialchars(basename($name)).''; + $text[] = ''.$owner.''; + $text[] = ''.$perm.''; + $text[] = ''.my_scale($size,$unit).' '.$unit.''; + $text[] = ''.my_time($time,$fmt).''; + $text[] = ''.my_devs($devs,$dev[3]??$dev[2],'deviceFileContextMenu').''; + $text[] = '...'; $files[] = gzdeflate(implode($text)); $total += $size; } } pclose($stat); -if ($link = parent_link()) echo "
$link"; -echo write($dirs),write($files),'',add($objs,'object'),': ',add($dirs,'director','y','ies'),', ',add($files,'file'),' (',my_scale($total,$unit),' ',$unit,' ',_('total'),')'; -?> \ No newline at end of file +if ($link = parent_link()) echo '
',$link,''; +echo write($dirs),write($files),'',add($objs,'object'),': ',add($dirs,'director','y','ies'),', ',add($files,'file'),' (',my_scale($total,$unit),' ',$unit,' ',_('total'),')'; +?> diff --git a/emhttp/plugins/dynamix/include/Control.php b/emhttp/plugins/dynamix/include/Control.php new file mode 100644 index 000000000..58b61994e --- /dev/null +++ b/emhttp/plugins/dynamix/include/Control.php @@ -0,0 +1,179 @@ + +diff($age); + if ($age->y > 0) return age($age->y,'year'); + if ($age->m > 0) return age($age->m,'month'); + if ($age->d > 0) return age($age->d,'day'); + if ($age->h > 0) return age($age->h,'hour'); + if ($age->i > 0) return age($age->i,'minute'); + return age($age->s,'second'); +} +function validname($name) { + $path = realpath(dirname($name)); + return in_array(explode('/',$path)[1]??'',['mnt','boot']) ? $path.'/'.basename($name) : ''; +} +function escape($name) {return escapeshellarg(validname($name));} +function quoted($name) {return is_array($name) ? implode(' ',array_map('escape',$name)) : escape($name);} + +switch ($_POST['mode']) { +case 'upload': + $file = validname(htmlspecialchars_decode(rawurldecode($_POST['file']))); + if (!$file) die('stop'); + $local = "/var/tmp/".basename($file).".tmp"; + if ($_POST['start']==0) { + $my = pathinfo($file); $n = 0; + while (file_exists($file)) $file = $my['dirname'].'/'.preg_replace('/ \(\d+\)$/','',$my['filename']).' ('.++$n.')'.($my['extension'] ? '.'.$my['extension'] : ''); + file_put_contents($local,$file); + // create file with proper permissions and owner + touch($file); + chgrp($file,'users'); + chown($file,'nobody'); + chmod($file,0666); + } + $file = file_get_contents($local); + if ($_POST['cancel']==1) { + delete_file($file); + die('stop'); + } + if (file_put_contents($file,base64_decode($_POST['data']),FILE_APPEND)===false) { + delete_file($file); + die('error'); + } + die(); +case 'calc': + extract(parse_plugin_cfg('dynamix',true)); + $source = explode("\n",htmlspecialchars_decode(rawurldecode($_POST['source']))); + [$null,$root,$main,$rest] = my_explode('/',$source[0],4); + if ($root=='mnt' && in_array($main,['user','user0'])) { + $disks = parse_ini_file('state/disks.ini',true); + $tag = implode('|',array_merge(['disk'],pools_filter($disks))); + $loc = array_filter(explode(',',preg_replace("/($tag)/",',$1',exec("shopt -s dotglob; getfattr --no-dereference --absolute-names --only-values -n system.LOCATIONS ".quoted($source)."/* 2>/dev/null")))); + } else { + $loc = []; + foreach ($source as $path) { + [$null,$root,$main,$rest] = my_explode('/',$path,4); + $loc[] = $root=='mnt' ? ($main ?: '---') : ($root=='boot' ? _('flash') : '---'); + } + } + natcasesort($loc); + $awk = "awk 'BEGIN{ORS=\" \"}/Number of files|Total file size/{if(\$5==\"(reg:\")print \$4,\$8;if(\$5==\"(dir:\")print \$4,\$6;if(\$3==\"size:\")print \$4}'"; + [$files,$dirs,$size] = explode(' ',str_replace([',',')'],'',exec("rsync --stats -naI ".quoted($source)." /var/tmp 2>/dev/null|$awk"))); + $dirs = $dirs ?: 0; + $files -= $dirs; + $calc = []; + $calc[] = _('Name').": ".implode(', ',array_map('basename',$source)); + $calc[] = _('Location').": ".implode(', ',array_unique($loc)); + $calc[] = _('Last modified').': '.my_age(max(array_map('filemtime',$source))); + $calc[] = _('Total occupied space').": ".my_scale($size,$unit)." $unit"; + $calc[] = sprintf(_("in %s folder".($dirs==1?'':'s')." and %s file".($files==1?'':'s')),my_number($dirs),my_number($files)); + $calc = '
'.implode('
',$calc).'
'; + die($calc); +case 'home': + $source = explode("\n",htmlspecialchars_decode(rawurldecode($_POST['source']))); + $target = htmlspecialchars_decode(rawurldecode($_POST['target'])); + $disks = parse_ini_file('state/disks.ini',true); + $tag = implode('|',array_merge(['disk'],pools_filter($disks))); + $loc1 = implode(',',array_unique(array_filter(explode(',',preg_replace("/($tag)/",',$1',exec("getfattr --no-dereference --absolute-names --only-values -n system.LOCATIONS ".quoted($source)." 2>/dev/null")))))); + $loc2 = exec("getfattr --no-dereference --absolute-names --only-values -n system.LOCATIONS ".quoted($target)." 2>/dev/null"); + $home = $loc1==$loc2 ? '1' : '0'; + die($home); +case 'jobs': + $jobs = []; + $file = '/var/tmp/file.manager.jobs'; + $rows = file_exists($file) ? file($file,FILE_IGNORE_NEW_LINES) : []; + $job = 1; + for ($x = 0; $x < count($rows); $x+=9) { + $data = parse_ini_string(implode("\n",array_slice($rows,$x,9))); + $task = $data['task']; + $source = explode("\r",$data['source']); + $target = $data['target']; + $more = count($source) > 1 ? " (".sprintf("and %s more",count($source)-1).") " : ""; + $jobs[] = ''._('Job')." [".sprintf("%'.04d",$job++)."] - $task ".$source[0].$more.($target ? " --> $target" : ""); + } + $jobs = '
'.implode("
",$jobs).'
'; + die($jobs); +case 'edit': + $file = validname(rawurldecode($_POST['file'])); + die($file ? file_get_contents($file) : ''); +case 'save': + if ($file = validname(rawurldecode($_POST['file']))) file_put_contents($file,rawurldecode($_POST['data'])); + die(); +case 'stop': + $file = htmlspecialchars_decode(rawurldecode($_POST['file'])); + delete_file("/var/tmp/$file.tmp"); + die(); +case 'start': + $active = '/var/tmp/file.manager.active'; + $jobs = '/var/tmp/file.manager.jobs'; + $start = '0'; + if (file_exists($jobs)) { + exec("sed -n '2,9 p' $jobs > $active"); + exec("sed -i '1,9 d' $jobs"); + $start = filesize($jobs) > 0 ? '2' : '1'; + if ($start=='1') delete_file($jobs); + } + die($start); +case 'undo': + $jobs = '/var/tmp/file.manager.jobs'; + $undo = '0'; + if (file_exists($jobs)) { + $rows = array_reverse(explode(',',$_POST['row'])); + foreach ($rows as $row) { + $end = $row + 8; + exec("sed -i '$row,$end d' $jobs"); + } + $undo = filesize($jobs) > 0 ? '2' : '1'; + if ($undo=='1') delete_file($jobs); + } + die($undo); +case 'read': + $active = '/var/tmp/file.manager.active'; + $read = file_exists($active) ? json_encode(parse_ini_file($active)) : ''; + die($read); +case 'file': + $active = '/var/tmp/file.manager.active'; + $jobs = '/var/tmp/file.manager.jobs'; + $data[] = 'action="'.($_POST['action']??'').'"'; + $data[] = 'title="'.rawurldecode($_POST['title']??'').'"'; + $data[] = 'source="'.htmlspecialchars_decode(rawurldecode($_POST['source']??'')).'"'; + $data[] = 'target="'.rawurldecode($_POST['target']??'').'"'; + $data[] = 'H="'.(empty($_POST['hdlink']) ? '' : 'H').'"'; + $data[] = 'sparse="'.(empty($_POST['sparse']) ? '' : '--sparse').'"'; + $data[] = 'exist="'.(empty($_POST['exist']) ? '--ignore-existing' : '').'"'; + $data[] = 'zfs="'.rawurldecode($_POST['zfs']??'').'"'; + if (isset($_POST['task'])) { + // add task to queue + $task = rawurldecode($_POST['task']); + $data = "task=\"$task\"\n".implode("\n",$data)."\n"; + file_put_contents($jobs,$data,FILE_APPEND); + } else { + // start operation + file_put_contents($active,implode("\n",$data)); + } + die(); +} +?> diff --git a/emhttp/plugins/dynamix/include/Templates.php b/emhttp/plugins/dynamix/include/Templates.php new file mode 100644 index 000000000..5e2abdfaa --- /dev/null +++ b/emhttp/plugins/dynamix/include/Templates.php @@ -0,0 +1,367 @@ + + +"> +"> + + + +
+
+ + +
+  +:   + +_(New folder name)_: +: + +  +: + +
_(This creates a folder at the current level)_
+
+ +
+_(Folder name)_: +: + +  +: + +
+
+ +
+_(Current folder name)_: +: + +  +: _(rename to)_ ... + +_(New folder name)_: +: + +  +: + +
_(This renames the folder to the new name)_
+
+ +
+_(Source folder)_: +: + +  +: _(copy to)_ ... + +_(Target folder)_: +: + +_(Use sparse option)_
+_(Overwrite existing files)_ +: + + +
+
+ +
+_(Source folder)_: +: + +  +: _(move to)_ ... + +_(Target folder)_: +: + +_(Use sparse option)_
+_(Overwrite existing files)_ +: + +
+
+ +
+_(File name)_: +: + +  +: + +
_(This deletes the selected file)_
+
+ +
+_(Current file name)_: +: + +  +: _(rename to)_ ... + +_(New file name)_: +: + +  +: + +
_(This renames the selected file)_
+
+ +
+_(Source file)_: +: + +  +: _(copy to)_ ... + +_(Target file)_: +: + +_(Use sparse option)_
+_(Overwrite existing files)_ +: + +
_(This copies the selected file)_
+
+ +
+_(Source file)_: +: + +  +: _(move to)_ ... + +_(Target file)_: +: + +_(Use sparse option)_
+_(Overwrite existing files)_ +: + +
_(This moves the selected file)_
+
+ +
+_(Source)_: +: + +  +: + +
_(This deletes all selected sources)_
+
+ +
+_(Source)_: +: + +  +: _(rename to)_ ... + +_(Target)_: +: + +
_(This renames the selected source)_
+
+ +
+_(Source)_: +: + +  +: _(copy to)_ ... + +_(Target)_: +: + +_(Use sparse option)_
+_(Overwrite existing files)_ +: + +
_(This copies all the selected sources)_
+
+ +
+_(Source)_: +: + +  +: _(move to)_ ... + +_(Target)_: +: + +_(Use sparse option)_
+_(Overwrite existing files)_ +: + +
_(This moves all the selected sources)_
+
+ +
+_(Source)_: +: + +  +: _(change owner)_ ... + +_(New owner)_: +: + +  +: + +
_(This changes the owner of the source recursively)_
+
+ +
+_(Source)_: +: + +  +: _(change permission)_ ... + +_(New permission)_: +: + _(Owner)_: + _(Group)_: + _(Other)_: + +  +: + +
_(This changes the permission of the source recursively)_
+
+ +
+_(Source)_: +: + +_(Search pattern)_: +: + +  +: + +
+ +
+ +
+ +
+ +
+ +
+ +
+
diff --git a/emhttp/plugins/dynamix/javascript/EZView.js b/emhttp/plugins/dynamix/javascript/EZView.js new file mode 100644 index 000000000..37067634d --- /dev/null +++ b/emhttp/plugins/dynamix/javascript/EZView.js @@ -0,0 +1,2 @@ +/* EZView, Copyright 2017, Guillermo Diaz - modified by Bergware */ +"use strict";if(!window.jQuery)throw Error("jQuery is required");$.fn.EZView=function(A){var o=this;if(!$(o).length)throw Error("No jQuery elements found");return o.constructor=function(){var i=$(this);o.$EZView=null,o.arContent=[],o.index=[],o.icons={next:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAWCAYAAAAfD8YZAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA+5pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ1dWlkOjY1RTYzOTA2ODZDRjExREJBNkUyRDg4N0NFQUNCNDA3IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjA1OUQzQkZEMDZBODExRTI5OUZEQTZGODg4RDc1ODdCIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjA1OUQzQkZDMDZBODExRTI5OUZEQTZGODg4RDc1ODdCIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowMTgwMTE3NDA3MjA2ODExODA4M0ZFMkJBM0M1RUU2NSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowNjgwMTE3NDA3MjA2ODExODA4M0U3NkRBMDNEMDVDMSIvPiA8ZGM6dGl0bGU+IDxyZGY6QWx0PiA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPmdseXBoaWNvbnM8L3JkZjpsaT4gPC9yZGY6QWx0PiA8L2RjOnRpdGxlPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Po6G6sQAAAB/SURBVHjaYvj//z8DDANBARALIIvhw8gaFwAxiHGBWAPQNf4nxQBsGok2gAGq6D85BoCAALkGwEwgywBkJ5BsALofSDIAWyAQbQCuUCTKACYGSgDVnE12gJEdVWQnEoqSJyUZgwmqABu4CMQOQEUf8EYV2YUBxcUQuQUgQIABAENaIhLMSm8LAAAAAElFTkSuQmCC",back:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAWCAYAAAAfD8YZAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA+5pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ1dWlkOjY1RTYzOTA2ODZDRjExREJBNkUyRDg4N0NFQUNCNDA3IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjA1OUQzQzAxMDZBODExRTI5OUZEQTZGODg4RDc1ODdCIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjA1OUQzQzAwMDZBODExRTI5OUZEQTZGODg4RDc1ODdCIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowMTgwMTE3NDA3MjA2ODExODA4M0ZFMkJBM0M1RUU2NSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowNjgwMTE3NDA3MjA2ODExODA4M0U3NkRBMDNEMDVDMSIvPiA8ZGM6dGl0bGU+IDxyZGY6QWx0PiA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPmdseXBoaWNvbnM8L3JkZjpsaT4gPC9yZGY6QWx0PiA8L2RjOnRpdGxlPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PjJSsWEAAAB3SURBVHjaYvj//z8DMRgIBIC4AEWMBI0XgBjEWUC0ZjSN/5ENIEcj3AByNf4Hy5GtEaSGXI0YfiZFI4pmUjXCNZOjEaqPPI0gzMRACaDI2RQHGMVRRXEioTh5kmIAE55Y+ACkHID4Ig4lF2hXGFBcDOErAAECDAApfSISSEStFwAAAABJRU5ErkJggg==",download:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA+5pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ1dWlkOjY1RTYzOTA2ODZDRjExREJBNkUyRDg4N0NFQUNCNDA3IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjM2MDhDRDE0MDY5RDExRTI5OUZEQTZGODg4RDc1ODdCIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjM2MDhDRDEzMDY5RDExRTI5OUZEQTZGODg4RDc1ODdCIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowMTgwMTE3NDA3MjA2ODExODA4M0ZFMkJBM0M1RUU2NSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowNjgwMTE3NDA3MjA2ODExODA4M0U3NkRBMDNEMDVDMSIvPiA8ZGM6dGl0bGU+IDxyZGY6QWx0PiA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPmdseXBoaWNvbnM8L3JkZjpsaT4gPC9yZGY6QWx0PiA8L2RjOnRpdGxlPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqrnV/wAAACxSURBVHjaYvz//z8DIcDIyPgBSPGjiwP1MhLUS6QFWBURYwETA43BqAWjFgwBC0CZBQUDASjX/icTf0A3D5sPHID4I5nuDSDoA6gvEshwfQJWs7AJkmHJBJzm4JKAWrKACMMX4DUDnyTUkg14DL8AxAKUWiAANQjd8AeEDCfKAiRLkJMviG1AlF5iFEEtMUCyxIFYfYxQTfw0yscfGaEuGjplEaieRq6rh54P0AFAgAEAR8yvtAJdObEAAAAASUVORK5CYII=",zoomIn:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA+5pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ1dWlkOjY1RTYzOTA2ODZDRjExREJBNkUyRDg4N0NFQUNCNDA3IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjg5MkI5ODJFMDZBQjExRTI5OUZEQTZGODg4RDc1ODdCIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjg5MkI5ODJEMDZBQjExRTI5OUZEQTZGODg4RDc1ODdCIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowMTgwMTE3NDA3MjA2ODExODA4M0ZFMkJBM0M1RUU2NSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowNjgwMTE3NDA3MjA2ODExODA4M0U3NkRBMDNEMDVDMSIvPiA8ZGM6dGl0bGU+IDxyZGY6QWx0PiA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPmdseXBoaWNvbnM8L3JkZjpsaT4gPC9yZGY6QWx0PiA8L2RjOnRpdGxlPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PtK4jPoAAAFgSURBVHjarFZRccMwDLVz+58hlMHKYIYQCIUQCINQCBmDQMgYdAwMwUPgWYtyp2lSZa/R3bsktqwXyfJLfCnFSea9j/UyVpzZVKpY6rrFtRqQUGDQFaYMANnI10vgBJeG4BxzM8kdAsjqDQElyr1EO0EUFkLAk1DOgITcf7JI+B5czBJsTUHXQIZB8f2TxSI4QYAsjE8t2YBdmaNUorI1ohgg0f2TfAZ2Dj7qYHJ9Rs/Lq+QwsOeVHMZcUQBkrCCytEazwT1u2XJ4Ys9nogSBZoBjXogRWzKhexBrwNCZyUjuPzUSunHP2JbcvhBcRIHgRWmC3wLJ2rC0CB+WNlvtT0/82CUTmz8nWFsEclbk/IpBI2rWrVeR+RvO/5B6k8gpepQbA64tRFrNA5JJQW5YtpOR/XyXpBcW0SEkBtF0hHbt5w0+3+/CVDosEyWjny+s1/67HrEqN9A0af83+xZgABRpKnOx6YemAAAAAElFTkSuQmCC",zoomOut:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA+5pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ1dWlkOjY1RTYzOTA2ODZDRjExREJBNkUyRDg4N0NFQUNCNDA3IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjg5MkI5ODMyMDZBQjExRTI5OUZEQTZGODg4RDc1ODdCIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjg5MkI5ODMxMDZBQjExRTI5OUZEQTZGODg4RDc1ODdCIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowMTgwMTE3NDA3MjA2ODExODA4M0ZFMkJBM0M1RUU2NSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowNjgwMTE3NDA3MjA2ODExODA4M0U3NkRBMDNEMDVDMSIvPiA8ZGM6dGl0bGU+IDxyZGY6QWx0PiA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPmdseXBoaWNvbnM8L3JkZjpsaT4gPC9yZGY6QWx0PiA8L2RjOnRpdGxlPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqDoPeEAAAFMSURBVHjarJUBkYQwDEUpCioBB4eDq4SVUAknAQlIQMJKQALrgHPQVcAmO2Unl0loepCZPwy06WvSkLpt2xrJnHMRHgHUsaEFNIHf0lgNIVRgN9CKQwXNoJ77S+KAybA4VzRDFEAC3UFD1vwf0A4YBEf85oV0dhnM56upQ/N5x3T3xVyDjfyMjiA8ih/LYWbnuyWavST3SasVQFL3J8XSvBYGvkhFT02FwQJY6g/yKUjzWvY+k58xgTZFSfLRrG3OW6qF9CQVWL5OkS+liEN+yfutJgRIG8K+WV8Tjdd7qKguk69UhpjjzgCIzG8ttZVRAIUDgNiGShDPfspdS14w5PMaC9dALHVhDXS69fM0eCF1mtLBpqIKYT1pVBaZ86H7QvTxEFLZJIug0xADqL+idyEo5Qp8sKHn++yuiESJ6HPDXgohoIHeki8BBgDP751LFBCcbgAAAABJRU5ErkJggg==",close:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA+5pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ1dWlkOjY1RTYzOTA2ODZDRjExREJBNkUyRDg4N0NFQUNCNDA3IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjdGODJCNTUzMDZBMzExRTI5OUZEQTZGODg4RDc1ODdCIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjdGODJCNTUyMDZBMzExRTI5OUZEQTZGODg4RDc1ODdCIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowMTgwMTE3NDA3MjA2ODExODA4M0ZFMkJBM0M1RUU2NSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowNjgwMTE3NDA3MjA2ODExODA4M0U3NkRBMDNEMDVDMSIvPiA8ZGM6dGl0bGU+IDxyZGY6QWx0PiA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPmdseXBoaWNvbnM8L3JkZjpsaT4gPC9yZGY6QWx0PiA8L2RjOnRpdGxlPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PshQd58AAAB+SURBVHjarJTdCcAgDIRDJ3AU95+io7hBmkAsFuWSwgn3oudnflRRVbHRTcN0m5rPIbknvL6nx9wL0RCELZDpH8H4QCDsAFlhx4UNBiDTmxpayVM8LY1aiqHnqReLmTZDfnQGdvQS1qCnRik2pf20C8l8IrRHy/lGWB/bI8AA4oOVMKhFFb8AAAAASUVORK5CYII="},A=A||"EZView"+parseInt($(".EZView").length),o.createContainer(),o.index[A]=0,o.arContent[A]=[],i.each(function(i,n){let e=$(n);if(e.length){try{o.arContent[A][i]=[],o.arContent[A][i].href=e.attr("href")||e.attr("src"),o.arContent[A][i].name=e.attr("title")?e.attr("title").substring(0,30):"",o.arContent[A][i].isRender=!1,o.arContent[A][i].isImg=!0}catch(i){console.error(i),console.error(el)}e.data("index",i).css({cursor:"pointer"}).off().click(function(i){i.preventDefault(),i.stopPropagation(),o.runEZView(i)})}})},o.returnImageToOriginalMeasures=function(i){var n;o.isImg(i)&&(n=$("[index-content="+A+i+"]",o.$EZView),o.arContent[A][i]["original-height"]&&(n.animate({height:o.arContent[A][i]["original-height"]+"px",width:o.arContent[A][i]["original-width"]+"px"},200),n.css({top:"auto",left:"auto"})))},o.createContainer=function(){if(!$("#EZView"+A).length){var i='
',i='
'+i,n='
'+i+"
";try{$("body").append(n)}catch(i){console.error("Error creating container: "+i)}o.$EZView=$("#"+A).hide(),o.addEvents()}},o.isImg=function(i){return o.arContent[A][i].isImg},o.showOrHideControls=function(i=o.index[A]){var n=o.arContent[A][i].href,e=o.arContent[A][i].name;o.isImg(i)?$(".zoomIn, .zoomOut").fadeIn(200):$(".zoomIn, .zoomOut").fadeOut(200),$(".download").attr({href:n,download:n.split("/").pop()}),$(".name>b").html(e),0';return e&&(t='