From 4974e47ed1f6eb70a4d88056abec6ee204c6fd1d Mon Sep 17 00:00:00 2001 From: Owen Leonard Date: Fri, 22 Dec 2023 12:28:38 +0000 Subject: [PATCH] Bug 35638: Upgrade Enquire JS library from v2.0.1 to v2.1.6 This patch upgrades enquire.js in the OPAC to the latest version, v.2.1.6. The patch also moves the file (enquire.min.js) into its own directory for consistency's sake. Enquire.js lets us use CSS-like "breakpoints" to trigger JS actions. We use it to in "mobile" views (narrower browser widths): Collapsing the facets menu and triggering automatic scroll to "main content." The patch also removes media.match.js which should have been removed in Bug 29960. At that time Enquire.js required that we used Modernizr to load media.match.js as a polyfill for older browsers. To test, apply the patch and clear your browser cache if necessary. - In the OPAC, perform a catalog search which will return results. - At wider browser widths the facets menu will appear in the left-hand sidebar. - When you narrow your browser until the viewport is less than 992 pixels wide* the facets menu should collapse into a "Refine your search" button above the search results. - If you narrow your browser width to below 600 pixels wide and reload the search results page, the browser should automatically scroll down so that the "Your search..." heading is at the top of the viewport. * Use Firefox's Responsive Design Mode to get pixel-level control over viewport width: https://firefox-source-docs.mozilla.org/devtools-user/responsive_design_mode/ In Chrome it's called "Device Mode." Signed-off-by: David Nind --- .../intranet-tmpl/prog/en/modules/about.tt | 2 +- .../bootstrap/en/includes/opac-bottom.inc | 2 +- koha-tmpl/opac-tmpl/lib/enquire.js | 279 --------------- koha-tmpl/opac-tmpl/lib/enquire.min.js | 5 - .../opac-tmpl/lib/enquire/enquire.min.js | 6 + koha-tmpl/opac-tmpl/lib/media.match.js | 325 ------------------ koha-tmpl/opac-tmpl/lib/media.match.min.js | 8 - 7 files changed, 8 insertions(+), 619 deletions(-) delete mode 100644 koha-tmpl/opac-tmpl/lib/enquire.js delete mode 100644 koha-tmpl/opac-tmpl/lib/enquire.min.js create mode 100644 koha-tmpl/opac-tmpl/lib/enquire/enquire.min.js delete mode 100644 koha-tmpl/opac-tmpl/lib/media.match.js delete mode 100644 koha-tmpl/opac-tmpl/lib/media.match.min.js diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt index 16ba8f4791..23b35e2a29 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt @@ -1000,7 +1000,7 @@

Enquire.js

- Enquire.js v2.0.1: MIT License + Enquire.js v2.1.6: MIT License

FamFamFam icons

diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-bottom.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-bottom.inc index 943c862eab..b35fb46bc3 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-bottom.inc +++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-bottom.inc @@ -138,7 +138,7 @@ [% Asset.js("lib/jquery/jquery-migrate-3.3.2.min.js") | $raw %] [% Asset.js("lib/bootstrap/js/bootstrap.bundle.min.js") | $raw %] [% Asset.js("lib/fontfaceobserver.min.js") | $raw %] -[% Asset.js("lib/enquire.min.js") | $raw %] +[% Asset.js("lib/enquire/enquire.min.js") | $raw %] diff --git a/koha-tmpl/opac-tmpl/lib/enquire.js b/koha-tmpl/opac-tmpl/lib/enquire.js deleted file mode 100644 index 20366ef26c..0000000000 --- a/koha-tmpl/opac-tmpl/lib/enquire.js +++ /dev/null @@ -1,279 +0,0 @@ -// enquire.js v2.0.1 - Awesome Media Queries in JavaScript -// Copyright (c) 2013 Nick Williams - http://wicky.nillia.ms/enquire.js -// License: MIT (http://www.opensource.org/licenses/mit-license.php) - -;(function(global) { - - 'use strict'; - - var matchMedia = global.matchMedia; - /*jshint -W098 */ - /** - * Helper function for iterating over a collection - * - * @param collection - * @param fn - */ - function each(collection, fn) { - var i = 0, - length = collection.length, - cont; - - for(i; i < length; i++) { - cont = fn(collection[i], i); - if(cont === false) { - break; //allow early exit - } - } - } - - /** - * Helper function for determining whether target object is an array - * - * @param target the object under test - * @return {Boolean} true if array, false otherwise - */ - function isArray(target) { - return Object.prototype.toString.apply(target) === '[object Array]'; - } - - /** - * Helper function for determining whether target object is a function - * - * @param target the object under test - * @return {Boolean} true if function, false otherwise - */ - function isFunction(target) { - return typeof target === 'function'; - } - - /** - * Delegate to handle a media query being matched and unmatched. - * - * @param {object} options - * @param {function} options.match callback for when the media query is matched - * @param {function} [options.unmatch] callback for when the media query is unmatched - * @param {function} [options.setup] one-time callback triggered the first time a query is matched - * @param {boolean} [options.deferSetup=false] should the setup callback be run immediately, rather than first time query is matched? - * @constructor - */ - function QueryHandler(options) { - this.options = options; - !options.deferSetup && this.setup(); - } - QueryHandler.prototype = { - - /** - * coordinates setup of the handler - * - * @function - */ - setup : function() { - if(this.options.setup) { - this.options.setup(); - } - this.initialised = true; - }, - - /** - * coordinates setup and triggering of the handler - * - * @function - */ - on : function() { - !this.initialised && this.setup(); - this.options.match && this.options.match(); - }, - - /** - * coordinates the unmatch event for the handler - * - * @function - */ - off : function() { - this.options.unmatch && this.options.unmatch(); - }, - - /** - * called when a handler is to be destroyed. - * delegates to the destroy or unmatch callbacks, depending on availability. - * - * @function - */ - destroy : function() { - this.options.destroy ? this.options.destroy() : this.off(); - }, - - /** - * determines equality by reference. - * if object is supplied compare options, if function, compare match callback - * - * @function - * @param {object || function} [target] the target for comparison - */ - equals : function(target) { - return this.options === target || this.options.match === target; - } - - }; -/** - * Represents a single media query, manages it's state and registered handlers for this query - * - * @constructor - * @param {string} query the media query string - * @param {boolean} [isUnconditional=false] whether the media query should run regardless of whether the conditions are met. Primarily for helping older browsers deal with mobile-first design - */ -function MediaQuery(query, isUnconditional) { - this.query = query; - this.isUnconditional = isUnconditional; - this.handlers = []; - this.mql = matchMedia(query); - - var self = this; - this.listener = function(mql) { - self.mql = mql; - self.assess(); - }; - this.mql.addListener(this.listener); -} -MediaQuery.prototype = { - - /** - * add a handler for this query, triggering if already active - * - * @param {object} handler - * @param {function} handler.match callback for when query is activated - * @param {function} [handler.unmatch] callback for when query is deactivated - * @param {function} [handler.setup] callback for immediate execution when a query handler is registered - * @param {boolean} [handler.deferSetup=false] should the setup callback be deferred until the first time the handler is matched? - */ - addHandler : function(handler) { - var qh = new QueryHandler(handler); - this.handlers.push(qh); - - this.matches() && qh.on(); - }, - - /** - * removes the given handler from the collection, and calls it's destroy methods - * - * @param {object || function} handler the handler to remove - */ - removeHandler : function(handler) { - var handlers = this.handlers; - each(handlers, function(h, i) { - if(h.equals(handler)) { - h.destroy(); - return !handlers.splice(i,1); //remove from array and exit each early - } - }); - }, - - /** - * Determine whether the media query should be considered a match - * - * @return {Boolean} true if media query can be considered a match, false otherwise - */ - matches : function() { - return this.mql.matches || this.isUnconditional; - }, - - /** - * Clears all handlers and unbinds events - */ - clear : function() { - each(this.handlers, function(handler) { - handler.destroy(); - }); - this.mql.removeListener(this.listener); - this.handlers.length = 0; //clear array - }, - - /* - * Assesses the query, turning on all handlers if it matches, turning them off if it doesn't match - */ - assess : function() { - var action = this.matches() ? 'on' : 'off'; - - each(this.handlers, function(handler) { - handler[action](); - }); - } -}; - /** - * Allows for registration of query handlers. - * Manages the query handler's state and is responsible for wiring up browser events - * - * @constructor - */ - function MediaQueryDispatch () { - if(!matchMedia) { - throw new Error('matchMedia not present, legacy browsers require a polyfill'); - } - - this.queries = {}; - this.browserIsIncapable = !matchMedia('only all').matches; - } - - MediaQueryDispatch.prototype = { - - /** - * Registers a handler for the given media query - * - * @param {string} q the media query - * @param {object || Array || Function} options either a single query handler object, a function, or an array of query handlers - * @param {function} options.match fired when query matched - * @param {function} [options.unmatch] fired when a query is no longer matched - * @param {function} [options.setup] fired when handler first triggered - * @param {boolean} [options.deferSetup=false] whether setup should be run immediately or deferred until query is first matched - * @param {boolean} [shouldDegrade=false] whether this particular media query should always run on incapable browsers - */ - register : function(q, options, shouldDegrade) { - var queries = this.queries, - isUnconditional = shouldDegrade && this.browserIsIncapable; - - if(!queries[q]) { - queries[q] = new MediaQuery(q, isUnconditional); - } - - //normalise to object in an array - if(isFunction(options)) { - options = { match : options }; - } - if(!isArray(options)) { - options = [options]; - } - each(options, function(handler) { - queries[q].addHandler(handler); - }); - - return this; - }, - - /** - * unregisters a query and all it's handlers, or a specific handler for a query - * - * @param {string} q the media query to target - * @param {object || function} [handler] specific handler to unregister - */ - unregister : function(q, handler) { - var query = this.queries[q]; - - if(query) { - if(handler) { - query.removeHandler(handler); - } - else { - query.clear(); - delete this.queries[q]; - } - } - - return this; - } - }; - - - global.enquire = global.enquire || new MediaQueryDispatch(); - -}(this)); \ No newline at end of file diff --git a/koha-tmpl/opac-tmpl/lib/enquire.min.js b/koha-tmpl/opac-tmpl/lib/enquire.min.js deleted file mode 100644 index 1297dc961e..0000000000 --- a/koha-tmpl/opac-tmpl/lib/enquire.min.js +++ /dev/null @@ -1,5 +0,0 @@ -// enquire.js v2.0.1 - Awesome Media Queries in JavaScript -// Copyright (c) 2013 Nick Williams - http://wicky.nillia.ms/enquire.js -// License: MIT (http://www.opensource.org/licenses/mit-license.php) - -(function(t){"use strict";function i(t,i){var s,n=0,e=t.length;for(n;e>n&&(s=i(t[n],n),s!==!1);n++);}function s(t){return"[object Array]"===Object.prototype.toString.apply(t)}function n(t){return"function"==typeof t}function e(t){this.options=t,!t.deferSetup&&this.setup()}function o(t,i){this.query=t,this.isUnconditional=i,this.handlers=[],this.mql=h(t);var s=this;this.listener=function(t){s.mql=t,s.assess()},this.mql.addListener(this.listener)}function r(){if(!h)throw Error("matchMedia not present, legacy browsers require a polyfill");this.queries={},this.browserIsIncapable=!h("only all").matches}var h=t.matchMedia;e.prototype={setup:function(){this.options.setup&&this.options.setup(),this.initialised=!0},on:function(){!this.initialised&&this.setup(),this.options.match&&this.options.match()},off:function(){this.options.unmatch&&this.options.unmatch()},destroy:function(){this.options.destroy?this.options.destroy():this.off()},equals:function(t){return this.options===t||this.options.match===t}},o.prototype={addHandler:function(t){var i=new e(t);this.handlers.push(i),this.matches()&&i.on()},removeHandler:function(t){var s=this.handlers;i(s,function(i,n){return i.equals(t)?(i.destroy(),!s.splice(n,1)):void 0})},matches:function(){return this.mql.matches||this.isUnconditional},clear:function(){i(this.handlers,function(t){t.destroy()}),this.mql.removeListener(this.listener),this.handlers.length=0},assess:function(){var t=this.matches()?"on":"off";i(this.handlers,function(i){i[t]()})}},r.prototype={register:function(t,e,r){var h=this.queries,a=r&&this.browserIsIncapable;return h[t]||(h[t]=new o(t,a)),n(e)&&(e={match:e}),s(e)||(e=[e]),i(e,function(i){h[t].addHandler(i)}),this},unregister:function(t,i){var s=this.queries[t];return s&&(i?s.removeHandler(i):(s.clear(),delete this.queries[t])),this}},t.enquire=t.enquire||new r})(this); \ No newline at end of file diff --git a/koha-tmpl/opac-tmpl/lib/enquire/enquire.min.js b/koha-tmpl/opac-tmpl/lib/enquire/enquire.min.js new file mode 100644 index 0000000000..57cdb3b30f --- /dev/null +++ b/koha-tmpl/opac-tmpl/lib/enquire/enquire.min.js @@ -0,0 +1,6 @@ +/*! + * enquire.js v2.1.6 - Awesome Media Queries in JavaScript + * Copyright (c) 2017 Nick Williams - http://wicky.nillia.ms/enquire.js + * License: MIT */ + +!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.enquire=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g= 0 && exprListStr !== '') { - do { - expr = exprList[exprIndex].match(_mediaExpr); - - if (!expr || !_features[expr[3]]) { - match = false; - break; - } - - prefix = expr[2]; - length = expr[5]; - value = length; - unit = expr[7]; - feature = _features[expr[3]]; - - // Convert unit types - if (unit) { - if (unit === 'px') { - // If unit is px - value = Number(length); - } else if (unit === 'em' || unit === 'rem') { - // Convert relative length unit to pixels - // Assumed base font size is 16px - value = 16 * length; - } else if (expr[8]) { - // Convert aspect ratio to decimal - value = (length / expr[8]).toFixed(2); - } else if (unit === 'dppx') { - // Convert resolution dppx unit to pixels - value = length * 96; - } else if (unit === 'dpcm') { - // Convert resolution dpcm unit to pixels - value = length * 0.3937; - } else { - // default - value = Number(length); - } - } - - // Test for prefix min or max - // Test value against feature - if (prefix === 'min-' && value) { - match = feature >= value; - } else if (prefix === 'max-' && value) { - match = feature <= value; - } else if (value) { - match = feature === value; - } else { - match = !!feature; - } - - // If 'match' is false, break loop - // Continue main loop through query list - if (!match) { - break; - } - } while (exprIndex--); - } - - // If match is true, break loop - // Once matched, no need to check other queries - if (match) { - break; - } - } while (mqIndex--); - - return negate ? !match : match; - }, - - /* - _setFeature - */ - _setFeature = function () { - // Sets properties of '_features' that change on resize and/or orientation. - var w = win.innerWidth || _viewport.clientWidth, - h = win.innerHeight || _viewport.clientHeight, - dw = win.screen.width, - dh = win.screen.height, - c = win.screen.colorDepth, - x = win.devicePixelRatio; - - _features.width = w; - _features.height = h; - _features['aspect-ratio'] = (w / h).toFixed(2); - _features['device-width'] = dw; - _features['device-height'] = dh; - _features['device-aspect-ratio'] = (dw / dh).toFixed(2); - _features.color = c; - _features['color-index'] = Math.pow(2, c); - _features.orientation = (h >= w ? 'portrait' : 'landscape'); - _features.resolution = (x && x * 96) || win.screen.deviceXDPI || 96; - _features['device-pixel-ratio'] = x || 1; - }, - - /* - _watch - */ - _watch = function () { - clearTimeout(_timer); - - _timer = setTimeout(function () { - var query = null, - qIndex = _queryID - 1, - qLength = qIndex, - match = false; - - if (qIndex >= 0) { - _setFeature(); - - do { - query = _queries[qLength - qIndex]; - - if (query) { - match = _matches(query.mql.media); - - if ((match && !query.mql.matches) || (!match && query.mql.matches)) { - query.mql.matches = match; - - if (query.listeners) { - for (var i = 0, il = query.listeners.length; i < il; i++) { - if (query.listeners[i]) { - query.listeners[i].call(win, query.mql); - } - } - } - } - } - } while(qIndex--); - } - - - }, 10); - }, - - /* - _init - */ - _init = function () { - var head = _doc.getElementsByTagName('head')[0], - style = _doc.createElement('style'), - info = null, - typeList = ['screen', 'print', 'speech', 'projection', 'handheld', 'tv', 'braille', 'embossed', 'tty'], - typeIndex = 0, - typeLength = typeList.length, - cssText = '#mediamatchjs { position: relative; z-index: 0; }', - eventPrefix = '', - addEvent = win.addEventListener || (eventPrefix = 'on') && win.attachEvent; - - style.type = 'text/css'; - style.id = 'mediamatchjs'; - - head.appendChild(style); - - // Must be placed after style is inserted into the DOM for IE - info = (win.getComputedStyle && win.getComputedStyle(style)) || style.currentStyle; - - // Create media blocks to test for media type - for ( ; typeIndex < typeLength; typeIndex++) { - cssText += '@media ' + typeList[typeIndex] + ' { #mediamatchjs { position: relative; z-index: ' + typeIndex + ' } }'; - } - - // Add rules to style element - if (style.styleSheet) { - style.styleSheet.cssText = cssText; - } else { - style.textContent = cssText; - } - - // Get media type - _type = typeList[(info.zIndex * 1) || 0]; - - head.removeChild(style); - - _setFeature(); - - // Set up listeners - addEvent(eventPrefix + 'resize', _watch); - addEvent(eventPrefix + 'orientationchange', _watch); - }; - - _init(); - - /* - A list of parsed media queries, ex. screen and (max-width: 400px), screen and (max-width: 800px) - */ - return function (media) { - var id = _queryID, - mql = { - matches : false, - media : media, - addListener : function addListener(listener) { - _queries[id].listeners || (_queries[id].listeners = []); - listener && _queries[id].listeners.push(listener); - }, - removeListener : function removeListener(listener) { - var query = _queries[id], - i = 0, - il = 0; - - if (!query) { - return; - } - - il = query.listeners.length; - - for ( ; i < il; i++) { - if (query.listeners[i] === listener) { - query.listeners.splice(i, 1); - } - } - } - }; - - if (media === '') { - mql.matches = true; - return mql; - } - - mql.matches = _matches(media); - - _queryID = _queries.push({ - mql : mql, - listeners : null - }); - - return mql; - }; -}(window)); \ No newline at end of file diff --git a/koha-tmpl/opac-tmpl/lib/media.match.min.js b/koha-tmpl/opac-tmpl/lib/media.match.min.js deleted file mode 100644 index 7b5115d97a..0000000000 --- a/koha-tmpl/opac-tmpl/lib/media.match.min.js +++ /dev/null @@ -1,8 +0,0 @@ -/* MediaMatch v.2.0.2 - Testing css media queries in Javascript. Authors & copyright (c) 2013: WebLinc, David Knight. */ - -window.matchMedia||(window.matchMedia=function(c){var a=c.document,w=a.documentElement,l=[],t=0,x="",h={},G=/\s*(only|not)?\s*(screen|print|[a-z\-]+)\s*(and)?\s*/i,H=/^\s*\(\s*(-[a-z]+-)?(min-|max-)?([a-z\-]+)\s*(:?\s*([0-9]+(\.[0-9]+)?|portrait|landscape)(px|em|dppx|dpcm|rem|%|in|cm|mm|ex|pt|pc|\/([0-9]+(\.[0-9]+)?))?)?\s*\)\s*$/,y=0,A=function(b){var z=-1!==b.indexOf(",")&&b.split(",")||[b],e=z.length-1,j=e,g=null,d=null,c="",a=0,l=!1,m="",f="",g=null,d=0,f=null,k="",p="",q="",n="",r="",k=!1;if(""=== -b)return!0;do{g=z[j-e];l=!1;if(d=g.match(G))c=d[0],a=d.index;if(!d||-1===g.substring(0,a).indexOf("(")&&(a||!d[3]&&c!==d.input))k=!1;else{f=g;l="not"===d[1];a||(m=d[2],f=g.substring(c.length));k=m===x||"all"===m||""===m;g=-1!==f.indexOf(" and ")&&f.split(" and ")||[f];d=g.length-1;if(k&&0<=d&&""!==f){do{f=g[d].match(H);if(!f||!h[f[3]]){k=!1;break}k=f[2];n=p=f[5];q=f[7];r=h[f[3]];q&&(n="px"===q?Number(p):"em"===q||"rem"===q?16*p:f[8]?(p/f[8]).toFixed(2):"dppx"===q?96*p:"dpcm"===q?0.3937*p:Number(p)); -k="min-"===k&&n?r>=n:"max-"===k&&n?r<=n:n?r===n:!!r;if(!k)break}while(d--)}if(k)break}}while(e--);return l?!k:k},B=function(){var b=c.innerWidth||w.clientWidth,a=c.innerHeight||w.clientHeight,e=c.screen.width,j=c.screen.height,g=c.screen.colorDepth,d=c.devicePixelRatio;h.width=b;h.height=a;h["aspect-ratio"]=(b/a).toFixed(2);h["device-width"]=e;h["device-height"]=j;h["device-aspect-ratio"]=(e/j).toFixed(2);h.color=g;h["color-index"]=Math.pow(2,g);h.orientation=a>=b?"portrait":"landscape";h.resolution= -d&&96*d||c.screen.deviceXDPI||96;h["device-pixel-ratio"]=d||1},C=function(){clearTimeout(y);y=setTimeout(function(){var b=null,a=t-1,e=a,j=!1;if(0<=a){B();do if(b=l[e-a])if((j=A(b.mql.media))&&!b.mql.matches||!j&&b.mql.matches)if(b.mql.matches=j,b.listeners)for(var j=0,g=b.listeners.length;j