From 9e9067f2bf0dadec4adad95be873acde84e55415 Mon Sep 17 00:00:00 2001 From: Valery Letroye Date: Thu, 2 Nov 2017 16:23:16 +0100 Subject: [PATCH 1/7] Fix issue with 'ESC' messing fullscreen mode in slideshow mode --- src/js/slideshow.js | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/js/slideshow.js b/src/js/slideshow.js index 18bfcdf..5c9076e 100644 --- a/src/js/slideshow.js +++ b/src/js/slideshow.js @@ -42,7 +42,7 @@ function toggleFullScreen() { var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen; var cancelFullScreen = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen; - if(!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement && !doc.msFullscreenElement) { + if(!isFullScreen()) { requestFullScreen.call(docEl); } else { @@ -50,16 +50,22 @@ function toggleFullScreen() { } } +function isFullScreen() { + var doc = window.document; + return (doc.fullscreenElement || doc.mozFullScreenElement || doc.webkitFullscreenElement || doc.msFullscreenElement); +} + function start_slideshow(){ slideshow_status = 1; - timer = setInterval('run_slideshow()',7000); + timer = setInterval('run_slideshow()',3000); $(".image_panel").css("position","fixed"); $(".image_panel").css("z-index",5000); $(".image_panel").animate({bottom:'0'},200); hide_links(); - toggleFullScreen(); - + if(!isFullScreen()) { + toggleFullScreen(); + } } function play_pause_slideshow(){ @@ -71,7 +77,8 @@ function play_pause_slideshow(){ } function play_slideshow(){ - start_slideshow(); + slideshow_status = 1; + timer = setInterval('run_slideshow()',3000); $("#pause").show(); $("#play").hide(); } @@ -90,7 +97,10 @@ function stop_slideshow(){ $(".image_panel").css("position","absolute"); $(".image_panel").css("z-index",50); show_links(); - toggleFullScreen(); + + if(isFullScreen()) { + toggleFullScreen(); + } } function init_slideshow_panel(){ @@ -145,5 +155,5 @@ function hide_links(){ }else{ $('#image_bar #play').show(); $('#image_bar #pause').hide(); - } + } } From 7c9e310e4b6ffd9b6ced6c3ea885ef271ca5cd6a Mon Sep 17 00:00:00 2001 From: Valery Letroye Date: Thu, 2 Nov 2017 16:24:51 +0100 Subject: [PATCH 2/7] Open image in fullscreen instead of page --- src/classes/ImageBar.php | 12 +++++------- src/js/image_panel.js | 3 +++ src/js/slideshow.js | 23 ++++++++++++++++++++++- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/classes/ImageBar.php b/src/classes/ImageBar.php index 1da69c2..41028b6 100644 --- a/src/classes/ImageBar.php +++ b/src/classes/ImageBar.php @@ -71,10 +71,10 @@ public function __construct($fs=false){ $this->buttons['back'] = "?f=".urlencode(File::a2r(dirname(CurrentUser::$path))); $this->awesome['back'] = ""; - if(!Settings::$nodownload){ - $this->buttons['img'] = "?t=Big&f=".$file; - $this->awesome['img'] = ""; + $this->buttons['img'] = "?f=".$file; + $this->awesome['img'] = ""; + if(!Settings::$nodownload){ $this->buttons['get'] = "?t=BDl&f=".$file; $this->awesome['get'] = ""; } @@ -87,9 +87,6 @@ public function __construct($fs=false){ $this->awesome['pshere'] = ""; } - $this->buttons['next'] = "?p=n&f=".$file; - $this->awesome['next'] = ""; - $this->buttons['pause'] = "?f=".$file; $this->awesome['pause'] = ""; @@ -99,7 +96,8 @@ public function __construct($fs=false){ $this->buttons['stop'] = "?f=".$file; $this->awesome['stop'] = ""; - + $this->buttons['next'] = "?p=n&f=".$file; + $this->awesome['next'] = ""; } diff --git a/src/js/image_panel.js b/src/js/image_panel.js index 1fc5a37..156efa1 100644 --- a/src/js/image_panel.js +++ b/src/js/image_panel.js @@ -81,6 +81,9 @@ function init_image_panel(){ // On clicking NEXT $("#image_bar #next a").click(function(){ + if(slideshow_status == 1){ + clearInterval(timer); + } var curr_select = $(".linear_panel .selected"); var new_select = curr_select.next(); diff --git a/src/js/slideshow.js b/src/js/slideshow.js index 5c9076e..6140f85 100644 --- a/src/js/slideshow.js +++ b/src/js/slideshow.js @@ -55,6 +55,18 @@ function isFullScreen() { return (doc.fullscreenElement || doc.mozFullScreenElement || doc.webkitFullscreenElement || doc.msFullscreenElement); } +function show_image(){ + slideshow_status = -1; + $(".image_panel").css("position","fixed"); + $(".image_panel").css("z-index",5000); + $(".image_panel").animate({bottom:'0'},200); + hide_links(); + + if(!isFullScreen()) { + toggleFullScreen(); + } +} + function start_slideshow(){ slideshow_status = 1; timer = setInterval('run_slideshow()',3000); @@ -108,6 +120,12 @@ function init_slideshow_panel(){ $("#image_bar #play").hide(); $("#image_bar #stop").hide(); + $("#img").unbind(); + $("#img").click(function(){ + show_image(); + return false; + }); + $("#slideshow").unbind(); $("#slideshow").click(function(){ start_slideshow(); @@ -149,7 +167,10 @@ function hide_links(){ $('#image_bar #get').hide(); $('#image_bar #slideshow').hide(); $('#image_bar #stop').show(); - if(slideshow_status == 1){ + if(slideshow_status == -1){ + $('#image_bar #play').hide(); + $('#image_bar #pause').hide(); + } else if(slideshow_status == 1){ $('#image_bar #pause').show(); $('#image_bar #play').hide(); }else{ From 74330587ec04579f34703a3cf020f7088f77e4a0 Mon Sep 17 00:00:00 2001 From: Valery Letroye Date: Thu, 2 Nov 2017 16:25:58 +0100 Subject: [PATCH 3/7] Fix Cycling in Slideshow mode --- src/js/image_panel.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/image_panel.js b/src/js/image_panel.js index 156efa1..99f5d12 100644 --- a/src/js/image_panel.js +++ b/src/js/image_panel.js @@ -92,7 +92,7 @@ function init_image_panel(){ } if(! new_select.length){ - new_select = $(".linear_panel .item").last(); + new_select = $(".linear_panel .item").first(); } new_url = new_select.children("a").attr("href"); @@ -137,7 +137,7 @@ function init_image_panel(){ } if(! new_select.length){ - new_select = $(".linear_panel .item").first(); + new_select = $(".linear_panel .item").last(); } new_url = new_select.children("a").attr("href") From 6c60adbb8135c9407ac2ead045fb446ce2a495ca Mon Sep 17 00:00:00 2001 From: Valery Letroye Date: Thu, 2 Nov 2017 16:58:43 +0100 Subject: [PATCH 4/7] Lazy Loading Images --- src/classes/BoardItem.php | 2 +- src/classes/Page.php | 2 + src/js/image_panel.js | 16 +- src/js/lazy.js | 870 +++++++++++++++++++++++++++++++ user/themes/Default/style.css | 5 + user/themes/Demo/style.css | 5 + user/themes/Vendredi/loading.gif | Bin 0 -> 12832 bytes user/themes/Vendredi/style.css | 7 +- 8 files changed, 903 insertions(+), 4 deletions(-) create mode 100644 src/js/lazy.js create mode 100644 user/themes/Vendredi/loading.gif diff --git a/src/classes/BoardItem.php b/src/classes/BoardItem.php index 1366806..52c3392 100644 --- a/src/classes/BoardItem.php +++ b/src/classes/BoardItem.php @@ -81,7 +81,7 @@ public function toHTML(){ echo "\n"; diff --git a/src/classes/Page.php b/src/classes/Page.php index 20d007f..7b48dd5 100644 --- a/src/classes/Page.php +++ b/src/classes/Page.php @@ -90,6 +90,8 @@ public function header($head_content=NULL){ echo "\n"; echo "\n"; echo "\n"; + + echo "\n"; echo "\n"; echo "\n"; echo "\n"; diff --git a/src/js/image_panel.js b/src/js/image_panel.js index 99f5d12..2b05d15 100644 --- a/src/js/image_panel.js +++ b/src/js/image_panel.js @@ -170,6 +170,14 @@ function init_image_panel(){ } }); + // On scroll + $("#page").scroll(function(){ + instance.update(); + }); + $(".linear_panel").scroll(function(){ + instance.update(); + }); + $(".linear_panel").scrollTo($(".linear_panel .selected")).scrollTo("-="+$(".linear_panel").width()/2); init_comments(); @@ -196,6 +204,12 @@ function init_description(){ }); } -$("document").ready(function(){ +var instance; + +$("document").ready(function(){ + instance = $('.lazy').lazy({ + chainable: false + }); + init_image_panel(); }); \ No newline at end of file diff --git a/src/js/lazy.js b/src/js/lazy.js new file mode 100644 index 0000000..2d30ff3 --- /dev/null +++ b/src/js/lazy.js @@ -0,0 +1,870 @@ +/*! + * jQuery & Zepto Lazy - v1.7.6 + * http://jquery.eisbehr.de/lazy/ + * + * Copyright 2012 - 2017, Daniel 'Eisbehr' Kern + * + * Dual licensed under the MIT and GPL-2.0 licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl-2.0.html + * + * $("img.lazy").lazy(); + */ + +;(function(window, undefined) { + "use strict"; + + // noinspection JSUnresolvedVariable + /** + * library instance - here and not in construct to be shorter in minimization + * @return void + */ + var $ = window.jQuery || window.Zepto, + + /** + * unique plugin instance id counter + * @type {number} + */ + lazyInstanceId = 0, + + /** + * helper to register window load for jQuery 3 + * @type {boolean} + */ + windowLoaded = false; + + /** + * make lazy available to jquery - and make it a bit more case-insensitive :) + * @access public + * @type {function} + * @param {object} settings + * @return void + */ + $.fn.Lazy = $.fn.lazy = function(settings) { + return new LazyPlugin(this, settings); + }; + + /** + * helper to add plugins to lazy prototype configuration + * @access public + * @type {function} + * @param {string|Array} names + * @param {string|Array} [elements] + * @param {function} loader + * @return void + */ + $.Lazy = $.lazy = function(names, elements, loader) { + // make second parameter optional + if( $.isFunction(elements) ) { + loader = elements; + elements = []; + } + + // exit here if parameter is not a callable function + if( !$.isFunction(loader) ) { + return; + } + + // make parameters an array of names to be sure + names = $.isArray(names) ? names : [names]; + elements = $.isArray(elements) ? elements : [elements]; + + var config = LazyPlugin.prototype.config, + forced = config._f || (config._f = {}); + + // add the loader plugin for every name + for( var i = 0, l = names.length; i < l; i++ ) { + if( config[names[i]] === undefined || $.isFunction(config[names[i]]) ) { + config[names[i]] = loader; + } + } + + // add forced elements loader + for( var c = 0, a = elements.length; c < a; c++ ) { + forced[elements[c]] = names[0]; + } + }; + + /** + * contains all logic and the whole element handling + * is packed in a private function outside class to reduce memory usage, because it will not be created on every plugin instance + * @access private + * @type {function} + * @param {LazyPlugin} instance + * @param {object} config + * @param {object|Array} items + * @param {object} events + * @param {string} namespace + * @return void + */ + function _executeLazy(instance, config, items, events, namespace) { + /** + * a helper to trigger the 'onFinishedAll' callback after all other events + * @access private + * @type {number} + */ + var _awaitingAfterLoad = 0, + + /** + * visible content width + * @access private + * @type {number} + */ + _actualWidth = -1, + + /** + * visible content height + * @access private + * @type {number} + */ + _actualHeight = -1, + + /** + * determine possibly detected high pixel density + * @access private + * @type {boolean} + */ + _isRetinaDisplay = false, + + /** + * dictionary entry for better minimization + * @access private + * @type {string} + */ + _afterLoad = "afterLoad", + + /** + * dictionary entry for better minimization + * @access private + * @type {string} + */ + _load = "load", + + /** + * dictionary entry for better minimization + * @access private + * @type {string} + */ + _error = "error", + + /** + * dictionary entry for better minimization + * @access private + * @type {string} + */ + _img = "img", + + /** + * dictionary entry for better minimization + * @access private + * @type {string} + */ + _src = "src", + + /** + * dictionary entry for better minimization + * @access private + * @type {string} + */ + _srcset = "srcset", + + /** + * dictionary entry for better minimization + * @access private + * @type {string} + */ + _sizes = "sizes", + + /** + * dictionary entry for better minimization + * @access private + * @type {string} + */ + _backgroundImage = "background-image"; + + /** + * initialize plugin + * bind loading to events or set delay time to load all items at once + * @access private + * @return void + */ + function _initialize() { + // detect actual device pixel ratio + // noinspection JSUnresolvedVariable + _isRetinaDisplay = window.devicePixelRatio > 1; + + // prepare all initial items + items = _prepareItems(items); + + // if delay time is set load all items at once after delay time + if( config.delay >= 0 ) { + setTimeout(function() { + _lazyLoadItems(true); + }, config.delay); + } + + // if no delay is set or combine usage is active bind events + if( config.delay < 0 || config.combined ) { + // create unique event function + events.e = _throttle(config.throttle, function(event) { + // reset detected window size on resize event + if( event.type === "resize" ) { + _actualWidth = _actualHeight = -1; + } + + // execute 'lazy magic' + _lazyLoadItems(event.all); + }); + + // create function to add new items to instance + events.a = function(additionalItems) { + additionalItems = _prepareItems(additionalItems); + items.push.apply(items, additionalItems); + }; + + // create function to get all instance items left + events.g = function() { + // filter loaded items before return in case internal filter was not running until now + return (items = $(items).filter(function() { + return !$(this).data(config.loadedName); + })); + }; + + // create function to force loading elements + events.f = function(forcedItems) { + for( var i = 0; i < forcedItems.length; i++ ) { + // only handle item if available in current instance + // use a compare function, because Zepto can't handle object parameter for filter + // var item = items.filter(forcedItems[i]); + /* jshint loopfunc: true */ + var item = items.filter(function() { + return this === forcedItems[i]; + }); + + if( item.length ) { + _lazyLoadItems(false, item); + } + } + }; + + // load initial items + _lazyLoadItems(); + + // bind lazy load functions to scroll and resize event + // noinspection JSUnresolvedVariable + $(config.appendScroll).on("scroll." + namespace + " resize." + namespace, events.e); + } + } + + /** + * prepare items before handle them + * @access private + * @param {Array|object|jQuery} items + * @return {Array|object|jQuery} + */ + function _prepareItems(items) { + // fetch used configurations before loops + var defaultImage = config.defaultImage, + placeholder = config.placeholder, + imageBase = config.imageBase, + srcsetAttribute = config.srcsetAttribute, + loaderAttribute = config.loaderAttribute, + forcedTags = config._f || {}; + + // filter items and only add those who not handled yet and got needed attributes available + items = $(items).filter(function() { + var element = $(this), + tag = _getElementTagName(this); + + return !element.data(config.handledName) && + (element.attr(config.attribute) || element.attr(srcsetAttribute) || element.attr(loaderAttribute) || forcedTags[tag] !== undefined); + }) + + // append plugin instance to all elements + .data("plugin_" + config.name, instance); + + for( var i = 0, l = items.length; i < l; i++ ) { + var element = $(items[i]), + tag = _getElementTagName(items[i]), + elementImageBase = element.attr(config.imageBaseAttribute) || imageBase; + + // generate and update source set if an image base is set + if( tag === _img && elementImageBase && element.attr(srcsetAttribute) ) { + element.attr(srcsetAttribute, _getCorrectedSrcSet(element.attr(srcsetAttribute), elementImageBase)); + } + + // add loader to forced element types + if( forcedTags[tag] !== undefined && !element.attr(loaderAttribute) ) { + element.attr(loaderAttribute, forcedTags[tag]); + } + + // set default image on every element without source + if( tag === _img && defaultImage && !element.attr(_src) ) { + element.attr(_src, defaultImage); + } + + // set placeholder on every element without background image + else if( tag !== _img && placeholder && (!element.css(_backgroundImage) || element.css(_backgroundImage) === "none") ) { + element.css(_backgroundImage, "url('" + placeholder + "')"); + } + } + + return items; + } + + /** + * the 'lazy magic' - check all items + * @access private + * @param {boolean} [allItems] + * @param {object} [forced] + * @return void + */ + function _lazyLoadItems(allItems, forced) { + // skip if no items where left + if( !items.length ) { + // destroy instance if option is enabled + if( config.autoDestroy ) { + // noinspection JSUnresolvedFunction + instance.destroy(); + } + + return; + } + + var elements = forced || items, + loadTriggered = false, + imageBase = config.imageBase || "", + srcsetAttribute = config.srcsetAttribute, + handledName = config.handledName; + + // loop all available items + for( var i = 0; i < elements.length; i++ ) { + // item is at least in loadable area + if( allItems || forced || _isInLoadableArea(elements[i]) ) { + var element = $(elements[i]), + tag = _getElementTagName(elements[i]), + attribute = element.attr(config.attribute), + elementImageBase = element.attr(config.imageBaseAttribute) || imageBase, + customLoader = element.attr(config.loaderAttribute); + + // is not already handled + if( !element.data(handledName) && + // and is visible or visibility doesn't matter + (!config.visibleOnly || element.is(":visible")) && ( + // and image source or source set attribute is available + (attribute || element.attr(srcsetAttribute)) && ( + // and is image tag where attribute is not equal source or source set + (tag === _img && (elementImageBase + attribute !== element.attr(_src) || element.attr(srcsetAttribute) !== element.attr(_srcset))) || + // or is non image tag where attribute is not equal background + (tag !== _img && elementImageBase + attribute !== element.css(_backgroundImage)) + ) || + // or custom loader is available + customLoader )) + { + // mark element always as handled as this point to prevent double handling + loadTriggered = true; + element.data(handledName, true); + + // load item + _handleItem(element, tag, elementImageBase, customLoader); + } + } + } + + // when something was loaded remove them from remaining items + if( loadTriggered ) { + items = $(items).filter(function() { + return !$(this).data(handledName); + }); + } + } + + /** + * load the given element the lazy way + * @access private + * @param {object} element + * @param {string} tag + * @param {string} imageBase + * @param {function} [customLoader] + * @return void + */ + function _handleItem(element, tag, imageBase, customLoader) { + // increment count of items waiting for after load + ++_awaitingAfterLoad; + + // extended error callback for correct 'onFinishedAll' handling + var errorCallback = function() { + _triggerCallback("onError", element); + _reduceAwaiting(); + + // prevent further callback calls + errorCallback = $.noop; + }; + + // trigger function before loading image + _triggerCallback("beforeLoad", element); + + // fetch all double used data here for better code minimization + var srcAttribute = config.attribute, + srcsetAttribute = config.srcsetAttribute, + sizesAttribute = config.sizesAttribute, + retinaAttribute = config.retinaAttribute, + removeAttribute = config.removeAttribute, + loadedName = config.loadedName, + elementRetina = element.attr(retinaAttribute); + + // handle custom loader + if( customLoader ) { + // on load callback + var loadCallback = function() { + // remove attribute from element + if( removeAttribute ) { + element.removeAttr(config.loaderAttribute); + } + + // mark element as loaded + element.data(loadedName, true); + + // call after load event + _triggerCallback(_afterLoad, element); + + // remove item from waiting queue and possibly trigger finished event + // it's needed to be asynchronous to run after filter was in _lazyLoadItems + setTimeout(_reduceAwaiting, 1); + + // prevent further callback calls + loadCallback = $.noop; + }; + + // bind error event to trigger callback and reduce waiting amount + element.off(_error).one(_error, errorCallback) + + // bind after load callback to element + .one(_load, loadCallback); + + // trigger custom loader and handle response + if( !_triggerCallback(customLoader, element, function(response) { + if( response ) { + element.off(_load); + loadCallback(); + } + else { + element.off(_error); + errorCallback(); + } + })) element.trigger(_error); + } + + // handle images + else { + // create image object + var imageObj = $(new Image()); + + // bind error event to trigger callback and reduce waiting amount + imageObj.one(_error, errorCallback) + + // bind after load callback to image + .one(_load, function() { + // remove element from view + element.hide(); + + // set image back to element + // do it as single 'attr' calls, to be sure 'src' is set after 'srcset' + if( tag === _img ) { + element.attr(_sizes, imageObj.attr(_sizes)); + element.attr(_srcset, imageObj.attr(_srcset)); + element.attr(_src, imageObj.attr(_src)); + } + else { + element.css(_backgroundImage, "url('" + imageObj.attr(_src) + "')"); + } + + // bring it back with some effect! + element[config.effect](config.effectTime); + + // remove attribute from element + if( removeAttribute ) { + element.removeAttr(srcAttribute + " " + srcsetAttribute + " " + retinaAttribute + " " + config.imageBaseAttribute); + + // only remove 'sizes' attribute, if it was a custom one + if( sizesAttribute !== _sizes ) { + element.removeAttr(sizesAttribute); + } + } + + // mark element as loaded + element.data(loadedName, true); + + // call after load event + _triggerCallback(_afterLoad, element); + + // cleanup image object + imageObj.remove(); + + // remove item from waiting queue and possibly trigger finished event + _reduceAwaiting(); + }); + + // set sources + // do it as single 'attr' calls, to be sure 'src' is set after 'srcset' + var imageSrc = (_isRetinaDisplay && elementRetina ? elementRetina : element.attr(srcAttribute)) || ""; + imageObj.attr(_sizes, element.attr(sizesAttribute)); + imageObj.attr(_srcset, element.attr(srcsetAttribute)); + imageObj.attr(_src, imageSrc ? imageBase + imageSrc : null); + + // call after load even on cached image + imageObj.complete && imageObj.trigger(_load); // jshint ignore : line + } + } + + /** + * check if the given element is inside the current viewport or threshold + * @access private + * @param {object} element + * @return {boolean} + */ + function _isInLoadableArea(element) { + var elementBound = element.getBoundingClientRect(), + direction = config.scrollDirection, + threshold = config.threshold, + vertical = // check if element is in loadable area from top + ((_getActualHeight() + threshold) > elementBound.top) && + // check if element is even in loadable are from bottom + (-threshold < elementBound.bottom), + horizontal = // check if element is in loadable area from left + ((_getActualWidth() + threshold) > elementBound.left) && + // check if element is even in loadable area from right + (-threshold < elementBound.right); + + if( direction === "vertical" ) { + return vertical; + } + else if( direction === "horizontal" ) { + return horizontal; + } + + return vertical && horizontal; + } + + /** + * receive the current viewed width of the browser + * @access private + * @return {number} + */ + function _getActualWidth() { + return _actualWidth >= 0 ? _actualWidth : (_actualWidth = $(window).width()); + } + + /** + * receive the current viewed height of the browser + * @access private + * @return {number} + */ + function _getActualHeight() { + return _actualHeight >= 0 ? _actualHeight : (_actualHeight = $(window).height()); + } + + /** + * get lowercase tag name of an element + * @access private + * @param {object} element + * @returns {string} + */ + function _getElementTagName(element) { + return element.tagName.toLowerCase(); + } + + /** + * prepend image base to all srcset entries + * @access private + * @param {string} srcset + * @param {string} imageBase + * @returns {string} + */ + function _getCorrectedSrcSet(srcset, imageBase) { + if( imageBase ) { + // trim, remove unnecessary spaces and split entries + var entries = srcset.split(","); + srcset = ""; + + for( var i = 0, l = entries.length; i < l; i++ ) { + srcset += imageBase + entries[i].trim() + (i !== l - 1 ? "," : ""); + } + } + + return srcset; + } + + /** + * helper function to throttle down event triggering + * @access private + * @param {number} delay + * @param {function} callback + * @return {function} + */ + function _throttle(delay, callback) { + var timeout, + lastExecute = 0; + + return function(event, ignoreThrottle) { + var elapsed = +new Date() - lastExecute; + + function run() { + lastExecute = +new Date(); + // noinspection JSUnresolvedFunction + callback.call(instance, event); + } + + timeout && clearTimeout(timeout); // jshint ignore : line + + if( elapsed > delay || !config.enableThrottle || ignoreThrottle ) { + run(); + } + else { + timeout = setTimeout(run, delay - elapsed); + } + }; + } + + /** + * reduce count of awaiting elements to 'afterLoad' event and fire 'onFinishedAll' if reached zero + * @access private + * @return void + */ + function _reduceAwaiting() { + --_awaitingAfterLoad; + + // if no items were left trigger finished event + if( !items.length && !_awaitingAfterLoad ) { + _triggerCallback("onFinishedAll"); + } + } + + /** + * single implementation to handle callbacks, pass element and set 'this' to current instance + * @access private + * @param {string|function} callback + * @param {object} [element] + * @param {*} [args] + * @return {boolean} + */ + function _triggerCallback(callback, element, args) { + if( (callback = config[callback]) ) { + // jQuery's internal '$(arguments).slice(1)' are causing problems at least on old iPads + // below is shorthand of 'Array.prototype.slice.call(arguments, 1)' + callback.apply(instance, [].slice.call(arguments, 1)); + return true; + } + + return false; + } + + // if event driven or window is already loaded don't wait for page loading + if( config.bind === "event" || windowLoaded ) { + _initialize(); + } + + // otherwise load initial items and start lazy after page load + else { + // noinspection JSUnresolvedVariable + $(window).on(_load + "." + namespace, _initialize); + } + } + + /** + * lazy plugin class constructor + * @constructor + * @access private + * @param {object} elements + * @param {object} settings + * @return {object|LazyPlugin} + */ + function LazyPlugin(elements, settings) { + /** + * this lazy plugin instance + * @access private + * @type {object|LazyPlugin|LazyPlugin.prototype} + */ + var _instance = this, + + /** + * this lazy plugin instance configuration + * @access private + * @type {object} + */ + _config = $.extend({}, _instance.config, settings), + + /** + * instance generated event executed on container scroll or resize + * packed in an object to be referenceable and short named because properties will not be minified + * @access private + * @type {object} + */ + _events = {}, + + /** + * unique namespace for instance related events + * @access private + * @type {string} + */ + _namespace = _config.name + "-" + (++lazyInstanceId); + + // noinspection JSUndefinedPropertyAssignment + /** + * wrapper to get or set an entry from plugin instance configuration + * much smaller on minify as direct access + * @access public + * @type {function} + * @param {string} entryName + * @param {*} [value] + * @return {LazyPlugin|*} + */ + _instance.config = function(entryName, value) { + if( value === undefined ) { + return _config[entryName]; + } + + _config[entryName] = value; + return _instance; + }; + + // noinspection JSUndefinedPropertyAssignment + /** + * add additional items to current instance + * @access public + * @param {Array|object|string} items + * @return {LazyPlugin} + */ + _instance.addItems = function(items) { + _events.a && _events.a($.type(items) === "string" ? $(items) : items); // jshint ignore : line + return _instance; + }; + + // noinspection JSUndefinedPropertyAssignment + /** + * get all left items of this instance + * @access public + * @returns {object} + */ + _instance.getItems = function() { + return _events.g ? _events.g() : {}; + }; + + // noinspection JSUndefinedPropertyAssignment + /** + * force lazy to load all items in loadable area right now + * by default without throttle + * @access public + * @type {function} + * @param {boolean} [useThrottle] + * @return {LazyPlugin} + */ + _instance.update = function(useThrottle) { + _events.e && _events.e({}, !useThrottle); // jshint ignore : line + return _instance; + }; + + // noinspection JSUndefinedPropertyAssignment + /** + * force element(s) to load directly, ignoring the viewport + * @access public + * @param {Array|object|string} items + * @return {LazyPlugin} + */ + _instance.force = function(items) { + _events.f && _events.f($.type(items) === "string" ? $(items) : items); // jshint ignore : line + return _instance; + }; + + // noinspection JSUndefinedPropertyAssignment + /** + * force lazy to load all available items right now + * this call ignores throttling + * @access public + * @type {function} + * @return {LazyPlugin} + */ + _instance.loadAll = function() { + _events.e && _events.e({all: true}, true); // jshint ignore : line + return _instance; + }; + + // noinspection JSUndefinedPropertyAssignment + /** + * destroy this plugin instance + * @access public + * @type {function} + * @return undefined + */ + _instance.destroy = function() { + // unbind instance generated events + // noinspection JSUnresolvedFunction, JSUnresolvedVariable + $(_config.appendScroll).off("." + _namespace, _events.e); + // noinspection JSUnresolvedVariable + $(window).off("." + _namespace); + + // clear events + _events = {}; + + return undefined; + }; + + // start using lazy and return all elements to be chainable or instance for further use + // noinspection JSUnresolvedVariable + _executeLazy(_instance, _config, elements, _events, _namespace); + return _config.chainable ? elements : _instance; + } + + /** + * settings and configuration data + * @access public + * @type {object} + */ + LazyPlugin.prototype.config = { + // general + name : "lazy", + chainable : true, + autoDestroy : true, + bind : "load", + threshold : 500, + visibleOnly : false, + appendScroll : window, + scrollDirection : "both", + imageBase : null, + defaultImage : "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==", + placeholder : null, + delay : -1, + combined : false, + + // attributes + attribute : "data-src", + srcsetAttribute : "data-srcset", + sizesAttribute : "data-sizes", + retinaAttribute : "data-retina", + loaderAttribute : "data-loader", + imageBaseAttribute : "data-imagebase", + removeAttribute : true, + handledName : "handled", + loadedName : "loaded", + + // effect + effect : "show", + effectTime : 0, + + // throttle + enableThrottle : true, + throttle : 250, + + // callbacks + beforeLoad : undefined, + afterLoad : undefined, + onError : undefined, + onFinishedAll : undefined + }; + + // register window load event globally to prevent not loading elements + // since jQuery 3.X ready state is fully async and may be executed after 'load' + $(window).on("load", function() { + windowLoaded = true; + }); +})(window); \ No newline at end of file diff --git a/user/themes/Default/style.css b/user/themes/Default/style.css index e69de29..bb429e2 100644 --- a/user/themes/Default/style.css +++ b/user/themes/Default/style.css @@ -0,0 +1,5 @@ +img.lazy { + background-image: url('loading.gif'); + background-repeat: no-repeat; + background-position: 50% 50%; +} \ No newline at end of file diff --git a/user/themes/Demo/style.css b/user/themes/Demo/style.css index 37b8b92..89f0a0b 100644 --- a/user/themes/Demo/style.css +++ b/user/themes/Demo/style.css @@ -21,4 +21,9 @@ h3{ #menuright-header{ background: black; +} +img.lazy { + background-image: url('loading.gif'); + background-repeat: no-repeat; + background-position: 50% 50%; } \ No newline at end of file diff --git a/user/themes/Vendredi/loading.gif b/user/themes/Vendredi/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..8b6be2d00d91deaea41fcd01c9349dfc318344e8 GIT binary patch literal 12832 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 G(*FR2jkD1J literal 0 HcmV?d00001 diff --git a/user/themes/Vendredi/style.css b/user/themes/Vendredi/style.css index 1b1eab1..349893b 100644 --- a/user/themes/Vendredi/style.css +++ b/user/themes/Vendredi/style.css @@ -55,5 +55,8 @@ li.currentSelected > a:hover { color: #333; } - - +img.lazy { + background-image: url('loading.gif'); + background-repeat: no-repeat; + background-position: 50% 50%; +} \ No newline at end of file From 60eb1e8fb848f7eeb5c29e081d755f8ad765932b Mon Sep 17 00:00:00 2001 From: Valery Letroye Date: Thu, 2 Nov 2017 17:00:50 +0100 Subject: [PATCH 5/7] Wait for Images and smooth transition --- src/classes/Image.php | 2 +- src/classes/Page.php | 2 +- src/js/image_panel.js | 27 ++++++++++++++++++++++----- src/js/waitforimages.js | 2 ++ user/themes/Vendredi/style.css | 4 ++++ 5 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 src/js/waitforimages.js diff --git a/src/classes/Image.php b/src/classes/Image.php index d656cfa..33ee989 100644 --- a/src/classes/Image.php +++ b/src/classes/Image.php @@ -111,7 +111,7 @@ public function toHTML(){ echo " background: black url(\"?t=".$this->t."&f=$this->fileweb\") no-repeat center center;"; echo " background-size: contain;"; echo " -moz-background-size: contain;"; - echo " height:100%;"; + echo " height:100%; display: none;"; echo "';>"; echo ""; diff --git a/src/classes/Page.php b/src/classes/Page.php index 7b48dd5..cb21b23 100644 --- a/src/classes/Page.php +++ b/src/classes/Page.php @@ -90,7 +90,7 @@ public function header($head_content=NULL){ echo "\n"; echo "\n"; echo "\n"; - + echo "\n"; echo "\n"; echo "\n"; echo "\n"; diff --git a/src/js/image_panel.js b/src/js/image_panel.js index 2b05d15..dddaf53 100644 --- a/src/js/image_panel.js +++ b/src/js/image_panel.js @@ -43,6 +43,14 @@ function init_image_panel(){ url = url.slice(url.indexOf('f=')); $('.linear_panel a[href$="' + url + '"]').parent().addClass("selected"); } + + $('#image_big').waitForImages().done(function() { + $("#image_big").fadeIn('slow', function(){ + if(slideshow_status == 1){ + timer = setInterval('run_slideshow()',3000); + } + }); + }); // On clicking the bigimage $("#bigimage a, #image_bar #back").click(function(){ @@ -68,9 +76,12 @@ function init_image_panel(){ $(".linear_panel .selected").removeClass("selected"); $(this).parent().addClass("selected"); update_url($(this).attr("href"),"Image"); - - $(".image_panel").load($(this).attr("href")+"&j=Pan",function(){ - init_image_panel(); + + var elem = $(this); + $("#image_big").fadeOut('normal', function(){ + $(".image_panel").load(elem.attr("href")+"&j=Pan",function(){ + init_image_panel(); + }); }); // Load infos @@ -97,7 +108,9 @@ function init_image_panel(){ new_url = new_select.children("a").attr("href"); - $(".image_panel").load(new_url + "&j=Pan",function(){ + var elem = $(this); + $("#image_big").fadeOut('normal', function(){ + $(".image_panel").load(new_url + "&j=Pan",function(){ update_url(new_url,"Image"); curr_select.removeClass("selected"); @@ -108,6 +121,7 @@ function init_image_panel(){ if(slideshow_status != 0){ hide_links(); } + }); }); // Load infos @@ -142,7 +156,9 @@ function init_image_panel(){ new_url = new_select.children("a").attr("href") - $(".image_panel").load(new_url+"&j=Pan",function(){ + var elem = $(this); + $("#image_big").fadeOut('normal', function(){ + $(".image_panel").load(new_url+"&j=Pan",function(){ update_url(new_url,"Image"); @@ -154,6 +170,7 @@ function init_image_panel(){ if(slideshow_status != 0){ hide_links(); } + }); }); // Load infos diff --git a/src/js/waitforimages.js b/src/js/waitforimages.js new file mode 100644 index 0000000..d61d85a --- /dev/null +++ b/src/js/waitforimages.js @@ -0,0 +1,2 @@ +/*! waitForImages jQuery Plugin 2017-02-20 */ +!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){var b="waitForImages",c=function(a){return a.srcset&&a.sizes}(new Image);a.waitForImages={hasImageProperties:["backgroundImage","listStyleImage","borderImage","borderCornerImage","cursor"],hasImageAttributes:["srcset"]},a.expr[":"]["has-src"]=function(b){return a(b).is('img[src][src!=""]')},a.expr[":"].uncached=function(b){return!!a(b).is(":has-src")&&!b.complete},a.fn.waitForImages=function(){var d,e,f,g=0,h=0,i=a.Deferred(),j=this,k=[],l=a.waitForImages.hasImageProperties||[],m=a.waitForImages.hasImageAttributes||[],n=/url\(\s*(['"]?)(.*?)\1\s*\)/g;if(a.isPlainObject(arguments[0])?(f=arguments[0].waitForAll,e=arguments[0].each,d=arguments[0].finished):1===arguments.length&&"boolean"===a.type(arguments[0])?f=arguments[0]:(d=arguments[0],e=arguments[1],f=arguments[2]),d=d||a.noop,e=e||a.noop,f=!!f,!a.isFunction(d)||!a.isFunction(e))throw new TypeError("An invalid callback was supplied.");return this.each(function(){var b=a(this);f?b.find("*").addBack().each(function(){var b=a(this);b.is("img:has-src")&&!b.is("[srcset]")&&k.push({src:b.attr("src"),element:b[0]}),a.each(l,function(a,c){var d,e=b.css(c);if(!e)return!0;for(;d=n.exec(e);)k.push({src:d[2],element:b[0]})}),a.each(m,function(a,c){var d=b.attr(c);return!d||void k.push({src:b.attr("src"),srcset:b.attr("srcset"),element:b[0]})})}):b.find("img:has-src").each(function(){k.push({src:this.src,element:this})})}),g=k.length,h=0,0===g&&(d.call(j),i.resolveWith(j)),a.each(k,function(f,k){var l=new Image,m="load."+b+" error."+b;a(l).one(m,function b(c){var f=[h,g,"load"==c.type];if(h++,e.apply(k.element,f),i.notifyWith(k.element,f),a(this).off(m,b),h==g)return d.call(j[0]),i.resolveWith(j[0]),!1}),c&&k.srcset&&(l.srcset=k.srcset,l.sizes=k.sizes),l.src=k.src}),i.promise()}}); \ No newline at end of file diff --git a/user/themes/Vendredi/style.css b/user/themes/Vendredi/style.css index 349893b..130cce3 100644 --- a/user/themes/Vendredi/style.css +++ b/user/themes/Vendredi/style.css @@ -48,6 +48,10 @@ li.currentSelected > a:hover { .pure-g { padding: 2%; } +.pure-g .item { + margin: 3px; +} + .pure-button-primary, .pure-button-selected, a.pure-button-primary, a.pure-button-selected { background-color: #ff0056; } From 4dabf484a70442a4dd16a487dd18b4cba5f16d3e Mon Sep 17 00:00:00 2001 From: Valery Letroye Date: Sat, 11 Nov 2017 14:14:20 +0100 Subject: [PATCH 6/7] Add loading.gif in themes --- user/themes/Default/loading.gif | Bin 0 -> 12832 bytes user/themes/Demo/loading.gif | Bin 0 -> 12832 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 user/themes/Default/loading.gif create mode 100644 user/themes/Demo/loading.gif diff --git a/user/themes/Default/loading.gif b/user/themes/Default/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..8b6be2d00d91deaea41fcd01c9349dfc318344e8 GIT binary patch literal 12832 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 G(*FR2jkD1J literal 0 HcmV?d00001 diff --git a/user/themes/Demo/loading.gif b/user/themes/Demo/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..8b6be2d00d91deaea41fcd01c9349dfc318344e8 GIT binary patch literal 12832 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 G(*FR2jkD1J literal 0 HcmV?d00001 From 11fec88fb9c6d1cec80ea5c22e63ce52493be557 Mon Sep 17 00:00:00 2001 From: Valery Letroye Date: Sat, 11 Nov 2017 14:17:54 +0100 Subject: [PATCH 7/7] Fix Play/Pause issue due to a bug setting multiple timers when navigating prev/next --- src/js/image_panel.js | 5 +-- src/js/slideshow.js | 75 ++++++++++++++++++++++++++++++++----------- 2 files changed, 57 insertions(+), 23 deletions(-) diff --git a/src/js/image_panel.js b/src/js/image_panel.js index dddaf53..30542fd 100644 --- a/src/js/image_panel.js +++ b/src/js/image_panel.js @@ -47,7 +47,7 @@ function init_image_panel(){ $('#image_big').waitForImages().done(function() { $("#image_big").fadeIn('slow', function(){ if(slideshow_status == 1){ - timer = setInterval('run_slideshow()',3000); + play_slideshow(); } }); }); @@ -92,9 +92,6 @@ function init_image_panel(){ // On clicking NEXT $("#image_bar #next a").click(function(){ - if(slideshow_status == 1){ - clearInterval(timer); - } var curr_select = $(".linear_panel .selected"); var new_select = curr_select.next(); diff --git a/src/js/slideshow.js b/src/js/slideshow.js index 6140f85..f261b70 100644 --- a/src/js/slideshow.js +++ b/src/js/slideshow.js @@ -29,11 +29,30 @@ */ var slideshow_status = 0; -var timer = 0; - -function run_slideshow(){ - $("#next a").click(); -} +var timer = { + id : 0, + + make : function ( fun, delay ) { + if ((typeof id !== 'undefined') && (id > 0)) { + clearInterval(id); + } + + id = setInterval.apply( + window, + [ fun, delay ].concat( [].slice.call(arguments, 2) ) + ); + + return id; + }, + + clear : function () { + if (typeof id !== 'undefined') { + var old = id; + id = 0; + return clearInterval( old ); + } + } +}; function toggleFullScreen() { var doc = window.document; @@ -68,16 +87,20 @@ function show_image(){ } function start_slideshow(){ - slideshow_status = 1; - timer = setInterval('run_slideshow()',3000); $(".image_panel").css("position","fixed"); $(".image_panel").css("z-index",5000); $(".image_panel").animate({bottom:'0'},200); - hide_links(); - + if(!isFullScreen()) { toggleFullScreen(); } + + play_slideshow(); +} + +function run_slideshow(){ + timer.clear(); + $("#next a").click(); } function play_pause_slideshow(){ @@ -89,30 +112,33 @@ function play_pause_slideshow(){ } function play_slideshow(){ + //timer = setInterval('run_slideshow()',3000); + timer.make('run_slideshow()',3000); slideshow_status = 1; - timer = setInterval('run_slideshow()',3000); - $("#pause").show(); - $("#play").hide(); + + hide_links(); } function pause_slideshow(){ + timer.clear(); slideshow_status = 2; - clearInterval(timer); - $("#play").show(); - $("#pause").hide(); + + hide_links(); } function stop_slideshow(){ + timer.clear(); slideshow_status = 0; - clearInterval(timer); + $(".image_panel").animate({bottom:'150'},200); $(".image_panel").css("position","absolute"); $(".image_panel").css("z-index",50); - show_links(); if(isFullScreen()) { toggleFullScreen(); } + + show_links(); } function init_slideshow_panel(){ @@ -159,6 +185,8 @@ function show_links(){ $('#image_bar #pause').hide(); $('#image_bar #play').hide(); $('#image_bar #stop').hide(); + $('#image_bar #prev').show(); + $('#image_bar #next').show(); } function hide_links(){ @@ -168,13 +196,22 @@ function hide_links(){ $('#image_bar #slideshow').hide(); $('#image_bar #stop').show(); if(slideshow_status == -1){ + // show image full scren $('#image_bar #play').hide(); - $('#image_bar #pause').hide(); + $('#image_bar #pause').hide(); + $('#image_bar #prev').show(); + $('#image_bar #next').show(); } else if(slideshow_status == 1){ + // play slideshow $('#image_bar #pause').show(); $('#image_bar #play').hide(); + //$('#image_bar #prev').hide(); + //$('#image_bar #next').hide(); }else{ + //pause slideshow $('#image_bar #play').show(); $('#image_bar #pause').hide(); + $('#image_bar #prev').show(); + $('#image_bar #next').show(); } -} +} \ No newline at end of file