mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-24 11:50:58 -04:00
Merge branch 'master' into production
This commit is contained in:
commit
0c8a5dabb0
30 changed files with 85 additions and 1440 deletions
app
lib
styles/courses/mock1
templates
courses
play
views
server/classrooms
|
@ -26,12 +26,13 @@ module.exports = class LevelSetupManager extends CocoClass
|
|||
levelURL = "/db/level/#{@options.levelID}"
|
||||
@level = new Level().setURL levelURL
|
||||
@level = @supermodel.loadModel(@level, 'level').model
|
||||
onLevelSync = ->
|
||||
return if @destroyed
|
||||
if @waitingToLoadModals
|
||||
@waitingToLoadModals = false
|
||||
@loadModals()
|
||||
onLevelSync.call @ if @level.loaded
|
||||
if @level.loaded then @onLevelSync() else @listenToOnce @level, 'sync', @onLevelSync
|
||||
|
||||
onLevelSync: ->
|
||||
return if @destroyed
|
||||
if @waitingToLoadModals
|
||||
@waitingToLoadModals = false
|
||||
@loadModals()
|
||||
|
||||
loadSession: ->
|
||||
sessionURL = "/db/level/#{@options.levelID}/session"
|
||||
|
@ -104,6 +105,7 @@ module.exports = class LevelSetupManager extends CocoClass
|
|||
lastHeroesPurchased = me.get('purchased')?.heroes ? []
|
||||
|
||||
@options.parent.openModalView(firstModal)
|
||||
@trigger 'open'
|
||||
# @inventoryModal.onShown() # replace?
|
||||
|
||||
#- Modal events
|
||||
|
|
|
@ -1,149 +0,0 @@
|
|||
#course-details-mock-view
|
||||
|
||||
.section-selector
|
||||
margin-bottom: 0px
|
||||
|
||||
.concept-completion-container
|
||||
font-size: 10pt
|
||||
|
||||
.summary-container
|
||||
font-size: 14pt
|
||||
|
||||
.statistics-container
|
||||
font-size: 12pt
|
||||
td
|
||||
padding-right: 8px
|
||||
|
||||
.table-concepts-summary
|
||||
width: 100%
|
||||
|
||||
.concept-summary
|
||||
width: 100%
|
||||
background-color: white
|
||||
cursor: default
|
||||
display: inline-block
|
||||
white-space: nowrap
|
||||
font-size: 9pt
|
||||
font-weight: normal
|
||||
border: 1px solid gray
|
||||
margin: 0px
|
||||
padding: 2px
|
||||
background-color: white
|
||||
|
||||
#editSettingsModal .modal-dialog
|
||||
background-color: white
|
||||
font-size: 14pt
|
||||
|
||||
.edit-description-input
|
||||
width: 100%
|
||||
|
||||
.edit-name-input
|
||||
width: 50%
|
||||
|
||||
.member-header
|
||||
cursor: pointer
|
||||
display: inline-block
|
||||
padding: 2px
|
||||
|
||||
.textarea-emails
|
||||
width: 50%
|
||||
|
||||
.select-language
|
||||
width: 200px
|
||||
display: inline
|
||||
|
||||
.select-session
|
||||
width: 300px
|
||||
display: inline
|
||||
|
||||
.progress-header
|
||||
margin-right: 14px
|
||||
cursor: pointer
|
||||
|
||||
.progress-key
|
||||
cursor: default
|
||||
display: inline-block
|
||||
white-space: nowrap
|
||||
font-size: 12px
|
||||
line-height: 12px
|
||||
font-weight: normal
|
||||
border: 1px solid gray
|
||||
margin: 0px
|
||||
padding: 2px
|
||||
|
||||
.progress-key-started
|
||||
background-color: lightgreen
|
||||
|
||||
.progress-key-complete
|
||||
background-color: lightgray
|
||||
|
||||
.expand-progress-checkbox
|
||||
margin-left: 14px
|
||||
|
||||
.expand-progress-label
|
||||
font-weight: normal
|
||||
font-size: 14px
|
||||
|
||||
.student-cell
|
||||
width: 150px
|
||||
|
||||
.progress-cell
|
||||
padding: 2px
|
||||
padding-bottom: 10px
|
||||
|
||||
.level-popup-container
|
||||
display: none
|
||||
position: absolute
|
||||
padding: 10px
|
||||
border: 1px solid black
|
||||
z-index: 3
|
||||
background-color: blanchedalmond
|
||||
font-size: 10pt
|
||||
|
||||
.level-progression-concepts
|
||||
color: #317EAC
|
||||
font-size: 12pt
|
||||
font-weight: bold
|
||||
margin-top: 8px
|
||||
margin-bottom: 4px
|
||||
|
||||
.level-progression-levels
|
||||
color: #317EAC
|
||||
font-size: 12pt
|
||||
font-weight: bold
|
||||
margin-top: 8px
|
||||
|
||||
.progress-level-cell
|
||||
display: inline-block
|
||||
white-space: nowrap
|
||||
font-size: 12px
|
||||
line-height: 12px
|
||||
border: 1px solid gray
|
||||
margin: 0px
|
||||
padding: 2px
|
||||
|
||||
.progress-level-cell-started
|
||||
cursor: pointer
|
||||
background-color: lightgreen
|
||||
|
||||
.progress-level-cell-complete
|
||||
cursor: pointer
|
||||
background-color: lightgray
|
||||
|
||||
.progress-concept-cell
|
||||
display: inline-block
|
||||
white-space: nowrap
|
||||
font-size: 12px
|
||||
line-height: 12px
|
||||
border: 1px solid gray
|
||||
margin: 0px
|
||||
padding: 2px
|
||||
|
||||
.progress-concept-cell-started
|
||||
background-color: lightgreen
|
||||
|
||||
.progress-concept-cell-complete
|
||||
background-color: lightgray
|
||||
|
||||
.condense-progress
|
||||
width: 100%
|
|
@ -1,14 +0,0 @@
|
|||
#course-enroll-mock-view
|
||||
|
||||
.btn-buy
|
||||
margin: 20px 0px
|
||||
|
||||
.center
|
||||
text-align: center
|
||||
|
||||
.enroll-container
|
||||
margin: 5% 20%
|
||||
width: 60%
|
||||
|
||||
.session-name
|
||||
width: 300px
|
|
@ -1,31 +0,0 @@
|
|||
#course-info-view
|
||||
|
||||
.btn-enroll
|
||||
margin-top: 20px
|
||||
|
||||
.center
|
||||
text-align: center
|
||||
|
||||
.caption-text
|
||||
font-size: 14px
|
||||
|
||||
.concepts-container
|
||||
width: 200px
|
||||
|
||||
.contact-container
|
||||
margin-top: 20px
|
||||
text-align: center
|
||||
|
||||
.info-container
|
||||
margin: 0% 10%
|
||||
font-size: 18px
|
||||
|
||||
.monitoring-img-container
|
||||
margin-top: 10px
|
||||
|
||||
.praise-quote
|
||||
font-size: 24px
|
||||
font-style: italic
|
||||
|
||||
.progress-container
|
||||
font-size: 20px
|
|
@ -1,71 +0,0 @@
|
|||
#courses-mock-view
|
||||
|
||||
.center
|
||||
text-align: center
|
||||
|
||||
.code-input
|
||||
width: 100%
|
||||
|
||||
.course-image
|
||||
width: 100%
|
||||
|
||||
.course-panel
|
||||
margin: 20px
|
||||
|
||||
.faq-blurb
|
||||
font-size: 14px
|
||||
|
||||
.row-pick-class
|
||||
display: none
|
||||
|
||||
#continueModal .modal-dialog
|
||||
background-color: white
|
||||
max-width: 400px
|
||||
|
||||
.instruction-label
|
||||
font-size: 14pt
|
||||
.or
|
||||
margin-bottom: 20px
|
||||
font-size: 14pt
|
||||
|
||||
.btn-enroll
|
||||
margin-top: 20px
|
||||
|
||||
.center
|
||||
text-align: center
|
||||
|
||||
.concepts-container
|
||||
width: 200px
|
||||
|
||||
.contact-container
|
||||
margin-top: 20px
|
||||
text-align: center
|
||||
|
||||
.info-container
|
||||
margin: 0% 10%
|
||||
font-size: 18px
|
||||
|
||||
.monitoring-img-container
|
||||
margin-top: 10px
|
||||
|
||||
.praise-caption
|
||||
font-size: 14px
|
||||
|
||||
.praise-quote
|
||||
font-size: 20px
|
||||
font-style: italic
|
||||
|
||||
.progress-container
|
||||
font-size: 20px
|
||||
|
||||
.img-quote
|
||||
height: 160px
|
||||
|
||||
.popover
|
||||
z-index: 1050
|
||||
min-width: 400px
|
||||
|
||||
h3
|
||||
background: transparent
|
||||
border: 0
|
||||
font-size: 30px
|
|
@ -1,14 +0,0 @@
|
|||
#purchase-courses-view
|
||||
|
||||
font-size: 18px
|
||||
|
||||
.enrollments-info
|
||||
width: 300px
|
||||
|
||||
#students-input
|
||||
width: 100px
|
||||
font-size: 30px
|
||||
text-align: center
|
||||
|
||||
.uppercase
|
||||
text-transform: uppercase
|
|
@ -1,4 +1,4 @@
|
|||
- var completed = session && session.get('state').complete;
|
||||
- var completed = session && session.get('state') && session.get('state').complete;
|
||||
h3 #{i}. #{level.name.replace('Course: ', '')}
|
||||
if session
|
||||
p
|
||||
|
|
|
@ -90,7 +90,7 @@ block content
|
|||
- var session = sessionMap[levelID];
|
||||
a(href=view.getLevelURL(level, course, courseInstance, session))
|
||||
- var content = view.levelPopoverContent(level, session, i);
|
||||
if session && session.get('state').complete
|
||||
if session && session.get('state') && session.get('state').complete
|
||||
.progress-bar.progress-bar-success(style=css, data-content=content, data-toggle='popover')= i
|
||||
else if session
|
||||
.progress-bar.progress-bar-warning(style=css, data-content=content, data-toggle='popover')= i
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
extends /templates/base
|
||||
|
||||
block content
|
||||
|
||||
h2
|
||||
if view.teacherMode
|
||||
a(href="/courses/teachers") Back to my classrooms
|
||||
else
|
||||
a(href="/courses") Back to my courses
|
||||
hr
|
||||
|
||||
if (noCourseInstance || noCourseInstanceSelected) && course
|
||||
h1= course.get('name')
|
||||
|
@ -36,7 +43,7 @@ block content
|
|||
else
|
||||
span(data-i18n='courses.unnamed_class')
|
||||
|
||||
if !view.owner.isNew() && view.getOwnerName()
|
||||
if !view.owner.isNew() && view.getOwnerName() && courseInstance.get('name') != 'Single Player'
|
||||
span.spl.spr - Teacher:
|
||||
//a(href="/user/#{view.owner.id}") // Don't link to profiles until we improve them
|
||||
span
|
||||
|
|
|
@ -34,7 +34,7 @@ block content
|
|||
p
|
||||
strong Hi adventurer, welcome back!
|
||||
p
|
||||
a#continue-playing-btn.btn.btn-success.btn-lg(href=view.continuePlayingLink()) Continue Playing
|
||||
button#continue-playing-btn.btn.btn-success.btn-lg Continue Playing
|
||||
p
|
||||
em.spr
|
||||
span.spr Last Played:
|
||||
|
|
|
@ -1,271 +0,0 @@
|
|||
extends /templates/base
|
||||
|
||||
block content
|
||||
|
||||
//- DO NOT localize / i18n
|
||||
|
||||
div
|
||||
span *UNDER CONSTRUCTION, send feedback to
|
||||
a.spl(href='mailto:team@codecombat.com') team@codecombat.com
|
||||
div
|
||||
input.student-mode-checkbox(type='checkbox', checked=studentMode)
|
||||
span.spl Student view
|
||||
div(style='border-bottom: 1px solid black;')
|
||||
|
||||
.modal#editSettingsModal
|
||||
.modal-dialog
|
||||
.modal-header
|
||||
button.close(data-dismiss='modal')
|
||||
span ×
|
||||
h3.modal-title Edit Class Settings
|
||||
.modal-body
|
||||
p
|
||||
strong Title
|
||||
p
|
||||
input.edit-name-input(type='text', value="#{instance.name}")
|
||||
p
|
||||
strong Description
|
||||
p
|
||||
textarea.edit-description-input(rows=2)= instance.description
|
||||
p Select programming languages available to the class:
|
||||
p
|
||||
select.form-control.select-language
|
||||
option(value="Python") Python
|
||||
option(value="JavaScript") JavaScript
|
||||
option(value="All Languages") All Languages
|
||||
p
|
||||
input(type='checkbox', checked)
|
||||
span.spl Show student progress to everyone in the class
|
||||
.modal-footer
|
||||
button.btn.btn-save-settings(data-i18n="common.save_changes")
|
||||
|
||||
h1= instance.name
|
||||
small.spl (#{course.title})
|
||||
|
||||
p
|
||||
if instance.description
|
||||
each line in instance.description.split('\n')
|
||||
div= line
|
||||
|
||||
if !studentMode
|
||||
p
|
||||
button.btn.btn-xs.btn-edit-settings(data-toggle='modal', data-target='#editSettingsModal') edit class settings
|
||||
|
||||
div.well.well-sm.section-selector(role='tabpanel')
|
||||
ul.nav.nav-pills(role='tablist')
|
||||
if studentMode
|
||||
li.active(role='presentation')
|
||||
a(href='#levels', aria-controls='levels', role='tab', data-toggle='tab') Levels
|
||||
li(role='presentation')
|
||||
a(href='#progress', aria-controls='progress', role='tab', data-toggle='tab') Class
|
||||
else
|
||||
li.active(role='presentation')
|
||||
a(href='#progress', aria-controls='progress', role='tab', data-toggle='tab') Class
|
||||
li(role='presentation')
|
||||
a(href='#invite', aria-controls='invite', role='tab', data-toggle='tab') Add Students
|
||||
li(role='presentation')
|
||||
a(href='#levels', aria-controls='levels', role='tab', data-toggle='tab') Levels
|
||||
|
||||
.tab-content
|
||||
if studentMode
|
||||
.tab-pane.active#levels(role='tabpanel')
|
||||
+levels-tab
|
||||
.tab-pane#progress(role='tabpanel')
|
||||
+progress-tab
|
||||
else
|
||||
.tab-pane.active#progress(role='tabpanel')
|
||||
+progress-tab
|
||||
.tab-pane#invite(role='tabpanel')
|
||||
br
|
||||
p Invite students to join this class.
|
||||
if course.title !== 'Introduction to Computer Science'
|
||||
p Student unlock code: #{instance.code}
|
||||
p Class capacity: 34/50
|
||||
textarea.textarea-emails(rows=3, placeholder="Enter student emails to invite, one per line")
|
||||
div(style='margin-top:10px;')
|
||||
button.btn.btn-success.btn-invite Send Invites
|
||||
.tab-pane#levels(role='tabpanel')
|
||||
+levels-tab
|
||||
|
||||
mixin progress-tab
|
||||
if instance.students
|
||||
.container-fluid.summary-container
|
||||
.row
|
||||
.col-md-6
|
||||
h3 Statistics
|
||||
table.statistics-container
|
||||
tr
|
||||
td Total students:
|
||||
td #{instance.students.length}
|
||||
tr
|
||||
td Average level play time:
|
||||
td= moment.duration(stats.averageLevelPlaytime, "seconds").humanize()
|
||||
tr
|
||||
td Total play time:
|
||||
td= moment.duration(stats.totalPlayTime, "seconds").humanize()
|
||||
tr
|
||||
td Average levels completed:
|
||||
td #{stats.averageLevelsCompleted}
|
||||
tr
|
||||
td Total levels completed:
|
||||
td #{stats.totalLevelsCompleted}
|
||||
tr
|
||||
td Furthest level completed:
|
||||
td #{stats.lastLevelCompleted}
|
||||
.col-md-6
|
||||
h3 Concepts Covered
|
||||
table.table-concepts-summary
|
||||
each concept in courseConcepts
|
||||
- var conceptCompletion = Math.round(parseFloat(conceptsCompleted[concept]) / instance.students.length * 100)
|
||||
if isNaN(conceptCompletion)
|
||||
- conceptCompletion = 0
|
||||
tr
|
||||
td.concept-completion-container
|
||||
span.concept-summary(style="width:#{conceptCompletion}%;")
|
||||
span.concept-completed-foreground(data-i18n="concepts." + concept)
|
||||
span.spl - #{conceptCompletion}%
|
||||
|
||||
h3 Students
|
||||
table.table.table-condensed
|
||||
thead
|
||||
tr
|
||||
th
|
||||
span.member-header.spr Name
|
||||
if memberSort === 'nameAsc'
|
||||
span.member-header.glyphicon.glyphicon-chevron-up
|
||||
else if memberSort === 'nameDesc'
|
||||
span.member-header.glyphicon.glyphicon-chevron-down
|
||||
th
|
||||
span.progress-header.spr Progress
|
||||
if memberSort === 'progressAsc'
|
||||
span.progress-header.glyphicon.glyphicon-chevron-up
|
||||
else if memberSort === 'progressDesc'
|
||||
span.progress-header.glyphicon.glyphicon-chevron-down
|
||||
else
|
||||
span(style='padding-left:16px;')
|
||||
span.progress-key.progress-key-complete complete
|
||||
span.progress-key.progress-key-started started
|
||||
span.progress-key not started
|
||||
input.expand-progress-checkbox(type='checkbox')
|
||||
span.spl.expand-progress-label Expand details
|
||||
tbody
|
||||
each student in instance.students
|
||||
tr
|
||||
td.student-cell
|
||||
a= student
|
||||
div #{stats[student].levelsCompleted} levels completed
|
||||
div #{moment.duration(stats[student].secondsPlayed, 'seconds').humanize()} played
|
||||
div Played #{moment().subtract(stats[student].secondsLastPlayed, 'seconds').fromNow()}
|
||||
td.progress-cell
|
||||
if showExpandedProgress
|
||||
.level-progression-concepts Concepts
|
||||
each concept in courseConcepts
|
||||
if userConceptsMap[student] && userConceptsMap[student][concept] === 'complete'
|
||||
span.spr.progress-concept-cell.progress-concept-cell-complete(data-i18n="concepts." + concept)
|
||||
else if userConceptsMap[student] && userConceptsMap[student][concept] === 'started'
|
||||
span.spr.progress-concept-cell.progress-concept-cell-started(data-i18n="concepts." + concept)
|
||||
else
|
||||
span.spr.progress-concept-cell.progress-concept-cell-not-started(data-i18n="concepts." + concept)
|
||||
|
||||
.level-progression-levels Levels
|
||||
- var i = 0
|
||||
each level in course.levels
|
||||
if userLevelStateMap[student][level] === 'complete'
|
||||
span.progress-level-cell.progress-level-cell-complete #{i + 1}
|
||||
span.spl= level.replace('Course: ', '')
|
||||
.level-popup-container
|
||||
h3 #{i + 1}. #{level.replace('Course: ', '')}
|
||||
p
|
||||
- var playTime = Math.round(Math.random() * 600)
|
||||
span Time to solve
|
||||
span : #{moment.duration(playTime, "seconds").humanize()}
|
||||
p
|
||||
- var completionDate = new Date()
|
||||
- completionDate.setUTCDate(completionDate.getUTCDate() - Math.round(Math.random() * 60))
|
||||
span Completed on
|
||||
span : #{moment(completionDate).format('MMMM Do YYYY, h:mm:ss a')}
|
||||
strong(data-i18n="clans.view_solution") Click to view solution.
|
||||
else if userLevelStateMap[student][level] === 'started'
|
||||
span.progress-level-cell.progress-level-cell-started #{i + 1} #{level.replace('Course: ', '')}
|
||||
.level-popup-container
|
||||
h3 #{i + 1}. #{level.replace('Course: ', '')}
|
||||
p
|
||||
- var completionDate = new Date()
|
||||
- completionDate.setUTCDate(completionDate.getUTCDate() - Math.round(Math.random() * 60))
|
||||
span Last played on
|
||||
span : #{moment(completionDate).format('MMMM Do YYYY, h:mm:ss a')}
|
||||
strong(data-i18n="clans.view_solution") Click to view solution.
|
||||
else
|
||||
span.progress-level-cell.level-progression-level-not-started #{i + 1} #{level.replace('Course: ', '')}
|
||||
- i++
|
||||
else
|
||||
//- Condensed view
|
||||
table
|
||||
tbody
|
||||
tr
|
||||
td
|
||||
.level-progression-concepts(style='margin:0px;') Concepts
|
||||
td.condense-progress
|
||||
each concept in courseConcepts
|
||||
if userConceptsMap[student] && userConceptsMap[student][concept] === 'complete'
|
||||
span.spr.progress-concept-cell.progress-concept-cell-complete(data-i18n="concepts." + concept)
|
||||
else if userConceptsMap[student] && userConceptsMap[student][concept] === 'started'
|
||||
span.spr.progress-concept-cell.progress-concept-cell-started(data-i18n="concepts." + concept)
|
||||
else
|
||||
break
|
||||
tr
|
||||
td
|
||||
.level-progression-levels(style='margin:0px;') Levels
|
||||
td.condense-progress
|
||||
- var levelCellWidth = 100.00 / course.levels.length
|
||||
- var i = 0
|
||||
each level in course.levels
|
||||
if userLevelStateMap[student][level] === 'complete'
|
||||
span.progress-level-cell.progress-level-cell-complete(style="width:#{levelCellWidth}%;") #{i + 1}
|
||||
.level-popup-container
|
||||
h3 #{i + 1}. #{level.replace('Course: ', '')}
|
||||
p
|
||||
- var playTime = Math.round(Math.random() * 600)
|
||||
span Time to solve
|
||||
span : #{moment.duration(playTime, "seconds").humanize()}
|
||||
p
|
||||
- var completionDate = new Date()
|
||||
- completionDate.setUTCDate(completionDate.getUTCDate() - Math.round(Math.random() * 60))
|
||||
span Completed on
|
||||
span : #{moment(completionDate).format('MMMM Do YYYY, h:mm:ss a')}
|
||||
strong(data-i18n="clans.view_solution") Click to view solution.
|
||||
else if userLevelStateMap[student][level] === 'started'
|
||||
span.progress-level-cell.progress-level-cell-started(style="width:#{levelCellWidth}%;") #{i + 1}
|
||||
.level-popup-container
|
||||
h3 #{i + 1}. #{level.replace('Course: ', '')}
|
||||
p
|
||||
- var completionDate = new Date()
|
||||
- completionDate.setUTCDate(completionDate.getUTCDate() - Math.round(Math.random() * 60))
|
||||
span Last played on
|
||||
span : #{moment(completionDate).format('MMMM Do YYYY, h:mm:ss a')}
|
||||
strong(data-i18n="clans.view_solution") Click to view solution.
|
||||
else
|
||||
break
|
||||
- i++
|
||||
|
||||
mixin levels-tab
|
||||
br
|
||||
table.table.table-striped.table-condensed
|
||||
thead
|
||||
tr
|
||||
th
|
||||
th Status
|
||||
th Level
|
||||
th Concepts
|
||||
tbody
|
||||
- var student = instance.students[0]
|
||||
each level in course.levels
|
||||
tr
|
||||
td
|
||||
button.btn.btn-success.btn-play-level(data-level=level) Play
|
||||
td= userLevelStateMap[student][level]
|
||||
td= level.replace('Course: ', '')
|
||||
td
|
||||
each concept in courseConcepts
|
||||
if levelConceptsMap[level] && levelConceptsMap[level][concept]
|
||||
span.spr.progress-level-cell.level-progression-level-not-started(data-i18n="concepts." + concept)
|
|
@ -1,53 +0,0 @@
|
|||
extends /templates/base
|
||||
|
||||
block content
|
||||
|
||||
//- DO NOT localize / i18n
|
||||
|
||||
div
|
||||
span *UNDER CONSTRUCTION, send feedback to
|
||||
a.spl(href='mailto:team@codecombat.com') team@codecombat.com
|
||||
div(style='border-bottom: 1px solid black')
|
||||
|
||||
.well.well-lg.enroll-container
|
||||
h1.center Buy Course
|
||||
h3 1. Course
|
||||
p Select 'All Courses' for a 50% discount!
|
||||
.form-group
|
||||
select.form-control.course-select
|
||||
each course in courses
|
||||
option(value="#{course.title}")= course.title
|
||||
option(value="All Courses") All Courses
|
||||
|
||||
h3 2. Number of students
|
||||
p Enter the number of students you need for this class.
|
||||
input.input-quantity(type='text', value="#{quantity}")
|
||||
|
||||
h3 3. Name your class
|
||||
p This will be displayed on the course page for you and your students. It can be changed later.
|
||||
input.session-name(type='text', placeholder="Mrs. Smith's 4th Period")
|
||||
|
||||
h3 4. Buy
|
||||
p
|
||||
span.spr You are purchasing a license for
|
||||
strong.spr #{selectedCourseTitle}
|
||||
span.spr for
|
||||
strong #{quantity} students
|
||||
| .
|
||||
p After purchase you will receive an unlock code to distribute to your students, which they can use to enroll in your class.
|
||||
p.center
|
||||
if price > 0
|
||||
button.btn.btn-success.btn-lg.btn-buy $#{price}
|
||||
else
|
||||
button.btn.btn-success.btn-lg.btn-buy FREE
|
||||
|
||||
h3 Free trial for teachers!
|
||||
p
|
||||
span.spr Please fill out our
|
||||
a(href='/teachers/freetrial', data-i18n="teachers.teacher_subs_2")
|
||||
span.spl to get individual access to all courses for evalutaion purposes.
|
||||
|
||||
h3 Questions?
|
||||
p
|
||||
span Please contact
|
||||
a.spl(href='mailto:team@codecombat.com') team@codecombat.com
|
|
@ -1,78 +0,0 @@
|
|||
extends /templates/base
|
||||
|
||||
block content
|
||||
|
||||
//- DO NOT localize / i18n
|
||||
|
||||
div
|
||||
span *UNDER CONSTRUCTION, send feedback to
|
||||
a.spl(href='mailto:team@codecombat.com') team@codecombat.com
|
||||
div(style='border-bottom: 1px solid black; margin-bottom: 20px')
|
||||
|
||||
|
||||
.info-container
|
||||
h1.center= course.title
|
||||
p.center.gameplay-img-container
|
||||
img(src="#{course.image}" width='800')
|
||||
p= course.description
|
||||
|
||||
h1.center Learn More in Less Time
|
||||
p
|
||||
span.spr Your students will learn
|
||||
strong.spr more computer science
|
||||
span.spr material in
|
||||
strong.spr less time
|
||||
span with CodeCombat!
|
||||
p
|
||||
span.spr In about
|
||||
strong.spr #{course.duration} hours
|
||||
span.spr your students will work through
|
||||
strong.spr #{course.levels.length} lessons
|
||||
span and cover these high-level topics:
|
||||
p
|
||||
ul
|
||||
each topic in course.topics
|
||||
li= topic
|
||||
|
||||
h1.center No Experience Necesssary
|
||||
p
|
||||
strong.spr No outside experience
|
||||
span is needed for students to complete this course. They will pick up where the left off from the previous CodeCombat course.
|
||||
p
|
||||
strong.spr Teachers do not need programming experience
|
||||
span to administer this course. Your students will learn on their own.
|
||||
p
|
||||
span.spr There are built-in
|
||||
strong.spr help videos, level guides, tool tips
|
||||
span to explain everything to your students.
|
||||
p
|
||||
span.spr Use
|
||||
strong.spr student progress monitoring
|
||||
span to match up stronger students with those that need a little extra help!
|
||||
|
||||
h1.center Monitor Student Progress
|
||||
p.center.monitoring-img-container
|
||||
img(src='/images/pages/clans/dashboard_preview.png' width='700')
|
||||
div.center.caption-text TODO: Add caption
|
||||
p.progress-container
|
||||
ul
|
||||
li
|
||||
strong Track concepts
|
||||
span.spl learned by each student
|
||||
li Track levels completed for each student
|
||||
li
|
||||
span See your students'
|
||||
strong.spl solutions
|
||||
li Sort students by name or progress
|
||||
|
||||
h1.center Teachers Love CodeCombat!
|
||||
p
|
||||
div.praise-quote "#{praise.quote}"
|
||||
div.caption-text - #{praise.source}
|
||||
|
||||
p.center
|
||||
button.btn.btn-info.btn-lg.btn-enroll(data-course-id="#{courseID}") Enroll Now
|
||||
|
||||
p.contact-container
|
||||
span For more information, please contact
|
||||
a.spl(href='mailto:team@codecombat.com') team@codecombat.com
|
|
@ -1,128 +0,0 @@
|
|||
extends /templates/base
|
||||
|
||||
block content
|
||||
|
||||
//- DO NOT localize / i18n
|
||||
|
||||
div(style='border-bottom: 1px solid black')
|
||||
span *UNDER CONSTRUCTION, send feedback to
|
||||
a.spl(href='mailto:team@codecombat.com') team@codecombat.com
|
||||
|
||||
.modal#continueModal
|
||||
.modal-dialog
|
||||
.modal-header
|
||||
button.close(data-dismiss='modal')
|
||||
span ×
|
||||
h3.modal-title Loading...
|
||||
.modal-body
|
||||
.container-fluid
|
||||
.row.button-row.row-pick-class
|
||||
.col-md-12
|
||||
.well.well-sm
|
||||
p
|
||||
div.instruction-label Pick from your current classes
|
||||
.container-fluid
|
||||
.row
|
||||
.col-md-8
|
||||
select.form-control.select-session
|
||||
each inst in instances
|
||||
option= inst.name
|
||||
.col-md-4
|
||||
button.btn.btn-success.btn-enter Enter
|
||||
.row.button-row.center.row-pick-class
|
||||
.col-md-12
|
||||
div.or Or
|
||||
if studentMode
|
||||
.row.button-row
|
||||
.col-md-12
|
||||
.well.well-sm
|
||||
p
|
||||
div.instruction-label Enter an unlock code
|
||||
.container-fluid
|
||||
.row
|
||||
.col-md-8
|
||||
input.code-input(type='text', placeholder="Enter unlock code")
|
||||
.col-md-4
|
||||
button.btn.btn-success.btn-enroll Enroll
|
||||
else
|
||||
.row.button-row.center
|
||||
.col-md-12
|
||||
button.btn.btn-success.btn-lg.btn-buy Buy this course
|
||||
|
||||
br
|
||||
if !studentMode
|
||||
button.btn.btn-warning.btn-student Students Click Here
|
||||
else
|
||||
button.btn.btn-warning.btn-teacher Teachers Click Here
|
||||
|
||||
h1.center Courses on CodeCombat
|
||||
|
||||
if !studentMode
|
||||
.info-container
|
||||
p Courses are designed to introduce computer science concepts using CodeCombat's fun and engaging environment. CodeCombat levels are organized around key topics to encourage progressive learning, over the course of 5 hours.
|
||||
|
||||
.container-fluid
|
||||
.row
|
||||
.col-md-6
|
||||
ul
|
||||
li Learn more in less time
|
||||
li No coding experience necesssary
|
||||
li Easily monitor student progress
|
||||
|
||||
p Purchase a course for your entire class. It's easy to sign up your students!
|
||||
p.faq-blurb
|
||||
span.spr See the courses
|
||||
a.spr.courses-faq FAQ
|
||||
span for more information.
|
||||
.col-md-6
|
||||
img.img-quote(src="/images/pages/courses/coco_complab.png")
|
||||
p
|
||||
.well.well-sm
|
||||
div.praise-quote "#{praise.quote}"
|
||||
div.praise-caption - #{praise.source}
|
||||
|
||||
h2.center Choose Your Course:
|
||||
|
||||
.container-fluid
|
||||
- var i = 0
|
||||
while i < courses.length
|
||||
.row
|
||||
+course-block(courses[i], i)
|
||||
- i++
|
||||
if i < courses.length
|
||||
+course-block(courses[i], i)
|
||||
- i++
|
||||
|
||||
mixin course-block(course, courseID)
|
||||
.col-md-6
|
||||
.well.panel.course-panel(class=course.unlocked ? 'panel-success' : 'panel-info')
|
||||
.panel-heading
|
||||
.panel-title
|
||||
span.spr #{course.title}
|
||||
strong #{course.unlocked ? '[ enrolled ]' : ''}
|
||||
.panel-body
|
||||
.container-fluid
|
||||
.row
|
||||
.col-md-12
|
||||
p
|
||||
img.course-image(src="#{course.image}")
|
||||
.row.button-row
|
||||
.col-md-6
|
||||
strong Topics
|
||||
ul
|
||||
each topic in course.topics
|
||||
li= topic
|
||||
strong Hours of content: #{course.duration}
|
||||
.col-md-6.center(style='margin-top: 40px;')
|
||||
if !studentMode
|
||||
if course.unlocked
|
||||
button.btn.btn-lg.btn-success.btn-continue(data-toggle='modal', data-target="#continueModal", data-course-title="#{course.title}", data-course-id="#{courseID}") #{course.unlocked ? 'Continue' : 'Enter'}
|
||||
else if course.title === 'Introduction to Computer Science'
|
||||
button.btn.btn-lg.btn-success.btn-buy(data-course-id="#{courseID}") Get FREE course
|
||||
else
|
||||
button.btn.btn-lg.btn-success.btn-buy(data-course-id="#{courseID}") Buy course
|
||||
else
|
||||
if course.unlocked
|
||||
a.btn.btn-lg.btn-success.btn-continue(href="/courses/mock1/#{courseID}?student=true") Continue
|
||||
else
|
||||
button.btn.btn-lg.btn-success.btn-continue(data-toggle='modal', data-target="#continueModal", data-course-title="#{course.title}", data-course-id="#{courseID}") Enter
|
|
@ -28,6 +28,8 @@ if campaign
|
|||
div(style="left: #{level.position.x}%; bottom: #{level.position.y}%", class="level-shadow" + (level.next ? " next" : "") + " " + (levelStatusMap[level.slug] || ""))
|
||||
.level-info-container(data-level-slug=level.slug, data-level-path=level.levelPath || 'level', data-level-name=level.name)
|
||||
- var playCount = levelPlayCountMap[level.slug]
|
||||
.progress.progress-striped.active.hide
|
||||
.progress-bar(style="width: 100%")
|
||||
div(class="level-info " + (levelStatusMap[level.slug] || "") + (level.requiresSubscription ? " premium" : ""))
|
||||
.level-status
|
||||
h3= i18n(level, 'name') + (level.disabled ? " (Coming soon!)" : (level.locked ? " (Locked)" : ""))
|
||||
|
|
|
@ -13,7 +13,15 @@ module.exports = class HomeView extends RootView
|
|||
window.tracker?.trackEvent 'Homepage Loaded', category: 'Homepage'
|
||||
if @getQueryVariable 'hour_of_code'
|
||||
application.router.navigate "/hoc", trigger: true
|
||||
if @justPlaysCourses()
|
||||
|
||||
isHourOfCodeWeek = true # Temporary: default to /hoc flow during the main event week
|
||||
if isHourOfCodeWeek and (@isNewPlayer() or (@justPlaysCourses() and me.isAnonymous()))
|
||||
# Go/return straight to playing single-player HoC course on Play click
|
||||
@playURL = '/hoc?go=true'
|
||||
@alternatePlayURL = '/play'
|
||||
@alternatePlayText = 'home.play_campaign_version'
|
||||
else if @justPlaysCourses()
|
||||
# Save players who might be in a classroom from getting into the campaign
|
||||
@playURL = '/courses'
|
||||
@alternatePlayURL = '/play'
|
||||
@alternatePlayText = 'home.play_campaign_version'
|
||||
|
@ -44,3 +52,6 @@ module.exports = class HomeView extends RootView
|
|||
justPlaysCourses: ->
|
||||
# This heuristic could be better, but currently we don't add to me.get('courseInstances') for single-player anonymous intro courses, so they have to beat a level without choosing a hero.
|
||||
return me.get('stats')?.gamesCompleted and not me.get('heroConfig')
|
||||
|
||||
isNewPlayer: ->
|
||||
not me.get('stats')?.gamesCompleted and not me.get('heroConfig')
|
||||
|
|
|
@ -89,7 +89,7 @@ module.exports = class SubscriptionView extends RootView
|
|||
onClickRecipientsSubscribe: (e) ->
|
||||
emails = @$el.find('.recipient-emails').val().split('\n')
|
||||
valid = @emailValidator.validateEmails(emails, =>@render?())
|
||||
@recipientSubs.startSubscribe(emails) if valid
|
||||
@recipientSubs.startSubscribe(emails) if valid
|
||||
|
||||
onClickRecipientUnsubscribe: (e) ->
|
||||
$(e.target).addClass('hide')
|
||||
|
@ -108,8 +108,8 @@ class EmailValidator
|
|||
|
||||
validateEmails: (emails, render) ->
|
||||
@lastEmails = emails.join('\n')
|
||||
#taken from http://www.regular-expressions.info/email.html
|
||||
emailRegex = /[A-z0-9._%+-]+@[A-z0-9.-]+\.[A-z]{2,4}/
|
||||
#taken from http://www.regular-expressions.info/email.html
|
||||
emailRegex = /[A-z0-9._%+-]+@[A-z0-9.-]+\.[A-z]{2,63}/
|
||||
@validEmails = (email for email in emails when emailRegex.test(email.trim().toLowerCase()))
|
||||
return @emailsInvalid(render) if @validEmails.length < emails.length
|
||||
return @emailsValid(render)
|
||||
|
|
|
@ -79,7 +79,7 @@ module.exports = class AuthModal extends ModalView
|
|||
|
||||
emailCheck: ->
|
||||
email = $('#email', @$el).val()
|
||||
filter = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i # https://news.ycombinator.com/item?id=5763990
|
||||
filter = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,63}$/i # https://news.ycombinator.com/item?id=5763990
|
||||
unless filter.test(email)
|
||||
forms.setErrorToProperty @$el, 'email', 'Please enter a valid email address', true
|
||||
return false
|
||||
|
|
|
@ -4,21 +4,21 @@ template = require 'templates/courses/choose-language-modal'
|
|||
module.exports = class ChooseLanguageModal extends ModalView
|
||||
id: 'choose-language-modal'
|
||||
template: template
|
||||
|
||||
|
||||
events:
|
||||
'click .lang-choice-btn': 'onClickLanguageChoiceButton'
|
||||
|
||||
|
||||
initialize: (options) ->
|
||||
options ?= {}
|
||||
@logoutFirst = options.logoutFirst
|
||||
|
||||
onClickLanguageChoiceButton: (e) ->
|
||||
@chosenLanguage = $(e.target).data('language')
|
||||
@chosenLanguage = $(e.target).closest('.lang-choice-btn').data('language')
|
||||
if @logoutFirst
|
||||
@logoutUser()
|
||||
else
|
||||
@saveLanguageSetting()
|
||||
|
||||
|
||||
logoutUser: ->
|
||||
$.ajax({
|
||||
method: 'POST'
|
||||
|
@ -45,7 +45,8 @@ module.exports = class ChooseLanguageModal extends ModalView
|
|||
@listenToOnce me, 'sync', @onLanguageSettingSaved
|
||||
else
|
||||
@onLanguageSettingSaved()
|
||||
|
||||
|
||||
onLanguageSettingSaved: ->
|
||||
@trigger('set-language')
|
||||
window.tracker?.trackEvent 'Chose language', category: 'Courses', label: @chosenLanguage
|
||||
@hide()
|
||||
|
|
|
@ -19,7 +19,7 @@ module.exports = class ClassroomView extends RootView
|
|||
id: 'classroom-view'
|
||||
template: template
|
||||
teacherMode: false
|
||||
|
||||
|
||||
events:
|
||||
'click #edit-class-details-link': 'onClickEditClassDetailsLink'
|
||||
'click #activate-licenses-btn': 'onClickActivateLicensesButton'
|
||||
|
@ -57,7 +57,7 @@ module.exports = class ClassroomView extends RootView
|
|||
@listenToOnce sessions, 'sync', (sessions) ->
|
||||
@sessions.add(sessions.slice())
|
||||
sessions.courseInstance.sessionsByUser = sessions.groupBy('creator')
|
||||
|
||||
|
||||
# generate course instance JIT, in the meantime have models w/out equivalents in the db
|
||||
for course in @courses.models
|
||||
query = {courseID: course.id, classroomID: @classroom.id}
|
||||
|
@ -83,7 +83,7 @@ module.exports = class ClassroomView extends RootView
|
|||
campaign = @campaigns.get(campaignID)
|
||||
courseInstance.sessions.campaign = campaign
|
||||
super()
|
||||
|
||||
|
||||
afterRender: ->
|
||||
@$('[data-toggle="popover"]').popover({
|
||||
html: true
|
||||
|
@ -131,7 +131,7 @@ module.exports = class ClassroomView extends RootView
|
|||
|
||||
classStats: ->
|
||||
stats = {}
|
||||
|
||||
|
||||
playtime = 0
|
||||
total = 0
|
||||
for session in @sessions.models
|
||||
|
@ -140,8 +140,8 @@ module.exports = class ClassroomView extends RootView
|
|||
total += 1
|
||||
stats.averagePlaytime = if playtime and total then moment.duration(playtime / total, "seconds").humanize() else 0
|
||||
stats.totalPlaytime = if playtime then moment.duration(playtime, "seconds").humanize() else 0
|
||||
|
||||
completeSessions = @sessions.filter (s) -> s.get('state').complete
|
||||
|
||||
completeSessions = @sessions.filter (s) -> s.get('state')?.complete
|
||||
stats.averageLevelsComplete = if @users.size() then (_.size(completeSessions) / @users.size()).toFixed(1) else 'N/A'
|
||||
stats.totalLevelsComplete = _.size(completeSessions)
|
||||
return stats
|
||||
|
@ -158,7 +158,7 @@ module.exports = class ClassroomView extends RootView
|
|||
onCourseInstanceCreated = =>
|
||||
courseInstance.addMember(userID)
|
||||
@listenToOnce courseInstance, 'sync', @render
|
||||
|
||||
|
||||
if courseInstance.isNew()
|
||||
# adding the first student to this course, so generate the course instance for it
|
||||
courseInstance.save(null, {validate: false})
|
||||
|
@ -175,7 +175,7 @@ module.exports = class ClassroomView extends RootView
|
|||
})
|
||||
@openModalView(modal)
|
||||
modal.once 'remove-student', @onStudentRemoved, @
|
||||
|
||||
|
||||
onStudentRemoved: (e) ->
|
||||
@users.remove(e.user)
|
||||
@render()
|
||||
|
@ -193,4 +193,4 @@ module.exports = class ClassroomView extends RootView
|
|||
|
||||
getLevelURL: (level, course, courseInstance, session) ->
|
||||
return null unless @teacherMode and _.all(arguments)
|
||||
"/play/level/#{level.slug}?course=#{course.id}&course-instance=#{courseInstance.id}&session=#{session.id}&observing=true"
|
||||
"/play/level/#{level.slug}?course=#{course.id}&course-instance=#{courseInstance.id}&session=#{session.id}&observing=true"
|
||||
|
|
|
@ -18,7 +18,7 @@ module.exports = class HourOfCodeView extends RootView
|
|||
template: template
|
||||
|
||||
events:
|
||||
'click #student-btn': 'onClickStudentButton'
|
||||
'click #continue-playing-btn': 'onClickContinuePlayingButton'
|
||||
'click #start-new-game-btn': 'onClickStartNewGameButton'
|
||||
'click #log-in-btn': 'onClickLogInButton'
|
||||
'click #log-out-link': 'onClickLogOutLink'
|
||||
|
@ -52,7 +52,7 @@ module.exports = class HourOfCodeView extends RootView
|
|||
project: 'name,slug'
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
setUpHourOfCode: ->
|
||||
# If we haven't tracked this player as an hourOfCode player yet, and it's a new account, we do that now.
|
||||
elapsed = new Date() - new Date(me.get('dateCreated'))
|
||||
|
@ -60,16 +60,26 @@ module.exports = class HourOfCodeView extends RootView
|
|||
me.set('hourOfCode', true)
|
||||
me.patch()
|
||||
$('body').append($('<img src="https://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">'))
|
||||
application.tracker?.trackEvent 'Hour of Code Begin'
|
||||
window.tracker?.trackEvent 'Hour of Code Begin'
|
||||
|
||||
onClickContinuePlayingButton: ->
|
||||
url = @continuePlayingLink()
|
||||
window.tracker?.trackEvent 'HoC continue playing ', category: 'HoC', label: url
|
||||
app.router.navigate(url, { trigger: true })
|
||||
|
||||
afterRender: ->
|
||||
super()
|
||||
@onClickStartNewGameButton() if @getQueryVariable('go') and not @lastLevel
|
||||
|
||||
onClickStartNewGameButton: ->
|
||||
# user without hour of code course instance, creates one, starts playing
|
||||
# User without hour of code course instance, creates one, starts playing
|
||||
modal = new ChooseLanguageModal({
|
||||
logoutFirst: @hourOfCodeCourseInstance?
|
||||
})
|
||||
@openModalView(modal)
|
||||
@listenToOnce modal, 'set-language', @startHourOfCodePlay
|
||||
|
||||
window.tracker?.trackEvent 'Start New Game', category: 'HoC', label: 'HoC Start New Game'
|
||||
|
||||
continuePlayingLink: ->
|
||||
ci = @hourOfCodeCourseInstance
|
||||
"/play/level/#{@lastLevel.get('slug')}?course=#{ci.get('courseID')}&course-instance=#{ci.id}"
|
||||
|
@ -87,11 +97,13 @@ module.exports = class HourOfCodeView extends RootView
|
|||
modal = new StudentLogInModal()
|
||||
@openModalView(modal)
|
||||
modal.on 'want-to-create-account', @onWantToCreateAccount, @
|
||||
window.tracker?.trackEvent 'Started Login', category: 'HoC', label: 'HoC Login'
|
||||
|
||||
onWantToCreateAccount: ->
|
||||
modal = new StudentSignUpModal()
|
||||
@openModalView(modal)
|
||||
window.tracker?.trackEvent 'Started Signup', category: 'HoC', label: 'HoC Sign Up'
|
||||
|
||||
onClickLogOutLink: ->
|
||||
auth.logoutUser()
|
||||
|
||||
window.tracker?.trackEvent 'Log Out', category: 'HoC', label: 'HoC Log Out'
|
||||
auth.logoutUser()
|
||||
|
|
|
@ -7,7 +7,7 @@ User = require 'models/User'
|
|||
module.exports = class StudentSignInModal extends ModalView
|
||||
id: 'student-log-in-modal'
|
||||
template: template
|
||||
|
||||
|
||||
events:
|
||||
'click #log-in-btn': 'onClickLogInButton'
|
||||
'submit form': 'onSubmitForm'
|
||||
|
@ -16,11 +16,13 @@ module.exports = class StudentSignInModal extends ModalView
|
|||
onSubmitForm: (e) ->
|
||||
e.preventDefault()
|
||||
@login()
|
||||
|
||||
|
||||
onClickLogInButton: ->
|
||||
@login()
|
||||
|
||||
login: ->
|
||||
# TODO: doesn't track failed login
|
||||
window.tracker?.trackEvent 'Finished Login', category: 'Courses', label: 'Courses Student Login'
|
||||
data = forms.formToObject @$el
|
||||
@enableModalInProgress(@$el)
|
||||
auth.loginUser data, (jqxhr) =>
|
||||
|
@ -39,4 +41,4 @@ module.exports = class StudentSignInModal extends ModalView
|
|||
|
||||
afterInsert: ->
|
||||
super()
|
||||
_.delay (=> @$('input:visible:first').focus()), 500
|
||||
_.delay (=> @$('input:visible:first').focus()), 500
|
||||
|
|
|
@ -23,7 +23,7 @@ module.exports = class StudentSignUpModal extends ModalView
|
|||
afterInsert: ->
|
||||
super()
|
||||
_.delay (=> @$('input:visible:first').focus()), 500
|
||||
|
||||
|
||||
onClickSkipLink: ->
|
||||
@trigger 'click-skip-link' # defer to view that opened this modal
|
||||
@hide?()
|
||||
|
@ -37,7 +37,7 @@ module.exports = class StudentSignUpModal extends ModalView
|
|||
|
||||
emailCheck: ->
|
||||
email = @$('#email').val()
|
||||
filter = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i # https://news.ycombinator.com/item?id=5763990
|
||||
filter = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,63}$/i # https://news.ycombinator.com/item?id=5763990
|
||||
unless filter.test(email)
|
||||
@$('#errors-alert').text($.i18n.t('share_progress_modal.email_invalid')).removeClass('hide')
|
||||
return false
|
||||
|
@ -71,7 +71,8 @@ module.exports = class StudentSignUpModal extends ModalView
|
|||
data.emails ?= {}
|
||||
data.emails.generalNews ?= {}
|
||||
data.emails.generalNews.enabled = false
|
||||
window.tracker?.trackEvent 'Finished Student Signup', label: 'CodeCombat'
|
||||
# TODO: Doesn't handle failed user creation. Double posts when placed in onCreateUserSuccess.
|
||||
window.tracker?.trackEvent 'Finished Student Signup', category: 'Courses', label: 'Courses Student Signup'
|
||||
@enableModalInProgress(@$el)
|
||||
user = new User(data)
|
||||
user.notyErrors = false
|
||||
|
|
|
@ -1,205 +0,0 @@
|
|||
app = require 'core/application'
|
||||
utils = require 'core/utils'
|
||||
RootView = require 'views/core/RootView'
|
||||
template = require 'templates/courses/mock1/course-details'
|
||||
CocoCollection = require 'collections/CocoCollection'
|
||||
Campaign = require 'models/Campaign'
|
||||
|
||||
module.exports = class CourseDetailsView extends RootView
|
||||
id: 'course-details-mock-view'
|
||||
template: template
|
||||
|
||||
events:
|
||||
'change .expand-progress-checkbox': 'onExpandedProgressCheckbox'
|
||||
'change .student-mode-checkbox': 'onChangeStudent'
|
||||
'click .btn-play-level': 'onClickPlayLevel'
|
||||
'click .btn-save-settings': 'onClickSaveSettings'
|
||||
'click .member-header': 'onClickMemberHeader'
|
||||
'click .progress-header': 'onClickProgressHeader'
|
||||
'mouseenter .progress-level-cell': 'onMouseEnterPoint'
|
||||
'mouseleave .progress-level-cell': 'onMouseLeavePoint'
|
||||
|
||||
constructor: (options, @courseID=0, @instanceID=0) ->
|
||||
super options
|
||||
@studentMode = utils.getQueryVariable('student', false) or options.studentMode
|
||||
@initData()
|
||||
|
||||
destroy: ->
|
||||
@stopListening?()
|
||||
|
||||
getRenderData: ->
|
||||
context = super()
|
||||
context.conceptsProgression = @conceptsProgression ? []
|
||||
context.course = @course ? {}
|
||||
context.courseConcepts = @courseConcepts ? []
|
||||
context.instance = @instances?[@currentInstanceIndex] ? {}
|
||||
context.instances = @instances ? []
|
||||
context.levelConceptsMap = @levelConceptsMap ? {}
|
||||
context.maxLastStartedIndex = @maxLastStartedIndex ? 0
|
||||
context.memberSort = @memberSort
|
||||
context.userConceptsMap = @userConceptsMap ? {}
|
||||
context.userLevelStateMap = @userLevelStateMap ? {}
|
||||
context.showExpandedProgress = @showExpandedProgress
|
||||
context.stats = @stats
|
||||
context.studentMode = @studentMode ? false
|
||||
|
||||
conceptsCompleted = {}
|
||||
for user of context.userConceptsMap
|
||||
for concept of context.userConceptsMap[user]
|
||||
conceptsCompleted[concept] ?= 0
|
||||
conceptsCompleted[concept]++
|
||||
context.conceptsCompleted = conceptsCompleted
|
||||
context
|
||||
|
||||
initData: ->
|
||||
@memberSort = 'nameAsc'
|
||||
mockData = require 'views/courses/mock1/CoursesMockData'
|
||||
@course = mockData.courses[@courseID]
|
||||
@currentInstanceIndex = @instanceID
|
||||
@instances = mockData.instances
|
||||
@updateLevelMaps()
|
||||
|
||||
@campaigns = new CocoCollection([], { url: "/db/campaign", model: Campaign, comparator:'_id' })
|
||||
@listenTo @campaigns, 'sync', @onCampaignSync
|
||||
@supermodel.loadModel @campaigns, 'clan', cache: false
|
||||
|
||||
updateLevelMaps: ->
|
||||
@levelMap = {}
|
||||
@levelMap[level] = true for level in @course.levels
|
||||
@userLevelStateMap = {}
|
||||
@stats =
|
||||
averageLevelPlaytime: _.random(30, 240)
|
||||
averageLevelsCompleted: _.random(1, @course.levels.length)
|
||||
students: {}
|
||||
@maxLastStartedIndex = -1
|
||||
for student in @instances?[@currentInstanceIndex].students
|
||||
@userLevelStateMap[student] = {}
|
||||
lastCompletedIndex = _.random(-1, @course.levels.length)
|
||||
for i in [0..lastCompletedIndex]
|
||||
@userLevelStateMap[student][@course.levels[i]] = 'complete'
|
||||
lastStartedIndex = lastCompletedIndex + 1
|
||||
@userLevelStateMap[student][@course.levels[lastStartedIndex]] = 'started'
|
||||
@maxLastStartedIndex = lastStartedIndex if lastStartedIndex > @maxLastStartedIndex
|
||||
|
||||
@stats[student] ?= {}
|
||||
@stats[student].levelsCompleted = 0
|
||||
@stats[student].levelsCompleted++ for level in @course.levels when @userLevelStateMap[student][level] is 'complete'
|
||||
@stats[student].secondsPlayed = Math.round(Math.random() * 1000 * (@stats[student].levelsCompleted + 1))
|
||||
@stats[student].secondsLastPlayed = Math.round(Math.random() * 100000)
|
||||
@sortMembers()
|
||||
@stats.totalPlayTime = @instances?[@currentInstanceIndex].students?.length * @stats.averageLevelPlaytime ? 0
|
||||
@stats.totalLevelsCompleted = @instances?[@currentInstanceIndex].students?.length * @stats.averageLevelsCompleted ? 0
|
||||
@stats.totalPlayTime = @instances?[@currentInstanceIndex].students?.length * @stats.averageLevelPlaytime ? 0
|
||||
@stats.lastLevelCompleted = @course.levels[0] ? @course.levels[@course.levels.length - 1]
|
||||
|
||||
sortMembers: ->
|
||||
# Progress sort precedence: most completed concepts, most started concepts, most levels, name sort
|
||||
instance = @instances?[@currentInstanceIndex] ? {}
|
||||
return if _.isEmpty(instance)
|
||||
switch @memberSort
|
||||
when "nameDesc"
|
||||
instance.students.sort (a, b) -> b.localeCompare(a)
|
||||
when "progressAsc"
|
||||
instance.students.sort (a, b) =>
|
||||
for level in @course.levels
|
||||
if @userLevelStateMap[a][level] isnt 'complete' and @userLevelStateMap[b][level] is 'complete'
|
||||
return -1
|
||||
else if @userLevelStateMap[a][level] is 'complete' and @userLevelStateMap[b][level] isnt 'complete'
|
||||
return 1
|
||||
0
|
||||
when "progressDesc"
|
||||
instance.students.sort (a, b) =>
|
||||
for level in @course.levels
|
||||
if @userLevelStateMap[a][level] isnt 'complete' and @userLevelStateMap[b][level] is 'complete'
|
||||
return 1
|
||||
else if @userLevelStateMap[a][level] is 'complete' and @userLevelStateMap[b][level] isnt 'complete'
|
||||
return -1
|
||||
0
|
||||
else
|
||||
instance.students.sort (a, b) -> a.localeCompare(b)
|
||||
|
||||
onCampaignSync: ->
|
||||
return unless @campaigns.loaded
|
||||
@conceptsProgression = []
|
||||
@courseConcepts = []
|
||||
@levelConceptsMap = {}
|
||||
@levelNameSlugMap = {}
|
||||
@userConceptsMap = {}
|
||||
# Update course levels if course has a specific campaign
|
||||
for campaign in @campaigns.models when campaign.get('slug') is @course.campaign
|
||||
@course.levels = []
|
||||
for levelID, level of campaign.get('levels')
|
||||
if campaign.get('slug') is @course.campaign
|
||||
@course.levels.push level.name
|
||||
@updateLevelMaps()
|
||||
|
||||
for campaign in @campaigns.models
|
||||
continue if campaign.get('slug') is 'auditions'
|
||||
for levelID, level of campaign.get('levels')
|
||||
@levelNameSlugMap[level.name] = level.slug
|
||||
if level.concepts?
|
||||
for concept in level.concepts
|
||||
@conceptsProgression.push concept unless concept in @conceptsProgression
|
||||
continue unless @levelMap[level.name]
|
||||
@courseConcepts.push concept unless concept in @courseConcepts
|
||||
@levelConceptsMap[level.name] ?= {}
|
||||
@levelConceptsMap[level.name][concept] = true
|
||||
for student in @instances?[@currentInstanceIndex].students
|
||||
@userConceptsMap[student] ?= {}
|
||||
if @userLevelStateMap[student][level.name] is 'complete'
|
||||
@userConceptsMap[student][concept] = 'complete'
|
||||
else if @userLevelStateMap[student][level.name] is 'started'
|
||||
@userConceptsMap[student][concept] ?= 'started'
|
||||
@courseConcepts.sort (a, b) => if @conceptsProgression.indexOf(a) < @conceptsProgression.indexOf(b) then -1 else 1
|
||||
@render?()
|
||||
|
||||
onChangeStudent: (e) ->
|
||||
@studentMode = $('.student-mode-checkbox').prop('checked')
|
||||
@render?()
|
||||
$('.student-mode-checkbox').attr('checked', @studentMode)
|
||||
|
||||
onExpandedProgressCheckbox: (e) ->
|
||||
@showExpandedProgress = $('.expand-progress-checkbox').prop('checked')
|
||||
# TODO: why does render reset the checkbox to be unchecked?
|
||||
@render?()
|
||||
$('.expand-progress-checkbox').attr('checked', @showExpandedProgress)
|
||||
|
||||
onClickMemberHeader: (e) ->
|
||||
@memberSort = if @memberSort is 'nameAsc' then 'nameDesc' else 'nameAsc'
|
||||
@sortMembers()
|
||||
@render?()
|
||||
|
||||
onClickProgressHeader: (e) ->
|
||||
@memberSort = if @memberSort is 'progressAsc' then 'progressDesc' else 'progressAsc'
|
||||
@sortMembers()
|
||||
@render?()
|
||||
|
||||
onClickPlayLevel: (e) ->
|
||||
levelName = $(e.target).data('level')
|
||||
levelSlug = @levelNameSlugMap[levelName]
|
||||
Backbone.Mediator.publish 'router:navigate', {
|
||||
route: "/play/level/#{levelSlug}"
|
||||
viewClass: 'views/play/level/PlayLevelView'
|
||||
viewArgs: [{}, levelSlug]
|
||||
}
|
||||
|
||||
onClickSaveSettings: (e) ->
|
||||
if name = $('.edit-name-input').val()
|
||||
@instances[@currentInstanceIndex].name = name
|
||||
description = $('.edit-description-input').val()
|
||||
@instances[@currentInstanceIndex].description = description
|
||||
$('#editSettingsModal').modal('hide')
|
||||
@render?()
|
||||
|
||||
onMouseEnterPoint: (e) ->
|
||||
$('.level-popup-container').hide()
|
||||
container = $(e.target).find('.level-popup-container').show()
|
||||
margin = 20
|
||||
offset = $(e.target).offset()
|
||||
scrollTop = $('#page-container').scrollTop()
|
||||
height = container.outerHeight()
|
||||
container.css('left', offset.left + e.offsetX)
|
||||
container.css('top', offset.top + scrollTop - height - margin)
|
||||
|
||||
onMouseLeavePoint: (e) ->
|
||||
$(e.target).find('.level-popup-container').hide()
|
|
@ -1,63 +0,0 @@
|
|||
app = require 'core/application'
|
||||
RootView = require 'views/core/RootView'
|
||||
template = require 'templates/courses/mock1/course-enroll'
|
||||
|
||||
module.exports = class CourseEnrollView extends RootView
|
||||
id: 'course-enroll-mock-view'
|
||||
template: template
|
||||
|
||||
events:
|
||||
'click .btn-buy': 'onClickBuy'
|
||||
'change .course-select': 'onChangeCourse'
|
||||
'change .input-quantity': 'onQuantityChange'
|
||||
|
||||
constructor: (options, @courseID=0) ->
|
||||
super options
|
||||
@initData()
|
||||
|
||||
getRenderData: ->
|
||||
context = super()
|
||||
context.courses = @courses ? {}
|
||||
context.courseID = @courseID
|
||||
context.price = @price ? 0
|
||||
context.quantity = @quantity
|
||||
context.selectedCourseTitle = @selectedCourseTitle
|
||||
context
|
||||
|
||||
afterRender: ->
|
||||
super()
|
||||
@$el.find('.course-select').val(@selectedCourseTitle)
|
||||
|
||||
initData: ->
|
||||
mockData = require 'views/courses/mock1/CoursesMockData'
|
||||
@courses = mockData.courses
|
||||
@selectedCourseTitle = @courses[@courseID]?.title
|
||||
@quantity = 20
|
||||
@updatePrice()
|
||||
|
||||
onClickBuy: (e) ->
|
||||
if @selectedCourseTitle is 'All Courses'
|
||||
app.router.navigate "/courses/mock1/0"
|
||||
else
|
||||
for course, i in @courses when course.title is @selectedCourseTitle
|
||||
app.router.navigate "/courses/mock1/#{i}"
|
||||
break
|
||||
window.location.reload()
|
||||
|
||||
onChangeCourse: (e) ->
|
||||
@selectedCourseTitle = $(e.target).val()
|
||||
@updatePrice()
|
||||
@render?()
|
||||
|
||||
onQuantityChange: (e) ->
|
||||
@quantity = $(e.target).val() ? 20
|
||||
@updatePrice()
|
||||
@render?()
|
||||
|
||||
updatePrice: ->
|
||||
if @selectedCourseTitle is 'All Courses'
|
||||
@price = (@courses.length - 1) * @quantity * 2
|
||||
else if @selectedCourseTitle is 'Introduction to Computer Science'
|
||||
@price = 0
|
||||
else
|
||||
@price = @quantity * 4
|
|
@ -1,31 +0,0 @@
|
|||
app = require 'core/application'
|
||||
RootView = require 'views/core/RootView'
|
||||
template = require 'templates/courses/mock1/course-info'
|
||||
|
||||
module.exports = class CourseInfoView extends RootView
|
||||
id: 'course-info-view'
|
||||
template: template
|
||||
|
||||
events:
|
||||
'click .btn-enroll': 'onClickEnroll'
|
||||
|
||||
constructor: (options, @courseID) ->
|
||||
super options
|
||||
@initData()
|
||||
|
||||
getRenderData: ->
|
||||
context = super()
|
||||
context.course = @course ? {}
|
||||
context.courseID = @courseID
|
||||
context.praise = @praise
|
||||
context
|
||||
|
||||
initData: ->
|
||||
mockData = require 'views/courses/mock1/CoursesMockData'
|
||||
@course = mockData.courses[@courseID]
|
||||
@praise = mockData.praise[_.random(0, mockData.praise.length - 1)]
|
||||
|
||||
onClickEnroll: (e) ->
|
||||
courseID = $(e.target).data('course-id')
|
||||
app.router.navigate "/courses/mock1/#{courseID}/enroll"
|
||||
window.location.reload()
|
|
@ -1,170 +0,0 @@
|
|||
data = {}
|
||||
|
||||
data.concepts = [
|
||||
'Advanced Strings'
|
||||
'Algorithms'
|
||||
'Arithmetic'
|
||||
'Arrays'
|
||||
'Basic Syntax'
|
||||
'Boolean Logic'
|
||||
'Break Statements'
|
||||
'Classes'
|
||||
'For Loops'
|
||||
'Functions'
|
||||
'If Statements'
|
||||
'Input Handling'
|
||||
'Math Operations'
|
||||
'Object Literals'
|
||||
'Strings'
|
||||
'Variables'
|
||||
'Vectors'
|
||||
'While Loops'
|
||||
]
|
||||
|
||||
data.courses = [
|
||||
{
|
||||
title: 'Introduction to Computer Science'
|
||||
description: 'Learn basic syntax, while loops, and the CodeCombat learning environment.'
|
||||
topics: ['Basic Syntax', 'Strings', 'Loops']
|
||||
duration: 1
|
||||
levels: ['Dungeons of Kithgard', 'Gems in the Deep', 'Shadow Guard', 'Kounter Kithwise', 'Crawlways of Kithgard', 'Enemy Mine', 'Illusory Interruption', 'Forgetful Gemsmith', 'Signs and Portents', 'Favorable Odds', 'True Names', 'The Prisoner', 'Banefire', 'The Raised Sword', 'Haunted Kithmaze', 'Riddling Kithmaze', 'Descending Further', 'The Second Kithmaze', 'Dread Door', 'Cupboards of Kithgard', 'Hack and Dash']
|
||||
campaign: 'intro'
|
||||
image: '/images/pages/courses/101_info.png'
|
||||
},
|
||||
{
|
||||
title: 'Computer Science 2'
|
||||
description: 'Introduce Arguments, Variables, If Statements, and Arithmetic.'
|
||||
topics: ['Arguments', 'Variables', 'If Statements', 'Arithmetic']
|
||||
duration: 5
|
||||
levels: ['Known Enemy', 'Master of Names', 'Lowly Kithmen', 'Closing the Distance', 'Tactical Strike', 'The Final Kithmaze', 'The Gauntlet', 'Radiant Aura', 'Kithgard Gates', 'Destroying Angel', 'Deadly Dungeon Rescue', 'Kithgard Brawl', 'Cavern Survival', 'Breakout', 'Attack Wisely!', 'Kithgard Mastery', 'Kithgard Apprentice', 'Long Kithmaze', 'Boom! and Bust', 'Defense of Plainswood', 'Winding Trail', 'Thumb Biter', 'Gems or Death', 'Backwoods Ambush', 'Patrol Buster', 'Endangered Burl', 'Village Guard', 'Thornbush Farm', 'Back to Back', 'Ogre Encampment', 'Woodland Cleaver', 'Shield Rush', 'Peasant Protection', 'Munchkin Swarm']
|
||||
image: '/images/pages/courses/102_info.png'
|
||||
},
|
||||
{
|
||||
title: 'Computer Science 3'
|
||||
description: 'Learn how to handle input.'
|
||||
topics: ['If Statements', 'Arithmetic', 'Input Handling']
|
||||
duration: 5
|
||||
levels: ['Munchkin Harvest', 'Swift Dagger', 'Shrapnel', 'Arcane Ally', 'Touch of Death', 'Bonemender', 'Coinucopia', 'Copper Meadows', 'Drop the Flag', 'Deadly Pursuit', 'Rich Forager', 'Siege of Stonehold', 'Multiplayer Treasure Grove', 'Dueling Grounds', 'Backwoods Brawl', 'Backwoods Treasure', 'Range Finder', 'Stillness in Motion', 'The Agrippa Defense', 'Storming the Towers of Areth', 'Hold the Forest Pass', 'Hold for Reinforcements', 'Storming the Farmhouse', 'Wild Horses', 'Boulder Woods', 'Unfair Support', 'Tactical Timing', 'Apocalypse', 'Doom Glade', 'Defend the Garrison', 'Lost Viking', 'Forest Flower Grove', 'The Dunes', 'The Mighty Sand Yak', 'Oasis', 'Sarven Road', 'Sarven Gaps', 'Thunderhooves', 'Medical Attention', 'The Great Yak Stampede', 'Minesweeper', 'Sarven Sentry', 'Keeping Time']
|
||||
image: '/images/pages/courses/103_info.png'
|
||||
},
|
||||
{
|
||||
title: 'Computer Science 4'
|
||||
description: 'Time to tackle arrays and some pvp stuff.'
|
||||
topics: ['Loops', 'Break Statements', 'Arrays']
|
||||
duration: 5
|
||||
levels: ['Hoarding Gold', 'Decoy Drill', 'Yakstraction', 'Sarven Brawl', 'Desert Combat', 'Dust', 'Sarven Rescue', 'Sacred Statue', 'Mirage Maker', 'Sarven Savior', 'Odd Sandstorm', 'Lurkers', 'Preferential Treatment', 'Sarven Shepherd', 'Shine Getter', 'The Trials', 'Mad Maxer', 'Mad Maxer Strikes Back', 'Mad Maxer Sells Out', 'Mad Maxer Gets Greedy', 'Mad Maxer: Redemption', 'Sarven Treasure', 'Harrowland', 'Sarven Siege', 'Clash of Clones', 'Sand Snakes', 'Crag Tag']
|
||||
image: '/images/pages/courses/104_info.png'
|
||||
},
|
||||
{
|
||||
title: 'Computer Science 5'
|
||||
description: 'Time to tackle arrays and some PVP.'
|
||||
topics: ['Break Statements', 'Arrays', 'Object Literals']
|
||||
duration: 5
|
||||
levels: ['Slalom', 'Black Diamond', 'Treasure Cave', 'Ogre Gorge Gouger', 'Dance-Off', 'Alpine Rally', 'Cloudrip Commander', 'Mountain Mercenaries']
|
||||
image: '/images/pages/courses/105_info.png'
|
||||
},
|
||||
{
|
||||
title: 'Computer Science 6'
|
||||
description: 'For loops!'
|
||||
topics: ['Break Statements', 'Object Literals', 'For loops']
|
||||
duration: 5
|
||||
levels: ['Timber Guard', 'Hunting Party', 'Zoo Keeper', 'Cloudrip Brawl', 'Cloudrip Treasure', 'Cloudrip Siege', 'Noble Sacrifice', 'Zero Sum', 'Borrowed Sword', 'Protect and Serve']
|
||||
image: '/images/pages/courses/106_info.png'
|
||||
},
|
||||
{
|
||||
title: 'Computer Science 7'
|
||||
description: 'Functions!'
|
||||
topics: ['Object Literals', 'For loops', 'Functions']
|
||||
duration: 5
|
||||
levels: ['Vital Powers', 'Timber Turncoat', 'Restless Dead', 'Ring Bearer', 'The Two Flowers', 'The Geometry of Flowers', 'Mountain Flower Grove', 'Hunters and Prey', 'Library Tactician']
|
||||
image: '/images/pages/courses/107_info.png'
|
||||
},
|
||||
{
|
||||
title: 'Computer Science 8'
|
||||
description: 'Maths.'
|
||||
topics: ['For loops', 'Functions', 'Math Operations']
|
||||
duration: 5
|
||||
levels: ['Steelclaw Gap', 'Pesky Yaks', 'Mixed Unit Tactics', 'Sowing Fire', 'Reaping Fire', 'Toil and Trouble', 'What in Carnation', 'Misty Island Mine', 'Raiders of the Long Dark', 'Grim Determination', 'Deadly Discs', "Summit's Gate"]
|
||||
image: '/images/pages/courses/107_info.png'
|
||||
},
|
||||
{
|
||||
title: 'Computer Science 9'
|
||||
description: 'Vectors and strings.'
|
||||
topics: ['Vectors', 'Advanced Strings']
|
||||
duration: 5
|
||||
levels: ['Circle Walking', 'Skating Away', 'Kelvintaph Crusader', 'Kelvintaph Burgler', 'Ice Soccer', 'Razorfray']
|
||||
image: '/images/pages/courses/107_info.png'
|
||||
}
|
||||
]
|
||||
|
||||
getStudents = ->
|
||||
students = ['Jill', 'Billy', 'Sarah', 'Tom', 'June', 'Bob', 'Kristin', 'Samantha', 'Eric']
|
||||
_.shuffle(students).slice(_.random(0, 5))
|
||||
|
||||
data.instances = [
|
||||
{
|
||||
name: "Mr. Smith's First Period"
|
||||
description: "Homework due on Friday."
|
||||
code: 'b2KF7'
|
||||
students: getStudents()
|
||||
},
|
||||
{
|
||||
name: "Mr. Smith's Second Period"
|
||||
description: "Test class description"
|
||||
code: 'b2KF7'
|
||||
students: getStudents()
|
||||
},
|
||||
{
|
||||
name: "Summer Camp 2015"
|
||||
description: "You should have received an email with extra credit homework."
|
||||
code: 'b2KF7'
|
||||
students: getStudents()
|
||||
},
|
||||
{
|
||||
name: "Maple High 4th"
|
||||
code: 'b2KF7'
|
||||
students: getStudents()
|
||||
},
|
||||
{
|
||||
name: "Test class name one"
|
||||
description: "Test class description"
|
||||
code: 'b2KF7'
|
||||
students: getStudents()
|
||||
}
|
||||
]
|
||||
|
||||
data.praise = [
|
||||
{
|
||||
quote: "The kids love it."
|
||||
source: "Leo Joseph Tran, Athlos Leadership Academy"
|
||||
},
|
||||
{
|
||||
quote: "My students have been using the site for a couple of weeks and they love it."
|
||||
source: "Scott Hatfield, Computer Applications Teacher, School Technology Coordinator, Eastside Middle School"
|
||||
},
|
||||
{
|
||||
quote: "Thanks for the captivating site. My eighth graders love it."
|
||||
source: "Janet Cook, Ansbach Middle/High School"
|
||||
},
|
||||
{
|
||||
quote: "My students have started working on CodeCombat and love it! I love that they are learning coding and problem solving skills without them even knowing it!!"
|
||||
source: "Kristin Huff, Special Education Teacher, Webb City School District"
|
||||
},
|
||||
{
|
||||
quote: "I recently introduced Code Combat to a few of my fifth graders and they are loving it!"
|
||||
source: "Shauna Hamman, Fifth Grade Teacher, Four Peaks Elementary School"
|
||||
},
|
||||
{
|
||||
quote: "Overall I think it's a fantastic service. Variables, arrays, loops, all covered in very fun and imaginative ways. Every kid who has tried it is a fan."
|
||||
source: "Aibinder Andrew, Technology Teacher"
|
||||
},
|
||||
{
|
||||
quote: "I love what you have created. The kids are so engaged."
|
||||
source: "Desmond Smith, 4KS Academy"
|
||||
},
|
||||
{
|
||||
quote: "My students love the website and I hope on having content structured around it in the near future."
|
||||
source: "Michael Leonard, Science Teacher, Clearwater Central Catholic High School"
|
||||
}
|
||||
]
|
||||
module.exports = data
|
|
@ -1,119 +0,0 @@
|
|||
app = require 'core/application'
|
||||
utils = require 'core/utils'
|
||||
RootView = require 'views/core/RootView'
|
||||
template = require 'templates/courses/mock1/courses'
|
||||
|
||||
module.exports = class CoursesView extends RootView
|
||||
id: 'courses-mock-view'
|
||||
template: template
|
||||
|
||||
events:
|
||||
'click .btn-buy': 'onClickBuy'
|
||||
'click .btn-continue': 'onClickContinue'
|
||||
'click .btn-enroll': 'onClickEnroll'
|
||||
'click .btn-enter': 'onClickEnter'
|
||||
'click .btn-student': 'onClickStudent'
|
||||
'click .btn-teacher': 'onClickTeacher'
|
||||
'hidden.bs.modal #continueModal': 'onHideContinueModal'
|
||||
|
||||
constructor: (options) ->
|
||||
super options
|
||||
@studentMode = utils.getQueryVariable('student', false) or options.studentMode
|
||||
@initData()
|
||||
|
||||
getRenderData: ->
|
||||
context = super()
|
||||
context.courses = @courses ? []
|
||||
context.instances = @instances ? []
|
||||
context.praise = @praise
|
||||
context.studentMode = @studentMode
|
||||
context
|
||||
|
||||
afterRender: ->
|
||||
super()
|
||||
@setupCoursesFAQPopover()
|
||||
|
||||
initData: ->
|
||||
mockData = require 'views/courses/mock1/CoursesMockData'
|
||||
@courses = mockData.courses
|
||||
for course, i in @courses
|
||||
if _.random(0, 2) > 0
|
||||
course.unlocked = true
|
||||
else
|
||||
break
|
||||
@instances = mockData.instances
|
||||
@praise = mockData.praise[_.random(0, mockData.praise.length - 1)]
|
||||
|
||||
setupCoursesFAQPopover: ->
|
||||
popoverTitle = "<h3>Courses FAQ<button type='button' class='close' onclick='$('.courses-faq').popover('hide');'>×</button></h3>"
|
||||
popoverContent = "<p><strong>Q:</strong> What's the difference between these courses and the single player game?</p>"
|
||||
popoverContent += "<p><strong>A:</strong> The single player game is designed for individuals, while the courses are designed for classes.</p>"
|
||||
popoverContent += "<p>The single player game has items, gems, hero selection, leveling up, and in-app purchases. Courses have classroom management features and streamlined student-focused level pacing.</p>"
|
||||
@$el.find('.courses-faq').popover(
|
||||
animation: true
|
||||
html: true
|
||||
placement: 'top'
|
||||
trigger: 'click'
|
||||
title: popoverTitle
|
||||
content: popoverContent
|
||||
container: @$el
|
||||
).on 'shown.bs.popover', =>
|
||||
application.tracker?.trackEvent 'Subscription payment methods hover'
|
||||
|
||||
|
||||
onClickBuy: (e) ->
|
||||
courseID = $(e.target).data('course-id') ? 0
|
||||
app.router.navigate("/courses/mock1/enroll/#{courseID}")
|
||||
window.location.reload()
|
||||
|
||||
onClickContinue: (e) ->
|
||||
courseID = $(e.target).data('course-id')
|
||||
courseTitle = $(e.target).data('course-title')
|
||||
$('#continueModal').find('.modal-title').text(courseTitle)
|
||||
$('#continueModal').find('.btn-buy').data('course-id', courseID)
|
||||
$('#continueModal').find('.btn-enroll').data('course-id', courseID)
|
||||
$('#continueModal').find('.btn-enter').data('course-id', courseID)
|
||||
$('#continueModal .row-pick-class').show() if @courses[courseID]?.unlocked
|
||||
if @courses[courseID]?.unlocked
|
||||
$('#continueModal .btn-buy').prop('innerText', 'Start new class')
|
||||
else if courseTitle is 'Introduction to Computer Science'
|
||||
$('#continueModal .btn-buy').prop('innerText', 'Get this FREE course!')
|
||||
else
|
||||
$('#continueModal .btn-buy').prop('innerText', 'Buy this course')
|
||||
|
||||
onClickEnroll: (e) ->
|
||||
$('#continueModal').modal('hide')
|
||||
courseID = $(e.target).data('course-id')
|
||||
instanceID = _.random(0, @instances.length - 1)
|
||||
viewClass = require 'views/courses/mock1/CourseDetailsView'
|
||||
viewArgs = [{studentMode: @studentMode}, courseID, instanceID]
|
||||
navigationEvent = route: "/courses/mock1/#{courseID}", viewClass: viewClass, viewArgs: viewArgs
|
||||
Backbone.Mediator.publish 'router:navigate', navigationEvent
|
||||
|
||||
onClickEnter: (e) ->
|
||||
$('#continueModal').modal('hide')
|
||||
courseID = $(e.target).data('course-id')
|
||||
instanceName = $('.select-session').val()
|
||||
for val, index in @instances when val.name is instanceName
|
||||
instanceID = index
|
||||
viewClass = require 'views/courses/mock1/CourseDetailsView'
|
||||
viewArgs = [{}, courseID, instanceID]
|
||||
navigationEvent = route: "/courses/mock1/#{courseID}", viewClass: viewClass, viewArgs: viewArgs
|
||||
Backbone.Mediator.publish 'router:navigate', navigationEvent
|
||||
|
||||
onClickStudent: (e) ->
|
||||
route = "/courses/mock1?student=true"
|
||||
viewClass = require 'views/courses/mock1/CoursesView'
|
||||
viewArgs = [studentMode: true]
|
||||
navigationEvent = route: route, viewClass: viewClass, viewArgs: viewArgs
|
||||
Backbone.Mediator.publish 'router:navigate', navigationEvent
|
||||
|
||||
onClickTeacher: (e) ->
|
||||
route = "/courses/mock1?student=false"
|
||||
viewClass = require 'views/courses/mock1/CoursesView'
|
||||
viewArgs = [studentMode: false]
|
||||
navigationEvent = route: route, viewClass: viewClass, viewArgs: viewArgs
|
||||
Backbone.Mediator.publish 'router:navigate', navigationEvent
|
||||
|
||||
onHideContinueModal: (e) ->
|
||||
$('#continueModal .row-pick-class').hide()
|
|
@ -499,8 +499,11 @@ module.exports = class CampaignView extends RootView
|
|||
levelSlug = levelElement.data 'level-slug'
|
||||
session = @preloadedSession if @preloadedSession?.loaded and @preloadedSession.levelSlug is levelSlug
|
||||
@setupManager = new LevelSetupManager supermodel: @supermodel, levelID: levelSlug, levelPath: levelElement.data('level-path'), levelName: levelElement.data('level-name'), hadEverChosenHero: @hadEverChosenHero, parent: @, session: session
|
||||
@$levelInfo.find('.level-info, .progress').toggleClass('hide')
|
||||
@listenToOnce @setupManager, 'open', ->
|
||||
@$levelInfo.find('.level-info, .progress').toggleClass('hide')
|
||||
@$levelInfo?.hide()
|
||||
@setupManager.open()
|
||||
@$levelInfo?.hide()
|
||||
|
||||
onClickViewSolutions: (e) ->
|
||||
levelElement = $(e.target).parents('.level-info-container')
|
||||
|
|
|
@ -124,6 +124,7 @@ ClassroomHandler = class ClassroomHandler extends Handler
|
|||
return @sendDatabaseError(res, err) if err
|
||||
return @sendSuccess(res, (@formatEntity(req, classroom) for classroom in classrooms))
|
||||
else if code = req.query.code.toLowerCase()
|
||||
code = code.toLowerCase()
|
||||
Classroom.findOne {code: code}, (err, classroom) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
return @sendNotFoundError(res) unless classroom
|
||||
|
|
Loading…
Add table
Reference in a new issue