Bug 11516: make OPAC search term highlighting work in results browser
[koha.git] / koha-tmpl / opac-tmpl / bootstrap / lib / media.match.js
1 /* MediaMatch v.2.0.2 - Testing css media queries in Javascript. Authors & copyright (c) 2013: WebLinc, David Knight. */
2
3 window.matchMedia || (window.matchMedia = function (win) {
4     'use strict';
5
6     // Internal globals
7     var _doc        = win.document,
8         _viewport   = _doc.documentElement,
9         _queries    = [],
10         _queryID    = 0,
11         _type       = '',
12         _features   = {},
13                     // only screen
14                     // only screen and
15                     // not screen
16                     // not screen and
17                     // screen
18                     // screen and
19         _typeExpr   = /\s*(only|not)?\s*(screen|print|[a-z\-]+)\s*(and)?\s*/i,
20                     // (-vendor-min-width: 300px)
21                     // (min-width: 300px)
22                     // (width: 300px)
23                     // (width)
24                     // (orientation: portrait|landscape)
25         _mediaExpr  = /^\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*$/,
26         _timer      = 0,
27
28         // Helper methods
29
30         /*
31             _matches
32          */
33         _matches = function (media) {
34             // screen and (min-width: 400px), screen and (max-width: 500px)
35             var mql         = (media.indexOf(',') !== -1 && media.split(',')) || [media],
36                 mqIndex     = mql.length - 1,
37                 mqLength    = mqIndex,
38                 mq          = null,
39
40                 // not screen, screen
41                 negateType      = null,
42                 negateTypeFound = '',
43                 negateTypeIndex = 0,
44                 negate          = false,
45                 type            = '',
46
47                 // (min-width: 400px), (min-width)
48                 exprListStr = '',
49                 exprList    = null,
50                 exprIndex   = 0,
51                 exprLength  = 0,
52                 expr        = null,
53
54                 prefix      = '',
55                 length      = '',
56                 unit        = '',
57                 value       = '',
58                 feature     = '',
59
60                 match       = false;
61
62             if (media === '') {
63                 return true;
64             }
65
66             do {
67                 mq          = mql[mqLength - mqIndex];
68                 negate      = false;
69                 negateType  = mq.match(_typeExpr);
70
71                 if (negateType) {
72                     negateTypeFound = negateType[0];
73                     negateTypeIndex = negateType.index;
74                 }
75
76                 if (!negateType || ((mq.substring(0, negateTypeIndex).indexOf('(') === -1) && (negateTypeIndex || (!negateType[3] && negateTypeFound !== negateType.input)))) {
77                     match = false;
78                     continue;
79                 }
80
81                 exprListStr = mq;
82
83                 negate = negateType[1] === 'not';
84
85                 if (!negateTypeIndex) {
86                     type        =  negateType[2];
87                     exprListStr = mq.substring(negateTypeFound.length);
88                 }
89
90                 // Test media type
91                 // Test type against this device or if 'all' or empty ''
92                 match       = type === _type || type === 'all' || type === '';
93
94                 exprList    = (exprListStr.indexOf(' and ') !== -1 && exprListStr.split(' and ')) || [exprListStr];
95                 exprIndex   = exprList.length - 1;
96                 exprLength  = exprIndex;
97
98                 if (match && exprIndex >= 0 && exprListStr !== '') {
99                     do {
100                         expr = exprList[exprIndex].match(_mediaExpr);
101
102                         if (!expr || !_features[expr[3]]) {
103                             match = false;
104                             break;
105                         }
106
107                         prefix  = expr[2];
108                         length  = expr[5];
109                         value   = length;
110                         unit    = expr[7];
111                         feature = _features[expr[3]];
112
113                         // Convert unit types
114                         if (unit) {
115                             if (unit === 'px') {
116                                 // If unit is px
117                                 value = Number(length);
118                             } else if (unit === 'em' || unit === 'rem') {
119                                 // Convert relative length unit to pixels
120                                 // Assumed base font size is 16px
121                                 value = 16 * length;
122                             } else if (expr[8]) {
123                                 // Convert aspect ratio to decimal
124                                 value = (length / expr[8]).toFixed(2);
125                             } else if (unit === 'dppx') {
126                                 // Convert resolution dppx unit to pixels
127                                 value = length * 96;
128                             } else if (unit === 'dpcm') {
129                                 // Convert resolution dpcm unit to pixels
130                                 value = length * 0.3937;
131                             } else {
132                                 // default
133                                 value = Number(length);
134                             }
135                         }
136
137                         // Test for prefix min or max
138                         // Test value against feature
139                         if (prefix === 'min-' && value) {
140                             match = feature >= value;
141                         } else if (prefix === 'max-' && value) {
142                             match = feature <= value;
143                         } else if (value) {
144                             match = feature === value;
145                         } else {
146                             match = !!feature;
147                         }
148
149                         // If 'match' is false, break loop
150                         // Continue main loop through query list
151                         if (!match) {
152                             break;
153                         }
154                     } while (exprIndex--);
155                 }
156
157                 // If match is true, break loop
158                 // Once matched, no need to check other queries
159                 if (match) {
160                     break;
161                 }
162             } while (mqIndex--);
163
164             return negate ? !match : match;
165         },
166
167         /*
168             _setFeature
169          */
170         _setFeature = function () {
171             // Sets properties of '_features' that change on resize and/or orientation.
172             var w   = win.innerWidth || _viewport.clientWidth,
173                 h   = win.innerHeight || _viewport.clientHeight,
174                 dw  = win.screen.width,
175                 dh  = win.screen.height,
176                 c   = win.screen.colorDepth,
177                 x   = win.devicePixelRatio;
178
179             _features.width                     = w;
180             _features.height                    = h;
181             _features['aspect-ratio']           = (w / h).toFixed(2);
182             _features['device-width']           = dw;
183             _features['device-height']          = dh;
184             _features['device-aspect-ratio']    = (dw / dh).toFixed(2);
185             _features.color                     = c;
186             _features['color-index']            = Math.pow(2, c);
187             _features.orientation               = (h >= w ? 'portrait' : 'landscape');
188             _features.resolution                = (x && x * 96) || win.screen.deviceXDPI || 96;
189             _features['device-pixel-ratio']     = x || 1;
190         },
191
192         /*
193             _watch
194          */
195         _watch = function () {
196             clearTimeout(_timer);
197
198             _timer = setTimeout(function () {
199                 var query   = null,
200                     qIndex  = _queryID - 1,
201                     qLength = qIndex,
202                     match   = false;
203
204                 if (qIndex >= 0) {
205                     _setFeature();
206
207                     do {
208                         query = _queries[qLength - qIndex];
209
210                         if (query) {
211                             match = _matches(query.mql.media);
212
213                             if ((match && !query.mql.matches) || (!match && query.mql.matches)) {
214                                 query.mql.matches = match;
215
216                                 if (query.listeners) {
217                                     for (var i = 0, il = query.listeners.length; i < il; i++) {
218                                         if (query.listeners[i]) {
219                                             query.listeners[i].call(win, query.mql);
220                                         }
221                                     }
222                                 }
223                             }
224                         }
225                     } while(qIndex--);
226                 }
227
228
229             }, 10);
230         },
231
232         /*
233             _init
234          */
235         _init = function () {
236             var head        = _doc.getElementsByTagName('head')[0],
237                 style       = _doc.createElement('style'),
238                 info        = null,
239                 typeList    = ['screen', 'print', 'speech', 'projection', 'handheld', 'tv', 'braille', 'embossed', 'tty'],
240                 typeIndex   = 0,
241                 typeLength  = typeList.length,
242                 cssText     = '#mediamatchjs { position: relative; z-index: 0; }',
243                 eventPrefix = '',
244                 addEvent    = win.addEventListener || (eventPrefix = 'on') && win.attachEvent;
245
246             style.type  = 'text/css';
247             style.id    = 'mediamatchjs';
248
249             head.appendChild(style);
250
251             // Must be placed after style is inserted into the DOM for IE
252             info = (win.getComputedStyle && win.getComputedStyle(style)) || style.currentStyle;
253
254             // Create media blocks to test for media type
255             for ( ; typeIndex < typeLength; typeIndex++) {
256                 cssText += '@media ' + typeList[typeIndex] + ' { #mediamatchjs { position: relative; z-index: ' + typeIndex + ' } }';
257             }
258
259             // Add rules to style element
260             if (style.styleSheet) {
261                 style.styleSheet.cssText = cssText;
262             } else {
263                 style.textContent = cssText;
264             }
265
266             // Get media type
267             _type = typeList[(info.zIndex * 1) || 0];
268
269             head.removeChild(style);
270
271             _setFeature();
272
273             // Set up listeners
274             addEvent(eventPrefix + 'resize', _watch);
275             addEvent(eventPrefix + 'orientationchange', _watch);
276         };
277
278     _init();
279
280     /*
281         A list of parsed media queries, ex. screen and (max-width: 400px), screen and (max-width: 800px)
282     */
283     return function (media) {
284         var id  = _queryID,
285             mql = {
286                 matches         : false,
287                 media           : media,
288                 addListener     : function addListener(listener) {
289                     _queries[id].listeners || (_queries[id].listeners = []);
290                     listener && _queries[id].listeners.push(listener);
291                 },
292                 removeListener  : function removeListener(listener) {
293                     var query   = _queries[id],
294                         i       = 0,
295                         il      = 0;
296
297                     if (!query) {
298                         return;
299                     }
300
301                     il = query.listeners.length;
302
303                     for ( ; i < il; i++) {
304                         if (query.listeners[i] === listener) {
305                             query.listeners.splice(i, 1);
306                         }
307                     }
308                 }
309             };
310
311         if (media === '') {
312             mql.matches = true;
313             return mql;
314         }
315
316         mql.matches = _matches(media);
317
318         _queryID = _queries.push({
319             mql         : mql,
320             listeners   : null
321         });
322
323         return mql;
324     };
325 }(window));