text: /*!
   *
   * Version:     1.8.0
   * Author:      Gianluca Guarini
   * Contact:     gianluca.guarini@gmail.com
   * Website:     http://www.gianlucaguarini.com/
   * Twitter:     @gianlucaguarini
   *
   * Copyright (c) Gianluca Guarini
   *
   * Permission is hereby granted, free of charge, to any person
   * obtaining a copy of this software and associated documentation
   * files (the "Software"), to deal in the Software without
   * restriction, including without limitation the rights to use,
   * copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the
   * Software is furnished to do so, subject to the following
   * conditions:
   *
   * The above copyright notice and this permission notice shall be
   * included in all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
   * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
   * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
   * OTHER DEALINGS IN THE SOFTWARE.
   **/
  /*global console*/
  (function($) {
  
    'use strict';
  
    $.html5Loader = function(customOptions) {
      var defaults = {
          filesToLoad: null,
          /* set the path to the JSON or pass an object containing the files to preload */
          debugMode: false,
          /**
           * do not execute the javascript files when they'll be preloaded
           * this option could be overridden into the json per each javascript file
           * @type {Boolean}
           */
          stopExecution: false,
          /**
           * decide the the buffer size before considering the media preloaded
           * by default it's the 20% of the entire size of the media file
           * Pay attention, many browsers cannot preload the entire media files, they just buffer luckily the half (or less) of them
           * more info: http://www.stevesouders.com/blog/2013/04/12/html5-video-preload/
           * @type {Float}
           */
          mediaBufferSizeToPreload: 0.2,
          /* let the browser decide when the media file has been buffered enough to be played by listening the canplaythrough event */
          forceMediaPreload: true,
          /* script files won't execute when loaded */
          onBeforeLoad: function() {},
          /* this functions is triggered before the preloader starts loading the sources */
          onComplete: function() {},
          /* set the onComplete is triggered when everything is loaded */
          onElementLoaded: function(obj, elm) {},
          /* this Callback is triggered anytime an object is loaded */
          onUpdate: function(percentage) {},
          /* this function returns alway the current percentage */
          onMediaError: function(obj, elm) {} /* This function is invoked in case of any error occurred during the media element fetch*/
        },
        // merging the custom options with the default ones
        options = $.extend(defaults, customOptions);
  
      /*
       *
       * PUBLIC VAR
       * Configuration
       *
       */
      var filesToLoad = options.filesToLoad,
        debugMode = options.debugMode,
        mediaBufferSizeToPreload = options.mediaBufferSizeToPreload,
        forceMediaPreload = options.forceMediaPreload,
        stopExecution = options.stopExecution,
        onBeforeLoad = options.onBeforeLoad,
        onComplete = options.onComplete,
        onElementLoaded = options.onElementLoaded,
        onUpdate = options.onUpdate,
        onMediaError = options.onMediaError;
  
      /*
       *
       * PRIVATE VARS
       *
       */
      var $head = $('head'),
        _currentSegment = 0,
        _bytesLoaded = 0,
        _bytesTotal = 0,
        _files = [],
        _isTablet = navigator.userAgent.match(/iPad|Android|(?=.*\bWindows\b)(?=.*\bARM\b)/i),
        _isMobile = (function(a) {
          if (/android.+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|meego.+mobile|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) return true;
          else return false;
        })(navigator.userAgent || navigator.vendor || window.opera),
        _support = {};
      /*
       *
       * PRIVATE METHODS
       *
       */
  
  
      /*
       *
       * @description Used to debug the application
       * @param msg: {string, object, function, array} anything we need to log in the console
       *
       */
  
      var log = function(msg) {
        if (debugMode && console) {
          console.log(msg);
        }
      };
  
      /*
       *
       * @description Check the support for HTML5 video and audio tags
       * @link http://modernizr.com/
       *
       */
  
      _support.video = function() {
        /* jshint -W053 */
        var elem = document.createElement('video'),
          bool = false;
  
        // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224
        try {
          if (!!elem.canPlayType) {
            bool = {
              ogg: elem.canPlayType('video/ogg; codecs="theora"').replace(/^no$/, ''),
              // Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546
              h264: elem.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/, ''),
              webm: elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/, ''),
              vp9: elem.canPlayType('video/webm; codecs="vp9"').replace(/^no$/, '')
            };
            // backward compatibility
            bool.mp4 = bool.h264;
          }
        } catch (e) {}
  
        return bool;
      }();
  
      _support.audio = function() {
        /* jshint -W053 */
        var elem = document.createElement('audio');
        var bool = false;
  
        try {
          if (!!elem.canPlayType) {
            bool = {
              ogg: elem.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ''),
              mp3: elem.canPlayType('audio/mpeg;').replace(/^no$/, ''),
              opus: elem.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/, ''),
              // Mimetypes accepted:
              // developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements
              // bit.ly/iphoneoscodecs
              wav: elem.canPlayType('audio/wav; codecs="1"').replace(/^no$/, ''),
              m4a: (elem.canPlayType('audio/x-m4a;') ||
                elem.canPlayType('audio/aac;')).replace(/^no$/, '')
            };
  
          }
        } catch (e) {}
  
        return bool;
      }();
  
      /**
       *
       * Check the svg support
       *
       */
  
      _support.svg = function() {
        return !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect;
      }();
  
      /*
       *
       * @description Loops on the available source files to find the right one to preload
       * @param file: {object} the node that we need to parse to check the source supported
       *
       */
  
      var findSupportedSource = function(file) {
        var type = file.type.toLowerCase(),
          sources = file.sources;
  
        $.each(sources, function(tmpSource) {
          if (_support[type][tmpSource]) {
            file = file.sources[tmpSource];
            file.type = type.toUpperCase();
            return false;
          }
        });
        if (file.source) {
          return file;
        } else {
          return false;
        }
      };
  
      /*
       *
       * @description Output the current percentage using the onUpdate function passed as parameter to the loader
       *
       */
  
      var updatePercentage = function() {
        var currPercentage = 0;
        log('_bytesTotal = ' + _bytesTotal);
        log('_bytesLoaded = ' + _bytesLoaded);
        currPercentage = Math.round((_bytesLoaded / _bytesTotal) * 100);
  
        log('Percentage: ' + currPercentage + '%');
  
        onUpdate(currPercentage);
  
        if (!_files[_currentSegment].length) {
  
          // Is there another group of files.
          _currentSegment++;
  
          if (_files[_currentSegment]) {
            startLoading();
          } else {
            onComplete();
          }
        }
      };
  
      /*
       *
       * @description Populate the _files array increasing the _bytesTotal var
       * @param index: file index
       * @param obj: the json node representing the file properties
       *
       */
  
      var arrangeData = function(segmentIndex, index, obj) {
        var file = obj;
  
        if (file.type === 'VIDEO' || file.type === 'AUDIO') {
          file = findSupportedSource(file);
        }
  
        if (file) {
          _bytesTotal += file.size;
          _files[segmentIndex].push(file);
        }
      };
  
      /*
       *
       * @description Deal with data received from the json loaded
       * @param data: object
       *
       */
  
      var onJsonLoaded = function(data) {
        var segment;
        log('json loaded');
  
        if (!data.files[0].length) {
          data.files = [data.files];
        }
  
        for (var i = 0, l = data.files.length; i < l; i++) {
          _files.push([]);
          segment = data.files[i];
  
          for (var m = 0, n = segment.length; m < n; m++) {
            arrangeData(i, m, segment[m]);
          }
        }
      };
  
      /*
       *
       * @description Load any kind of image
       * @param file: object
       *
       */
  
      var loadImage = function(file) {
        var defer = new $.Deferred(),
          size = file.size,
          $image = $('<img>'),
          onImageLoaded = function() {
  
            log('File Loaded:' + src);
            _bytesLoaded += size;
            onElementLoaded(file, this);
            // removing the file from the array
            _files[_currentSegment].splice(0, 1);
            updatePercentage();
            defer.resolve();
  
          },
          src;
  
        // load the svg or the fallback image
        if (file.source.toString() === '[object Object]') {
          if (file.source.svg && _support.svg) {
            src = file.source.svg;
          } else if (file.source.fallback) {
            src = file.source.fallback;
          }
        } else {
          src = file.source;
        }
  
        $image.on('load', onImageLoaded);
        $image.attr('src', src);
  
        // preventing a memory leak
        $image = null;
  
        return defer.promise();
      };
  
      /*
       *
       * @description Load video or audio files
       * @param file: object
       *
       */
  
      var loadMedia = function(file) {
  
        var defer = new $.Deferred(),
          size = file.size,
          $media = file.type === 'VIDEO' ? $('<video>') : $('<audio>'),
          onMediaLoaded = function() {
            log('File Loaded:' + file.source);
  
            _bytesLoaded += size;
  
            onElementLoaded(file, $media[0]);
  
            _files[_currentSegment].splice(0, 1);
  
            $media.off();
            $media = null;
  
            updatePercentage();
            defer.resolve();
          };
  
        // if it is a mobile or an iPad we avoid the media preloading
        if (!_isMobile && !_isTablet) {
  
          $media.on('loadstart', function() {
            if (this.networkState === 3) {
              onMediaError(file, this);
              onMediaLoaded();
            }
          });
  
          $media.on('error stalled', function() {
            onMediaError(file, this);
            onMediaLoaded();
          });
  
          // on Media Progress
          $media.on('loadedmetadata', function() {
            $media.on('progress', function() {
              var bytesTmpLoaded = 0;
              log('loading in progress file:' + file.source);
              if (this.buffered.length > 0) {
                bytesTmpLoaded = (size / this.duration) * this.buffered.end(0);
                size -= bytesTmpLoaded;
                _bytesLoaded += bytesTmpLoaded;
                updatePercentage();
                // Check if the video has been buffered enough to be considered preloaded
                if (1 / size * bytesTmpLoaded > mediaBufferSizeToPreload && !forceMediaPreload)
                  onMediaLoaded();
              }
            });
          });
  
          $media.attr({
            preload: 'auto',
            src: file.source,
            controls: 'controls'
          });
  
          // let the browser decide when the media file has been buffered enough
          // to be played
          if (forceMediaPreload)
            $media.on('canplaythrough', onMediaLoaded);
  
        } else {
          //  that means that media is loaded by default
          onMediaLoaded();
        }
  
  
        return defer.promise();
      };
  
      /*
       *
       * @description Load scripts making them available to the DOM
       * @param file: object
       *
       */
  
      var loadScript = function(file) {
        var defer = new $.Deferred(),
          size = file.size,
          args = {
            url: file.source,
            dataType: 'script'
          };
  
        // Disables script execution when loaded upon users request.
        if ((typeof file.stopExecution === 'undefined' && stopExecution) || file.stopExecution === true)
          args.converters = {
            'text script': function(text) {
              return text;
            }
          };
  
  
        $.ajax(args).done(function(data) {
  
          log('File Loaded:' + file.source);
  
          _bytesLoaded += size;
  
          onElementLoaded(file, data);
  
          // removing the file from the array
          _files[_currentSegment].splice(0, 1);
          updatePercentage();
          defer.resolve();
        })
          .fail(function(jqxhr, settings, exception) {
            log('\n File Failed: ' + file.source +
              '\n Message:     ' + exception.message + '\n');
          });
  
        return defer.promise();
      };
  
      /*
       *
       * @description Load any text file or CSS and applying it to current page
       * @param file: object
       * @param { Boolean } isCss: if it's true i will append the css into the head tag of the page
       *
       */
  
      var loadText = function(file, isCss) {
        var defer = new $.Deferred();
        $.ajax({
          url: file.source,
          dataType: 'text',
          success: function(data) {
            log('File Loaded:' + file.source);
            onElementLoaded(file, data);
            _bytesLoaded += file.size;
            _files[_currentSegment].splice(0, 1);
            updatePercentage();
            // IE8/7 fix
            // http://stackoverflow.com/questions/805384/how-to-apply-inline-and-or-external-css-loaded-dynamically-with-jquery
            if (isCss)
              if (document.createStyleSheet) {
                try {
                  document.createStyleSheet(file.source);
                } catch (e) {}
              } else {
                var css;
                css = document.createElement('link');
                css.rel = 'stylesheet';
                css.type = 'text/css';
                css.media = 'all';
                css.href = file.source;
                $head[0].appendChild(css);
              }
  
            defer.resolve(data);
          }
        });
  
        return defer.promise();
      };
  
      /*
       *
       * @description start loading all the files
       *
       */
  
      var startLoading = function() {
  
        var filesArray = _files[_currentSegment].slice();
  
        $.each(filesArray, function(i, file) {
  
          log('preloading files');
  
          log('file to preload:' + file.source);
          switch (file.type) {
            case 'IMAGE':
              loadImage(file);
              break;
            case 'VIDEO':
            case 'AUDIO':
              loadMedia(file);
              break;
            case 'SCRIPT':
              loadScript(file);
              break;
            case 'TEXT':
            case 'CSS':
              loadText(file, /CSS/g.test(file.type));
              break;
            default:
              return false;
          }
  
        });
  
      };
  
      /*
       *
       * PUBLIC METHODS
       *
       */
  
      /*
       *
       * Start preloading the page
       *
       */
  
      this.init = function() {
        log('plugin initialized');
  
        var defer = new $.Deferred(),
          promise = defer.promise();
        onBeforeLoad();
  
        if (typeof filesToLoad === 'object') {
          $.proxy(onJsonLoaded, this, filesToLoad)();
          defer.resolve(filesToLoad);
        } else {
          $.getJSON(filesToLoad, defer.resolve);
          // once the json has been loaded parse all data received
          defer.pipe($.proxy(onJsonLoaded, this));
        }
  
        // ready to preload all the files
        promise.then($.proxy(updatePercentage, this));
        promise.then($.proxy(startLoading, this));
  
      };
  
      this.init();
  
      return this;
  
    };
  
  })(jQuery);