|
19 | 19 | */
|
20 | 20 | var ElementQueries = this.ElementQueries = function() {
|
21 | 21 |
|
22 |
| - this.withTracking = false; |
| 22 | + var trackingActive = false; |
23 | 23 | var elements = [];
|
24 | 24 |
|
25 | 25 | /**
|
|
130 | 130 | }
|
131 | 131 |
|
132 | 132 | for (var k in attributes) {
|
| 133 | + if(!attributes.hasOwnProperty(k)) continue; |
| 134 | + |
133 | 135 | if (attrValues[attributes[k]]) {
|
134 | 136 | this.element.setAttribute(attributes[k], attrValues[attributes[k]].substr(1));
|
135 | 137 | } else {
|
|
155 | 157 | }
|
156 | 158 | element.elementQueriesSetupInformation.call();
|
157 | 159 |
|
158 |
| - if (ElementQueries.instance.withTracking && elements.indexOf(element) < 0) { |
| 160 | + if (trackingActive && elements.indexOf(element) < 0) { |
159 | 161 | elements.push(element);
|
160 | 162 | }
|
161 | 163 | }
|
|
174 | 176 | else allQueries[mode][property][value] += ','+selector;
|
175 | 177 | }
|
176 | 178 |
|
177 |
| - function executeQueries() { |
| 179 | + function getQuery() { |
178 | 180 | var query;
|
179 | 181 | if (document.querySelectorAll) query = document.querySelectorAll.bind(document);
|
180 | 182 | if (!query && 'undefined' !== typeof $$) query = $$;
|
|
184 | 186 | throw 'No document.querySelectorAll, jQuery or Mootools\'s $$ found.';
|
185 | 187 | }
|
186 | 188 |
|
| 189 | + return query; |
| 190 | + } |
| 191 | + |
| 192 | + /** |
| 193 | + * Start the magic. Go through all collected rules (readRules()) and attach the resize-listener. |
| 194 | + */ |
| 195 | + function findElementQueriesElements() { |
| 196 | + var query = getQuery(); |
| 197 | + |
187 | 198 | for (var mode in allQueries) if (allQueries.hasOwnProperty(mode)) {
|
188 |
| - for (var property in allQueries[mode]) if (allQueries[mode].hasOwnProperty(property)) { |
189 |
| - for (var value in allQueries[mode][property]) if (allQueries[mode][property].hasOwnProperty(value)) { |
190 |
| - var elements = query(allQueries[mode][property][value]); |
191 |
| - for (var i = 0, j = elements.length; i < j; i++) { |
192 |
| - setupElement(elements[i], { |
193 |
| - mode: mode, |
194 |
| - property: property, |
195 |
| - value: value |
196 |
| - }); |
197 |
| - } |
| 199 | + |
| 200 | + for (var property in allQueries[mode]) if (allQueries[mode].hasOwnProperty(property)) { |
| 201 | + for (var value in allQueries[mode][property]) if (allQueries[mode][property].hasOwnProperty(value)) { |
| 202 | + var elements = query(allQueries[mode][property][value]); |
| 203 | + for (var i = 0, j = elements.length; i < j; i++) { |
| 204 | + setupElement(elements[i], { |
| 205 | + mode: mode, |
| 206 | + property: property, |
| 207 | + value: value |
| 208 | + }); |
| 209 | + } |
| 210 | + } |
198 | 211 | }
|
199 |
| - } |
| 212 | + |
200 | 213 | }
|
| 214 | + } |
| 215 | + |
| 216 | + /** |
| 217 | + * |
| 218 | + * @param {HTMLElement} element |
| 219 | + */ |
| 220 | + function attachResponsiveImage(element) { |
| 221 | + var children = []; |
| 222 | + var rules = []; |
| 223 | + var sources = []; |
| 224 | + var defaultImageId = 0; |
| 225 | + var lastActiveImage = -1; |
| 226 | + var loadedImages = []; |
201 | 227 |
|
| 228 | + for (var i in element.children) { |
| 229 | + if(!element.children.hasOwnProperty(i)) continue; |
| 230 | + |
| 231 | + if (element.children[i].tagName.toLowerCase() === 'img') { |
| 232 | + children.push(element.children[i]); |
| 233 | + |
| 234 | + var minWidth = element.children[i].getAttribute('min-width') || element.children[i].getAttribute('data-min-width'); |
| 235 | + //var minHeight = element.children[i].getAttribute('min-height') || element.children[i].getAttribute('data-min-height'); |
| 236 | + var src = element.children[i].getAttribute('data-src') || element.children[i].getAttribute('url'); |
| 237 | + |
| 238 | + sources.push(src); |
| 239 | + |
| 240 | + var rule = { |
| 241 | + minWidth: minWidth |
| 242 | + }; |
| 243 | + |
| 244 | + rules.push(rule); |
| 245 | + |
| 246 | + if (!minWidth) { |
| 247 | + defaultImageId = children.length - 1; |
| 248 | + element.children[i].style.display = 'block'; |
| 249 | + } else { |
| 250 | + element.children[i].style.display = 'none'; |
| 251 | + } |
| 252 | + } |
| 253 | + } |
| 254 | + |
| 255 | + lastActiveImage = defaultImageId; |
| 256 | + |
| 257 | + function check() { |
| 258 | + var imageToDisplay = false, i; |
| 259 | + |
| 260 | + for (i in children){ |
| 261 | + if(!children.hasOwnProperty(i)) continue; |
| 262 | + |
| 263 | + if (rules[i].minWidth) { |
| 264 | + if (element.offsetWidth > rules[i].minWidth) { |
| 265 | + imageToDisplay = i; |
| 266 | + } |
| 267 | + } |
| 268 | + } |
| 269 | + |
| 270 | + if (!imageToDisplay) { |
| 271 | + //no rule matched, show default |
| 272 | + imageToDisplay = defaultImageId; |
| 273 | + } |
| 274 | + |
| 275 | + if (lastActiveImage != imageToDisplay) { |
| 276 | + //image change |
| 277 | + |
| 278 | + if (!loadedImages[imageToDisplay]){ |
| 279 | + //image has not been loaded yet, we need to load the image first in memory to prevent flash of |
| 280 | + //no content |
| 281 | + |
| 282 | + var image = new Image(); |
| 283 | + image.onload = function() { |
| 284 | + children[imageToDisplay].src = sources[imageToDisplay]; |
| 285 | + |
| 286 | + children[lastActiveImage].style.display = 'none'; |
| 287 | + children[imageToDisplay].style.display = 'block'; |
| 288 | + |
| 289 | + loadedImages[imageToDisplay] = true; |
| 290 | + |
| 291 | + lastActiveImage = imageToDisplay; |
| 292 | + }; |
| 293 | + |
| 294 | + image.src = sources[imageToDisplay]; |
| 295 | + } else { |
| 296 | + children[lastActiveImage].style.display = 'none'; |
| 297 | + children[imageToDisplay].style.display = 'block'; |
| 298 | + lastActiveImage = imageToDisplay; |
| 299 | + } |
| 300 | + } |
| 301 | + } |
| 302 | + |
| 303 | + element.resizeSensor = new ResizeSensor(element, check); |
| 304 | + check(); |
| 305 | + |
| 306 | + if (trackingActive) { |
| 307 | + elements.push(element); |
| 308 | + } |
| 309 | + } |
| 310 | + |
| 311 | + function findResponsiveImages(){ |
| 312 | + var query = getQuery(); |
| 313 | + |
| 314 | + var elements = query('[data-responsive-image],[responsive-image]'); |
| 315 | + for (var i = 0, j = elements.length; i < j; i++) { |
| 316 | + attachResponsiveImage(elements[i]); |
| 317 | + } |
202 | 318 | }
|
203 | 319 |
|
204 | 320 | var regex = /,?[\s\t]*([^,\n]*?)((?:\[[\s\t]*?(?:min|max)-(?:width|height)[\s\t]*?[~$\^]?=[\s\t]*?"[^"]*?"[\s\t]*?])+)([^,\n\s\{]*)/mgi;
|
|
249 | 365 | }
|
250 | 366 | }
|
251 | 367 |
|
| 368 | + var defaultCssInjected = false; |
| 369 | + |
252 | 370 | /**
|
253 | 371 | * Searches all css rules and setups the event listener to all elements with element query rules..
|
254 | 372 | *
|
255 | 373 | * @param {Boolean} withTracking allows and requires you to use detach, since we store internally all used elements
|
256 | 374 | * (no garbage collection possible if you don not call .detach() first)
|
257 | 375 | */
|
258 | 376 | this.init = function(withTracking) {
|
259 |
| - this.withTracking = withTracking; |
| 377 | + trackingActive = typeof withTracking === 'undefined' ? false : withTracking; |
| 378 | + |
260 | 379 | for (var i = 0, j = document.styleSheets.length; i < j; i++) {
|
261 | 380 | try {
|
262 | 381 | readRules(document.styleSheets[i].cssRules || document.styleSheets[i].rules || document.styleSheets[i].cssText);
|
|
266 | 385 | }
|
267 | 386 | }
|
268 | 387 | }
|
269 |
| - executeQueries(); |
| 388 | + |
| 389 | + if (!defaultCssInjected) { |
| 390 | + var style = document.createElement('style'); |
| 391 | + style.type = 'text/css'; |
| 392 | + style.innerHTML = '[responsive-image] > img, [data-responsive-image] {overflow: hidden; padding: 0; } [responsive-image] > img, [data-responsive-image] > img { width: 100%;}'; |
| 393 | + document.getElementsByTagName('head')[0].appendChild(style); |
| 394 | + defaultCssInjected = true; |
| 395 | + } |
| 396 | + |
| 397 | + findElementQueriesElements(); |
| 398 | + findResponsiveImages(); |
270 | 399 | };
|
271 | 400 |
|
272 | 401 | /**
|
|
275 | 404 | * (no garbage collection possible if you don not call .detach() first)
|
276 | 405 | */
|
277 | 406 | this.update = function(withTracking) {
|
278 |
| - this.withTracking = withTracking; |
279 |
| - this.init(); |
| 407 | + this.init(withTracking); |
280 | 408 | };
|
281 | 409 |
|
282 | 410 | this.detach = function() {
|
283 | 411 | if (!this.withTracking) {
|
284 | 412 | throw 'withTracking is not enabled. We can not detach elements since we don not store it.' +
|
285 |
| - 'Use ElementQueries.withTracking = true; before domready.'; |
| 413 | + 'Use ElementQueries.withTracking = true; before domready or call ElementQueryes.update(true).'; |
286 | 414 | }
|
287 | 415 |
|
288 | 416 | var element;
|
|
310 | 438 | */
|
311 | 439 | ElementQueries.detach = function(element) {
|
312 | 440 | if (element.elementQueriesSetupInformation) {
|
| 441 | + //element queries |
313 | 442 | element.elementQueriesSensor.detach();
|
314 | 443 | delete element.elementQueriesSetupInformation;
|
315 | 444 | delete element.elementQueriesSensor;
|
316 |
| - console.log('detached'); |
| 445 | + |
| 446 | + } else if (element.resizeSensor) { |
| 447 | + //responsive image |
| 448 | + |
| 449 | + element.resizeSensor.detach(); |
| 450 | + delete element.resizeSensor; |
317 | 451 | } else {
|
318 |
| - console.log('detached already', element); |
| 452 | + //console.log('detached already', element); |
319 | 453 | }
|
320 | 454 | };
|
321 | 455 |
|
|
0 commit comments