2013-02-20 13:15:50 -05:00
/*global Modernizr:true*/
2013-02-25 11:18:10 +11:00
/*global assetPath:true*/
2013-08-18 21:52:13 +10:00
/*global Favcount:true*/
2013-02-25 11:18:10 +11:00
2013-02-26 14:54:43 -05:00
/ * *
The main Discourse Application
2013-02-20 13:15:50 -05:00
2013-02-26 14:54:43 -05:00
@ class Discourse
@ extends Ember . Application
* * /
2013-07-26 15:12:40 -04:00
Discourse = Ember . Application . createWithMixins ( Discourse . Ajax , {
2013-02-22 15:41:12 -05:00
rootElement : '#main' ,
2013-02-20 13:15:50 -05:00
2013-02-26 14:54:43 -05:00
// Whether the app has focus or not
2013-02-22 15:41:12 -05:00
hasFocus : true ,
2013-02-26 14:54:43 -05:00
2013-06-20 15:02:02 -04:00
// Helps with integration tests
URL _FIXTURES : { } ,
2013-03-14 13:01:52 +01:00
getURL : function ( url ) {
2013-05-07 13:30:12 -04:00
// If it's a non relative URL, return it.
if ( url . indexOf ( 'http' ) === 0 ) return url ;
2013-04-04 00:26:47 +02:00
var u = ( Discourse . BaseUri === undefined ? "/" : Discourse . BaseUri ) ;
2013-03-14 13:01:52 +01:00
if ( u [ u . length - 1 ] === '/' ) {
u = u . substring ( 0 , u . length - 1 ) ;
}
return u + url ;
} ,
2013-07-26 15:04:29 -04:00
resolver : Discourse . Resolver ,
2013-06-03 16:12:24 -04:00
2013-02-26 14:54:43 -05:00
titleChanged : function ( ) {
2013-07-26 14:59:28 -04:00
var title = "" ;
2013-02-22 15:41:12 -05:00
if ( this . get ( 'title' ) ) {
title += "" + ( this . get ( 'title' ) ) + " - " ;
}
title += Discourse . SiteSettings . title ;
$ ( 'title' ) . text ( title ) ;
2013-06-03 10:38:57 +10:00
var notifyCount = this . get ( 'notifyCount' ) ;
2013-08-08 12:42:08 -04:00
if ( notifyCount > 0 && ! Discourse . User . currentProp ( 'dynamic_favicon' ) ) {
2013-06-03 10:38:57 +10:00
title = "(" + notifyCount + ") " + title ;
2013-02-22 15:41:12 -05:00
}
// chrome bug workaround see: http://stackoverflow.com/questions/2952384/changing-the-window-title-when-focussing-the-window-doesnt-work-in-chrome
2013-02-26 14:54:43 -05:00
window . setTimeout ( function ( ) {
2013-02-22 15:41:12 -05:00
document . title = "." ;
document . title = title ;
2013-02-26 14:54:43 -05:00
} , 200 ) ;
2013-06-03 10:38:57 +10:00
} . observes ( 'title' , 'hasFocus' , 'notifyCount' ) ,
2013-02-20 13:15:50 -05:00
2013-06-07 17:15:49 -07:00
faviconChanged : function ( ) {
2013-08-08 12:42:08 -04:00
if ( Discourse . User . currentProp ( 'dynamic_favicon' ) ) {
2013-08-18 11:26:03 -07:00
new Favcount ( Discourse . SiteSettings . favicon _url ) . set (
this . get ( 'notifyCount' )
2013-06-07 17:15:49 -07:00
) ;
}
} . observes ( 'notifyCount' ) ,
2013-02-26 14:54:43 -05:00
// 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' ) ,
2013-06-03 10:38:57 +10:00
notifyTitle : function ( count ) {
this . set ( 'notifyCount' , count ) ;
2013-02-22 15:41:12 -05:00
} ,
2013-02-20 13:15:50 -05:00
2013-02-26 14:54:43 -05:00
/ * *
Establishes global DOM events and bindings via jQuery .
2013-02-20 13:15:50 -05:00
2013-02-26 14:54:43 -05:00
@ method bindDOMEvents
* * /
2013-02-22 15:41:12 -05:00
bindDOMEvents : function ( ) {
2013-02-26 14:54:43 -05:00
var $html , hasTouch ;
2013-02-20 13:15:50 -05:00
2013-02-26 14:54:43 -05:00
$html = $ ( 'html' ) ;
2013-02-22 15:41:12 -05:00
hasTouch = false ;
2013-02-26 14:54:43 -05:00
2013-02-22 15:41:12 -05:00
if ( $html . hasClass ( 'touch' ) ) {
hasTouch = true ;
}
2013-02-26 14:54:43 -05:00
2013-02-22 15:41:12 -05:00
if ( Modernizr . prefixed ( "MaxTouchPoints" , navigator ) > 1 ) {
hasTouch = true ;
}
2013-02-26 14:54:43 -05:00
2013-02-22 15:41:12 -05:00
if ( hasTouch ) {
$html . addClass ( 'discourse-touch' ) ;
this . touch = true ;
this . hasTouch = true ;
} else {
$html . addClass ( 'discourse-no-touch' ) ;
this . touch = false ;
}
2013-02-26 14:54:43 -05:00
2013-02-22 15:41:12 -05:00
$ ( '#main' ) . on ( 'click.discourse' , '[data-not-implemented=true]' , function ( e ) {
e . preventDefault ( ) ;
2013-07-09 01:32:16 +02:00
alert ( I18n . t ( 'not_implemented' ) ) ;
2013-02-22 15:41:12 -05:00
return false ;
} ) ;
2013-02-26 14:54:43 -05:00
2013-02-22 15:41:12 -05:00
$ ( '#main' ) . on ( 'click.discourse' , 'a' , function ( e ) {
2013-07-03 14:06:34 -04:00
if ( e . isDefaultPrevented ( ) || e . shiftKey || e . metaKey || e . ctrlKey ) { return ; }
2013-02-26 14:54:43 -05:00
var $currentTarget = $ ( e . currentTarget ) ;
var href = $currentTarget . attr ( 'href' ) ;
2013-07-03 14:06:34 -04:00
if ( ! href ) { return ; }
if ( href === '#' ) { return ; }
if ( $currentTarget . attr ( 'target' ) ) { return ; }
if ( $currentTarget . data ( 'auto-route' ) ) { return ; }
2013-04-04 12:59:44 -04:00
// If it's an ember #linkTo skip it
2013-07-03 14:06:34 -04:00
if ( $currentTarget . hasClass ( 'ember-view' ) ) { return ; }
2013-04-04 12:59:44 -04:00
2013-07-03 14:06:34 -04:00
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 ; }
2013-02-26 14:54:43 -05:00
2013-02-22 15:41:12 -05:00
e . preventDefault ( ) ;
2013-02-26 14:54:43 -05:00
Discourse . URL . routeTo ( href ) ;
2013-02-22 15:41:12 -05:00
return false ;
} ) ;
2013-02-26 14:54:43 -05:00
$ ( window ) . focus ( function ( ) {
Discourse . set ( 'hasFocus' , true ) ;
Discourse . set ( 'notify' , false ) ;
2013-02-22 15:41:12 -05:00
} ) . blur ( function ( ) {
2013-02-26 14:54:43 -05:00
Discourse . set ( 'hasFocus' , false ) ;
} ) ;
// Add a CSRF token to all AJAX requests
2013-08-27 15:56:12 +10:00
Discourse . csrfToken = $ ( 'meta[name=csrf-token]' ) . attr ( 'content' ) ;
2013-03-05 15:39:21 -05:00
$ . ajaxPrefilter ( function ( options , originalOptions , xhr ) {
2013-02-26 14:54:43 -05:00
if ( ! options . crossDomain ) {
2013-08-27 15:56:12 +10:00
xhr . setRequestHeader ( 'X-CSRF-Token' , Discourse . csrfToken ) ;
2013-02-26 14:54:43 -05:00
}
2013-02-22 15:41:12 -05:00
} ) ;
2013-06-05 09:32:03 +10:00
2013-06-24 15:00:57 -04:00
bootbox . animate ( false ) ;
bootbox . backdrop ( true ) ; // clicking outside a bootbox modal closes it
2013-09-10 16:43:51 -04:00
Discourse . Mobile . init ( ) ;
2013-08-27 13:41:36 -04:00
2013-06-05 09:32:03 +10:00
setInterval ( function ( ) {
Discourse . Formatter . updateRelativeAge ( $ ( '.relative-date' ) ) ;
} , 60 * 1000 ) ;
2013-02-22 15:41:12 -05:00
} ,
2013-02-26 14:54:43 -05:00
/ * *
Log the current user out of Discourse
@ method logout
* * /
2013-02-22 15:41:12 -05:00
logout : function ( ) {
2013-05-28 11:08:32 -04:00
Discourse . User . logout ( ) . then ( function ( ) {
2013-04-03 16:06:55 -04:00
// Reloading will refresh unbound properties
2013-05-28 11:08:32 -04:00
Discourse . KeyValueStore . abandonLocal ( ) ;
2013-08-06 16:04:02 -04:00
window . location . pathname = Discourse . getURL ( '/' ) ;
2013-06-21 14:06:20 -04:00
} ) ;
2013-02-22 15:41:12 -05:00
} ,
authenticationComplete : function ( options ) {
2013-05-30 14:12:33 -04:00
// TODO, how to dispatch this to the controller without the container?
var loginController = Discourse . _ _container _ _ . lookup ( 'controller:login' ) ;
return loginController . authenticationComplete ( options ) ;
2013-02-22 15:41:12 -05:00
} ,
2013-02-26 14:54:43 -05:00
2013-06-04 15:37:53 -07:00
loginRequired : function ( ) {
return (
Discourse . SiteSettings . login _required && ! Discourse . User . current ( )
) ;
} . property ( ) ,
2013-06-04 15:39:35 -07:00
redirectIfLoginRequired : function ( route ) {
if ( this . get ( 'loginRequired' ) ) { route . transitionTo ( 'login' ) ; }
} ,
2013-05-28 17:12:37 -04:00
/ * *
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 ) {
2013-08-08 12:49:58 -04:00
var site = Discourse . Site . current ( ) ;
2013-06-11 06:48:50 +10:00
_ . each ( data . categories , function ( c ) {
2013-06-21 14:06:20 -04:00
site . updateCategory ( c ) ;
2013-05-28 17:12:37 -04:00
} ) ;
} ) ;
}
} ,
2013-09-19 17:59:17 -07:00
/ * *
Add an initializer hook for after the Discourse Application starts up .
@ method addInitializer
@ param { Function } init the initializer to add .
* * /
addInitializer : function ( init ) {
Discourse . initializers = Discourse . initializers || [ ] ;
Discourse . initializers . push ( init ) ;
} ,
2013-04-01 16:28:26 -04:00
/ * *
Start up the Discourse application .
@ method start
* * /
2013-02-22 15:41:12 -05:00
start : function ( ) {
2013-02-26 14:54:43 -05:00
Discourse . bindDOMEvents ( ) ;
2013-03-20 16:26:46 +01:00
Discourse . SiteSettings = PreloadStore . get ( 'siteSettings' ) ;
2013-04-19 13:06:00 +10:00
Discourse . MessageBus . alwaysLongPoll = Discourse . Environment === "development" ;
2013-02-22 15:41:12 -05:00
Discourse . MessageBus . start ( ) ;
Discourse . KeyValueStore . init ( "discourse_" , Discourse . MessageBus ) ;
2013-06-02 14:05:12 -04:00
2013-02-26 14:54:43 -05:00
// Developer specific functions
Discourse . Development . observeLiveChanges ( ) ;
2013-05-28 17:12:37 -04:00
Discourse . subscribeUserToNotifications ( ) ;
2013-09-19 17:59:17 -07:00
if ( Discourse . initializers ) {
Discourse . initializers . forEach ( function ( init ) {
init . call ( this ) ;
} ) ;
}
2013-02-22 15:41:12 -05:00
}
2013-02-20 13:15:50 -05:00
2013-02-22 15:41:12 -05:00
} ) ;
2013-02-20 13:15:50 -05:00
2013-03-12 20:06:58 -07:00
Discourse . Router = Discourse . Router . reopen ( { location : 'discourse_location' } ) ;
2013-07-26 13:10:52 -04:00