Skip to content

Commit

Permalink
feat(agcLibraryLoader): add swappable loader strategies
Browse files Browse the repository at this point in the history
  • Loading branch information
nbering committed Apr 14, 2017
1 parent 92a0d0a commit eef66a3
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 5 deletions.
91 changes: 91 additions & 0 deletions src/agcGstaticLoader.js
@@ -0,0 +1,91 @@
/* global angular */
(function() {
angular.module('googlechart')
.provider('agcGstaticLoader', agcGstaticLoaderProvider);

function agcGstaticLoaderProvider(){
var useBothLoaders = false;
var version = "current";
var options = {
packages: ["corechart"]
}

this.setVersion = function(value){
version = value;
if (needsBothLoaders())
useBothLoaders = true;
}

this.addPackage = function(packageName){
options.packages = options.packages || [];
options.packages.push(packageName);

if (needsBothLoaders())
useBothLoaders = true;
}

this.removePackage = function(packageName){
options.packages = this._options.packages || [];
var index = options.packages.indexOf(packageName);
if (index > -1)
options.packages.splice(index, 1);
}

this.setOption = function(option, value){
options[option] = value;
}

this.setOptions = function(value){
options = value;
}

this.clearOption = function(option){
delete this._options["option"]
}

this.useBothLoaders = function(value){
useBothLoaders = !!value;
}

function needsBothLoaders(){
var versionCheck, packageCheck;

versionCheck = !isNaN(+version) && +version < 45;
packageCheck = options.packages.indexOf("geochart") > -1 ||
options.packages.indexOf("map") > -1;

return versionCheck && packageCheck;
}

this.$get = function($rootScope, $q, agcScriptTagHelper){

function scriptLoadCallback(){
if (!google ||
!google.charts ||
typeof google.charts.setOnLoadCallback !== 'function'){
return $q.reject("Google charts library loader not present.");
}

var deferred = $q.defer();

google.charts.load(version, options);

google.charts.setOnLoadCallback(function(){
$rootScope.$apply(function(){
deferred.resolve(google);
});
});

return deferred.promise;
}

var tagPromise = agcScriptTagHelper("https://www.gstatic.com/charts/loader.js");
if (useBothLoaders)
tagPromise = tagPromise.then(function(){ return agcScriptTagHelper("https://www.google.com/jsapi")})
var libraryPromise = tagPromise.then(scriptLoadCallback);

return libraryPromise;
}
this.$get.$inject = ["$rootScope", "$q", "agcScriptTagHelper"];
}
})();
41 changes: 41 additions & 0 deletions src/agcJsapiLoader.js
@@ -0,0 +1,41 @@
/* global angular */
(function() {
angular.module("googlechart")
.factory("agcJsapiLoader", agcJsapiLoaderFactory);

agcJsapiLoaderFactory.$inject = ["$log", "$rootScope", "$q", "agcScriptTagHelper", "googleChartApiConfig"];
function agcJsapiLoaderFactory($log, $rootScope, $q, agcScriptTagHelper, googleChartApiConfig){
$log.debug("[AGC] jsapi loader invoked.");
var apiReady = $q.defer();
// Massage configuration as needed.
googleChartApiConfig.optionalSettings = googleChartApiConfig.optionalSettings || {};

var userDefinedCallback = googleChartApiConfig.optionalSettings.callback;

var settings = {
callback: function() {
if (angular.isFunction(userDefinedCallback))
userDefinedCallback.call(this);

$rootScope.$apply(function(){
apiReady.resolve(google);
});
}
};

settings = angular.extend({}, googleChartApiConfig.optionalSettings, settings);

$log.debug("[AGC] Calling tag helper...");
agcScriptTagHelper("//www.google.com/jsapi")
.then(function(){
$log.debug("[AGC] Tag helper returned success.");
window.google.load('visualization', googleChartApiConfig.version || '1', settings);
})
.catch(function(){
$log("[AGC] Tag helper returned error.");
apiReady.reject();
});

return apiReady.promise;
}
})();
28 changes: 28 additions & 0 deletions src/agcLibraryLoader.js
@@ -0,0 +1,28 @@
/* global angular */
(function(){
angular.module("googlechart")
.provider("agcLibraryLoader", AgcLibraryLoaderProvider);

AgcLibraryLoaderProvider.$inject = ["$injector"];

function AgcLibraryLoaderProvider($injector){

this.$get = function(loader){
return loader;
}

this.setLoader = function(loaderName){
loaderName = loaderName.charAt(0).toUpperCase() + loaderName.slice(1);
if ($injector.has(this.getProviderName(loaderName)))
this.$get.$inject = [this.getProviderName(loaderName)];
else
console.warn("AGC loader type doesn't exist. Defaulting to JSAPI.");
}

this.getProviderName = function(loaderName){
return "agc" + loaderName + "Loader";
}

this.setLoader("Jsapi");
}
})();
52 changes: 52 additions & 0 deletions src/agcNullLoader.js
@@ -0,0 +1,52 @@
/* global angular */
(function(){
angular.module("googlechart")
.provider("agcNullLoader", AgcNullLoaderProvider);

/** Fake loader strategy. Use this if you're loading the google charts library
* in some non-standard way.
*/

function AgcNullLoaderProvider(){
this._hasTrigger = false;
this._libraryOverride = null;
this._triggerFunction = (function(){
// If the trigger function is called before $get,
// just act as if it was never fetched.
if (this._deferred)
this._deferred.resolve(this._libraryOverride || google);
else
this._hasTrigger = false;
}).bind(this);
this._deferred = null;
}

AgcNullLoaderProvider.prototype.$get = function($q){
this._deferred = $q.defer();

if (!this._hasTrigger)
this._deferred.resolve(this._libraryOverride || google);

return function agcNullLoader(){
return this._deferred.promise;
};
};
AgcNullLoaderProvider.prototype.$get.$inject = ["$q"];

AgcNullLoaderProvider.prototype.getTriggerFunction = function(){
// Records that the trigger function was fetched.
// Will wait for it to be called to resolve.
// This is useful for manual, but deferred, loading of
// the google charts library.
this._hasTrigger = true;
return this._triggerFunction;
};

/** Forces angular-google-chart to load this object as the google library.
* Makes no checks to ensure that the object passed is compatible. Use
* at own risk.
*/
AgcNullLoaderProvider.prototype.overrideLibrary = function(library){
this._libraryOverride = library;
};
})();
47 changes: 47 additions & 0 deletions src/agcScriptTagHelper.js
@@ -0,0 +1,47 @@
/* global angular */
(function() {
angular.module("googlechart")
.factory("agcScriptTagHelper", agcScriptTagHelperFactory);

agcScriptTagHelperFactory.$inject = ["$q"];
function agcScriptTagHelperFactory($q)
{
/** Add a script tag to the document's head section and return an angular
* promise that resolves when the script has loaded.
*/
function agcScriptTagHelper(url)
{
var deferred = $q.defer();
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');

script.setAttribute('type', 'text/javascript');
script.src = url;

if (script.addEventListener) { // Standard browsers (including IE9+)
script.addEventListener('load', onLoad, false);
script.onerror = onError;
} else { // IE8 and below
script.onreadystatechange = function () {
if (script.readyState === 'loaded' || script.readyState === 'complete') {
script.onreadystatechange = null;
onLoad();
}
};
}
head.appendChild(script);

function onLoad() {
deferred.resolve();
}

function onError() {
deferred.reject();
}

return deferred.promise;
}

return agcScriptTagHelper;
}
})();
11 changes: 6 additions & 5 deletions src/googleChartApiPromise.js
Expand Up @@ -3,9 +3,10 @@
angular.module('googlechart')
.factory('googleChartApiPromise', googleChartApiPromiseFactory);

googleChartApiPromiseFactory.$inject = ['$rootScope', '$q', 'googleChartApiConfig', 'agcJsapiLoaderStrategy'];

function googleChartApiPromiseFactory($rootScope, $q, apiConfig, agcJsapiLoaderStrategy) {
return agcJsapiLoaderStrategy();
googleChartApiPromiseFactory.$inject = ['agcLibraryLoader'];

/** Here for backward-compatibility only. */
function googleChartApiPromiseFactory(agcLibraryLoader) {
return agcLibraryLoader;
}
})();
})();

0 comments on commit eef66a3

Please sign in to comment.