Gaya/QueryLoader2

View on GitHub
src/ImagePreloader/index.js

Summary

Maintainability
A
2 hrs
Test Coverage
'use strict';
var QueryLoaderImage = require('./Image.js');

var ImagePreloader = {
  getImageSrcs: function(element) {
    this.sources = [];

    if (typeof element !== 'undefined') {
      this.findImageInElement(element);

      if (this.deepSearch === true) {
        var elements = element.querySelectorAll('*');
        for (var i = 0; i < elements.length; i++) {
          if (elements[i].tagName !== 'SCRIPT') {
            this.findImageInElement(elements[i]);
          }
        }
      }
    }

    // if there's no img or bg-img, use a 1px transparent data uri as a fallback,
    // otherwise the user will be blocked for 10s
    if (!this.sources.length) {
      this.sources.push('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');
    }

    return this.sources;
  },

  findAndPreload: function(element) {
    if (typeof element === 'undefined') {
      return;
    }

    this.sources = this.getImageSrcs(element);

    for (var i = 0; i < this.sources.length; i++) {
      var image = QueryLoaderImage(this.sources[i]);
      image.preload(this.imageLoaded.bind(this));
      this.images.push(image);
    }
  },

  imageLoaded: function() {
    this.loaded++;

    this.updateProgress();
  },

  updateProgress: function() {
    this.parent.updateProgress(this.loaded, this.sources.length);
  },

  findImageInElement: function(element) {
    var urlType = this.determineUrlAndType(element);

    //skip if gradient
    if (!this.hasGradient(urlType.url)) {
      //remove unwanted chars
      urlType.url = this.stripUrl(urlType.url);

      //split urls
      var urls = urlType.url.split(', ');

      for (var i = 0; i < urls.length; i++) {
        if (this.validUrl(urls[i]) && this.urlIsNew(urls[i])) {
          var extra = '';

          if (this.isIE() || this.isOpera()) {
            //filthy always no cache for IE, sorry peeps!
            extra = '?rand=' + Math.random();
          }

          //add image to found list
          this.sources.push(urls[i] + extra);
        }
      }
    }
  },

  determineUrlAndType: function(element) {
    var url = '';
    var type = 'normal';
    var style = element.currentStyle || window.getComputedStyle(element, null);
    var blacklisted = ['', 'none', 'initial', 'inherit'];

    if (typeof style.backgroundImage !== 'undefined' && blacklisted.indexOf(style.backgroundImage) === -1) {
      //if object has background image (computed style)
      url = style.backgroundImage;
      type = 'background';
    } else if (typeof element.style.backgroundImage !== 'undefined' && blacklisted.indexOf(element.style.backgroundImage) === -1) {
      //if object has background image (inline style)
      url = element.style.backgroundImage;
      type = 'background';
    } else if (element.nodeName.toLowerCase() === 'img') {
      //if is img and has src
      url = element.src;
    }

    return {
      url: url,
      type: type,
    };
  },

  hasGradient: function(url) {
    return (url && typeof url.indexOf !== 'undefined' ? url.indexOf('gradient(') !== -1 : false);
  },

  stripUrl: function(url) {
    url = url.replace(/url\(\'/g, '');
    url = url.replace(/url\(/g, '');
    url = url.replace(/\'\)/g, '');
    url = url.replace(/\)/g, '');
    url = url.replace(/"/g, '');

    return url;
  },

  validUrl: function(url) {
    if (url.length > 0 && !url.match(/^(data:)/i)) {
      return true;
    } else {
      return false;
    }
  },

  urlIsNew: function(url) {
    return this.sources.indexOf(url) === -1;
  },

  isIE: function() {
    return navigator.userAgent.match(/msie/i);
  },

  isOpera: function() {
    return navigator.userAgent.match(/Opera/i);
  },
};

module.exports = function(parent) {
  var imagePreloader = Object.create(ImagePreloader);

  imagePreloader.parent = parent;
  imagePreloader.sources = [];
  imagePreloader.images = [];
  imagePreloader.loaded = 0;
  imagePreloader.deepSearch = true;

  return imagePreloader;
};