]> _ Git - cubeextranet.git/commitdiff
#807
authorvincent@cubedesigners.com <vincent@cubedesigners.com@f5622870-0f3c-0410-866d-9cb505b7a8ef>
Wed, 11 Oct 2017 11:44:51 +0000 (11:44 +0000)
committervincent@cubedesigners.com <vincent@cubedesigners.com@f5622870-0f3c-0410-866d-9cb505b7a8ef>
Wed, 11 Oct 2017 11:44:51 +0000 (11:44 +0000)
inc/ws/Controlleur/class.ws.maintenance.php
inc/ws/Util/html5/burgermenu/class.ws.html5.compiler.php
inc/ws/Util/html5/burgermenu/class.ws.html5.links.php
inc/ws/Util/html5/master/class.ws.html5.compiler.php
inc/ws/Util/html5/master/class.ws.html5.links.php

index 57b2db00e5d8d329d4569c9c4917cfa446a7892b..b08d74417b5b0d59f691e10765701e98ea6292c4 100644 (file)
@@ -1429,7 +1429,7 @@ class wsMaintenance {
        }
 
        public static function iconSetToInterface() {
-               $iconSets = array(2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);
+               $iconSets = array(1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);
 
                $names = array('nav-share' => 'nav-friend',
                               'nav-bookmarks' => 'nav-bookmark',
index a5d54f7844bd8b995c1f53a20785d2cc32a6690d..e6b59a44ee063b93655a8d531d0b6f6ccbec8a0f 100644 (file)
@@ -960,18 +960,6 @@ class wsHTML5Compiler {
                $this->config->navOrder = array_filter(array_map('trim', explode(',', $this->config->navOrder)), 'strlen');
                $this->config->navOrderH = array_filter(array_map('trim', explode(',', $this->config->navOrderH)), 'strlen');
 
-               //              $o = trim($this->config->navOrderH) != '' ? $this->config->navOrderH : $this->config->navOrder;
-               //              $navOrder = array();
-               //              $oo = explode(',', $o);
-               //              foreach ($oo as $ooo) {
-               //                      $ooo = trim($ooo);
-               //                      if ($ooo == '') {
-               //                              continue;
-               //                      }
-               //                      $navOrder[] = $ooo;
-               //              }
-               //              $this->config->navOrder = $navOrder;
-
                $this->config->standalone = $this->standalone;
                if ($this->config->phonegap) {
                        $this->config->manifest = $this->writeManifest();
@@ -1249,20 +1237,6 @@ class wsHTML5Compiler {
                $res[] = 'svg .fill-c-menu-back{fill:' . wsHTML5::colorToCSS($this->theme->parametres->couleurB) . ';}';
                $res[] = 'svg .fill-c-menu-text{fill:' . wsHTML5::colorToCSS($this->theme->parametres->subTextColor) . ';}';
 
-               // Search field
-               //              $searchColor = wsHTML5::colorToCSS($this->theme->parametres->couleurS);
-               //              $searchBackColor = wsHTML5::colorToCSS($this->theme->parametres->searchFieldColor);
-               //              $search = '#q{';
-               //              $search .= 'color:' . $searchColor . ';';
-               //              $search .= 'background-color:' . $searchBackColor . ';';
-               //              if ($this->theme->parametres->searchShadeAlpha > 0) {
-               //                      $search .= wsHTML5::writeCSSUA('box-shadow', '1px 1px 4px rgba(0,0,0,' . ($this->theme->parametres->searchShadeAlpha / 100) . ')') . ' inset;';
-               //              }
-               //              $search .= '}';
-               //              $search .= '#searchHints,.hint{color:' . $searchColor . ';background-color:' . $searchBackColor . ';}';
-               //              $search .= '.hint:hover{color:' . $searchBackColor . ';background-color:' . $searchColor . ';}';
-               //
-               //              $res[] = $search;
 
                // Background
                $res[] = $this->_cssBackground();
@@ -1408,6 +1382,7 @@ class wsHTML5Compiler {
                        $less->setArg(null, $destination_less);
                        $less->setArg(null, $destination_css);
                        $less->execute();
+                       $less->debug();
                        if (!file_exists($destination_css)) {
                                die($less->output);
                        }
index c946903c55ce0ecae4ff4b9e5cdf4edc7f5d3dbb..548c918e49c021e9c8cbebe5edb37d504fd3eda6 100644 (file)
@@ -752,7 +752,8 @@ class actionLink extends internalLink {
                if (in_array($this->to, $this->_share)) {
                        return parent::getAdditionnalContent() . ' data-service="' . $this->to . '" ';
                } else {
-                       return parent::getClasses() . ' data-action="' . $this->to . '" ';
+                       return /*parent::getClasses()*/
+                               ' data-action="' . $this->to . '" ';
                }
        }
 
index 3539b6b3de302f58291b86fad71fb2960dd2677c..fce22ec44ef836d954d3cb56391264750e56a119 100644 (file)
@@ -5,29 +5,27 @@ class wsHTML5Compiler {
        protected $maxRes = 300;
 
        public $jsFiles = array(
-               'js/libs/modernizr/modernizr.js',
+               'js/libs/modernizr/modernizr.min.js',
                'js/libs/modernizr/tests.js',
                'js/libs/cube/fb.js',
                'js/libs/cube/util.js',
-               'js/libs/json.js',
-               'js/libs/flashdetect.js',
-               'js/libs/screenfull.js',
+               'js/libs/screenfull.min.js',
                'js/libs/storage.js',
                'js/libs/keymaster.js',
-               'js/libs/jquery/jquery.js',
+               'js/libs/jquery/jquery.min.js',
                'js/libs/jquery/jquery.transform.js',
-               'js/libs/jquery/jquery.form.js',
-               'js/libs/jquery/jquery.mousewheel.js',
-               'js/libs/jquery/jquery.hashchange.js',
-               'js/libs/jquery/jquery.scrollto.js',
-               'js/libs/jquery/jquery.localscroll.js',
-               'js/libs/jquery/perfect-scrollbar.jquery.js',
-               'js/libs/gsap/TweenMax.js',
-               'js/libs/gsap/jquery.gsap.js',
+               'js/libs/jquery/jquery.form.min.js',
+               'js/libs/jquery/jquery.mousewheel.min.js',
+               'js/libs/jquery/jquery.hashchange.min.js',
+               'js/libs/jquery/perfect-scrollbar.jquery.min.js',
+               'js/libs/mmenu/jquery.mmenu.min.js',
+               'js/libs/mmenu/jquery.mmenu.rtl.min.js',
+               'js/libs/mmenu/jquery.mmenu.offcanvas.min.js',
+               'js/libs/gsap/TweenMax.min.js',
+               'js/libs/gsap/jquery.gsap.min.js',
                'js/libs/gal/gal.js',
                'js/libs/gal/gal.filesystem.js',
-               'js/libs/interact.js',
-               'js/libs/hammer.js',
+               'js/libs/hammer.min.js',
                'js/libs/fluidbook/forms/fluidbook.form.bulle.js',
                'js/libs/fluidbook/fluidbook.utils.js',
                'js/libs/fluidbook/fluidbook.links.js',
@@ -41,7 +39,6 @@ class wsHTML5Compiler {
                'js/libs/fluidbook/fluidbook.slider.js',
                'js/libs/fluidbook/fluidbook.nav.js',
                'js/libs/fluidbook/fluidbook.touch.js',
-               'js/libs/fluidbook/fluidbook.interact.js',
                'js/libs/fluidbook/fluidbook.loader.js',
                'js/libs/fluidbook/fluidbook.search.js',
                'js/libs/fluidbook/fluidbook.help.js',
@@ -67,21 +64,20 @@ class wsHTML5Compiler {
        public $debugJsFiles = array(
                'js/libs/Three.js',
                'data/search.index.js',
-               'data/search.texts.js',
        );
        public $testJsFiles = array(
                'js/libs/cube/fb.js',
-               'js/libs/modernizr/modernizr.js',
+               'js/libs/modernizr/modernizr.min.js',
                'js/libs/modernizr/tests.js',
-               'js/libs/jquery/jquery.js',
-               'js/libs/jquery/jquery.transform.js',
-               'js/libs/jquery/jquery.mousewheel.js',
-               'js/libs/jquery/jquery.hashchange.js',
+               'js/libs/jquery/jquery.min.js',
+               'js/libs/jquery/jquery.transform.min.js',
+               'js/libs/jquery/jquery.mousewheel.min.js',
+               'js/libs/jquery/jquery.hashchange.min.js',
                'js/tester.js'
        );
        public $widgetJsFiles = array(
                'js/libs/cube/fb.js',
-               'js/libs/modernizr/modernizr.js',
+               'js/libs/modernizr/modernizr.min.js',
                'js/libs/modernizr/tests.js',
                'js/libs/jquery/jquery.js',
                'js/libs/jquery/jquery.transit.js',
@@ -91,7 +87,6 @@ class wsHTML5Compiler {
        // Collection of LESS files to be compiled
        // Filename with no extension, relative to the /style directory in the player build folder
        public $lessFiles = ['fluidbook'];
-       public $lessFilesExtras = []; // Extra files that will be copied when compiling LESS (eg. used to allow video-js-core.css to be copied)
 
        public $specialCSS = array();
        public $phonegapStandardPlugins = array('ios' => array('ExternalFileUtil'),
@@ -198,16 +193,9 @@ class wsHTML5Compiler {
                } else {
                        $this->dir = $dir;
                }
-               $this->vdir = $this->dir;
+               $this->vdir = new CubeIT_Files_VirtualDirectory($this->dir);
                $this->wdir = WS_BOOKS . '/working/' . $this->book_id . '/';
 
-               // Clean the folder
-               `rm -rf $this->vdir`;
-               if (!file_exists($this->dir)) {
-                       mkdir($this->dir, 0777, true);
-               }
-               $this->log('Clean vdir');
-
                $this->daoBook = new wsDAOBook($core->con);
                $this->book = $this->daoBook->selectById($book_id);
                $this->pages = $this->daoBook->getPagesOfBook($book_id);
@@ -235,6 +223,8 @@ class wsHTML5Compiler {
                $firstDoc = $daoDoc->selectById($this->pages[1]['document_id']);
                $size = $firstDoc->generalInfos['size'];
 
+               $this->log('Got data from database');
+
                $this->width = round($size[0], 3);
                $this->height = round($size[1], 3);
 
@@ -261,6 +251,8 @@ class wsHTML5Compiler {
                $this->numerotation = explode(',', $this->book->numerotation);
 
                $this->config = cubeObject::merge($this->book->parametres->toStandardObject(), $this->theme->parametres->toStandardObject());
+
+               $this->log('Defined dimensions');
        }
 
        public function log($step) {
@@ -309,40 +301,21 @@ class wsHTML5Compiler {
 
                $this->log('Start compile process');
 
-               $dirsToCreate = array('data/images', 'data/contents', 'data/background', 'data/thumbnails', 'data/style');
-               if (in_array('flv', $this->getVideosFormats())) {
-                       $dirsToCreate[] = 'swf';
-               }
-
-               foreach ($dirsToCreate as $dir) {
-                       $d = $this->vdir . '/' . $dir;
-                       if (!file_exists($d)) {
-                               mkdir($d, 0777, true);
-                       }
-               }
-
                // Copy fonts folder
-               $from = $this->assets . '/style/fonts/*';
-               $to = $this->vdir . '/style/fonts';
-               if (!file_exists($to)) {
-                       mkdir($to, 0777, true);
-               }
-               `cp -p -r $from $to`;
+               $from = $this->assets . '/style/fonts';
+               $to = 'style/fonts';
+               $this->vdir->copyDirectory($from, $to);
 
                // Copy images folder
                $from = $this->assets . '/images';
-               $to = $this->vdir;
-               `cp -p -r $from $to`;
+               $to = 'images';
+               $this->vdir->copyDirectory($from, $to);
 
                // Copy images folder
                $from = $this->assets . '/video';
-               $to = $this->vdir;
-               `cp -p -r $from $to`;
+               $to = 'video';
+               $this->vdir->copyDirectory($from, $to);
 
-               // Copy swf
-               if (file_exists($this->vdir . '/swf')) {
-                       $this->copy($this->assets . '/swf/video.swf', $this->vdir . '/swf/video.swf');
-               }
                $this->log('Copied assets');
                $this->loadPlugins();
                $this->log('Plugins loaded');
@@ -350,7 +323,7 @@ class wsHTML5Compiler {
                $this->log('Images written');
                $linksCSS = $this->writeLinks();
                $this->log('Links written');
-               $this->writeCSS($this->vdir . '/data/style/style_%d.css', $linksCSS);
+               $this->writeCSS('data/style/style_%d.css', $linksCSS);
                $this->log('CSS written');
                $this->writeLangs();
                $this->log('Langs written');
@@ -362,8 +335,8 @@ class wsHTML5Compiler {
                $this->log('Extras written');
                $this->writeJs();
                $this->log('Js written');
-               $this->writeCache();
-               $this->log('Cache written');
+               $this->vdir->sync(true);
+               $this->log('Files Synced');
        }
 
        protected function loadPlugins() {
@@ -390,12 +363,12 @@ class wsHTML5Compiler {
                        if (file_exists($dir . '/plugin.js')) {
                                $f = $d . '/plugin.js';
                                $this->pluginJs[] = $f;
-                               $this->copy($dir . '/plugin.js', $this->vdir . '/' . $f);
+                               $this->vdir->copy($dir . '/plugin.js', $f);
                        }
                        if (file_exists($dir . '/plugin.css')) {
                                $f = $d . '/plugin.css';
                                $this->pluginCSS[] = $f;
-                               $this->copy($dir . '/plugin.css', $this->vdir . '/' . $f);
+                               $this->vdir->copy($dir . '/plugin.css', $f);
                        }
                }
 
@@ -423,55 +396,6 @@ class wsHTML5Compiler {
                return $res;
        }
 
-       protected function writeCache() {
-               if (!$this->appcache) {
-                       return;
-               }
-
-               $cacheFile = $this->vdir . '/cache.appcache';
-
-               if (file_exists($cacheFile)) {
-                       unlink($cacheFile);
-               }
-
-               $dest = realpath($this->vdir);
-               $lines = array();
-
-               $network = array('NETWORK:', '*');
-
-               $lines[] = 'CACHE MANIFEST';
-               $lines[] = '# ' . date('Y-m-d H:i:s');
-               $lines[] = '';
-               $lines[] = 'index.html';
-               $lines[] = '';
-               $lines[] = 'FALLBACK:';
-               $lines[] = '/ index.html';
-               $lines[] = 'index.html* index.html';
-               $lines[] = '';
-               $lines[] = 'CACHE:';
-
-               $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dest), RecursiveIteratorIterator::SELF_FIRST);
-
-               $exclude = array('index.html', 'indexu.html', 'widget.html', 'indext.html');
-
-               foreach ($iterator as $path) {
-                       if (!$path->isFile()) {
-                               continue;
-                       }
-
-                       $p = str_replace($dest . '/', '', $path);
-                       if (in_array($p, $exclude)) {
-                               continue;
-                       }
-                       $lines[] = $p;
-               }
-
-               $lines[] = '';
-               $lines = array_merge($lines, $network);
-
-               file_put_contents($cacheFile, implode("\n", $lines));
-       }
-
        /**
         * Helper function to add a unique script entry to the JS stack.
         * Normally this is a relative path but it can be an external URL.
@@ -496,13 +420,10 @@ class wsHTML5Compiler {
         * @param $path The path of the file relative to the /style folder, without any extension
         * @param $extra_files Optional array of extra files that should be copied across for use during LESS compilation
         */
-       public function addLess($path, $extra_files = []) {
+       public function addLess($path) {
                if (!in_array($path, $this->lessFiles)) {
                        $this->lessFiles[] = $path;
                }
-
-               // Make sure no duplicates are added here either
-               $this->lessFilesExtras = array_unique(array_merge($this->lessFilesExtras, $extra_files));
        }
 
        protected function writeIndex() {
@@ -587,7 +508,7 @@ class wsHTML5Compiler {
 
                $favicon = '';
                if ($this->theme->parametres->favicon != '') {
-                       copy($this->themeRoot . '/' . $this->theme->parametres->favicon, $this->vdir . '/data/favicon.png');
+                       $this->vdir->copy($this->themeRoot . '/' . $this->theme->parametres->favicon, 'data/favicon.png');
                        $favicon = '<link rel="apple-touch-icon" href="data/favicon.png" />';
                }
 
@@ -595,19 +516,21 @@ class wsHTML5Compiler {
                $message = sprintf($this->__('Your browser is not up to date and is not able to run this publication. %sLearn more%s'), '<br /><a href="http://www.whatbrowser.org/intl/' . $this->config->defaultLang . '/" target="_blank">', '</a>');
 
                $splash = '';
-               if ($this->theme->parametres->logoLoader) {
-                       $dim = getimagesize($this->themeRoot . '/' . $this->theme->parametres->logoLoader);
+               if ($this->theme->parametres->logoLoader && file_exists($this->themeRoot . $this->theme->parametres->logoLoader)) {
+                       $dim = getimagesize($this->themeRoot . $this->theme->parametres->logoLoader);
                        if ($dim !== false) {
-                               $this->copy($this->themeRoot . '/' . $this->theme->parametres->logoLoader, $this->vdir . '/data/images/' . $this->theme->parametres->logoLoader);
+                               $this->vdir->copy($this->themeRoot . '/' . $this->theme->parametres->logoLoader, 'data/images/' . $this->theme->parametres->logoLoader);
                                $splash .= '<div class="logo"><img src="data/images/' . $this->theme->parametres->logoLoader . '" width="' . $dim[0] . '" height="' . $dim[1] . '" alt="" /></div>';
                        }
                }
 
-               $svgfiles = array($this->assets . '/images/interface.svg');
+               $svgfiles = array($this->assets . '/images/interface.svg', WS_ICONS . '/' . $this->theme->parametres->iconSet . '/interface.svg');
                $svg = '';
                foreach ($svgfiles as $svgfile) {
                        if (file_exists($svgfile)) {
                                $svg .= file_get_contents($svgfile);
+                       }else{
+                               die($svgfile.' does not exist');
                        }
                }
 
@@ -652,9 +575,9 @@ class wsHTML5Compiler {
                        $thtml = str_replace('<!-- $' . $v . ' -->', $$v, $thtml);
                }
 
-               file_put_contents($this->vdir . '/index.html', $html);
-               file_put_contents($this->vdir . '/indexu.html', $uhtml);
-               file_put_contents($this->vdir . '/indext.html', $uhtml);
+               $this->vdir->file_put_contents('index.html', $html);
+               $this->vdir->file_put_contents('indexu.html', $uhtml);
+               $this->vdir->file_put_contents('indext.html', $thtml);
 
                // Write widget html
                if ($this->widget) {
@@ -667,7 +590,7 @@ class wsHTML5Compiler {
                        foreach ($vars as $v) {
                                $whtml = str_replace('<!-- $' . $v . ' -->', $$v, $whtml);
                        }
-                       file_put_contents($this->vdir . '/widget.html', $whtml);
+                       $this->vdir->file_put_contents('widget.html', $whtml);
                }
        }
 
@@ -686,7 +609,7 @@ class wsHTML5Compiler {
                foreach ($vars as $v) {
                        $manifest = str_replace('$' . $v, $this->book->parametres->$v, $manifest);
                }
-               file_put_contents($this->vdir . '/imsmanifest.xml', $manifest);
+               $this->vdir->file_put_contents('imsmanifest.xml', $manifest);
        }
 
        protected function writePrint() {
@@ -695,7 +618,7 @@ class wsHTML5Compiler {
                        return;
                }
 
-               $this->copy(WS_BOOKS . '/final/' . $this->book->book_id . '/data/' . $this->book->parametres->pdfName, $this->vdir . '/data/' . $this->book->parametres->pdfName);
+               $this->vdir->copy(WS_BOOKS . '/final/' . $this->book->book_id . '/data/' . $this->book->parametres->pdfName, 'data/' . $this->book->parametres->pdfName);
                return '';
        }
 
@@ -744,7 +667,7 @@ class wsHTML5Compiler {
                }
                $iso = l10n::getISOcodes();
                if ($this->book->parametres->multilang != '') {
-                       $flagsDir = $this->vdir . '/images/flags';
+                       $flagsDir = 'images/flags';
                        if (!file_exists($flagsDir)) {
                                mkdir($flagsDir);
                        }
@@ -758,7 +681,7 @@ class wsHTML5Compiler {
 
                                $ll = explode('-', $l[0]);
 
-                               $this->copy(cubeMedia::getFlagFile($flag), $flagsDir . '/' . $flag . '.png');
+                               $this->vdir->copy(cubeMedia::getFlagFile($flag), $flagsDir . '/' . $flag . '.png');
                                $l[3] = cubeText::ucfirst($iso[$l[0]]);
                                $l[4] = cubeCountry::getCountryName($flag, $ll[0]);
                                $m[] = implode(',', $l);
@@ -770,12 +693,21 @@ class wsHTML5Compiler {
 
        protected function writeExtras() {
                if ($this->theme->parametres->afterSearch != '') {
-                       $this->copy($this->themeRoot . '/' . $this->theme->parametres->afterSearch, $this->vdir . '/data/images/' . $this->theme->parametres->afterSearch);
+                       $this->vdir->copy($this->themeRoot . '/' . $this->theme->parametres->afterSearch, 'data/images/' . $this->theme->parametres->afterSearch);
                }
                if ($this->book->parametres->externalArchives != '') {
                        $this->addFilesInfos('archives', $this->wdir . '/' . $this->book->parametres->externalArchives);
-                       $this->copy($this->wdir . '/' . $this->book->parametres->externalArchives, $this->vdir . '/data/images/' . $this->book->parametres->externalArchives);
+                       $this->vdir->copy($this->wdir . '/' . $this->book->parametres->externalArchives, 'data/images/' . $this->book->parametres->externalArchives);
                }
+
+               if ($this->book->parametres->navExtraImage != '') {
+                       $this->vdir->copy($this->wdir . '/' . $this->book->parametres->navExtraImage, 'data/images/' . $this->book->parametres->navExtraImage);
+               }
+
+               if ($this->book->parametres->navExtraImageMobile != '') {
+                       $this->vdir->copy($this->wdir . '/' . $this->book->parametres->navExtraImageMobile, 'data/images/' . $this->book->parametres->navExtraImageMobile);
+               }
+
        }
 
        protected function writeLinks() {
@@ -831,6 +763,13 @@ class wsHTML5Compiler {
                                continue;
                        }
 
+                       // Make old "aftersearch" link compatible with new "extra" menu option by extracting link URL
+                       if ($link->page == 'aftersearch') {
+                               $this->config->afterSearchLink = $link->to;
+                               $this->config->afterSearchTooltip = $link->infobulle;
+                       }
+
+
                        $c = $link->getHTMLContainer();
                        $css[] = $link->getCSSContainer();
                        if (!isset($pages[$link->page])) {
@@ -898,7 +837,7 @@ class wsHTML5Compiler {
 
        protected function writeJs() {
                $config = $this->writeConfig();
-               file_put_contents($this->vdir . '/data/datas.js', $config);
+               $this->vdir->file_put_contents('data/datas.js', $config);
                $finals = array('fluidbook' => $this->jsFiles);
                if ($this->book->parametres->scorm_enable) {
                        $finals['scorm'] = array();
@@ -937,13 +876,13 @@ class wsHTML5Compiler {
                        }
 
                        if (!$reminimize) {
-                               if (filemtime(__FILE__) > $mintime || filemtime(__DIR__ . '/class.ws.html5.links.php') > $mintime) {
+                               if (filemtime(__FILE__) > $mintime || (file_exists(__DIR__ . '/class.ws.html5.links.php') && filemtime(__DIR__ . '/class.ws.html5.links.php') > $mintime)) {
                                        $reminimize = true;
                                }
                        }
 
                        if ($reminimize) {
-                               $js = 'var files=' . json_encode($files) . ';';
+                               $js = '';
                                foreach ($files as $file) {
                                        $js .= file_get_contents($this->assets . '/' . $file);
                                        $js .= ";\n\n";
@@ -957,26 +896,22 @@ class wsHTML5Compiler {
                                $uglify->execute();
                                $uglify->debug();
                        }
-                       $dest = $this->vdir . '/data/' . $jsfinal . '.js';
-                       copy($minimized, $dest);
+                       $dest = 'data/' . $jsfinal . '.js';
+                       $this->vdir->copy($minimized, $dest);
                }
 
 
                if ($this->phonegap) {
-                       $this->copy(WS_COMPILE_ASSETS . '/_html5/js/libs/phonegap/' . $this->phonegapVersion . '/cordova-' . $this->phonegap . '.js', $this->vdir . '/data/cordova.js');
+                       $this->vdir->copy(WS_COMPILE_ASSETS . '/_html5/js/libs/phonegap/' . $this->phonegapVersion . '/cordova-' . $this->phonegap . '.js', 'data/cordova.js');
                }
 
        }
 
        public function writeTexts() {
                $this->daoBook->makeTextsIndexes($this->book, $this->pages, $index, $textes, true);
-               $jsindex = 'var INDEX=' . $index . ';' . "\r";
-               $jstexts = 'var TEXTS=' . $textes . ';' . "\r";
-
-               file_put_contents($this->vdir . '/data/search.index.js', $jsindex);
-               file_put_contents($this->vdir . '/data/search.texts.js', $jstexts);
+               $this->vdir->file_put_contents('data/search.index.js', 'var INDEX=' . $index . ';' . "\r");
                if ($this->book->parametres->highlightResults) {
-                       file_put_contents($this->vdir . '/data/search.highlight.js', 'var HIGHLIGHTS=' . json_encode($this->daoBook->makeHighlightIndex($this->book, $this->pages)) . ";\r");
+                       $this->vdir->file_put_contents('data/search.highlight.js', 'var HIGHLIGHTS=' . json_encode($this->daoBook->makeHighlightIndex($this->book, $this->pages)) . ";\r");
                }
        }
 
@@ -1021,17 +956,11 @@ class wsHTML5Compiler {
                if ($this->phonegap && ($this->book->parametres->offlineLink == '' || $this->book->parametres->offlineLink == 'http://')) {
                        $this->config->share = false;
                }
-               $o = trim($this->config->navOrderH) != '' ? $this->config->navOrderH : $this->config->navOrder;
-               $navOrder = array();
-               $oo = explode(',', $o);
-               foreach ($oo as $ooo) {
-                       $ooo = trim($ooo);
-                       if ($ooo == '') {
-                               continue;
-                       }
-                       $navOrder[] = $ooo;
-               }
-               $this->config->navOrder = $navOrder;
+
+               // We need to be able to reference both navOrder and navOrderH so convert both to arrays
+               // We also make sure there are no empty items in the arrays (see: http://php.net/manual/en/function.array-filter.php#111091)
+               $this->config->navOrder = array_filter(array_map('trim', explode(',', $this->config->navOrder)), 'strlen');
+               $this->config->navOrderH = array_filter(array_map('trim', explode(',', $this->config->navOrderH)), 'strlen');
 
                $this->config->standalone = $this->standalone;
                if ($this->config->phonegap) {
@@ -1050,7 +979,7 @@ class wsHTML5Compiler {
                                if ($ext == 'xlsx') {
                                        $this->config->basketReferences = wsUtil::excelToArray($referencesFile);
                                        if ($this->book->parametres->customLinkClass == 'AtlanticDownloadLink') {
-                                               $this->config->basketReferences = wsUtil::atlanticReferences($this->config->basketReferences, $this->vdir . '/local/', array($this, 'log'));
+                                               $this->config->basketReferences = wsUtil::atlanticReferences($this->config->basketReferences, 'local/', array($this, 'log'), array($this->vdir, "copy"));
                                        }
                                }
                                $this->log("Done cart references");
@@ -1082,34 +1011,27 @@ class wsHTML5Compiler {
 
                $arrowsColor = $this->theme->parametres->arrowsColor;
                // Set the icon list with the color
-               $icons = array('nav-bookmark' => $couleurI, 'nav-friend' => $couleurI, 'nav-help' => $couleurI, 'nav-index' => $couleurI, 'nav-sommaire' => $couleurI,
-                              'nav-zoomin' => $couleurI, 'nav-zoomout' => $couleurI, 'nav-fullscreen' => $couleurI,
-                              'interface-next' => $arrowsColor, 'interface-previous' => $arrowsColor,
-                              'interface-sharp-next' => $arrowsColor, 'interface-sharp-previous' => $arrowsColor,
-                              'interface-search' => $couleurI, 'interface-back-arrow' => $subTextColor, 'interface-print' => $subTextColor,
-                              'interface-down' => $arrowsColor, 'interface-close' => $arrowsColor,
+               $icons = array('interface-down' => $arrowsColor, 'interface-close' => $arrowsColor,
                               'interface-audio-description-on' => $arrowsColor, 'interface-audio-description-off' => $arrowsColor,
-                              'help-fingers' => $couleurI, 'help-mouse' => $couleurI, 'nav-home' => $couleurI, 'nav-archives' => $couleurI, 'nav-map' => $couleurI,
-                              'nav-tag' => $couleurI, 'nav-print' => $couleurI, 'nav-friend' => $couleurI,
-                              'share-facebook' => $couleurM, 'share-twitter' => $couleurM, 'share-email' => $couleurM, 'share-googleplus' => $couleurM, 'share-linkedin' => $couleurM, 'share-viadeo' => $couleurM,
+                              'help-fingers' => $couleurI, 'help-mouse' => $couleurI,
                               'bookmark-left-off' => $bookmarksDisabledColors, 'bookmark-left-on' => $bookmarksEnabledColors,
                               'bookmark-right-off' => $bookmarksDisabledColors, 'bookmark-right-on' => $bookmarksEnabledColors
                );
 
                $this->config->iconsDimensions = array();
                $makepng = !$this->supportSVG();
+               $tmpdir = CubeIT_Files::tmpdir();
                foreach ($icons as $icon => $color) {
-                       wsTools::colorizeAndRasterizeIcon($this->theme->parametres->iconSet, $icon, $color, $this->vdir . '/data/images/', 4, $w, $h);
+                       wsTools::colorizeAndRasterizeIcon($this->theme->parametres->iconSet, $icon, $color, $tmpdir, 4, $w, $h);
                        $this->config->iconsDimensions[$icon] = array($w, $h);
                }
+               $this->vdir->copyDirectory($tmpdir, 'data/images');
+               $this->vdir->addTemp($tmpdir);
                return $res;
        }
 
        protected function writeImages() {
                global $core;
-               foreach ($this->getResolutions() as $r) {
-                       mkdir($this->vdir . '/data/background/' . $r, 0777, true);
-               }
 
                $thumbs = array();
                foreach ($this->pages as $page => $infos) {
@@ -1129,7 +1051,7 @@ class wsHTML5Compiler {
                                wsDocument::extractTexts($full, $orig);
                                wsTools::optimizeSVG($orig, $opt);
 
-                               $this->copy($opt, $this->vdir . '/data/contents/p' . $page . '.svg');
+                               $this->vdir->copy($opt, 'data/contents/p' . $page . '.svg');
                        }
 
                        foreach ($this->getResolutions() as $r) {
@@ -1138,12 +1060,12 @@ class wsHTML5Compiler {
                                        if ($backgroundsPrefix == 'p') {
                                                $srcPrefix = 'h';
                                        }
-                                       $ok = $this->copy($docdir . 'html/' . $srcPrefix . $r . '-' . $infos['document_page'] . '.jpg', $this->vdir . '/data/background/' . $r . '/' . $backgroundsPrefix . $page . '.jpg');
+                                       $ok = $this->vdir->copy($docdir . 'html/' . $srcPrefix . $r . '-' . $infos['document_page'] . '.jpg', 'data/background/' . $r . '/' . $backgroundsPrefix . $page . '.jpg');
                                        if (!$ok && $r = 300) {
                                                $this->maxRes = 150;
                                        }
                                }
-                               $this->copy(WS_DOCS . '/' . $infos['document_id'] . '/p' . $infos['document_page'] . '.jpg', $this->vdir . '/data/thumbnails/p' . $page . '.jpg');
+                               $this->vdir->copy(WS_DOCS . '/' . $infos['document_id'] . '/p' . $infos['document_page'] . '.jpg', 'data/thumbnails/p' . $page . '.jpg');
                        }
 
                        $thumb = false;
@@ -1191,14 +1113,14 @@ class wsHTML5Compiler {
                        }
 
                        $cache = WS_CACHE . '/thumbsprites/' . hash('sha256', $hash) . '.jpg';
-                       $dest = $this->vdir . '/data/thumbnails/s' . $k . '.jpg';
+                       $dest = 'data/thumbnails/s' . $k . '.jpg';
                        if (!file_exists($cache)) {
                                $ratio = $this->width / $this->height;
                                $thumbh = round(100 / $ratio);
                                $cmd = 'montage ' . implode(' ', $files) . ' -geometry 100x' . $thumbh . '!+0+0 -background transparent -tile ' . $cols . 'x' . $srows . ' ' . $cache;
                                $res .= `$cmd`;
                        }
-                       copy($cache, $dest);
+                       $this->vdir->copy($cache, $dest);
                        $k++;
                }
                return $res;
@@ -1209,8 +1131,7 @@ class wsHTML5Compiler {
                $w = $size[0];
                $h = $size[1];
 
-               //$tmp = cubeFiles::tempnam() . '.png';
-               $tmp = $this->vdir . '/covers.png';
+               $tmp = cubeFiles::tempnam() . '.png';
 
                $c = new cubeCommandLine('convert');
                $c->setArg(null, ROOT . '/images/ws/shade-cover-app.png');
@@ -1218,31 +1139,18 @@ class wsHTML5Compiler {
                $c->setArg(null, $tmp);
                $c->execute();
 
+               $res = cubeFiles::tempnam() . '.jpg';
+
                $convert = new cubeCommandLine('composite');
                $cmd = '-compose Multiply ';
                $cmd .= $tmp . ' ' . $orig . ' ';
-               $cmd .= $this->vdir . '/cover.jpg';
+               $cmd .= $res;
                $convert->setManualArg($cmd);
                $convert->execute();
 
-               unlink($tmp);
-       }
-
-       protected function copy($s, $t) {
-               if (!file_exists($s)) {
-                       fb($s . ' dont exists');
-                       return false;
-               }
-               if (file_exists($t) && filemtime($t) >= filemtime($s) && filesize($s) == filesize($t)) {
-                       return true;
-               }
-               if (!file_exists(dirname($t))) {
-                       mkdir(dirname($t), 0777, true);
-               }
+               $this->vdir->copy($res, 'cover.jpg', true);
 
-               $res = copy($s, $t);
-               touch($t, filemtime($s));
-               return true;
+               unlink($tmp);
        }
 
        protected function _lessBoolean($val) {
@@ -1331,20 +1239,6 @@ class wsHTML5Compiler {
                $res[] = 'svg .fill-c-menu-back{fill:' . wsHTML5::colorToCSS($this->theme->parametres->couleurB) . ';}';
                $res[] = 'svg .fill-c-menu-text{fill:' . wsHTML5::colorToCSS($this->theme->parametres->subTextColor) . ';}';
 
-               // Search field
-               $searchColor = wsHTML5::colorToCSS($this->theme->parametres->couleurS);
-               $searchBackColor = wsHTML5::colorToCSS($this->theme->parametres->searchFieldColor);
-               $search = '#q{';
-               $search .= 'color:' . $searchColor . ';';
-               $search .= 'background-color:' . $searchBackColor . ';';
-               if ($this->theme->parametres->searchShadeAlpha > 0) {
-                       $search .= wsHTML5::writeCSSUA('box-shadow', '1px 1px 4px rgba(0,0,0,' . ($this->theme->parametres->searchShadeAlpha / 100) . ')') . ' inset;';
-               }
-               $search .= '}';
-               $search .= '#searchHints,.hint{color:' . $searchColor . ';background-color:' . $searchBackColor . ';}';
-               $search .= '.hint:hover{color:' . $searchBackColor . ';background-color:' . $searchColor . ';}';
-
-               $res[] = $search;
 
                // Background
                $res[] = $this->_cssBackground();
@@ -1355,7 +1249,7 @@ class wsHTML5Compiler {
                $header .= 'height:' . $this->theme->parametres->menuHeight . 'px;';
                $header .= 'background-color:' . wsHTML5::colorToCSS($this->theme->parametres->menuColor) . ';';
                if ($this->theme->parametres->menuImage != '') {
-                       $this->copy($this->themeRoot . '/' . $this->theme->parametres->menuImage, $this->vdir . '/data/images/' . $this->theme->parametres->menuImage);
+                       $this->vdir->copy($this->themeRoot . '/' . $this->theme->parametres->menuImage, 'data/images/' . $this->theme->parametres->menuImage);
                        $header .= 'background-image:url(../images/' . $this->theme->parametres->menuImage . ');';
                        $header .= 'background-repeat:no-repeat;';
                        $header .= 'background-size:100% ' . $this->theme->parametres->menuHeight . 'px;';
@@ -1370,8 +1264,8 @@ class wsHTML5Compiler {
                // Logo
                $logo = '#logo{';
                if ($this->theme->parametres->logo) {
-                       $this->copy($this->themeRoot . '/' . $this->theme->parametres->logo, $this->vdir . '/data/images/' . $this->theme->parametres->logo);
-                       $dim = getimagesize($this->vdir . '/data/images/' . $this->theme->parametres->logo);
+                       $this->vdir->copy($this->themeRoot . '/' . $this->theme->parametres->logo, 'data/images/' . $this->theme->parametres->logo);
+                       $dim = getimagesize($this->themeRoot . '/' . $this->theme->parametres->logo);
                        $logo .= 'background-image:url(../images/' . $this->theme->parametres->logo . ');width:' . $dim[0] . 'px;height:' . $dim[1] . 'px;';
                }
                $logo .= '}';
@@ -1381,13 +1275,15 @@ class wsHTML5Compiler {
                $res[] = 'footer,footer a{color:' . wsHTML5::colorToCSS($this->theme->parametres->creditsColor) . ';}';
 
                // Arrows
-               $res[] = '#next,#previous{background-color:' . wsHTML5::colorToCSS($this->theme->parametres->couleurA) . ';}';
+               $lessVariables['arrows-background'] = wsHTML5::colorToCSS($this->theme->parametres->couleurA);
+               $lessVariables['arrows-color'] = wsHTML5::colorToCSS($this->theme->parametres->arrowsColor);
 
                // Audio description buttons
-               $res[] = '.audio-description-button{background-color:' . wsHTML5::colorToCSS($this->theme->parametres->couleurA) . ';}';
-               $lessVariables['links-color'] = wsHTML5::colorToCSS($this->theme->parametres->linksColor);
+               $lessVariables['audiodescription-background'] = wsHTML5::colorToCSS($this->theme->parametres->couleurA);
+               $lessVariables['audiodescription-color'] = wsHTML5::colorToCSS($this->theme->parametres->couleurA);
 
                // Links Styles
+               $lessVariables['links-color'] = wsHTML5::colorToCSS($this->theme->parametres->linksColor);
                $res = array_merge($res, $links);
 
                // Bookmarks
@@ -1402,11 +1298,16 @@ class wsHTML5Compiler {
                $menuColor = new CubeIT_Graphics_Color($this->theme->parametres->couleurB);
                $menuColor->setAlpha(1);
                $menuTextColor = wsHTML5::colorToCSS($this->theme->parametres->subTextColor);
+               $menuBreakpoint = empty($this->book->parametres->menuBreakpoint) ? '1080px' : $this->book->parametres->menuBreakpoint;
 
+               $lessVariables['menu-breakpoint'] = $menuBreakpoint;
                $lessVariables['menu-background'] = $menuColor->toCSS();
                $lessVariables['menu-text'] = $menuTextColor;
                $lessVariables['menu-field-background'] = wsHTML5::colorToCSS($this->theme->parametres->subFieldColor);
                $lessVariables['menu-field-text'] = wsHTML5::colorToCSS($this->theme->parametres->subTextFieldColor);
+               $lessVariables['menu-select-background'] = wsHTML5::colorToCSS($this->theme->parametres->subSelectColor);
+               $lessVariables['menu-select-text'] = wsHTML5::colorToCSS($this->theme->parametres->subTextSelectColor);
+               $lessVariables['icon-color'] = wsHTML5::colorToCSS($this->theme->parametres->couleurI);
 
 
                $menuMultiply = $menuColor->multiply($menuColor);
@@ -1415,7 +1316,7 @@ class wsHTML5Compiler {
 
                // Archives
                if ($this->book->parametres->externalArchivesBack) {
-                       $this->copy($this->wdir . '/' . $this->book->parametres->externalArchivesBack, $this->vdir . '/data/images/' . $this->book->parametres->externalArchivesBack);
+                       $this->vdir->copy($this->wdir . '/' . $this->book->parametres->externalArchivesBack, 'data/images/' . $this->book->parametres->externalArchivesBack);
                        $res[] = '.mview.archives{background-image:url("../images/' . $this->book->parametres->externalArchivesBack . '");}';
                }
 
@@ -1438,7 +1339,7 @@ class wsHTML5Compiler {
                $res = array_chunk($res, 3500);
                foreach ($res as $k => $css) {
                        $this->stylesheets[] = 'data/style/style_' . $k . '.css';
-                       file_put_contents(sprintf($file, $k), implode("\n", $css));
+                       $this->vdir->file_put_contents(sprintf($file, $k), implode("\n", $css));
                }
                return count($res);
        }
@@ -1462,21 +1363,11 @@ class wsHTML5Compiler {
                }
                file_put_contents($tmp . '/book-variables.less', implode("\n", $bookVariables));
 
-               // Also copy any "extra" files that might be needed by LESS compilation
-               // (eg. video-js-core.css is needed by videojs.less)
-               foreach ($this->lessFilesExtras as $extra) {
-                       $destination_extra = $tmp . '/' . $extra;
-                       if (!is_dir(dirname($destination_extra))) {
-                               mkdir(dirname($destination_extra), 0777, true);
-                       }
-                       copy($this->assets . '/style/' . $extra, $destination_extra);
-               }
-
                foreach ($this->lessFiles as $f) {
 
                        $source_less = $this->assets . '/style/' . $f . '.less';
                        $destination_less = $tmp . '/' . $f . '.less';
-                       $destination_css = 'style/' . $f . '.css';
+                       $destination_css = $tmp . '/' . $f . '.css';
 
                        if (!file_exists($source_less)) {
                                continue;
@@ -1486,25 +1377,21 @@ class wsHTML5Compiler {
                        if (!is_dir(dirname($destination_less))) {
                                mkdir(dirname($destination_less), 0777, true);
                        }
-                       // Generated CSS file might be in a subfolder, so create if it doesn't exist
-                       if (!is_dir(dirname($this->vdir . '/' . $f . '.css'))) {
-                               mkdir(dirname($this->vdir . '/' . $f . '.css'), 0777, true);
-                       }
 
                        // Less files must be copied to temporary directory so they'll
                        // have access to the variables generated in book-variables.less
                        copy($source_less, $destination_less);
                        $less = new CubeIT_CommandLine('/usr/local/bin/lessc');
-                       //$less->setArg('clean-css', '--s1 --advanced --compatibility=ie10');
                        $less->setArg(null, $destination_less);
-                       $less->setArg(null, $this->vdir . '/' . $destination_css);
+                       $less->setArg(null, $destination_css);
                        $less->execute();
                        $less->debug();
-                       if (!file_exists($this->vdir . '/' . $destination_css)) {
+                       if (!file_exists($destination_css)) {
                                die($less->output);
                        }
+                       $this->vdir->copy($destination_css, 'style/' . $f . '.css');
                        if ($f != 'widget') {
-                               $this->stylesheets[] = $destination_css;
+                               $this->stylesheets[] = 'style/' . $f . '.css';
                        }
                }
        }
@@ -1535,7 +1422,7 @@ class wsHTML5Compiler {
                                $this->config->backgroundImageDimensions = array('width' => $dbi[0], 'height' => $dbi[1]);
                        }
 
-                       $this->copy($this->themeRoot . '/' . $this->theme->parametres->backgroundImage, $this->vdir . '/data/images/' . $this->theme->parametres->backgroundImage);
+                       $this->vdir->copy($this->themeRoot . '/' . $this->theme->parametres->backgroundImage, 'data/images/' . $this->theme->parametres->backgroundImage);
                        $body .= 'background-image:url(../images/' . $this->theme->parametres->backgroundImage . ');';
                        $body .= 'background-position:';
 
@@ -1591,21 +1478,15 @@ class wsHTML5Compiler {
        }
 
        public function copyLinkDir($source, $dest) {
-               `cp -a $source $this->vdir/$dest`;
+               $this->vdir->copyDirectory($source, $dest);
        }
 
        public function simpleCopyLinkFile($source, $dest, $addVdir = true) {
                if ($addVdir) {
-                       $dest = $this->vdir . '/' . $dest;
-               }
-               if (!file_exists(dirname($dest))) {
-                       mkdir(dirname($dest), 0777, true);
-               }
-               if (file_exists($dest) && filemtime($dest) >= filemtime($source) && filesize($dest) == filesize($source)) {
-                       return;
+                       $dest = $dest;
                }
 
-               $this->copy($source, $dest);
+               $this->vdir->copy($source, $dest);
        }
 
        public function copyLinkFile($source, $dest, $video = false) {
@@ -1633,7 +1514,7 @@ class wsHTML5Compiler {
                foreach ($source as $so) {
                        $s = $origDir . $so;
                        if (file_exists($s)) {
-                               $d = $this->vdir . '/' . $dest . '/' . $so;
+                               $d = $dest . '/' . $so;
                                $this->simpleCopyLinkFile($s, $d, false);
                        }
                }
index e919b7476d469fb09b2aa3073e9fd97da3ddeda3..548c918e49c021e9c8cbebe5edb37d504fd3eda6 100644 (file)
@@ -221,13 +221,15 @@ class wsHTML5Link {
                $this->compiler->copyLinkFile($file, 'data/links/', $video);
        }
 
-       public function copyExternalDir($dir) {
-               $this->compiler->copyLinkDir($dir, 'data/links');
+       public function copyExternalDir($dir, $dest = 'data/links') {
+               $this->compiler->copyLinkDir($dir, $dest);
        }
 
        public function unzipFile($file, $moveAssets = false) {
                $fdir = 'data/links/' . $file;
-               $dir = $this->compiler->vdir . '/' . $fdir;
+
+               $tmp = CubeIT_Files::tmpdir();
+               $dir = $tmp . '/' . $fdir;
                if (file_exists($dir) && is_file($dir)) {
                        unlink($dir);
                }
@@ -262,8 +264,8 @@ class wsHTML5Link {
                }
 
                $css = '#l_' . $this->id . '{';
-               $css .= 'left:' . $this->left * $this->getCssScale() . 'px;top:' . $this->top * $this->getCssScale() . 'px;';
-               $css .= 'width:' . $this->width * $this->getCssScale() . 'px;height:' . $this->height * $this->getCssScale() . 'px;';
+               $css .= 'left:' . round($this->left * $this->getCssScale()) . 'px;top:' . round($this->top * $this->getCssScale()) . 'px;';
+               $css .= 'width:' . round($this->width * $this->getCssScale()) . 'px;height:' . round($this->height * $this->getCssScale()) . 'px;';
                $origin = false;
                if ($this->rot) {
                        $css .= wsHTML5::writeCSSUA('transform', 'rotate(' . $this->rot . 'deg)');
@@ -511,7 +513,7 @@ class internalLink extends normalLink {
 
 class videoLink extends wsHTML5Link {
        public static function addVideoJS($compiler) {
-               $compiler->addJs('js/libs/videojs/video.js');
+               $compiler->addJs('js/libs/videojs/video.min.js');
                $compiler->addLess('videojs/videojs');
        }
 
@@ -820,21 +822,18 @@ class htmlMultimediaLink extends wsHTML5Link {
                        if ($ext == 'oam') {
                                $d = $this->unzipFile($this->alternative, true);
                                $this->_config = $this->getConfigOAM($d['dir']);
-                               $this->copyExternalDir($d['dir']);
+                               $this->copyExternalDir($d['dir'], $d['fdir']);
                        } elseif ($ext == 'zip') {
                                $d = $this->unzipFile($this->alternative, false);
                                $this->_config = $this->getConfigZIP($d['dir']);
-                               $this->copyExternalDir($d['dir']);
+                               $this->copyExternalDir($d['dir'], $d['fdir']);
                        } elseif ($ext == 'html') {
                                $fdir = 'data/links';
-                               $dir = $this->compiler->vdir . '/' . $fdir;
+                               $dir = $fdir;
 
                                $d = array('fdir' => $fdir, 'dir' => $dir);
-                               if (!file_exists($d['dir'])) {
-                                       fb($d['dir']);
-                                       mkdir($d['dir'], 0777, true);
-                               }
-                               copy($this->compiler->wdir . '/' . $this->alternative, $d['dir'] . '/' . $this->alternative);
+
+                               $this->compiler->vdir->copy($this->compiler->wdir . '/' . $this->alternative, $d['dir'] . '/' . $this->alternative);
                                $this->_config = $this->getConfigHTML($d['dir'], $this->alternative);
                                $this->copyExternalFile($d['dir'] . '/' . $this->alternative);
                        }
@@ -1227,6 +1226,8 @@ class zoomLink extends normalLink {
                // Data attributes
                $attributes = [
                        'maxzoom' => $this->to,
+                       'width' => round($this->width),
+                       'height' => round($this->height)
                ];
 
                // Set data attributes
@@ -1240,26 +1241,47 @@ class zoomLink extends normalLink {
        }
 
        public function generateImage() {
-               $left = CubeIT_Files::tempnam();
+
+               $maxzoom = ((int)$this->to !== 0) ? $this->to : 2; // Max zoom level might not always be set in the link editor
+
+               // TODO: Consider generating higher-res images (eg. 2x) for HiDPI screens. Maybe some extra optimisations can be done on the larger images...
+
+               $extractOptions = [
+                       // The Poppler::extractArea function accepts a resolution setting and uses that to determine the
+                       // scale factor on the extracted images. It does so by dividing by 72, so we can pass our own scale
+                       // factor by setting the resolution to 72 * $maxzoom
+                       'resolution' => 72 * $maxzoom
+               ];
+
+               // Round all link co-ordinates because there seems to be a problem with the the Workshop link editor
+               // where link "left" values (and maybe others) change fractionally upon saves. This causes problems later when
+               // extracting the zoom images from the PDF because it causes a cache-miss and the images are regenerated again.
+               $x = round($this->left);
+               $y = round($this->top);
+               $w = round($this->width);
+               $h = round($this->height);
+               $bookwidth = round($this->compiler->book->parametres->width);
+
+               //error_log("--- Book Width: $bookwidth ---");
 
                $p = wsDAOBook::getDocumentPage($this->compiler->book_id, $this->page);
                $pdfpath = wsDocument::getDir($p['document_id']) . 'original.pdf';
+
+               $left = CubeIT_Files::tempnam();
                $leftfile = CubeIT_CommandLine_Poppler::extractArea($pdfpath,
                        $p['document_page'],
-                       array('x' => $this->left, 'y' => $this->top, 'width' => $this->width, 'height' => $this->height),
-                       $left, array(), WS_CACHE . '/zoomarea/' . $this->compiler->book_id . '/');
-
-               $bookwidth = $this->compiler->book->parametres->width;
+                       array('x' => $x, 'y' => $y, 'width' => $w, 'height' => $h),
+                       $left, $extractOptions, WS_CACHE . '/zoomarea/' . $this->compiler->book_id . '/');
 
-               if (($this->left + $this->width) > $bookwidth) {
+               if (($x + $w) > $bookwidth) {
                        $p = wsDAOBook::getDocumentPage($this->compiler->book_id, $this->page + 1);
                        $pdfpath = wsDocument::getDir($p['document_id']) . 'original.pdf';
-                       $diff = ($this->width + $this->left) - $bookwidth;
+                       $diff = ($w + $x) - $bookwidth;
                        $right = CubeIT_Files::tempnam();
                        $rightfile = CubeIT_CommandLine_Poppler::extractArea($pdfpath,
                                $p['document_page'],
-                               array('x' => 0, 'y' => $this->top, 'width' => $diff, 'height' => $this->height),
-                               $right, array(), WS_CACHE . '/zoomarea/' . $this->compiler->book_id . '/');
+                               array('x' => 0, 'y' => $y, 'width' => $diff, 'height' => $h),
+                               $right, $extractOptions, WS_CACHE . '/zoomarea/' . $this->compiler->book_id . '/');
 
                        $both = CubeIT_Files::tempnam() . '.jpg';
                        CubeIT_CommandLine_Imagemagick::append(array($leftfile, $rightfile), $both, 'horizontal');
@@ -1272,8 +1294,8 @@ class zoomLink extends normalLink {
                // Perform tidy up and delete temporary files if they exist
                $files_to_delete = ['left', 'leftfile', 'right', 'rightfile', 'both'];
                foreach ($files_to_delete as $file) {
-                       if (isset($$file) && file_exists($$file)) {
-                               unlink($$file);
+                       if (isset($$file)) {
+                               $this->compiler->vdir->addTemp($$file);
                        }
                }
        }