Issues (149)

js/util/siteapp.util.mediaQuery.js (5 issues)

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
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);
}
Loading history...
58
      var query = this.queries[i];
59
      if (size === query.name) return query.value;
0 ignored issues
show
Coding Style Best Practice introduced by
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 b = 42 will always be executed, while the logging statement will be executed conditionally.

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.

Loading history...
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);
}
Loading history...
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);
}
Loading history...
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
The variable matched seems to not be initialized for all possible execution paths.
Loading history...
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