1 2(function() { 3 4 Polymer('core-shared-lib',{ 5 6 notifyEvent: 'core-shared-lib-load', 7 8 ready: function() { 9 if (!this.url && this.defaultUrl) { 10 this.url = this.defaultUrl; 11 } 12 }, 13 14 urlChanged: function() { 15 require(this.url, this, this.callbackName); 16 }, 17 18 provide: function() { 19 this.async('notify'); 20 }, 21 22 notify: function() { 23 this.fire(this.notifyEvent, arguments); 24 } 25 26 }); 27 28 var apiMap = {}; 29 30 function require(url, notifiee, callbackName) { 31 // make hashable string form url 32 var name = nameFromUrl(url); 33 // lookup existing loader instance 34 var loader = apiMap[name]; 35 // create a loader as needed 36 if (!loader) { 37 loader = apiMap[name] = new Loader(name, url, callbackName); 38 } 39 loader.requestNotify(notifiee); 40 } 41 42 function nameFromUrl(url) { 43 return url.replace(/[\:\/\%\?\&\.\=\-]/g, '_') + '_api'; 44 } 45 46 var Loader = function(name, url, callbackName) { 47 this.instances = []; 48 this.callbackName = callbackName; 49 if (this.callbackName) { 50 window[this.callbackName] = this.success.bind(this); 51 } else { 52 if (url.indexOf(this.callbackMacro) >= 0) { 53 this.callbackName = name + '_loaded'; 54 window[this.callbackName] = this.success.bind(this); 55 url = url.replace(this.callbackMacro, this.callbackName); 56 } else { 57 // TODO(sjmiles): we should probably fallback to listening to script.load 58 throw 'core-shared-api: a %%callback%% parameter is required in the API url'; 59 } 60 } 61 // 62 this.addScript(url); 63 }; 64 65 Loader.prototype = { 66 67 callbackMacro: '%%callback%%', 68 loaded: false, 69 70 addScript: function(src) { 71 var script = document.createElement('script'); 72 script.src = src; 73 script.onerror = this.error.bind(this); 74 var s = document.querySelector('script'); 75 s.parentNode.insertBefore(script, s); 76 this.script = script; 77 }, 78 79 removeScript: function() { 80 if (this.script.parentNode) { 81 this.script.parentNode.removeChild(this.script); 82 } 83 this.script = null; 84 }, 85 86 error: function() { 87 this.cleanup(); 88 }, 89 90 success: function() { 91 this.loaded = true; 92 this.cleanup(); 93 this.result = Array.prototype.slice.call(arguments); 94 this.instances.forEach(this.provide, this); 95 this.instances = null; 96 }, 97 98 cleanup: function() { 99 delete window[this.callbackName]; 100 }, 101 102 provide: function(instance) { 103 instance.notify(instance, this.result); 104 }, 105 106 requestNotify: function(instance) { 107 if (this.loaded) { 108 this.provide(instance); 109 } else { 110 this.instances.push(instance); 111 } 112 } 113 114 }; 115 116})(); 117