foundation.interchange.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. ;(function ($, window, document, undefined) {
  2. 'use strict';
  3. Foundation.libs.interchange = {
  4. name : 'interchange',
  5. version : '5.3.3',
  6. cache : {},
  7. images_loaded : false,
  8. nodes_loaded : false,
  9. settings : {
  10. load_attr : 'interchange',
  11. named_queries : {
  12. 'default' : 'only screen',
  13. small : Foundation.media_queries.small,
  14. medium : Foundation.media_queries.medium,
  15. large : Foundation.media_queries.large,
  16. xlarge : Foundation.media_queries.xlarge,
  17. xxlarge: Foundation.media_queries.xxlarge,
  18. landscape : 'only screen and (orientation: landscape)',
  19. portrait : 'only screen and (orientation: portrait)',
  20. retina : 'only screen and (-webkit-min-device-pixel-ratio: 2),' +
  21. 'only screen and (min--moz-device-pixel-ratio: 2),' +
  22. 'only screen and (-o-min-device-pixel-ratio: 2/1),' +
  23. 'only screen and (min-device-pixel-ratio: 2),' +
  24. 'only screen and (min-resolution: 192dpi),' +
  25. 'only screen and (min-resolution: 2dppx)'
  26. },
  27. directives : {
  28. replace: function (el, path, trigger) {
  29. // The trigger argument, if called within the directive, fires
  30. // an event named after the directive on the element, passing
  31. // any parameters along to the event that you pass to trigger.
  32. //
  33. // ex. trigger(), trigger([a, b, c]), or trigger(a, b, c)
  34. //
  35. // This allows you to bind a callback like so:
  36. // $('#interchangeContainer').on('replace', function (e, a, b, c) {
  37. // console.log($(this).html(), a, b, c);
  38. // });
  39. if (/IMG/.test(el[0].nodeName)) {
  40. var orig_path = el[0].src;
  41. if (new RegExp(path, 'i').test(orig_path)) return;
  42. el[0].src = path;
  43. return trigger(el[0].src);
  44. }
  45. var last_path = el.data(this.data_attr + '-last-path'),
  46. self = this;
  47. if (last_path == path) return;
  48. if (/\.(gif|jpg|jpeg|tiff|png)([?#].*)?/i.test(path)) {
  49. $(el).css('background-image', 'url('+path+')');
  50. el.data('interchange-last-path', path);
  51. return trigger(path);
  52. }
  53. return $.get(path, function (response) {
  54. el.html(response);
  55. el.data(self.data_attr + '-last-path', path);
  56. trigger();
  57. });
  58. }
  59. }
  60. },
  61. init : function (scope, method, options) {
  62. Foundation.inherit(this, 'throttle random_str');
  63. this.data_attr = this.set_data_attr();
  64. $.extend(true, this.settings, method, options);
  65. this.bindings(method, options);
  66. this.load('images');
  67. this.load('nodes');
  68. },
  69. get_media_hash : function() {
  70. var mediaHash='';
  71. for (var queryName in this.settings.named_queries ) {
  72. mediaHash += matchMedia(this.settings.named_queries[queryName]).matches.toString();
  73. }
  74. return mediaHash;
  75. },
  76. events : function () {
  77. var self = this, prevMediaHash;
  78. $(window)
  79. .off('.interchange')
  80. .on('resize.fndtn.interchange', self.throttle(function () {
  81. var currMediaHash = self.get_media_hash();
  82. if (currMediaHash !== prevMediaHash) {
  83. self.resize();
  84. }
  85. prevMediaHash = currMediaHash;
  86. }, 50));
  87. return this;
  88. },
  89. resize : function () {
  90. var cache = this.cache;
  91. if(!this.images_loaded || !this.nodes_loaded) {
  92. setTimeout($.proxy(this.resize, this), 50);
  93. return;
  94. }
  95. for (var uuid in cache) {
  96. if (cache.hasOwnProperty(uuid)) {
  97. var passed = this.results(uuid, cache[uuid]);
  98. if (passed) {
  99. this.settings.directives[passed
  100. .scenario[1]].call(this, passed.el, passed.scenario[0], function () {
  101. if (arguments[0] instanceof Array) {
  102. var args = arguments[0];
  103. } else {
  104. var args = Array.prototype.slice.call(arguments, 0);
  105. }
  106. passed.el.trigger(passed.scenario[1], args);
  107. });
  108. }
  109. }
  110. }
  111. },
  112. results : function (uuid, scenarios) {
  113. var count = scenarios.length;
  114. if (count > 0) {
  115. var el = this.S('[' + this.add_namespace('data-uuid') + '="' + uuid + '"]');
  116. while (count--) {
  117. var mq, rule = scenarios[count][2];
  118. if (this.settings.named_queries.hasOwnProperty(rule)) {
  119. mq = matchMedia(this.settings.named_queries[rule]);
  120. } else {
  121. mq = matchMedia(rule);
  122. }
  123. if (mq.matches) {
  124. return {el: el, scenario: scenarios[count]};
  125. }
  126. }
  127. }
  128. return false;
  129. },
  130. load : function (type, force_update) {
  131. if (typeof this['cached_' + type] === 'undefined' || force_update) {
  132. this['update_' + type]();
  133. }
  134. return this['cached_' + type];
  135. },
  136. update_images : function () {
  137. var images = this.S('img[' + this.data_attr + ']'),
  138. count = images.length,
  139. i = count,
  140. loaded_count = 0,
  141. data_attr = this.data_attr;
  142. this.cache = {};
  143. this.cached_images = [];
  144. this.images_loaded = (count === 0);
  145. while (i--) {
  146. loaded_count++;
  147. if (images[i]) {
  148. var str = images[i].getAttribute(data_attr) || '';
  149. if (str.length > 0) {
  150. this.cached_images.push(images[i]);
  151. }
  152. }
  153. if (loaded_count === count) {
  154. this.images_loaded = true;
  155. this.enhance('images');
  156. }
  157. }
  158. return this;
  159. },
  160. update_nodes : function () {
  161. var nodes = this.S('[' + this.data_attr + ']').not('img'),
  162. count = nodes.length,
  163. i = count,
  164. loaded_count = 0,
  165. data_attr = this.data_attr;
  166. this.cached_nodes = [];
  167. this.nodes_loaded = (count === 0);
  168. while (i--) {
  169. loaded_count++;
  170. var str = nodes[i].getAttribute(data_attr) || '';
  171. if (str.length > 0) {
  172. this.cached_nodes.push(nodes[i]);
  173. }
  174. if(loaded_count === count) {
  175. this.nodes_loaded = true;
  176. this.enhance('nodes');
  177. }
  178. }
  179. return this;
  180. },
  181. enhance : function (type) {
  182. var i = this['cached_' + type].length;
  183. while (i--) {
  184. this.object($(this['cached_' + type][i]));
  185. }
  186. return $(window).trigger('resize').trigger('resize.fndtn.interchange');
  187. },
  188. convert_directive : function (directive) {
  189. var trimmed = this.trim(directive);
  190. if (trimmed.length > 0) {
  191. return trimmed;
  192. }
  193. return 'replace';
  194. },
  195. parse_scenario : function (scenario) {
  196. // This logic had to be made more complex since some users were using commas in the url path
  197. // So we cannot simply just split on a comma
  198. var directive_match = scenario[0].match(/(.+),\s*(\w+)\s*$/),
  199. media_query = scenario[1];
  200. if (directive_match) {
  201. var path = directive_match[1],
  202. directive = directive_match[2];
  203. }
  204. else {
  205. var cached_split = scenario[0].split(/,\s*$/),
  206. path = cached_split[0],
  207. directive = '';
  208. }
  209. return [this.trim(path), this.convert_directive(directive), this.trim(media_query)];
  210. },
  211. object : function(el) {
  212. var raw_arr = this.parse_data_attr(el),
  213. scenarios = [],
  214. i = raw_arr.length;
  215. if (i > 0) {
  216. while (i--) {
  217. var split = raw_arr[i].split(/\((.*?)(\))$/);
  218. if (split.length > 1) {
  219. var params = this.parse_scenario(split);
  220. scenarios.push(params);
  221. }
  222. }
  223. }
  224. return this.store(el, scenarios);
  225. },
  226. store : function (el, scenarios) {
  227. var uuid = this.random_str(),
  228. current_uuid = el.data(this.add_namespace('uuid', true));
  229. if (this.cache[current_uuid]) return this.cache[current_uuid];
  230. el.attr(this.add_namespace('data-uuid'), uuid);
  231. return this.cache[uuid] = scenarios;
  232. },
  233. trim : function(str) {
  234. if (typeof str === 'string') {
  235. return $.trim(str);
  236. }
  237. return str;
  238. },
  239. set_data_attr: function (init) {
  240. if (init) {
  241. if (this.namespace.length > 0) {
  242. return this.namespace + '-' + this.settings.load_attr;
  243. }
  244. return this.settings.load_attr;
  245. }
  246. if (this.namespace.length > 0) {
  247. return 'data-' + this.namespace + '-' + this.settings.load_attr;
  248. }
  249. return 'data-' + this.settings.load_attr;
  250. },
  251. parse_data_attr : function (el) {
  252. var raw = el.attr(this.attr_name()).split(/\[(.*?)\]/),
  253. i = raw.length,
  254. output = [];
  255. while (i--) {
  256. if (raw[i].replace(/[\W\d]+/, '').length > 4) {
  257. output.push(raw[i]);
  258. }
  259. }
  260. return output;
  261. },
  262. reflow : function () {
  263. this.load('images', true);
  264. this.load('nodes', true);
  265. }
  266. };
  267. }(jQuery, window, window.document));