2016-03-30 16:57:19 -04:00
|
|
|
go = (path, options) -> -> @routeDirectly path, arguments, options
|
|
|
|
redirect = (path) -> -> @navigate(path, { trigger: true, replace: true })
|
2016-04-13 12:54:24 -04:00
|
|
|
utils = require './utils'
|
2016-04-07 22:06:57 -04:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
module.exports = class CocoRouter extends Backbone.Router
|
2014-07-23 10:02:45 -04:00
|
|
|
|
|
|
|
initialize: ->
|
2016-03-30 19:20:37 -04:00
|
|
|
# http://nerds.airbnb.com/how-to-add-google-analytics-page-tracking-to-57536
|
2014-07-23 10:02:45 -04:00
|
|
|
@bind 'route', @_trackPageView
|
2014-01-03 13:32:13 -05:00
|
|
|
Backbone.Mediator.subscribe 'router:navigate', @onNavigate, @
|
2014-10-15 16:43:26 -04:00
|
|
|
@initializeSocialMediaServices = _.once @initializeSocialMediaServices
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
routes:
|
2016-01-26 19:28:29 -05:00
|
|
|
'': ->
|
2016-02-16 23:39:59 -05:00
|
|
|
if window.serverConfig.picoCTF
|
|
|
|
return @routeDirectly 'play/CampaignView', ['picoctf'], {}
|
2016-04-13 12:54:24 -04:00
|
|
|
if utils.getQueryVariable 'hour_of_code'
|
|
|
|
return @navigate "/play", {trigger: true, replace: true}
|
2016-03-01 12:48:50 -05:00
|
|
|
return @routeDirectly('NewHomeView', [])
|
2014-07-18 20:06:20 -04:00
|
|
|
|
2014-07-17 20:16:32 -04:00
|
|
|
'about': go('AboutView')
|
2014-08-13 22:17:26 -04:00
|
|
|
|
2014-07-29 11:28:13 -04:00
|
|
|
'account': go('account/MainAccountView')
|
2014-11-25 15:43:17 -05:00
|
|
|
'account/settings': go('account/AccountSettingsRootView')
|
2014-07-23 10:02:45 -04:00
|
|
|
'account/unsubscribe': go('account/UnsubscribeView')
|
2014-11-25 14:09:29 -05:00
|
|
|
'account/payments': go('account/PaymentsView')
|
2014-12-10 16:42:12 -05:00
|
|
|
'account/subscription': go('account/SubscriptionView')
|
2015-03-04 18:40:32 -05:00
|
|
|
'account/invoices': go('account/InvoicesView')
|
2015-09-25 13:03:44 -04:00
|
|
|
'account/prepaid': go('account/PrepaidView')
|
2014-08-13 22:17:26 -04:00
|
|
|
|
2014-07-23 10:02:45 -04:00
|
|
|
'admin': go('admin/MainAdminView')
|
|
|
|
'admin/clas': go('admin/CLAsView')
|
2016-06-30 11:29:27 -04:00
|
|
|
'admin/classroom-levels': go('admin/AdminClassroomLevelsView')
|
2016-01-06 19:11:28 -05:00
|
|
|
'admin/design-elements': go('admin/DesignElementsView')
|
2014-07-23 10:02:45 -04:00
|
|
|
'admin/files': go('admin/FilesView')
|
2015-11-04 13:54:40 -05:00
|
|
|
'admin/analytics': go('admin/AnalyticsView')
|
2015-03-27 14:22:21 -04:00
|
|
|
'admin/analytics/subscriptions': go('admin/AnalyticsSubscriptionsView')
|
2014-07-23 10:02:45 -04:00
|
|
|
'admin/level-sessions': go('admin/LevelSessionsView')
|
2016-07-18 12:41:42 -04:00
|
|
|
'admin/school-counts': go('admin/SchoolCountsView')
|
|
|
|
'admin/school-licenses': go('admin/SchoolLicensesView')
|
2014-07-23 10:02:45 -04:00
|
|
|
'admin/users': go('admin/UsersView')
|
2014-08-30 23:27:58 -04:00
|
|
|
'admin/base': go('admin/BaseView')
|
2016-05-11 14:52:30 -04:00
|
|
|
'admin/demo-requests': go('admin/DemoRequestsView')
|
2015-06-05 09:48:09 -04:00
|
|
|
'admin/trial-requests': go('admin/TrialRequestsView')
|
2014-10-24 18:11:55 -04:00
|
|
|
'admin/user-code-problems': go('admin/UserCodeProblemsView')
|
2015-03-28 16:54:44 -04:00
|
|
|
'admin/pending-patches': go('admin/PendingPatchesView')
|
2016-02-08 17:24:08 -05:00
|
|
|
'admin/codelogs': go('admin/CodeLogsView')
|
2014-07-18 20:06:20 -04:00
|
|
|
|
2016-04-04 14:03:07 -04:00
|
|
|
'artisans': go('artisans/ArtisansView')
|
|
|
|
|
|
|
|
'artisans/level-tasks': go('artisans/LevelTasksView')
|
2016-05-16 14:45:06 -04:00
|
|
|
'artisans/solution-problems': go('artisans/SolutionProblemsView')
|
2016-04-04 14:03:07 -04:00
|
|
|
'artisans/thang-tasks': go('artisans/ThangTasksView')
|
2016-07-19 20:58:10 -04:00
|
|
|
'artisans/level-concepts': go('artisans/LevelConceptMap')
|
2016-04-04 14:03:07 -04:00
|
|
|
|
2014-07-18 20:06:20 -04:00
|
|
|
'beta': go('HomeView')
|
|
|
|
|
2015-09-25 18:49:00 -04:00
|
|
|
'careers': => window.location.href = 'https://jobs.lever.co/codecombat'
|
2015-10-26 23:39:36 -04:00
|
|
|
'Careers': => window.location.href = 'https://jobs.lever.co/codecombat'
|
2015-09-14 20:38:18 -04:00
|
|
|
|
2014-07-17 20:16:32 -04:00
|
|
|
'cla': go('CLAView')
|
2015-03-31 16:28:57 -04:00
|
|
|
|
|
|
|
'clans': go('clans/ClansView')
|
|
|
|
'clans/:clanID': go('clans/ClanDetailsView')
|
|
|
|
|
2014-07-17 20:16:32 -04:00
|
|
|
'community': go('CommunityView')
|
2014-08-13 22:17:26 -04:00
|
|
|
|
2014-07-17 20:16:32 -04:00
|
|
|
'contribute': go('contribute/MainContributeView')
|
|
|
|
'contribute/adventurer': go('contribute/AdventurerView')
|
|
|
|
'contribute/ambassador': go('contribute/AmbassadorView')
|
|
|
|
'contribute/archmage': go('contribute/ArchmageView')
|
|
|
|
'contribute/artisan': go('contribute/ArtisanView')
|
|
|
|
'contribute/diplomat': go('contribute/DiplomatView')
|
|
|
|
'contribute/scribe': go('contribute/ScribeView')
|
2014-07-18 20:06:20 -04:00
|
|
|
|
2016-06-14 20:15:48 -04:00
|
|
|
'courses': go('courses/CoursesView')
|
|
|
|
'Courses': go('courses/CoursesView')
|
2016-03-30 16:57:19 -04:00
|
|
|
'courses/students': redirect('/courses')
|
|
|
|
'courses/teachers': redirect('/teachers/classes')
|
2016-05-25 08:52:43 -04:00
|
|
|
'courses/purchase': redirect('/teachers/licenses')
|
|
|
|
'courses/enroll(/:courseID)': redirect('/teachers/licenses')
|
2016-05-16 17:33:20 -04:00
|
|
|
'courses/update-account': go('courses/CoursesUpdateAccountView')
|
2016-05-26 17:25:34 -04:00
|
|
|
'courses/:classroomID': go('courses/ClassroomView', { studentsOnly: true })
|
|
|
|
'courses/:courseID/:courseInstanceID': go('courses/CourseDetailsView', { studentsOnly: true })
|
2015-06-29 15:15:07 -04:00
|
|
|
|
2014-07-23 10:02:45 -04:00
|
|
|
'db/*path': 'routeToServer'
|
2014-07-18 20:06:20 -04:00
|
|
|
'demo(/*subpath)': go('DemoView')
|
2014-08-29 01:32:55 -04:00
|
|
|
'docs/components': go('docs/ComponentsDocumentationView')
|
|
|
|
'docs/systems': go('docs/SystemsDocumentationView')
|
2014-08-13 22:17:26 -04:00
|
|
|
|
2014-08-26 20:34:00 -04:00
|
|
|
'editor': go('CommunityView')
|
2014-07-23 10:02:45 -04:00
|
|
|
|
|
|
|
'editor/achievement': go('editor/achievement/AchievementSearchView')
|
2014-08-03 17:58:51 -04:00
|
|
|
'editor/achievement/:articleID': go('editor/achievement/AchievementEditView')
|
2014-07-23 10:02:45 -04:00
|
|
|
'editor/article': go('editor/article/ArticleSearchView')
|
|
|
|
'editor/article/preview': go('editor/article/ArticlePreviewView')
|
|
|
|
'editor/article/:articleID': go('editor/article/ArticleEditView')
|
|
|
|
'editor/level': go('editor/level/LevelSearchView')
|
|
|
|
'editor/level/:levelID': go('editor/level/LevelEditView')
|
|
|
|
'editor/thang': go('editor/thang/ThangTypeSearchView')
|
|
|
|
'editor/thang/:thangID': go('editor/thang/ThangTypeEditView')
|
2014-12-16 20:46:24 -05:00
|
|
|
'editor/campaign/:campaignID': go('editor/campaign/CampaignEditorView')
|
2015-03-07 19:30:25 -05:00
|
|
|
'editor/poll': go('editor/poll/PollSearchView')
|
|
|
|
'editor/poll/:articleID': go('editor/poll/PollEditView')
|
2015-12-21 15:44:22 -05:00
|
|
|
'editor/thang-tasks': go('editor/ThangTasksView')
|
2016-04-07 22:06:57 -04:00
|
|
|
'editor/verifier': go('editor/verifier/VerifierView')
|
|
|
|
'editor/verifier/:levelID': go('editor/verifier/VerifierView')
|
2016-08-16 12:24:34 -04:00
|
|
|
'editor/course': go('editor/course/CourseSearchView')
|
|
|
|
'editor/course/:courseID': go('editor/course/CourseEditView')
|
2014-08-13 22:17:26 -04:00
|
|
|
|
2014-07-23 10:02:45 -04:00
|
|
|
'file/*path': 'routeToServer'
|
|
|
|
|
2014-08-15 10:20:45 -04:00
|
|
|
'github/*path': 'routeToServer'
|
|
|
|
|
2016-05-16 17:50:34 -04:00
|
|
|
'hoc': ->
|
|
|
|
# Matching /?hour_of_code=true behavior
|
|
|
|
@navigate "/play", {trigger: true, replace: true}
|
2016-01-26 19:28:29 -05:00
|
|
|
'home': go('NewHomeView')
|
2015-10-12 19:25:23 -04:00
|
|
|
|
2014-10-27 20:11:48 -04:00
|
|
|
'i18n': go('i18n/I18NHomeView')
|
|
|
|
'i18n/thang/:handle': go('i18n/I18NEditThangTypeView')
|
|
|
|
'i18n/component/:handle': go('i18n/I18NEditComponentView')
|
|
|
|
'i18n/level/:handle': go('i18n/I18NEditLevelView')
|
|
|
|
'i18n/achievement/:handle': go('i18n/I18NEditAchievementView')
|
2015-01-29 12:07:25 -05:00
|
|
|
'i18n/campaign/:handle': go('i18n/I18NEditCampaignView')
|
2015-03-07 19:30:25 -05:00
|
|
|
'i18n/poll/:handle': go('i18n/I18NEditPollView')
|
2016-08-16 12:24:34 -04:00
|
|
|
'i18n/course/:handle': go('i18n/I18NEditCourseView')
|
2014-10-27 20:11:48 -04:00
|
|
|
|
2015-04-30 16:35:21 -04:00
|
|
|
'identify': go('user/IdentifyView')
|
|
|
|
|
2014-07-18 20:06:20 -04:00
|
|
|
'legal': go('LegalView')
|
2014-08-13 22:17:26 -04:00
|
|
|
|
2015-10-21 19:34:25 -04:00
|
|
|
'play(/)': go('play/CampaignView') # extra slash is to get Facebook app to work
|
2015-08-15 09:45:38 -04:00
|
|
|
'play/ladder/:levelID/:leagueType/:leagueID': go('ladder/LadderView')
|
2014-11-29 19:46:36 -05:00
|
|
|
'play/ladder/:levelID': go('ladder/LadderView')
|
|
|
|
'play/ladder': go('ladder/MainLadderView')
|
2014-07-23 10:02:45 -04:00
|
|
|
'play/level/:levelID': go('play/level/PlayLevelView')
|
2016-07-12 17:07:10 -04:00
|
|
|
'play/game-dev-level/:levelID/:sessionID': go('play/level/PlayGameDevLevelView')
|
2016-07-15 23:03:12 -04:00
|
|
|
'play/web-dev-level/:levelID/:sessionID': go('play/level/PlayWebDevLevelView')
|
2014-07-23 10:02:45 -04:00
|
|
|
'play/spectate/:levelID': go('play/SpectateView')
|
2014-12-28 16:55:20 -05:00
|
|
|
'play/:map': go('play/CampaignView')
|
2014-07-23 10:02:45 -04:00
|
|
|
|
2014-07-18 20:06:20 -04:00
|
|
|
'preview': go('HomeView')
|
|
|
|
|
2016-02-01 11:39:00 -05:00
|
|
|
'privacy': go('PrivacyView')
|
|
|
|
|
2016-02-05 16:47:32 -05:00
|
|
|
'schools': go('NewHomeView')
|
2016-05-17 13:50:25 -04:00
|
|
|
'seen': go('NewHomeView')
|
|
|
|
'SEEN': go('NewHomeView')
|
2015-12-14 23:43:34 -05:00
|
|
|
|
2016-04-05 12:34:45 -04:00
|
|
|
'teachers': redirect('/teachers/classes')
|
2016-05-26 17:25:34 -04:00
|
|
|
'teachers/classes': go('courses/TeacherClassesView', { teachersOnly: true })
|
|
|
|
'teachers/classes/:classroomID': go('courses/TeacherClassView', { teachersOnly: true })
|
2016-03-30 16:57:19 -04:00
|
|
|
'teachers/courses': go('courses/TeacherCoursesView')
|
2016-08-11 18:29:53 -04:00
|
|
|
'teachers/course-solution/:courseID/:language': go('teachers/TeacherCourseSolutionView')
|
2016-03-09 17:40:52 -05:00
|
|
|
'teachers/demo': go('teachers/RequestQuoteView')
|
2016-05-25 08:52:43 -04:00
|
|
|
'teachers/enrollments': redirect('/teachers/licenses')
|
2016-05-26 17:25:34 -04:00
|
|
|
'teachers/licenses': go('courses/EnrollmentsView', { teachersOnly: true })
|
2016-03-09 17:40:52 -05:00
|
|
|
'teachers/freetrial': go('teachers/RequestQuoteView')
|
2016-05-26 17:25:34 -04:00
|
|
|
'teachers/quote': redirect('/teachers/demo')
|
2016-03-09 17:40:52 -05:00
|
|
|
'teachers/signup': ->
|
|
|
|
return @routeDirectly('teachers/CreateTeacherAccountView', []) if me.isAnonymous()
|
2016-04-05 12:16:22 -04:00
|
|
|
@navigate('/teachers/update-account', {trigger: true, replace: true})
|
|
|
|
'teachers/update-account': ->
|
2016-03-09 17:40:52 -05:00
|
|
|
return @navigate('/teachers/signup', {trigger: true, replace: true}) if me.isAnonymous()
|
|
|
|
@routeDirectly('teachers/ConvertToTeacherAccountView', [])
|
2014-08-13 22:17:26 -04:00
|
|
|
|
2014-07-18 20:06:20 -04:00
|
|
|
'test(/*subpath)': go('TestView')
|
|
|
|
|
2014-07-21 13:49:16 -04:00
|
|
|
'user/:slugOrID': go('user/MainUserView')
|
2016-05-11 17:39:26 -04:00
|
|
|
'user/:userID/verify/:verificationCode': go('user/EmailVerifiedView')
|
2014-07-06 14:45:27 -04:00
|
|
|
|
2015-10-21 19:34:25 -04:00
|
|
|
'*name/': 'removeTrailingSlash'
|
2015-06-24 17:46:59 -04:00
|
|
|
'*name': go('NotFoundView')
|
2014-07-13 16:34:32 -04:00
|
|
|
|
2014-07-23 10:02:45 -04:00
|
|
|
routeToServer: (e) ->
|
|
|
|
window.location.href = window.location.href
|
2014-07-10 14:50:16 -04:00
|
|
|
|
2015-10-21 19:34:25 -04:00
|
|
|
removeTrailingSlash: (e) ->
|
|
|
|
@navigate e, {trigger: true}
|
|
|
|
|
2016-03-30 16:57:19 -04:00
|
|
|
routeDirectly: (path, args=[], options={}) ->
|
2016-05-31 14:15:37 -04:00
|
|
|
if options.teachersOnly and not (me.isTeacher() or me.isAdmin())
|
2016-03-30 16:57:19 -04:00
|
|
|
return @routeDirectly('teachers/RestrictedToTeachersView')
|
2016-05-31 14:15:37 -04:00
|
|
|
if options.studentsOnly and not (me.isStudent() or me.isAdmin())
|
2016-03-30 16:57:19 -04:00
|
|
|
return @routeDirectly('courses/RestrictedToStudentsView')
|
2016-04-19 13:20:56 -04:00
|
|
|
leavingMessage = _.result(window.currentView, 'onLeaveMessage')
|
|
|
|
if leavingMessage
|
|
|
|
if not confirm(leavingMessage)
|
|
|
|
return @navigate(this.path, {replace: true})
|
|
|
|
else
|
|
|
|
window.currentView.onLeaveMessage = _.noop # to stop repeat confirm calls
|
2016-04-07 22:06:57 -04:00
|
|
|
|
2016-02-17 12:53:45 -05:00
|
|
|
path = 'play/CampaignView' if window.serverConfig.picoCTF and not /^(views)?\/?play/.test(path)
|
2015-01-04 11:05:38 -05:00
|
|
|
path = "views/#{path}" if not _.string.startsWith(path, 'views/')
|
2014-07-23 10:02:45 -04:00
|
|
|
ViewClass = @tryToLoadModule path
|
2014-11-28 19:38:50 -05:00
|
|
|
if not ViewClass and application.moduleLoader.load(path)
|
|
|
|
@listenToOnce application.moduleLoader, 'load-complete', ->
|
2016-01-26 19:28:29 -05:00
|
|
|
@routeDirectly(path, args, options)
|
2014-11-28 19:38:50 -05:00
|
|
|
return
|
2016-07-12 17:07:10 -04:00
|
|
|
return go('NotFoundView') if not ViewClass
|
2016-01-26 19:28:29 -05:00
|
|
|
view = new ViewClass(options, args...) # options, then any path fragment args
|
2014-07-23 10:02:45 -04:00
|
|
|
view.render()
|
|
|
|
@openView(view)
|
2014-07-10 14:50:16 -04:00
|
|
|
|
2014-07-23 10:02:45 -04:00
|
|
|
tryToLoadModule: (path) ->
|
|
|
|
try
|
|
|
|
return require(path)
|
|
|
|
catch error
|
|
|
|
if error.toString().search('Cannot find module "' + path + '" from') is -1
|
|
|
|
throw error
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
openView: (view) ->
|
|
|
|
@closeCurrentView()
|
|
|
|
$('#page-container').empty().append view.el
|
|
|
|
window.currentView = view
|
|
|
|
@activateTab()
|
|
|
|
view.afterInsert()
|
2014-07-23 10:02:45 -04:00
|
|
|
view.didReappear()
|
2016-04-19 13:20:56 -04:00
|
|
|
@path = document.location.pathname + document.location.search
|
2014-07-23 10:02:45 -04:00
|
|
|
|
|
|
|
closeCurrentView: ->
|
|
|
|
if window.currentView?.reloadOnClose
|
|
|
|
return document.location.reload()
|
|
|
|
window.currentModal?.hide?()
|
|
|
|
return unless window.currentView?
|
|
|
|
window.currentView.destroy()
|
2014-10-06 21:09:27 -04:00
|
|
|
$('.popover').popover 'hide'
|
2016-02-17 12:59:08 -05:00
|
|
|
$('#flying-focus').css({top: 0, left: 0}) # otherwise it might make the page unnecessarily tall
|
2016-03-30 16:57:19 -04:00
|
|
|
_.delay (->
|
2016-02-17 14:21:43 -05:00
|
|
|
$('html')[0].scrollTop = 0
|
|
|
|
$('body')[0].scrollTop = 0
|
|
|
|
), 10
|
2014-01-03 13:32:13 -05:00
|
|
|
|
2014-10-15 16:43:26 -04:00
|
|
|
initializeSocialMediaServices: ->
|
|
|
|
return if application.testing or application.demoing
|
2016-03-15 16:50:33 -04:00
|
|
|
application.facebookHandler.loadAPI()
|
2016-02-25 18:24:16 -05:00
|
|
|
application.gplusHandler.loadAPI()
|
2014-11-28 20:49:41 -05:00
|
|
|
require('core/services/twitter')()
|
2014-01-03 13:32:13 -05:00
|
|
|
|
2016-03-15 16:50:33 -04:00
|
|
|
renderSocialButtons: =>
|
2016-03-30 19:20:37 -04:00
|
|
|
# TODO: Refactor remaining services to Handlers, use loadAPI success callback
|
2014-10-15 16:43:26 -04:00
|
|
|
@initializeSocialMediaServices()
|
2014-01-07 02:45:33 -05:00
|
|
|
$('.share-buttons, .partner-badges').addClass('fade-in').delay(10000).removeClass('fade-in', 5000)
|
2016-03-15 16:50:33 -04:00
|
|
|
application.facebookHandler.renderButtons()
|
|
|
|
application.gplusHandler.renderButtons()
|
2014-01-09 13:48:51 -05:00
|
|
|
twttr?.widgets?.load?()
|
2016-04-07 22:06:57 -04:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
activateTab: ->
|
|
|
|
base = _.string.words(document.location.pathname[1..], '/')[0]
|
|
|
|
$("ul.nav li.#{base}").addClass('active')
|
|
|
|
|
|
|
|
_trackPageView: ->
|
2015-02-27 19:07:41 -05:00
|
|
|
window.tracker?.trackPageView()
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
onNavigate: (e) ->
|
2014-11-29 19:46:36 -05:00
|
|
|
if _.isString e.viewClass
|
|
|
|
ViewClass = @tryToLoadModule e.viewClass
|
|
|
|
if not ViewClass and application.moduleLoader.load(e.viewClass)
|
|
|
|
@listenToOnce application.moduleLoader, 'load-complete', ->
|
|
|
|
@onNavigate(e)
|
|
|
|
return
|
|
|
|
e.viewClass = ViewClass
|
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
manualView = e.view or e.viewClass
|
2014-11-07 11:54:22 -05:00
|
|
|
if (e.route is document.location.pathname) and not manualView
|
|
|
|
return document.location.reload()
|
2014-03-17 00:33:46 -04:00
|
|
|
@navigate e.route, {trigger: not manualView}
|
2015-03-09 15:46:34 -04:00
|
|
|
@_trackPageView()
|
2014-01-03 13:32:13 -05:00
|
|
|
return unless manualView
|
|
|
|
if e.viewClass
|
|
|
|
args = e.viewArgs or []
|
|
|
|
view = new e.viewClass(args...)
|
|
|
|
view.render()
|
|
|
|
@openView view
|
|
|
|
else
|
|
|
|
@openView e.view
|
2014-10-01 13:58:19 -04:00
|
|
|
|
|
|
|
navigate: (fragment, options) ->
|
|
|
|
super fragment, options
|
|
|
|
Backbone.Mediator.publish 'router:navigated', route: fragment
|
2016-03-09 17:40:52 -05:00
|
|
|
|
|
|
|
reload: ->
|
2016-03-30 19:20:37 -04:00
|
|
|
document.location.reload()
|