PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/files/flickrbomb/1.0/js/flickrbomb.js

https://gitlab.com/Mirros/jsdelivr
JavaScript | 419 lines | 295 code | 80 blank | 44 comment | 30 complexity | e7aa5227b739cb4bc1798eb18a15e0bf MD5 | raw file
  1. /*
  2. * flickrBomb v1
  3. * www.ZURB.com/playground
  4. * Copyright 2011, ZURB
  5. * Free to use under the MIT license.
  6. * http://www.opensource.org/licenses/mit-license.php
  7. */
  8. // @param [key] Optionally pass a Flickr API key on instantiation, or just hardcode it below.
  9. var flickrBomb = function flickrBomb(key) {
  10. if (!(this instanceof flickrBomb)) return new flickrBomb(arguments[0]);
  11. var flickrbombAPIkey = key || '66b5c17019403c96779e8fe88d5b576d', // replace with your Flickr API key (fallback)
  12. /* flickrbombLicenseTypes values (comma delimited)
  13. empty means all license types
  14. 0: All Rights Reserved
  15. 4: Attribution License http://creativecommons.org/licenses/by/2.0/
  16. 6: Attribution-NoDerivs License http://creativecommons.org/licenses/by-nd/2.0/
  17. 3: Attribution-NonCommercial-NoDerivs License http://creativecommons.org/licenses/by-nc-nd/2.0/
  18. 2: Attribution-NonCommercial License http://creativecommons.org/licenses/by-nc/2.0/
  19. 1: Attribution-NonCommercial-ShareAlike License http://creativecommons.org/licenses/by-nc-sa/2.0/
  20. 5: Attribution-ShareAlike License http://creativecommons.org/licenses/by-sa/2.0/
  21. 7: No known copyright restrictions http://www.flickr.com/commons/usage/
  22. 8: United States Government Work http://www.usa.gov/copyright.shtml
  23. ex. flickrbombLicenseTypes = '5,7,8';
  24. */
  25. flickrbombLicenseTypes = '',
  26. localStorage,
  27. localSync;
  28. if (!flickrbombAPIkey) return new Error('flickr API key required');
  29. function supports_local_storage() { try { return 'localStorage' in window && window.localStorage !== null; } catch(e){ return false; } }
  30. if (supports_local_storage()) {
  31. // A simple module to replace `Backbone.sync` with *localStorage*-based
  32. // persistence. Models are given GUIDS, and saved into a JSON object. Simple
  33. // as that.
  34. // Generate four random hex digits.
  35. var S4 = function() {
  36. return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
  37. };
  38. // Generate a pseudo-GUID by concatenating random hexadecimal.
  39. var guid = function() {
  40. return (S4()+S4()+'-'+S4()+'-'+S4()+'-'+S4()+'-'+S4()+S4()+S4());
  41. };
  42. // Our Store is represented by a single JS object in *localStorage*. Create it
  43. // with a meaningful name, like the name you'd give a table.
  44. var Store = function(name) {
  45. this.name = name;
  46. var store = window.localStorage.getItem(this.name);
  47. this.data = (store && JSON.parse(store)) || {};
  48. };
  49. _.extend(Store.prototype, {
  50. // Save the current state of the **Store** to *localStorage*.
  51. save: function() {
  52. window.localStorage.setItem(this.name, JSON.stringify(this.data));
  53. },
  54. // Add a model, giving it a (hopefully)-unique GUID, if it doesn't already
  55. // have an id of it's own.
  56. create: function(model) {
  57. if (!model.id) model.id = model.attributes.id = guid();
  58. this.data[model.id] = model;
  59. this.save();
  60. return model;
  61. },
  62. // Update a model by replacing its copy in `this.data`.
  63. update: function(model) {
  64. this.data[model.id] = model;
  65. this.save();
  66. return model;
  67. },
  68. // Retrieve a model from `this.data` by id.
  69. find: function(model) {
  70. return this.data[model.id];
  71. },
  72. // Return the array of all models currently in storage.
  73. findAll: function() {
  74. return _.values(this.data);
  75. },
  76. // Delete a model from `this.data`, returning it.
  77. destroy: function(model) {
  78. delete this.data[model.id];
  79. this.save();
  80. return model;
  81. }
  82. });
  83. // Override `Model.sync`, `Collection.sync`, or `Backbone.sync` to use delegate to the model or collection's
  84. // *localStorage* property, which should be an instance of `Store`.
  85. localSync = function(method, model, cb) {
  86. var resp,
  87. store = model.localStorage || model.collection.localStorage;
  88. switch (method) {
  89. case 'read': resp = model.id ? store.find(model) : store.findAll(); break;
  90. case 'create': resp = store.create(model); break;
  91. case 'update': resp = store.update(model); break;
  92. case 'delete': resp = store.destroy(model); break;
  93. }
  94. if (resp && cb && cb.success) {
  95. cb.success(resp);
  96. } else {
  97. // Swallow errors for now
  98. // error('Record not found');
  99. }
  100. };
  101. localStorage = new Store('flickrBombImages');
  102. } else {
  103. localStorage = null;
  104. }
  105. var FlickrImage = Backbone.Model.extend({
  106. sync: localSync,
  107. fullsize_url: function () {
  108. return this.image_url('medium');
  109. },
  110. thumb_url: function () {
  111. return this.image_url('square');
  112. },
  113. image_url: function (size) {
  114. var size_code;
  115. switch (size) {
  116. case 'square': size_code = '_s'; break; // 75x75
  117. case 'medium': size_code = '_z'; break; // 640 on the longest side
  118. case 'large': size_code = '_b'; break; // 1024 on the longest side
  119. default: size_code = '';
  120. }
  121. return 'http://farm' + this.get('farm') + '.static.flickr.com/' + this.get('server') + '/' + this.get('id') + '_' + this.get('secret') + size_code + '.jpg';
  122. }
  123. }),
  124. Image = Backbone.Model.extend({
  125. sync: localSync,
  126. localStorage: localStorage,
  127. initialize: function () {
  128. _.bindAll(this, 'loadFirstImage');
  129. this.flickrImages = new FlickrImages();
  130. this.flickrImages.fetch(this.get('keywords'), this.loadFirstImage);
  131. this.set({id: this.get('id') || this.get('keywords')});
  132. this.bind('change:src', this.changeSrc);
  133. },
  134. changeSrc: function () {
  135. this.save();
  136. },
  137. loadFirstImage: function () {
  138. if (this.get('src') === undefined) {
  139. this.set({src: this.flickrImages.first().image_url()});
  140. }
  141. }
  142. }),
  143. FlickrImages = Backbone.Collection.extend({
  144. sync: localSync,
  145. model: FlickrImage,
  146. key: flickrbombAPIkey,
  147. page: 1,
  148. fetch: function (keywords, success) {
  149. var self = this;
  150. success = success || $.noop;
  151. this.keywords = keywords || this.keywords;
  152. $.ajax({
  153. url: 'http://api.flickr.com/services/rest/',
  154. data: {
  155. api_key: self.key,
  156. format: 'json',
  157. method: 'flickr.photos.search',
  158. tags: this.keywords,
  159. per_page: 9,
  160. page: this.page,
  161. license: flickrbombLicenseTypes
  162. },
  163. dataType: 'jsonp',
  164. jsonp: 'jsoncallback',
  165. success: function (response) {
  166. self.add(response.photos.photo);
  167. success();
  168. }
  169. });
  170. },
  171. nextPage: function (callback) {
  172. this.page += 1;
  173. this.remove(this.models);
  174. this.fetch(null, callback);
  175. },
  176. prevPage: function(callback) {
  177. if (this.page > 1) {this.page -= 1;}
  178. this.remove(this.models);
  179. this.fetch(null, callback);
  180. }
  181. }),
  182. FlickrImageView = Backbone.View.extend({
  183. tagName: 'a',
  184. template: _.template("<img src='<%= thumb_url() %>' />"),
  185. className: 'photo',
  186. events: {'click': 'setImageSrc'},
  187. render: function() {
  188. $(this.el).html(this.template(this.model));
  189. $(this.el).addClass('photo');
  190. return this;
  191. },
  192. setImageSrc: function (event) {
  193. this.options.image.set({'src': this.model.fullsize_url()});
  194. }
  195. }),
  196. ImageView = Backbone.View.extend({
  197. tagName: 'div',
  198. className: 'flickrbombContainer',
  199. lock: false,
  200. template: _.template('<div id="<%= id %>" class="flickrbombWrapper"><img class="flickrbomb" src="" /><a href="#" title="Setup" class="setupIcon"></a></div><div class="flickrbombFlyout"><div class="flickrbombContent"><a href="#" title="Previous Page" class="prev">&#9664;</a><a href="#" title="Next Page" class="next">&#9654;</a></div></div>'),
  201. initialize: function (options) {
  202. _.bindAll(this, 'addImage', 'updateSrc', 'setDimentions', 'updateDimentions');
  203. var keywords = options.img.attr('src').replace('flickr://', '');
  204. this.$el = $(this.el);
  205. this.ratio = this.options.img.attr('data-ratio');
  206. this.image = new Image({keywords: keywords, id: options.img.attr('id')});
  207. this.image.flickrImages.bind('add', this.addImage);
  208. this.image.bind('change:src', this.updateSrc);
  209. },
  210. events: {
  211. 'click .setupIcon' : 'clickSetup',
  212. 'click .flickrbombFlyout a.photo' : 'selectImage',
  213. 'click .flickrbombFlyout a.next' : 'nextFlickrPhotos',
  214. 'click .flickrbombFlyout a.prev' : 'prevFlickrPhotos'
  215. },
  216. render: function() {
  217. $(this.el).html(this.template({ id: this.image.id.replace(' ', '') }));
  218. this.image.fetch();
  219. if (!this.ratio) {
  220. this.resize();
  221. } else {
  222. this.$('.flickrbombWrapper').append('<img style="width: 100%;" class="placeholder" src="http://placehold.it/' + this.ratio + '" />');
  223. }
  224. return this;
  225. },
  226. updateSrc: function (model, src) {
  227. var self = this;
  228. this.$('img.flickrbomb')
  229. .css({top: 'auto', left: 'auto', width: 'auto', height: 'auto'})
  230. .attr('src', '')
  231. .bind('load', self.setDimentions)
  232. .attr('src', src);
  233. },
  234. setDimentions: function (event) {
  235. this.image.set({
  236. width: this.$('img').width(),
  237. height: this.$('img').height()
  238. });
  239. this.updateDimentions(this.image);
  240. $(event.target).unbind('load');
  241. },
  242. updateDimentions: function () {
  243. var image = this.$('img.flickrbomb'),
  244. flickrWidth = this.image.get('width'),
  245. flickrHeight = this.image.get('height'),
  246. flickrAspectRatio = flickrWidth / flickrHeight,
  247. clientWidth = this.$('div.flickrbombWrapper').width(),
  248. clientHeight = this.$('div.flickrbombWrapper').height(),
  249. clientAspectRatio = clientWidth / clientHeight;
  250. if (flickrAspectRatio < clientAspectRatio) {
  251. image.css({
  252. width: '100%',
  253. height: null
  254. });
  255. image.css({
  256. top: ((clientHeight - image.height()) / 2) + 'px',
  257. left: null
  258. });
  259. } else {
  260. image.css({
  261. height: '100%',
  262. width: null
  263. });
  264. image.css({
  265. left: ((clientWidth - image.width()) / 2) + 'px',
  266. top: null
  267. });
  268. }
  269. },
  270. addImage: function (image) {
  271. this.flickrImageView = new FlickrImageView({model: image, image: this.image});
  272. this.$('.flickrbombFlyout').append(this.flickrImageView.render().el);
  273. },
  274. clickSetup: function (event) {
  275. event.preventDefault();
  276. this.toggleFlyout();
  277. },
  278. toggleFlyout: function (event) {
  279. this.$('.flickrbombFlyout').toggle();
  280. },
  281. selectImage: function (event) {
  282. event.preventDefault();
  283. this.toggleFlyout();
  284. },
  285. nextFlickrPhotos: function (event) {
  286. event.preventDefault();
  287. var self = this;
  288. if(!this.lock) {
  289. this.lock = true;
  290. this.$('.flickrbombFlyout').find('a.photo').remove();
  291. this.image.flickrImages.nextPage(function() {
  292. self.lock = false;
  293. });
  294. }
  295. },
  296. prevFlickrPhotos: function (event) {
  297. event.preventDefault();
  298. var self = this;
  299. if(!this.lock) {
  300. this.lock = true;
  301. this.$('.flickrbombFlyout').find('a.photo').remove();
  302. this.image.flickrImages.prevPage(function() {
  303. self.lock = false;
  304. });
  305. }
  306. },
  307. resize: function () {
  308. this.$('div.flickrbombWrapper').css({
  309. width: this.width() + 'px',
  310. height: this.height() + 'px'
  311. });
  312. },
  313. width: function () {
  314. return parseInt(this.options.img.width(), 10);
  315. },
  316. height: function () {
  317. return parseInt(this.options.img.height(), 10);
  318. }
  319. });
  320. // Replace any placeholders with flickr images
  321. this.bomb = function() {
  322. var self = this;
  323. $("img[src^='flickr://']").each(function () {
  324. var img = $(this),
  325. imageView = new ImageView({img: img});
  326. img.replaceWith(imageView.render().el);
  327. });
  328. };
  329. // Listener to close any open flickrbomb menus if you click on body
  330. $('body').click(function(event) {
  331. if (!$(event.target).closest('.setupIcon').length && !$(event.target).closest('.flickrbombFlyout').length) {
  332. $('.flickrbombFlyout').hide();
  333. }
  334. });
  335. };
  336. // Bomb on sight! Just on include.
  337. if ($("img[src^='flickr://']").length) flickrBomb().bomb();