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