(angular => { 'use strict'; angular.module('oc.lazyLoad').config($provide => { $provide.decorator('$ocLazyLoad', function($delegate, $q, $window, $interval) { var uaCssChecked = false, useCssLoadPatch = false, anchor = $window.document.getElementsByTagName('head')[0] || $window.document.getElementsByTagName('body')[0]; /** * Load a js/css file * @param type * @param path * @param params * @returns promise */ $delegate.buildElement = function buildElement(type, path, params) { var deferred = $q.defer(), el, loaded, filesCache = $delegate._getFilesCache(), cacheBuster = function cacheBuster(url) { var dc = new Date().getTime(); if(url.indexOf('?') >= 0) { if(url.substring(0, url.length - 1) === '&') { return `${ url }_dc=${ dc }`; } return `${ url }&_dc=${ dc }`; } else { return `${ url }?_dc=${ dc }`; } }; // Store the promise early so the file load can be detected by other parallel lazy loads // (ie: multiple routes on one page) a 'true' value isn't sufficient // as it causes false positive load results. if(angular.isUndefined(filesCache.get(path))) { filesCache.put(path, deferred.promise); } // Switch in case more content types are added later switch(type) { case 'css': el = $window.document.createElement('link'); el.type = 'text/css'; el.rel = 'stylesheet'; el.href = params.cache === false ? cacheBuster(path) : path; break; case 'js': el = $window.document.createElement('script'); el.src = params.cache === false ? cacheBuster(path) : path; break; default: filesCache.remove(path); deferred.reject(new Error(`Requested type "${ type }" is not known. Could not inject "${ path }"`)); break; } el.onload = el['onreadystatechange'] = function(e) { if((el['readyState'] && !/^c|loade/.test(el['readyState'])) || loaded) return; el.onload = el['onreadystatechange'] = null; loaded = 1; $delegate._broadcast('ocLazyLoad.fileLoaded', path); deferred.resolve(); }; el.onerror = function() { filesCache.remove(path); deferred.reject(new Error(`Unable to load ${ path }`)); }; el.async = params.serie ? 0 : 1; var insertBeforeElem = anchor.lastChild; if(params.insertBefore) { var element = angular.element(angular.isDefined(window.jQuery) ? params.insertBefore : document.querySelector(params.insertBefore)); if(element && element.length > 0) { insertBeforeElem = element[0]; } } insertBeforeElem.parentNode.insertBefore(el, insertBeforeElem); /* The event load or readystatechange doesn't fire in: - iOS < 6 (default mobile browser) - Android < 4.4 (default mobile browser) - Safari < 6 (desktop browser) */ if(type == 'css') { if(!uaCssChecked) { var ua = $window.navigator.userAgent.toLowerCase(); // iOS < 6 if(/iP(hone|od|ad)/.test($window.navigator.platform)) { var v = ($window.navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/); var iOSVersion = parseFloat([parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)].join('.')); useCssLoadPatch = iOSVersion < 6; } else if(ua.indexOf("android") > -1) { // Android < 4.4 var androidVersion = parseFloat(ua.slice(ua.indexOf("android") + 8)); useCssLoadPatch = androidVersion < 4.4; } else if(ua.indexOf('safari') > -1) { var versionMatch = ua.match(/version\/([\.\d]+)/i); useCssLoadPatch = (versionMatch && versionMatch[1] && parseFloat(versionMatch[1]) < 6); } } if(useCssLoadPatch) { var tries = 1000; // * 20 = 20000 miliseconds var interval = $interval(() => { try { el.sheet.cssRules; $interval.cancel(interval); el.onload(); } catch(e) { if(--tries <= 0) { el.onerror(); } } }, 20); } } return deferred.promise; }; return $delegate; }) }); })(angular);