From 274743b8770b21907cc549eb166d9eb61e87311d Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 3 Dec 2013 14:22:32 -0500 Subject: [PATCH] Refactor: Move initialization code into separate initializers that use the API we developed for plugins. --- app/assets/javascripts/discourse.js | 105 +----------------- .../initializers/1_init_message_bus.js | 8 ++ .../discourse/initializers/bootbox.js | 11 ++ .../initializers/click_interceptor.js | 32 ++++++ .../discourse/initializers/csrf_token.js | 19 ++++ .../discourse/initializers/focus_event.js | 16 +++ .../discourse/initializers/init_mobile.js | 7 ++ .../initializers/live_development.js | 73 ++++++++++++ .../discourse/initializers/relative_ages.js | 12 ++ .../subscribe_user_notifications.js | 32 ++++++ .../javascripts/discourse/lib/development.js | 81 -------------- .../javascripts/discourse/lib/mobile.js | 6 +- .../javascripts/discourse/mixins/ajax.js | 4 +- test/javascripts/test_helper.js | 2 +- 14 files changed, 219 insertions(+), 189 deletions(-) create mode 100644 app/assets/javascripts/discourse/initializers/1_init_message_bus.js create mode 100644 app/assets/javascripts/discourse/initializers/bootbox.js create mode 100644 app/assets/javascripts/discourse/initializers/click_interceptor.js create mode 100644 app/assets/javascripts/discourse/initializers/csrf_token.js create mode 100644 app/assets/javascripts/discourse/initializers/focus_event.js create mode 100644 app/assets/javascripts/discourse/initializers/init_mobile.js create mode 100644 app/assets/javascripts/discourse/initializers/live_development.js create mode 100644 app/assets/javascripts/discourse/initializers/relative_ages.js create mode 100644 app/assets/javascripts/discourse/initializers/subscribe_user_notifications.js delete mode 100644 app/assets/javascripts/discourse/lib/development.js diff --git a/app/assets/javascripts/discourse.js b/app/assets/javascripts/discourse.js index ae189896d..65db695ae 100644 --- a/app/assets/javascripts/discourse.js +++ b/app/assets/javascripts/discourse.js @@ -11,9 +11,6 @@ Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { rootElement: '#main', - // Whether the app has focus or not - hasFocus: true, - // Helps with integration tests URL_FIXTURES: {}, @@ -69,61 +66,6 @@ Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { this.set('notifyCount', count); }, - /** - Establishes global DOM events and bindings via jQuery. - - @method bindDOMEvents - **/ - bindDOMEvents: function() { - $('#main').on('click.discourse', 'a', function(e) { - if (e.isDefaultPrevented() || e.shiftKey || e.metaKey || e.ctrlKey) { return; } - - var $currentTarget = $(e.currentTarget), - href = $currentTarget.attr('href'); - - if (!href || - href === '#' || - $currentTarget.attr('target') || - $currentTarget.data('ember-action') || - $currentTarget.data('auto-route') || - $currentTarget.hasClass('ember-view') || - $currentTarget.hasClass('lightbox') || - href.indexOf("mailto:") === 0 || - (href.match(/^http[s]?:\/\//i) && !href.match(new RegExp("^http:\\/\\/" + window.location.hostname, "i")))) { - return; - } - - e.preventDefault(); - Discourse.URL.routeTo(href); - return false; - }); - - $(window).focus(function() { - Discourse.set('hasFocus', true); - Discourse.set('notify', false); - }).blur(function() { - Discourse.set('hasFocus', false); - }); - - // Add a CSRF token to all AJAX requests - Discourse.csrfToken = $('meta[name=csrf-token]').attr('content'); - - $.ajaxPrefilter(function(options, originalOptions, xhr) { - if (!options.crossDomain) { - xhr.setRequestHeader('X-CSRF-Token', Discourse.csrfToken); - } - }); - - bootbox.animate(false); - bootbox.backdrop(true); // clicking outside a bootbox modal closes it - - Discourse.Mobile.init(); - - setInterval(function(){ - Discourse.Formatter.updateRelativeAge($('.relative-date')); - },60 * 1000); - }, - /** Log the current user out of Discourse @@ -153,37 +95,6 @@ Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { if(this.get('loginRequired')) { route.transitionTo('login'); } }, - /** - Subscribes the current user to receive message bus notifications - **/ - subscribeUserToNotifications: function() { - var user = Discourse.User.current(); - if (user) { - var bus = Discourse.MessageBus; - bus.callbackInterval = Discourse.SiteSettings.polling_interval; - bus.enableLongPolling = true; - bus.baseUrl = Discourse.getURL("/"); - - if (user.admin || user.moderator) { - bus.subscribe("/flagged_counts", function(data) { - user.set('site_flagged_posts_count', data.total); - }); - } - bus.subscribe("/notification/" + user.get('id'), (function(data) { - user.set('unread_notifications', data.unread_notifications); - user.set('unread_private_messages', data.unread_private_messages); - }), user.notification_channel_position); - - bus.subscribe("/categories", function(data){ - var site = Discourse.Site.current(); - _.each(data.categories,function(c){ - site.updateCategory(c); - }); - }); - - } - }, - /** Add an initializer hook for after the Discourse Application starts up. @@ -196,24 +107,16 @@ Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { }, /** - Start up the Discourse application. + Start up the Discourse application by running all the initializers we've defined. @method start **/ start: function() { - Discourse.bindDOMEvents(); - Discourse.MessageBus.alwaysLongPoll = Discourse.Environment === "development"; - Discourse.MessageBus.start(); - Discourse.KeyValueStore.init("discourse_", Discourse.MessageBus); - - // Developer specific functions - Discourse.Development.observeLiveChanges(); - Discourse.subscribeUserToNotifications(); - - if (Discourse.initializers) { + var initializers = this.initializers; + if (initializers) { var self = this; Em.run.next(function() { - Discourse.initializers.forEach(function (init) { + initializers.forEach(function (init) { init.call(self); }); }); diff --git a/app/assets/javascripts/discourse/initializers/1_init_message_bus.js b/app/assets/javascripts/discourse/initializers/1_init_message_bus.js new file mode 100644 index 000000000..68a678f8b --- /dev/null +++ b/app/assets/javascripts/discourse/initializers/1_init_message_bus.js @@ -0,0 +1,8 @@ +/** + Initialize the message bus to receive messages. +**/ +Discourse.addInitializer(function() { + Discourse.MessageBus.alwaysLongPoll = Discourse.Environment === "development"; + Discourse.MessageBus.start(); + Discourse.KeyValueStore.init("discourse_", Discourse.MessageBus); +}); diff --git a/app/assets/javascripts/discourse/initializers/bootbox.js b/app/assets/javascripts/discourse/initializers/bootbox.js new file mode 100644 index 000000000..9d8b73fe2 --- /dev/null +++ b/app/assets/javascripts/discourse/initializers/bootbox.js @@ -0,0 +1,11 @@ +/** + Default settings for bootbox +**/ +Discourse.addInitializer(function() { + + bootbox.animate(false); + + // clicking outside a bootbox modal closes it + bootbox.backdrop(true); + +}); \ No newline at end of file diff --git a/app/assets/javascripts/discourse/initializers/click_interceptor.js b/app/assets/javascripts/discourse/initializers/click_interceptor.js new file mode 100644 index 000000000..81f7050da --- /dev/null +++ b/app/assets/javascripts/discourse/initializers/click_interceptor.js @@ -0,0 +1,32 @@ +/** + Discourse does some server side rendering of HTML, such as the `cooked` contents of + posts. The downside of this in an Ember app is the links will not go through the router. + This jQuery code intercepts clicks on those links and routes them properly. +**/ + +Discourse.addInitializer(function() { + + $('#main').on('click.discourse', 'a', function(e) { + if (e.isDefaultPrevented() || e.shiftKey || e.metaKey || e.ctrlKey) { return; } + + var $currentTarget = $(e.currentTarget), + href = $currentTarget.attr('href'); + + if (!href || + href === '#' || + $currentTarget.attr('target') || + $currentTarget.data('ember-action') || + $currentTarget.data('auto-route') || + $currentTarget.hasClass('ember-view') || + $currentTarget.hasClass('lightbox') || + href.indexOf("mailto:") === 0 || + (href.match(/^http[s]?:\/\//i) && !href.match(new RegExp("^http:\\/\\/" + window.location.hostname, "i")))) { + return; + } + + e.preventDefault(); + Discourse.URL.routeTo(href); + return false; + }); + +}); \ No newline at end of file diff --git a/app/assets/javascripts/discourse/initializers/csrf_token.js b/app/assets/javascripts/discourse/initializers/csrf_token.js new file mode 100644 index 000000000..36ff38de8 --- /dev/null +++ b/app/assets/javascripts/discourse/initializers/csrf_token.js @@ -0,0 +1,19 @@ +/** + Append our CSRF token to AJAX requests when necessary. +**/ + +Discourse.addInitializer(function() { + + var session = Discourse.Session; + + // Add a CSRF token to all AJAX requests + session.currentProp('csrfToken', $('meta[name=csrf-token]').attr('content')); + + $.ajaxPrefilter(function(options, originalOptions, xhr) { + if (!options.crossDomain) { + xhr.setRequestHeader('X-CSRF-Token', session.currentProp('csrfToken')); + } + }); + +}); + diff --git a/app/assets/javascripts/discourse/initializers/focus_event.js b/app/assets/javascripts/discourse/initializers/focus_event.js new file mode 100644 index 000000000..6c6ad80aa --- /dev/null +++ b/app/assets/javascripts/discourse/initializers/focus_event.js @@ -0,0 +1,16 @@ +/** + Keep track of when the browser is in focus. +**/ +Discourse.addInitializer(function() { + + // Default to true + this.set('hasFocus', true); + + var self = this; + $(window).focus(function() { + self.setProperties({hasFocus: true, notify: false}); + }).blur(function() { + self.set('hasFocus', false); + }); + +}); \ No newline at end of file diff --git a/app/assets/javascripts/discourse/initializers/init_mobile.js b/app/assets/javascripts/discourse/initializers/init_mobile.js new file mode 100644 index 000000000..d16f4ca9a --- /dev/null +++ b/app/assets/javascripts/discourse/initializers/init_mobile.js @@ -0,0 +1,7 @@ +/** + Initializes the `Discourse.Mobile` helper object. +**/ +Discourse.addInitializer(function() { + Discourse.Mobile.init(); +}); + diff --git a/app/assets/javascripts/discourse/initializers/live_development.js b/app/assets/javascripts/discourse/initializers/live_development.js new file mode 100644 index 000000000..28fecc67c --- /dev/null +++ b/app/assets/javascripts/discourse/initializers/live_development.js @@ -0,0 +1,73 @@ +/** + Use the message bus for live reloading of components for faster development. +**/ +Discourse.addInitializer(function() { + + console.log('live reloading'); + + // subscribe to any site customizations that are loaded + $('link.custom-css').each(function() { + var split = this.href.split("/"), + id = split[split.length - 1].split(".css")[0], + self = this; + + return Discourse.MessageBus.subscribe("/file-change/" + id, function(data) { + if (!$(self).data('orig')) { + $(self).data('orig', self.href); + } + var orig = $(self).data('orig'), + sp = orig.split(".css?"); + + self.href = sp[0] + ".css?" + data; + }); + }); + + // Custom header changes + $('header.custom').each(function() { + var header = $(this); + return Discourse.MessageBus.subscribe("/header-change/" + $(this).data('key'), function(data) { + return header.html(data); + }); + }); + + // Observe file changes + return Discourse.MessageBus.subscribe("/file-change", function(data) { + Ember.TEMPLATES.empty = Handlebars.compile("
"); + _.each(data,function(me,idx) { + + if (me === "refresh") { + // Refresh if necessary + document.location.reload(true); + } else if (me.name.substr(-10) === "handlebars") { + + // Reload handlebars + var js = me.name.replace(".handlebars", "").replace("app/assets/javascripts", "/assets"); + $LAB.script(js + "?hash=" + me.hash).wait(function() { + var templateName; + templateName = js.replace(".js", "").replace("/assets/", ""); + return _.each(Ember.View.views, function(view) { + if (view.get('templateName') === templateName) { + view.set('templateName', 'empty'); + view.rerender(); + Em.run.schedule('afterRender', function() { + view.set('templateName', templateName); + view.rerender(); + }); + } + }); + }); + + } else { + $('link').each(function() { + if (this.href.match(me.name) && me.hash) { + if (!$(this).data('orig')) { + $(this).data('orig', this.href); + } + this.href = $(this).data('orig') + "&hash=" + me.hash; + } + }); + } + }); + }); + +}); \ No newline at end of file diff --git a/app/assets/javascripts/discourse/initializers/relative_ages.js b/app/assets/javascripts/discourse/initializers/relative_ages.js new file mode 100644 index 000000000..331d3cd66 --- /dev/null +++ b/app/assets/javascripts/discourse/initializers/relative_ages.js @@ -0,0 +1,12 @@ +/** + Updates the relative ages of dates on the screen. + +**/ +Discourse.addInitializer(function() { + + setInterval(function(){ + Discourse.Formatter.updateRelativeAge($('.relative-date')); + }, 60 * 1000); + +}); + diff --git a/app/assets/javascripts/discourse/initializers/subscribe_user_notifications.js b/app/assets/javascripts/discourse/initializers/subscribe_user_notifications.js new file mode 100644 index 000000000..048d40cdd --- /dev/null +++ b/app/assets/javascripts/discourse/initializers/subscribe_user_notifications.js @@ -0,0 +1,32 @@ +/** + Updates the relative ages of dates on the screen. + +**/ +Discourse.addInitializer(function() { + + var user = Discourse.User.current(); + if (user) { + var bus = Discourse.MessageBus; + bus.callbackInterval = Discourse.SiteSettings.polling_interval; + bus.enableLongPolling = true; + bus.baseUrl = Discourse.getURL("/"); + + if (user.admin || user.moderator) { + bus.subscribe("/flagged_counts", function(data) { + user.set('site_flagged_posts_count', data.total); + }); + } + bus.subscribe("/notification/" + user.get('id'), (function(data) { + user.set('unread_notifications', data.unread_notifications); + user.set('unread_private_messages', data.unread_private_messages); + }), user.notification_channel_position); + + bus.subscribe("/categories", function(data){ + var site = Discourse.Site.current(); + _.each(data.categories,function(c){ + site.updateCategory(c); + }); + }); + } +}); + diff --git a/app/assets/javascripts/discourse/lib/development.js b/app/assets/javascripts/discourse/lib/development.js deleted file mode 100644 index 4040a9da6..000000000 --- a/app/assets/javascripts/discourse/lib/development.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - Functions to help development of Discourse, such as inserting probes - - @class Development - @namespace Discourse - @module Discourse -**/ -Discourse.Development = { - - /** - Use the message bus for live reloading of components for faster development. - - @method observeLiveChanges - **/ - observeLiveChanges: function() { - - // subscribe to any site customizations that are loaded - $('link.custom-css').each(function() { - var id, split, stylesheet, - _this = this; - split = this.href.split("/"); - id = split[split.length - 1].split(".css")[0]; - stylesheet = this; - return Discourse.MessageBus.subscribe("/file-change/" + id, function(data) { - var orig, sp; - if (!$(stylesheet).data('orig')) { - $(stylesheet).data('orig', stylesheet.href); - } - orig = $(stylesheet).data('orig'); - sp = orig.split(".css?"); - stylesheet.href = sp[0] + ".css?" + data; - }); - }); - - // Custom header changes - $('header.custom').each(function() { - var header; - header = $(this); - return Discourse.MessageBus.subscribe("/header-change/" + ($(this).data('key')), function(data) { - return header.html(data); - }); - }); - - // Observe file changes - return Discourse.MessageBus.subscribe("/file-change", function(data) { - Ember.TEMPLATES.empty = Handlebars.compile("
"); - _.each(data,function(me,idx) { - var js; - if (me === "refresh") { - return document.location.reload(true); - } else if (me.name.substr(-10) === "handlebars") { - js = me.name.replace(".handlebars", "").replace("app/assets/javascripts", "/assets"); - return $LAB.script(js + "?hash=" + me.hash).wait(function() { - var templateName; - templateName = js.replace(".js", "").replace("/assets/", ""); - return _.each(Ember.View.views, function(view) { - if (view.get('templateName') === templateName) { - view.set('templateName', 'empty'); - view.rerender(); - Em.run.schedule('afterRender', function() { - view.set('templateName', templateName); - view.rerender(); - }); - } - }); - }); - } else { - return $('link').each(function() { - if (this.href.match(me.name) && me.hash) { - if (!$(this).data('orig')) { - $(this).data('orig', this.href); - } - this.href = $(this).data('orig') + "&hash=" + me.hash; - } - }); - } - }); - }); - } - -}; diff --git a/app/assets/javascripts/discourse/lib/mobile.js b/app/assets/javascripts/discourse/lib/mobile.js index 3678ccd24..c1b904970 100644 --- a/app/assets/javascripts/discourse/lib/mobile.js +++ b/app/assets/javascripts/discourse/lib/mobile.js @@ -1,12 +1,10 @@ /** - A class that is responsible for logic related to mobile devices. + An object that is responsible for logic related to mobile devices. - @class Mobile @namespace Discourse - @module Discourse + @module Mobile **/ Discourse.Mobile = { - isMobileDevice: false, mobileView: false, diff --git a/app/assets/javascripts/discourse/mixins/ajax.js b/app/assets/javascripts/discourse/mixins/ajax.js index 80db39d51..85b3814c2 100644 --- a/app/assets/javascripts/discourse/mixins/ajax.js +++ b/app/assets/javascripts/discourse/mixins/ajax.js @@ -59,7 +59,7 @@ Discourse.Ajax = Em.Mixin.create({ // note: for bad CSRF we don't loop an extra request right away. // this allows us to eliminate the possibility of having a loop. if (xhr.status === 403 && xhr.responseText === "['BAD CSRF']") { - Discourse.csrfToken = null; + Discourse.Session.current().set('csrfToken', null); } // If it's a parseerror, don't reject @@ -79,7 +79,7 @@ Discourse.Ajax = Em.Mixin.create({ // For cached pages we strip out CSRF tokens, need to round trip to server prior to sending the // request (bypass for GET, not needed) - if(args.type && args.type !== 'GET' && !Discourse.csrfToken){ + if(args.type && args.type !== 'GET' && !Discourse.Session.currentProp('csrfToken')){ return Ember.Deferred.promise(function(promise){ $.ajax(Discourse.getURL('/session/csrf')) .success(function(result){ diff --git a/test/javascripts/test_helper.js b/test/javascripts/test_helper.js index 5c36c21e6..190b849b2 100644 --- a/test/javascripts/test_helper.js +++ b/test/javascripts/test_helper.js @@ -104,7 +104,7 @@ d.write('