1 | /** |
||
2 | * [Siteapp] - multi-purpose frontend application |
||
3 | * |
||
4 | * Siteapp media-query detector |
||
5 | * |
||
6 | * @package [Siteapp] |
||
7 | * @subpackage [Siteapp] core |
||
8 | * @author Björn Bartels <[email protected]> |
||
9 | * @link https://gitlab.bjoernbartels.earth/groups/themes |
||
10 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 |
||
11 | * @copyright copyright (c) 2016 Björn Bartels <[email protected]> |
||
12 | * |
||
13 | * @namespace Siteapp |
||
14 | * @module Siteapp.MediaQuery |
||
15 | */ |
||
16 | |||
17 | // Default set of media queries |
||
18 | var defaultQueries = { |
||
19 | 'default' : 'only screen', |
||
20 | landscape : 'only screen and (orientation: landscape)', |
||
21 | portrait : 'only screen and (orientation: portrait)', |
||
22 | retina : 'only screen and (-webkit-min-device-pixel-ratio: 2),' + |
||
23 | 'only screen and (min--moz-device-pixel-ratio: 2),' + |
||
24 | 'only screen and (-o-min-device-pixel-ratio: 2/1),' + |
||
25 | 'only screen and (min-device-pixel-ratio: 2),' + |
||
26 | 'only screen and (min-resolution: 192dpi),' + |
||
27 | 'only screen and (min-resolution: 2dppx)' |
||
28 | }; |
||
29 | |||
30 | var MediaQuery = { |
||
31 | queries: [], |
||
32 | current: '', |
||
33 | |||
34 | /** |
||
35 | * Checks if the screen is at least as wide as a breakpoint. |
||
36 | * @function |
||
37 | * @param {String} size - Name of the breakpoint to check. |
||
38 | * @returns {Boolean} `true` if the breakpoint matches, `false` if it's smaller. |
||
39 | */ |
||
40 | atLeast: function(size) { |
||
41 | var query = this.get(size); |
||
42 | |||
43 | if (query) { |
||
44 | return window.matchMedia(query).matches; |
||
45 | } |
||
46 | |||
47 | return false; |
||
48 | }, |
||
49 | |||
50 | /** |
||
51 | * Gets the media query of a breakpoint. |
||
52 | * @function |
||
53 | * @param {String} size - Name of the breakpoint to get. |
||
54 | * @returns {String|null} - The media query of the breakpoint, or `null` if the breakpoint doesn't exist. |
||
55 | */ |
||
56 | get: function(size) { |
||
57 | for (var i in this.queries) { |
||
0 ignored issues
–
show
|
|||
58 | var query = this.queries[i]; |
||
59 | if (size === query.name) return query.value; |
||
0 ignored issues
–
show
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later. Consider: if (a > 0)
b = 42;
If you or someone else later decides to put another statement in, only the first statement will be executed. if (a > 0)
console.log("a > 0");
b = 42;
In this case the statement if (a > 0) {
console.log("a > 0");
b = 42;
}
ensures that the proper code will be executed conditionally no matter how many statements are added or removed. ![]() |
|||
60 | } |
||
61 | |||
62 | return null; |
||
63 | }, |
||
64 | |||
65 | /** |
||
66 | * Initializes the media query helper, by extracting the breakpoint list from the CSS and activating the breakpoint watcher. |
||
67 | * @function |
||
68 | * @private |
||
69 | */ |
||
70 | _init: function() { |
||
71 | var self = this; |
||
72 | var extractedStyles = $('.Siteapp-mq').css('font-family'); |
||
73 | var namedQueries; |
||
74 | |||
75 | namedQueries = parseStyleToObject(extractedStyles); |
||
76 | |||
77 | for (var key in namedQueries) { |
||
0 ignored issues
–
show
A for in loop automatically includes the property of any prototype object, consider checking the key using
hasOwnProperty .
When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically: var someObject;
for (var key in someObject) {
if ( ! someObject.hasOwnProperty(key)) {
continue; // Skip keys from the prototype.
}
doSomethingWith(key);
}
![]() |
|||
78 | self.queries.push({ |
||
79 | name: key, |
||
80 | value: 'only screen and (min-width: ' + namedQueries[key] + ')' |
||
81 | }); |
||
82 | } |
||
83 | |||
84 | this.current = this._getCurrentSize(); |
||
85 | |||
86 | this._watcher(); |
||
87 | |||
88 | // Extend default queries |
||
89 | // namedQueries = $.extend(defaultQueries, namedQueries); |
||
90 | }, |
||
91 | |||
92 | /** |
||
93 | * Gets the current breakpoint name by testing every breakpoint and returning the last one to match (the biggest one). |
||
94 | * @function |
||
95 | * @private |
||
96 | * @returns {String} Name of the current breakpoint. |
||
97 | */ |
||
98 | _getCurrentSize: function() { |
||
99 | var matched; |
||
100 | |||
101 | for (var i in this.queries) { |
||
0 ignored issues
–
show
A for in loop automatically includes the property of any prototype object, consider checking the key using
hasOwnProperty .
When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically: var someObject;
for (var key in someObject) {
if ( ! someObject.hasOwnProperty(key)) {
continue; // Skip keys from the prototype.
}
doSomethingWith(key);
}
![]() |
|||
102 | var query = this.queries[i]; |
||
103 | |||
104 | if (window.matchMedia(query.value).matches) { |
||
105 | matched = query; |
||
106 | } |
||
107 | } |
||
108 | |||
109 | if(typeof matched === 'object') { |
||
0 ignored issues
–
show
|
|||
110 | return matched.name; |
||
111 | } else { |
||
112 | return matched; |
||
113 | } |
||
114 | }, |
||
115 | |||
116 | /** |
||
117 | * Activates the breakpoint watcher, which fires an event on the window whenever the breakpoint changes. |
||
118 | * @function |
||
119 | * @private |
||
120 | */ |
||
121 | _watcher: function() { |
||
122 | var _this = this; |
||
123 | |||
124 | $(window).on('resize.'+MediaQuery._ns+'.mediaquery', function() { |
||
125 | var newSize = _this._getCurrentSize(); |
||
126 | |||
127 | if (newSize !== _this.current) { |
||
128 | // Broadcast the media query change on the window |
||
129 | $(window).trigger('changed.'+MediaQuery._ns+'.mediaquery', [newSize, _this.current]); |
||
130 | |||
131 | // Change the current media query |
||
132 | _this.current = newSize; |
||
133 | } |
||
134 | }); |
||
135 | } |
||
136 | }; |
||
137 | |||
138 | //Siteapp.MediaQuery = MediaQuery; |
||
139 | |||
140 | // matchMedia() polyfill - Test a CSS media type/query in JS. |
||
141 | // Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas, David Knight. Dual MIT/BSD license |
||
142 | window.matchMedia || (window.matchMedia = function() { |
||
143 | 'use strict'; |
||
144 | |||
145 | // For browsers that support matchMedium api such as IE 9 and webkit |
||
146 | var styleMedia = (window.styleMedia || window.media); |
||
147 | |||
148 | // For those that don't support matchMedium |
||
149 | if (!styleMedia) { |
||
150 | var style = document.createElement('style'), |
||
151 | script = document.getElementsByTagName('script')[0], |
||
152 | info = null; |
||
153 | |||
154 | style.type = 'text/css'; |
||
155 | style.id = 'matchmediajs-test'; |
||
156 | |||
157 | script.parentNode.insertBefore(style, script); |
||
158 | |||
159 | // 'style.currentStyle' is used by IE <= 8 and 'window.getComputedStyle' for all other browsers |
||
160 | info = ('getComputedStyle' in window) && window.getComputedStyle(style, null) || style.currentStyle; |
||
161 | |||
162 | styleMedia = { |
||
163 | matchMedium: function(media) { |
||
164 | var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }'; |
||
165 | |||
166 | // 'style.styleSheet' is used by IE <= 8 and 'style.textContent' for all other browsers |
||
167 | if (style.styleSheet) { |
||
168 | style.styleSheet.cssText = text; |
||
169 | } else { |
||
170 | style.textContent = text; |
||
171 | } |
||
172 | |||
173 | // Test if media query is true or false |
||
174 | return info.width === '1px'; |
||
175 | } |
||
176 | }; |
||
177 | } |
||
178 | |||
179 | return function(media) { |
||
180 | return { |
||
181 | matches: styleMedia.matchMedium(media || 'all'), |
||
182 | media: media || 'all' |
||
183 | }; |
||
184 | }; |
||
185 | }()); |
||
186 | |||
187 | // Thank you: https://github.com/sindresorhus/query-string |
||
188 | function parseStyleToObject(str) { |
||
189 | var styleObject = {}; |
||
190 | |||
191 | if (typeof str !== 'string') { |
||
192 | return styleObject; |
||
193 | } |
||
194 | |||
195 | str = str.trim().slice(1, -1); // browsers re-quote string style values |
||
196 | |||
197 | if (!str) { |
||
198 | return styleObject; |
||
199 | } |
||
200 | |||
201 | styleObject = str.split('&').reduce(function(ret, param) { |
||
202 | var parts = param.replace(/\+/g, ' ').split('='); |
||
203 | var key = parts[0]; |
||
204 | var val = parts[1]; |
||
205 | key = decodeURIComponent(key); |
||
206 | |||
207 | // missing `=` should be `null`: |
||
208 | // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters |
||
209 | val = val === undefined ? null : decodeURIComponent(val); |
||
210 | |||
211 | if (!ret.hasOwnProperty(key)) { |
||
212 | ret[key] = val; |
||
213 | } else if (Array.isArray(ret[key])) { |
||
214 | ret[key].push(val); |
||
215 | } else { |
||
216 | ret[key] = [ret[key], val]; |
||
217 | } |
||
218 | return ret; |
||
219 | }, {}); |
||
220 | |||
221 | return styleObject; |
||
222 | } |
||
223 | |||
224 | export {MediaQuery}; |
||
225 | |||
226 |
When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically: