/*global Modernizr:true*/ /*global assetPath:true*/ /*global Favcount:true*/ /** The main Discourse Application @class Discourse @extends Ember.Application **/ Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { rootElement: '#main', // Whether the app has focus or not hasFocus: true, // Helps with integration tests URL_FIXTURES: {}, getURL: function(url) { // If it's a non relative URL, return it. if (url.indexOf('http') === 0) return url; var u = (Discourse.BaseUri === undefined ? "/" : Discourse.BaseUri); if (u[u.length-1] === '/') { u = u.substring(0, u.length-1); } return u + url; }, resolver: Discourse.Resolver, titleChanged: function() { var title = ""; if (this.get('title')) { title += "" + (this.get('title')) + " - "; } title += Discourse.SiteSettings.title; $('title').text(title); var notifyCount = this.get('notifyCount'); if (notifyCount > 0 && !Discourse.User.currentProp('dynamic_favicon')) { title = "(" + notifyCount + ") " + title; } // chrome bug workaround see: http://stackoverflow.com/questions/2952384/changing-the-window-title-when-focussing-the-window-doesnt-work-in-chrome window.setTimeout(function() { document.title = "."; document.title = title; }, 200); }.observes('title', 'hasFocus', 'notifyCount'), faviconChanged: function() { if(Discourse.User.currentProp('dynamic_favicon')) { new Favcount(Discourse.SiteSettings.favicon_url).set( this.get('notifyCount') ); } }.observes('notifyCount'), // The classes of buttons to show on a post postButtons: function() { return Discourse.SiteSettings.post_menu.split("|").map(function(i) { return (i.replace(/\+/, '').capitalize()); }); }.property('Discourse.SiteSettings.post_menu'), notifyTitle: function(count) { this.set('notifyCount', count); }, /** Establishes global DOM events and bindings via jQuery. @method bindDOMEvents **/ bindDOMEvents: function() { var $html, hasTouch; $html = $('html'); hasTouch = false; if ($html.hasClass('touch')) { hasTouch = true; } if (Modernizr.prefixed("MaxTouchPoints", navigator) > 1) { hasTouch = true; } if (hasTouch) { $html.addClass('discourse-touch'); this.touch = true; this.hasTouch = true; } else { $html.addClass('discourse-no-touch'); this.touch = false; } $('#main').on('click.discourse', '[data-not-implemented=true]', function(e) { e.preventDefault(); alert(I18n.t('not_implemented')); return false; }); $('#main').on('click.discourse', 'a', function(e) { if (e.isDefaultPrevented() || e.shiftKey || e.metaKey || e.ctrlKey) { return; } var $currentTarget = $(e.currentTarget); var href = $currentTarget.attr('href'); if (!href) { return; } if (href === '#') { return; } if ($currentTarget.attr('target')) { return; } if ($currentTarget.data('auto-route')) { return; } // If it's an ember #linkTo skip it if ($currentTarget.hasClass('ember-view')) { return; } if ($currentTarget.hasClass('lightbox')) { return; } if (href.indexOf("mailto:") === 0) { return; } if (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 var csrfToken = $('meta[name=csrf-token]').attr('content'); $.ajaxPrefilter(function(options, originalOptions, xhr) { if (!options.crossDomain) { // This may be delay set csrfToken = csrfToken || $('meta[name=csrf-token]').attr('content'); xhr.setRequestHeader('X-CSRF-Token', csrfToken); } }); bootbox.animate(false); bootbox.backdrop(true); // clicking outside a bootbox modal closes it setInterval(function(){ Discourse.Formatter.updateRelativeAge($('.relative-date')); },60 * 1000); }, /** Log the current user out of Discourse @method logout **/ logout: function() { Discourse.User.logout().then(function() { // Reloading will refresh unbound properties Discourse.KeyValueStore.abandonLocal(); window.location.pathname = Discourse.getURL('/'); }); }, authenticationComplete: function(options) { // TODO, how to dispatch this to the controller without the container? var loginController = Discourse.__container__.lookup('controller:login'); return loginController.authenticationComplete(options); }, loginRequired: function() { return ( Discourse.SiteSettings.login_required && !Discourse.User.current() ); }.property(), redirectIfLoginRequired: function(route) { 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; 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); }); }); } }, /** Start up the Discourse application. @method start **/ start: function() { Discourse.bindDOMEvents(); Discourse.SiteSettings = PreloadStore.get('siteSettings'); Discourse.MessageBus.alwaysLongPoll = Discourse.Environment === "development"; Discourse.MessageBus.start(); Discourse.KeyValueStore.init("discourse_", Discourse.MessageBus); // Developer specific functions Discourse.Development.observeLiveChanges(); Discourse.subscribeUserToNotifications(); } }); Discourse.Router = Discourse.Router.reopen({ location: 'discourse_location' });