PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/js/main.js

https://bitbucket.org/AliGH/browserhacks
JavaScript | 375 lines | 205 code | 72 blank | 98 comment | 29 complexity | 7bdebf3ff52c1479c26494c75ab98c75 MD5 | raw file
  1. (function() {
  2. window.App = {
  3. Models : {},
  4. Collections : {},
  5. Views : {}
  6. };
  7. window.template = function(id) {
  8. return _.template($('#' + id).html());
  9. };
  10. window.vent = _.extend({}, Backbone.Events);
  11. window.$.fn._show = function() {
  12. this.show();
  13. }
  14. window.$.fn._hide = function() {
  15. this.hide();
  16. }
  17. /***************************************************
  18. *
  19. * Models
  20. */
  21. App.Models.Browser = Backbone.Model.extend({
  22. defaults : {
  23. 'hackTypes' : []
  24. }
  25. });
  26. /***************************************************
  27. *
  28. * Collections
  29. */
  30. App.Collections.Browser = Backbone.Collection.extend({
  31. model : App.Models.Browser
  32. });
  33. /***************************************************
  34. *
  35. * Views
  36. */
  37. /*
  38. * All browser
  39. */
  40. App.Views.Master = Backbone.View.extend({
  41. initialize : function() {
  42. // Create all browser views
  43. this.collection.each(function(browser) {
  44. new App.Views.Browser({model: browser});
  45. }, this);
  46. }
  47. });
  48. /*
  49. * A single browser
  50. */
  51. App.Views.Browser = Backbone.View.extend({
  52. hackChilds : null,
  53. initialize : function() {
  54. // The browser
  55. this.$el = $('#' + this.model.get('browser'));
  56. /*
  57. * @TODO [TimPietrusky] - This code is SHIT. Please prettify it.
  58. */
  59. var type = '',
  60. hackParents,
  61. hackTypes = [];
  62. // Type of hacks (e.g. selector, javascript)
  63. hackParents = this.$el.find('[data-type*="-parent"]');
  64. // Create an array of hack types
  65. _.each(hackParents, function(item, index) {
  66. hackTypes.push($(item).attr('data-type').split('-')[0]);
  67. }, this);
  68. // Save the hack types into the model
  69. this.model.set('hackTypes', hackTypes);
  70. // The specific hacks
  71. this.hackChilds = this.$el.find('pre');
  72. // Listen to events
  73. vent.bind("search", this.handleSearch, this);
  74. vent.bind("searchCancelled", this.searchCancelled, this);
  75. vent.bind("searchNumber", this.searchNumber, this);
  76. },
  77. /*
  78. * Show or hide the browser after the search was triggered
  79. */
  80. handleSearch : function(data) {
  81. var names = this.model.get('names'),
  82. matched = false;
  83. // Match the browser
  84. _.each(names, function(browser) {
  85. if (browser.indexOf(data.browser) == 0 && !matched) {
  86. this.show(data);
  87. matched = true;
  88. }
  89. }, this);
  90. // Could not match browser
  91. if (!matched) {
  92. this.hide(data);
  93. }
  94. },
  95. /*
  96. * Show browser + filter versions.
  97. */
  98. show : function(data) {
  99. this.$el.show();
  100. this.$el.addClass('active');
  101. // Filter version
  102. if (data.version != null) {
  103. // data.version = ~~data.version;
  104. // Hide all childs
  105. this.hackChilds.hide();
  106. // Find only matched childs
  107. var matched = this.$el.find('pre[data-version*="'+data.version+'"]');
  108. // Filter the matched childs
  109. _.each(matched, function(item) {
  110. _item = $(item);
  111. // Get all versions
  112. versions = _item.attr('data-version').split('|');
  113. _.each(versions, function(version) {
  114. // Show version which starts with value
  115. if (version.indexOf(data.version) == 0) {
  116. _item.show();
  117. }
  118. }, this);
  119. }, this);
  120. // Find +
  121. _.each(this.$el.find('pre[data-version*="+"]'), function(item) {
  122. _item = $(item);
  123. version_plus = _item.attr('data-version').split('+');
  124. if (version_plus.length == 2) {
  125. version_plus = parseFloat(version_plus[0]);
  126. if (version_plus <= data.version) {
  127. _item.show();
  128. }
  129. }
  130. }, this);
  131. // Find -
  132. _.each(this.$el.find('pre[data-version*="-"]'), function(item) {
  133. _item = $(item);
  134. version_minus = _item.attr('data-version').split('-');
  135. if (version_minus.length == 2) {
  136. version_minus = parseFloat(version_minus[0]);
  137. if (version_minus >= data.version) {
  138. _item.show();
  139. }
  140. }
  141. }, this);
  142. // Change the style of filtered elements
  143. this.$el.addClass('filtered');
  144. // Hide empty hack types
  145. _.each(this.model.get('hackTypes'), function(type) {
  146. // Get the amount of visible hacks
  147. count = this.$el.find('[data-type="'+type+'-childs"] pre:visible').length;
  148. // Hide title if no hacks are visible
  149. if (count == 0) {
  150. this.$el.find('[data-type="'+type+'-parent"] h3').hide();
  151. }
  152. }, this);
  153. // Show all versions
  154. } else {
  155. this.hackChilds.show();
  156. this.$el.removeClass('filtered');
  157. this.$el.find('[data-type*="-parent"] h3').show();
  158. }
  159. },
  160. /*
  161. * Hide browser
  162. */
  163. hide : function(data) {
  164. this.$el.hide();
  165. this.$el.removeClass('active');
  166. },
  167. /*
  168. * Show browser + childs because the search was canceled.
  169. */
  170. searchCancelled : function() {
  171. this.$el.show();
  172. this.$el.removeClass('filtered');
  173. this.$el.removeClass('active');
  174. this.hackChilds.show();
  175. this.$el.find('[data-type*="-parent"] h3').show();
  176. },
  177. /*
  178. * Hide browser when just a number was entered into the search.
  179. */
  180. searchNumber : function(data) {
  181. this.hide();
  182. }
  183. });
  184. /*
  185. * Search
  186. */
  187. App.Views.Search = Backbone.View.extend({
  188. el : 'input#search',
  189. el_parent : null,
  190. el_description : null,
  191. el_buttons : null,
  192. events : {
  193. 'keyup' : 'keyup',
  194. 'focus' : 'focus',
  195. 'blur' : 'blur'
  196. },
  197. isSearching : false,
  198. regex_split : null,
  199. value : null,
  200. split : null,
  201. browser : null,
  202. version : null,
  203. timeoutId : null,
  204. initialize : function() {
  205. this.regex_split = new RegExp("([a-z\\s]+)", "gm");
  206. // Get elements
  207. this.el_parent = $();
  208. this.el_description = $('article[data-type="description"]');
  209. this.el_buttons = $('div[data-type="top-buttons"]');
  210. },
  211. /*
  212. * There was some interaction with the search field.
  213. */
  214. keyup : function(e) {
  215. this.value = this.$el.val().toLowerCase().trim();
  216. // Something was entered
  217. if (this.value != '') {
  218. this.isSearching = true;
  219. // Split Browser from version
  220. this.split = this.value.split(this.regex_split);
  221. // Searched for a browser not a number
  222. if (this.split.length > 1) {
  223. // Get the browser
  224. this.browser = this.split[1].trim();
  225. // Get the version
  226. if (this.split[2] != "") {
  227. this.version = this.split[2].trim();
  228. } else {
  229. this.version = null;
  230. }
  231. vent.trigger("search", {'browser' : this.browser, 'version' : this.version});
  232. // Searched for a number
  233. } else {
  234. this.version = this.split;
  235. vent.trigger("searchNumber", {'version' : this.version});
  236. }
  237. // Hide description
  238. this.el_description.hide();
  239. // Field is empty
  240. } else {
  241. this.isSearching = false;
  242. // Show all browser
  243. vent.trigger("searchCancelled");
  244. // Show description
  245. this.el_description.show();
  246. }
  247. },
  248. /*
  249. * Handle focus obtained.
  250. */
  251. focus : function(e) {
  252. // Hide buttons
  253. this.el_buttons.hide();
  254. clearTimeout(this.timeoutId);
  255. // Hide description
  256. this.el_description.hide();
  257. // Search active
  258. $('div[data-type="search"]').addClass('active');
  259. },
  260. /*
  261. * Handle focus lost.
  262. */
  263. blur : function(e) {
  264. // Search inactive
  265. $('div[data-type="search"]').removeClass('active');
  266. // Show buttons & description
  267. this.timeoutId = setTimeout(_.bind(function() {
  268. this.el_buttons.show();
  269. if (!this.isSearching) {
  270. this.el_description.show();
  271. }
  272. }, this), 175);
  273. }
  274. });
  275. /*--------------------------------------------------
  276. *
  277. * Start the app
  278. */
  279. // A collection of browsers
  280. var collection_browser = new App.Collections.Browser([
  281. {'browser' : 'ch', 'names' : ['chrome', 'ch']},
  282. {'browser' : 'fx', 'names' : ['firefox', 'mozilla firefox', 'ff']},
  283. {'browser' : 'ie', 'names' : ['internet explorer', 'ie']},
  284. {'browser' : 'sa', 'names' : ['safari', 'apple safari']},
  285. {'browser' : 'op', 'names' : ['opera', 'op']}
  286. ]);
  287. // Holds all browser
  288. var view_master = new App.Views.Master({collection : collection_browser});
  289. // Handles the search
  290. var view_search = new App.Views.Search();
  291. /*--------------------------------------------------
  292. *
  293. * Fallback JS animation for the CSS rotating catch-phrase
  294. */
  295. if (!Modernizr.cssanimations){
  296. var tips = ["_","-", "£", "¬", "¦", "!", "$", "&", "*", "(", ")", "=", "%", "+", "@", ",", ".", "/", "`", "[", "]", "#", "~", "?", ":", "<", ">", "|"];
  297. setInterval(function() {
  298. var i = Math.round((Math.random()) * tips.length);
  299. if (i == tips.length) --i;
  300. $(".catch-phrase__anim").html(tips[i]);
  301. }, 400);
  302. }
  303. })();