PageRenderTime 66ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/ajax/libs/fallback/1.1.4/fallback.js

https://gitlab.com/Mirros/cdnjs
JavaScript | 524 lines | 342 code | 118 blank | 64 comment | 79 complexity | 4121cf58e8755494a30ebce99bb8d28a MD5 | raw file
  1. /* fallback.js v1.1.4 | http://fallback.io/ | Salvatore Garbesi <sal@dolox.com> | (c) 2013 Dolox Inc. */
  2. /*jslint browser: true*/
  3. (function (window, document, undefined) {
  4. 'use strict';
  5. var fallback = {
  6. // Callback storage from .ready and inline callback.
  7. callbacks: [],
  8. // Libraries that have completed/exhausted.
  9. completed: {},
  10. completed_count: 0,
  11. // Page `head` element.
  12. head: document.getElementsByTagName('head')[0],
  13. // Libraries that are imported are stored here.
  14. libraries: {},
  15. libraries_count: 0,
  16. // Spawned libraries, so they don't run over themselves.
  17. spawned: [],
  18. // Libraries that failed to load.
  19. failed: {},
  20. failed_count: 0,
  21. // Shims that are imported are stored here.
  22. shims: {},
  23. // Libraries successfully loaded.
  24. success: {},
  25. success_count: 0
  26. };
  27. // Bootstrap our library.
  28. fallback.bootstrap = function() {
  29. var index, type;
  30. for (index in fallback.utilities) {
  31. type = fallback.utilities[index];
  32. fallback.utility(type);
  33. }
  34. };
  35. // Utility functions to check against variables.
  36. fallback.utilities = ['Array', 'Function', 'Object', 'String'];
  37. // Setup individual utility function.
  38. fallback.utility = function(type) {
  39. fallback['is_' + type.toLowerCase()] = function(variable) {
  40. return Object.prototype.toString.call(variable) == '[object ' + type + ']';
  41. };
  42. };
  43. // Check if our variable is a function, if not make it one.
  44. fallback.callback = function(variable) {
  45. var me = this;
  46. if (!me.is_function(variable)) {
  47. variable = function() {};
  48. }
  49. return variable;
  50. };
  51. // indexOf isn't supported on arrays in older versions of IE!
  52. fallback.index_of = function(object, value) {
  53. var index;
  54. for (index in object) {
  55. if (object[index] === value) {
  56. return index;
  57. }
  58. }
  59. return -1;
  60. };
  61. // Check if our variable is defined.
  62. fallback.is_defined = function(variable) {
  63. try {
  64. /*jslint evil: true*/
  65. if (eval('window.' + variable)) {
  66. return true;
  67. }
  68. } catch (exception) {
  69. return false;
  70. }
  71. return false;
  72. };
  73. // Import and cleanse libraries from user input.
  74. fallback.importer = function(libraries, options) {
  75. var me = this, current, index;
  76. var library, urls;
  77. var cleansed_shims, shim, shims, shim_libraries, shim_libraries_cleansed;
  78. // Cleanse the libraries.
  79. var cleansed_libraries = {};
  80. for (library in libraries) {
  81. // URL list for each library.
  82. urls = libraries[library];
  83. // If `urls` is undefined, null or an empty string, skip, it's invalid.
  84. if (!urls) {
  85. continue;
  86. }
  87. // If `urls` is a string, convert it to any array.
  88. if (me.is_string(urls)) {
  89. urls = [urls];
  90. }
  91. // If `urls` is not an array, skip, it's invalid.
  92. if (!me.is_array(urls)) {
  93. continue;
  94. }
  95. // Check to see if the library already exists, if it does, merge the new URLs.
  96. current = [];
  97. if (me.is_array(me.libraries[library])) {
  98. current = me.libraries[library];
  99. }
  100. cleansed_libraries[library] = urls;
  101. me.libraries[library] = current.concat(urls);
  102. }
  103. // Cleanse the shims.
  104. cleansed_shims = {};
  105. if (me.is_object(options) && me.is_object(options.shim)) {
  106. shims = options.shim;
  107. for (shim in shims) {
  108. shim_libraries = shims[shim];
  109. // If `shim` doesn't exist in libraries, skip, it's invalid.
  110. if (!me.libraries[shim]) {
  111. continue;
  112. }
  113. // If `shim_libraries` is undefined, null or an empty string, skip, it's invalid.
  114. if (!shim_libraries) {
  115. continue;
  116. }
  117. // If `shim_libraries` is a string, convert it to any array.
  118. if (me.is_string(shim_libraries)) {
  119. shim_libraries = [shim_libraries];
  120. }
  121. // If `shim_libraries` is not an array, skip, it's invalid.
  122. if (!me.is_array(shim_libraries)) {
  123. continue;
  124. }
  125. // Check to make sure the libraries exist otherwise remove them.
  126. shim_libraries_cleansed = [];
  127. for (index in shim_libraries) {
  128. library = shim_libraries[index];
  129. // Make sure the library actually exists and that it's not itself.
  130. if (me.libraries[library] && library !== shim) {
  131. shim_libraries_cleansed.push(library);
  132. }
  133. }
  134. // Check to see if the shim already exists, if it does, merge the new shims.
  135. current = [];
  136. if (me.is_array(me.shims[shim])) {
  137. current = me.shims[shim];
  138. }
  139. cleansed_shims[shim] = shim_libraries_cleansed;
  140. me.shims[shim] = current.concat(shim_libraries_cleansed);
  141. }
  142. }
  143. return {
  144. libraries: cleansed_libraries,
  145. shims: cleansed_shims
  146. };
  147. };
  148. // CSS check if selector exists.
  149. fallback.css = {};
  150. fallback.css.check = function(selector) {
  151. var me = fallback;
  152. if (!document.styleSheets) {
  153. return false;
  154. }
  155. var index, stylesheet, found;
  156. for (index in document.styleSheets) {
  157. stylesheet = document.styleSheets[index];
  158. if (stylesheet === 0) {
  159. continue;
  160. }
  161. if (stylesheet.rules) {
  162. found = me.css.scan(stylesheet.rules, selector);
  163. if (found) {
  164. return found;
  165. }
  166. }
  167. // Issues with CORS at times, don't let the script bomb.
  168. try {
  169. if (stylesheet.cssRules) {
  170. found = me.css.scan(stylesheet.cssRules, selector);
  171. if (found) {
  172. return found;
  173. }
  174. }
  175. } catch (e) {
  176. continue;
  177. }
  178. }
  179. return false;
  180. };
  181. fallback.css.scan = function(ruleset, selector) {
  182. var index, rule;
  183. for (index in ruleset) {
  184. rule = ruleset[index];
  185. if (rule.selectorText === selector) {
  186. return true;
  187. }
  188. }
  189. return false;
  190. };
  191. // Spawn an instance of the library.
  192. fallback.load = function(libraries, options, callback) {
  193. var me = this;
  194. var imported, library, urls;
  195. // If `libraries` is not a object, die out.
  196. if (!me.is_object(libraries)) {
  197. return false;
  198. }
  199. // If `options` is a function, then it needs to become the callback.
  200. if (me.is_function(options)) {
  201. callback = options;
  202. options = {};
  203. }
  204. // If `options` is not an object, convert it.
  205. if (!me.is_object(options)) {
  206. options = {};
  207. }
  208. // Import libraries.
  209. imported = me.importer(libraries, options);
  210. // Spawn library instances from user input.
  211. for (library in imported.libraries) {
  212. urls = imported.libraries[library];
  213. if (!me.shims[library]) {
  214. me.spawn(library, urls);
  215. }
  216. }
  217. // Fork the callback over to the `ready` function.
  218. if (me.is_function(options['callback'])) {
  219. me.ready([], options['callback']);
  220. }
  221. if (me.is_function(callback)) {
  222. me.ready([], callback);
  223. }
  224. };
  225. // Callback array of objects.
  226. fallback.ready = function(libraries, callback) {
  227. var me = this;
  228. var index, library;
  229. if (me.is_function(libraries)) {
  230. callback = libraries;
  231. libraries = [];
  232. } else {
  233. if (!me.is_array(libraries) || me.is_string(libraries)) {
  234. libraries = [libraries];
  235. }
  236. for (index in libraries) {
  237. library = libraries[index];
  238. if (me.libraries[library] && !me.shims[library]) {
  239. me.spawn(library, me.libraries[library]);
  240. }
  241. }
  242. }
  243. me.callbacks.push({
  244. callback: me.callback(callback),
  245. libraries: libraries
  246. });
  247. return me.ready_invocation();
  248. };
  249. // Invoke any `ready` callbacks.
  250. fallback.ready_invocation = function() {
  251. var me = this, index, count, library, wipe, payload, processed = [], callbacks = [];
  252. for (index in me.callbacks) {
  253. // If callback is not an object, skip and remove it;
  254. payload = me.callbacks[index];
  255. if (!me.is_object(payload) || !me.is_array(payload.libraries) || !me.is_function(payload.callback)) {
  256. continue;
  257. }
  258. wipe = false;
  259. if (payload.libraries.length > 0) {
  260. count = 0;
  261. for (library in me.success) {
  262. if (me.index_of(payload.libraries, library) >= 0) {
  263. count++;
  264. }
  265. }
  266. if (count === payload.libraries.length) {
  267. wipe = true;
  268. }
  269. } else if (me.libraries_count === me.success_count + me.failed_count) {
  270. wipe = true;
  271. }
  272. if (wipe) {
  273. callbacks.push(payload.callback);
  274. } else {
  275. processed.push(me.callbacks[index]);
  276. }
  277. }
  278. me.callbacks = processed;
  279. // We need to process the callbacks here that way they can run in parallel as well in nested callbacks and not get caught in a endless loop.
  280. for (index in callbacks) {
  281. callbacks[index](me.success, me.failed);
  282. }
  283. };
  284. // Invoke any `shim` dependencies.
  285. fallback.shim_invocation = function() {
  286. var me = this;
  287. var count, index, shim, shimming;
  288. for (shim in me.shims) {
  289. shimming = me.shims[shim];
  290. // If there are no shims, or the if the shim is already loaded, skip it.
  291. if (!shimming || me.success[shim]) {
  292. continue;
  293. }
  294. // Reset our counter back to 0.
  295. count = 0;
  296. // Iterate through shim dependencies and find out of all dependencies for shim were loaded.
  297. for (index in shimming) {
  298. if (me.success[shimming[index]]) {
  299. count++;
  300. }
  301. }
  302. // If all dependencies were loaded, spawn the shim.
  303. if (count === shimming.length) {
  304. me.spawn(shim, me.libraries[shim]);
  305. // Remove the shim from shim list that way it doesn't try to load it again.
  306. delete me.shims[shim];
  307. }
  308. }
  309. };
  310. // Initialize the spawning of a library.
  311. fallback.spawn = function(library, urls) {
  312. var me = this;
  313. // Library is already attempting to be loaded.
  314. if (me.index_of(me.spawned, library) !== -1) {
  315. return false;
  316. }
  317. me.libraries_count++;
  318. me.spawned.push(library);
  319. return me.spawn.instance(library, urls);
  320. };
  321. // Spawn a url from the library.
  322. fallback.spawn.instance = function(library, urls) {
  323. var me = fallback;
  324. var element;
  325. var type = 'js';
  326. var payload = {
  327. loaded: false,
  328. library: library,
  329. spawned: true,
  330. url: urls.shift(),
  331. urls: urls
  332. };
  333. if (payload.url.indexOf('.css') > -1) {
  334. type = 'css';
  335. // CSS selector already exists, do not attempt to spawn library.
  336. if (me.css.check(library)) {
  337. payload.spawned = false;
  338. return me.spawn.success(payload);
  339. }
  340. element = document.createElement('link');
  341. element.crossorigin = true;
  342. element.rel = 'stylesheet';
  343. element.href = payload.url;
  344. } else {
  345. // JavaScript variable already exists, do not attempt to spawn library
  346. if (me.is_defined(library)) {
  347. payload.spawned = false;
  348. return me.spawn.success(payload);
  349. }
  350. element = document.createElement('script');
  351. element.src = payload.url;
  352. }
  353. element.onload = function() {
  354. // Checks for JavaScript library.
  355. if (type === 'js' && !me.is_defined(library)) {
  356. return me.spawn.failed(payload);
  357. }
  358. return me.spawn.success(payload);
  359. };
  360. element.onreadystatechange = function() {
  361. if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {
  362. this.onreadystatechange = null;
  363. if (type === 'js' && !me.is_defined(library)) {
  364. return me.spawn.failed(payload);
  365. }
  366. return me.spawn.success(payload);
  367. }
  368. };
  369. element.onerror = function() {
  370. return me.spawn.failed(payload);
  371. };
  372. return me.head.appendChild(element);
  373. };
  374. // Spawn failure callback.
  375. fallback.spawn.failed = function(payload) {
  376. var me = fallback;
  377. payload.spawned = false;
  378. if (!me.failed[payload.library]) {
  379. me.failed[payload.library] = [];
  380. }
  381. me.failed[payload.library].push(payload.url);
  382. // All URLs for the library have exhausted.
  383. if (!payload.urls.length) {
  384. me.failed_count++;
  385. return me.ready_invocation();
  386. }
  387. // Attempt to spawn another URL.
  388. return me.spawn.instance(payload.library, payload.urls);
  389. };
  390. // Spawn success callback.
  391. fallback.spawn.success = function(payload) {
  392. var me = fallback;
  393. // Mark the library and url as successful.
  394. if (!payload.loaded) {
  395. payload.loaded = true;
  396. me.success[payload.library] = payload.url;
  397. me.success_count++;
  398. }
  399. // Invoke any `shim` dependencies.
  400. me.shim_invocation();
  401. // Invoke any `ready` callbacks.
  402. return me.ready_invocation();
  403. };
  404. fallback.bootstrap();
  405. window.fallback = fallback;
  406. })(window, document, undefined);