Compare commits
3 commits
master
...
no-js-rend
Author | SHA1 | Date | |
---|---|---|---|
|
a93aadf0fa | ||
|
f7f20edb70 | ||
|
d8733f0add |
7 changed files with 300 additions and 25 deletions
87
app/templates/main.jade
Normal file
87
app/templates/main.jade
Normal file
|
@ -0,0 +1,87 @@
|
|||
doctype html
|
||||
//[if lt IE 7]> <html class="lt-ie10 lt-ie9 lt-ie8 lt-ie7" lang="en"> <![endif]
|
||||
//[if IE 7]> <html class="lt-ie10 lt-ie9 lt-ie8" lang="en"> <![endif]
|
||||
//[if IE 8]> <html class="lt-ie10 lt-ie9" lang="en"> <![endif]
|
||||
//[if IE 9]> <html class="lt-ie10" lang="en"> <![endif]
|
||||
//[if !IE] <!
|
||||
html(lang='en')
|
||||
// <![endif]
|
||||
head
|
||||
meta(charset='utf-8')
|
||||
meta(name='viewport', content='width=1024')
|
||||
title CodeCombat - Learn how to code by playing a game
|
||||
meta(name='description', content='Learn programming with a multiplayer live coding strategy game for beginners. Learn Python or JavaScript as you defeat ogres, solve mazes, and level up. Open source HTML5 game!')
|
||||
meta(property='og:title', content='CodeCombat: Learn to Code by Playing a Game')
|
||||
meta(property='og:url', content='http://codecombat.com')
|
||||
meta(property='og:type', content='game')
|
||||
meta(property='og:image', content='http://codecombat.com/images/pages/home/play_img.png')
|
||||
meta(property='og:site_name', content='CodeCombat')
|
||||
meta(name='twitter:card', content='summary')
|
||||
meta(name='twitter:title', content='CodeCombat: Learn to Code by Playing a Game')
|
||||
meta(name='twitter:url', content='http://codecombat.com')
|
||||
meta(name='twitter:site', content='CodeCombat')
|
||||
meta(name='twitter:image:src', content='http://codecombat.com/images/pages/base/logo_square_250.png')
|
||||
meta(name='twitter:description', content='Learn programming with a multiplayer live coding strategy game for beginners. Learn Python or JavaScript as you defeat ogres, solve mazes, and level up. Open source HTML5 game!')
|
||||
link(href='https://plus.google.com/115285980638641924488', rel='publisher')
|
||||
link(rel='shortcut icon', href='/images/favicon.ico')
|
||||
link(rel='stylesheet', href='/stylesheets/app.css')
|
||||
link(href='//fonts.googleapis.com/css?family=Merriweather', rel='stylesheet', type='text/css')
|
||||
// Google Analytics
|
||||
script.
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', 'UA-39724129-1', 'auto');
|
||||
// Mixpanel
|
||||
script(type='text/javascript').
|
||||
(function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable time_event track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.union people.track_charge people.clear_charges people.delete_user".split(" ");for(g=0;g<i.length;g++)f(c,i[g]);b._i.push([a,e,d])};b.__SV=1.2;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?MIXPANEL_CUSTOM_LIB_URL:"file:"===e.location.protocol&&"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\\/\//)?"https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js":"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f)}})(document,window.mixpanel||[]);
|
||||
mixpanel.init("e71a4e60db7e1dc5e685be96776280f9");
|
||||
script(src='https://checkout.stripe.com/checkout.js')
|
||||
// IE9 doesn't support defer attribute: https://github.com/h5bp/lazyweb-requests/issues/42
|
||||
//[if IE 9]>
|
||||
script(src='/lib/ace/ace.js')
|
||||
script(src='/javascripts/box2d.js')
|
||||
script(src='/javascripts/vendor.js')
|
||||
script(src='/javascripts/aether.js')
|
||||
script(src='/javascripts/app.js')
|
||||
<![endif]
|
||||
|
||||
// IE9 cors support breaks analytics logging: http://caniuse.com/#feat=cors
|
||||
//[if IE 9]>
|
||||
script(type='text/javascript', src='http://cdnjs.cloudflare.com/ajax/libs/jquery-ajaxtransport-xdomainrequest/1.0.2/jquery.xdomainrequest.min.js')
|
||||
<![endif]
|
||||
script(src='/lib/ace/ace.js', defer='')
|
||||
script(src='/javascripts/vendor.js', defer='')
|
||||
script(src='/javascripts/aether.js', defer='')
|
||||
script(src='/javascripts/app.js', defer='')
|
||||
script.
|
||||
// IMPORTANT: If you edit here, make sure app/assets/javascripts/run-tests.js puts in placeholders for
|
||||
// running client tests on Travis.
|
||||
// Placeholder for iPad, which inspects the user object at the bottom of an injected page.
|
||||
window.serverConfig = !{JSON.stringify(serverConfig)};
|
||||
window.userObject = !{userObjectTag};
|
||||
window.amActually = !{amActually ? JSON.stringify(amActually) : 'undefined'};
|
||||
window.me = {
|
||||
get: function(attribute) { return window.userObject[attribute]; }
|
||||
}
|
||||
onLoad = function() {
|
||||
try {
|
||||
// IE10 warning
|
||||
var htmlElement = document.querySelector("html");
|
||||
if (htmlElement) {
|
||||
if ($.browser.msie && $.browser.versionNumber < 11) {
|
||||
alert("CodeCombat does not run in Internet Explorer 11 or older. Sorry!");
|
||||
}
|
||||
}
|
||||
// IE8 can't handle this
|
||||
FastClick.attach(document.body);
|
||||
require('core/initialize');
|
||||
} catch (error) { }
|
||||
}
|
||||
body.clearfix(onload='onLoad();')
|
||||
#fb-root
|
||||
#page-container !{pageContent}
|
||||
#modal-wrapper.modal-content.hide
|
||||
#module-load-progress.progress
|
||||
.progress-bar
|
|
@ -26,7 +26,7 @@ if view.showAds()
|
|||
img(src="/images/pages/base/logo.png", title="Powered by CodeCombat - Learn how to code by playing a game ", alt="Powered by CodeCombat")
|
||||
|
||||
if campaign
|
||||
.map
|
||||
.map(style="width: 800px; height: 600px")
|
||||
.gradient.horizontal-gradient.top-gradient
|
||||
.gradient.vertical-gradient.right-gradient
|
||||
.gradient.horizontal-gradient.bottom-gradient
|
||||
|
@ -118,7 +118,7 @@ if view.showAds()
|
|||
if campaign && campaign.locked && !godmode
|
||||
h3.campaign-locked(data-i18n="play.locked") Locked
|
||||
else if campaign
|
||||
btn(data-i18n="common.play").btn.btn-illustrated.btn-lg.btn-success.play-button
|
||||
a(data-i18n="common.play", href="/play/#{campaignSlug}").btn.btn-illustrated.btn-lg.btn-success.play-button
|
||||
if campaign && campaign.get('description')
|
||||
p.campaign-description
|
||||
span= i18n(campaign.attributes, 'description')
|
||||
|
|
|
@ -663,6 +663,8 @@ module.exports = class CampaignView extends RootView
|
|||
console.error "CampaignView hero update couldn't find hero slug for original:", hero
|
||||
|
||||
onClickPortalCampaign: (e) ->
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
campaign = $(e.target).closest('.campaign')
|
||||
return if campaign.is('.locked') or campaign.is('.silhouette')
|
||||
campaignSlug = campaign.data('campaign-slug')
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
"bluebird": "^3.2.1",
|
||||
"chalk": "^1.1.3",
|
||||
"co": "^4.6.0",
|
||||
"cheerio": "^0.20.0",
|
||||
"co-express": "^1.2.1",
|
||||
"coffee-script": "1.9.x",
|
||||
"connect": "2.7.x",
|
||||
|
@ -69,6 +70,7 @@
|
|||
"geoip-lite": "^1.1.6",
|
||||
"graceful-fs": "~2.0.1",
|
||||
"gridfs-stream": "~1.1.1",
|
||||
"jade": "^1.11.0",
|
||||
"jsondiffpatch": "0.1.17",
|
||||
"lodash": "~2.4.1",
|
||||
"lz-string": "^1.3.3",
|
||||
|
|
|
@ -56,7 +56,8 @@ module.exports.routes =
|
|||
'routes/sprites'
|
||||
'routes/queue'
|
||||
'routes/stacklead'
|
||||
'routes/stripe'
|
||||
'routes/stripe',
|
||||
'routes/html'
|
||||
]
|
||||
|
||||
mongoose = require 'mongoose'
|
||||
|
|
199
server/routes/html.coffee
Normal file
199
server/routes/html.coffee
Normal file
|
@ -0,0 +1,199 @@
|
|||
User = require '../models/User'
|
||||
UserHandler = require '../handlers/user_handler'
|
||||
LevelSession = require '../models/LevelSession'
|
||||
Campaign = require '../models/Campaign'
|
||||
Level = require '../models/Level'
|
||||
config = require '../../server_config'
|
||||
log = require 'winston'
|
||||
Mandate = require '../models/Mandate'
|
||||
utils = require '../lib/utils'
|
||||
cheerio = require 'cheerio'
|
||||
en = require '../../app/locale/en'
|
||||
_ = require 'lodash'
|
||||
|
||||
translate = (key) ->
|
||||
html = /^\[html\]/.test(key)
|
||||
key = key.substring(6) if html
|
||||
|
||||
t = en.translation
|
||||
#TODO: Replace with _.property when we get modern lodash
|
||||
path = key.split(/[.]/)
|
||||
while path.length > 0
|
||||
k = path.shift()
|
||||
t = t[k]
|
||||
return key unless t?
|
||||
|
||||
return out =
|
||||
text: t
|
||||
html: html
|
||||
|
||||
|
||||
makeContext = (req) ->
|
||||
view =
|
||||
isIPadBrowser: -> false
|
||||
isMobile: -> false
|
||||
isOldBrowser: -> false
|
||||
forumLink: -> 'http://discourse.codecombat.com/'
|
||||
showAds: -> true
|
||||
|
||||
me =
|
||||
getPhotoURL: -> ''
|
||||
isAnonymous: -> true
|
||||
get: (what) -> ''
|
||||
level: -> ''
|
||||
displayName: -> ''
|
||||
isTeacher: -> false
|
||||
isOnPremiumServer: -> false
|
||||
isAdmin: -> false
|
||||
gems: -> 0
|
||||
isPremium: -> false
|
||||
|
||||
opts =
|
||||
view: view
|
||||
me: me
|
||||
i18n: (a, b) ->
|
||||
return a.i18n.en[a] if 'i18n' in a
|
||||
a[b]
|
||||
|
||||
injectView = (res, view, opts, next) ->
|
||||
res.render (view + '.jade'), opts, (err, data) ->
|
||||
if err
|
||||
if config.isProduction
|
||||
res.locals.pageContent = ''
|
||||
else
|
||||
res.locals.pageContent = ('<pre>' + err + '</pre>')
|
||||
return next()
|
||||
|
||||
c = cheerio.load(data)
|
||||
name = view.split(/\//).pop()
|
||||
name += '-view' unless /-view$/.test(name)
|
||||
roots = c.root().children()
|
||||
elms = c('[data-i18n]')
|
||||
elms.each (i, e) ->
|
||||
i = c(this)
|
||||
t = translate(i.data('i18n'))
|
||||
if t.html
|
||||
i.html(t.text)
|
||||
else
|
||||
i.text(t.text);
|
||||
|
||||
o = cheerio.load("<div>")
|
||||
container = o('div')
|
||||
container.attr('id', name)
|
||||
container.html(c.html())
|
||||
|
||||
unless o('.style-flat').length > 0
|
||||
container.addClass('site-chrome')
|
||||
container.addClass('show-background') unless opts.showBackground is false
|
||||
container.attr('style', 'display: none') if opts.dontActuallyShow
|
||||
|
||||
res.locals.pageContent = o.html()
|
||||
next()
|
||||
|
||||
exports.setup = (app) ->
|
||||
handle = (route, view, next) ->
|
||||
app.get route, (req, res) ->
|
||||
try
|
||||
next req, res, (opts) ->
|
||||
injectView res, view, opts, ->
|
||||
renderMain req, res
|
||||
catch e
|
||||
renderMain req, res
|
||||
|
||||
handleSimply = (route, view, extra) ->
|
||||
handle route, view, (req, res, next) ->
|
||||
opts = makeContext req
|
||||
_.extend opts.view, extra(req, res) if extra
|
||||
next(opts)
|
||||
|
||||
handleSimply '/', 'new-home-view', ->
|
||||
justPlaysCourses: -> false
|
||||
courses: {models: []}
|
||||
|
||||
handleSimply '/about', 'about'
|
||||
handleSimply '/community', 'community-view'
|
||||
handleSimply '/contribute', 'contribute/contribute'
|
||||
handleSimply '/teachers/quote', 'request-quote-view', ->
|
||||
trialRequest:
|
||||
isNew: -> true
|
||||
handleSimply '/courses/teachers', 'courses/teacher-courses-view', ->
|
||||
classrooms:
|
||||
models: []
|
||||
courses:
|
||||
models: []
|
||||
handleSimply '/legal', 'legal'
|
||||
handleSimply '/privacy', 'privacy'
|
||||
|
||||
handle '/play', 'play/campaign-view', (req, res, next) ->
|
||||
opts = makeConext req
|
||||
Campaign.find({type: 'hero'}).exec (err, docs) ->
|
||||
levels = {}
|
||||
docs.forEach (m,k) ->
|
||||
v = m.toObject()
|
||||
levels[v.slug] =
|
||||
attributes:
|
||||
fullName: v.name
|
||||
name: v.name
|
||||
slug: v.slug
|
||||
description: v.description
|
||||
i18n: v.i18n
|
||||
get: (name) -> v[name]
|
||||
|
||||
res.locals.campaigns = levels
|
||||
res.locals.adjacentCampaigns = []
|
||||
res.locals.levels = []
|
||||
opts.showBackground = false
|
||||
next opts
|
||||
|
||||
handle '/play/:campaign', 'play/campaign-view', (req, res, next) ->
|
||||
opts = makeContext req
|
||||
Campaign.findOne({type: 'hero', slug: req.params.campaign}).exec (err, doc) ->
|
||||
levels = {}
|
||||
v = doc.toObject()
|
||||
|
||||
res.locals.campaign =
|
||||
attributes:
|
||||
fullName: v.name
|
||||
name: v.name
|
||||
slug: v.slug
|
||||
description: v.description
|
||||
i18n: v.i18n
|
||||
get: (name) -> v[name]
|
||||
res.locals.adjacentCampaigns = []
|
||||
res.locals.levelStatusMap = {}
|
||||
res.locals.levelDifficultyMap = {}
|
||||
res.locals.levelPlayCountMap = {}
|
||||
res.locals.marked = (o) -> o
|
||||
res.locals.levels = v.levels
|
||||
opts.dontActuallyShow = true
|
||||
opts.showBackground = false
|
||||
next opts
|
||||
|
||||
handle '/play/level/:level', 'play/level', (req, res, next) ->
|
||||
opts = makeContext req
|
||||
Level.findOne({slug: req.params.level}).exec (err, doc) ->
|
||||
opts.showBackground = false
|
||||
opts.dontActuallyShow = true
|
||||
next opts
|
||||
|
||||
|
||||
exports.renderMain = renderMain = (req,res) ->
|
||||
user = if req.user then JSON.stringify(UserHandler.formatEntity(req, req.user)).replace(/\//g, '\\/') else '{}'
|
||||
res.locals.pageContent = res.locals.pageContent || '!'
|
||||
Mandate.findOne({}).cache(5 * 60 * 1000).exec (err, mandate) ->
|
||||
if err
|
||||
log.error "Error getting mandate config: #{err}"
|
||||
configData = {}
|
||||
else
|
||||
configData = _.omit mandate?.toObject() or {}, '_id'
|
||||
configData.picoCTF = config.picoCTF
|
||||
configData.production = config.isProduction
|
||||
|
||||
res.locals.serverConfig = configData
|
||||
res.locals.userObjectTag = user
|
||||
res.locals.amActually = req.session.amActually
|
||||
|
||||
res.header 'Cache-Control', 'no-cache, no-store, must-revalidate'
|
||||
res.header 'Pragma', 'no-cache'
|
||||
res.header 'Expires', 0
|
||||
res.render 'main.jade'
|
|
@ -16,6 +16,7 @@ config = require './server_config'
|
|||
auth = require './server/commons/auth'
|
||||
routes = require './server/routes'
|
||||
UserHandler = require './server/handlers/user_handler'
|
||||
html = require './server/routes/html'
|
||||
slack = require './server/slack'
|
||||
Mandate = require './server/models/Mandate'
|
||||
global.tv4 = require 'tv4' # required for TreemaUtils to work
|
||||
|
@ -193,26 +194,7 @@ setupJavascript404s = (app) ->
|
|||
|
||||
setupFallbackRouteToIndex = (app) ->
|
||||
app.all '*', (req, res) ->
|
||||
fs.readFile path.join(__dirname, 'public', 'main.html'), 'utf8', (err, data) ->
|
||||
log.error "Error modifying main.html: #{err}" if err
|
||||
# insert the user object directly into the html so the application can have it immediately. Sanitize </script>
|
||||
user = if req.user then JSON.stringify(UserHandler.formatEntity(req, req.user)).replace(/\//g, '\\/') else '{}'
|
||||
|
||||
Mandate.findOne({}).cache(5 * 60 * 1000).exec (err, mandate) ->
|
||||
if err
|
||||
log.error "Error getting mandate config: #{err}"
|
||||
configData = {}
|
||||
else
|
||||
configData = _.omit mandate?.toObject() or {}, '_id'
|
||||
configData.picoCTF = config.picoCTF
|
||||
configData.production = config.isProduction
|
||||
data = data.replace '"serverConfigTag"', JSON.stringify configData
|
||||
data = data.replace('"userObjectTag"', user)
|
||||
data = data.replace('"amActuallyTag"', JSON.stringify(req.session.amActually))
|
||||
res.header 'Cache-Control', 'no-cache, no-store, must-revalidate'
|
||||
res.header 'Pragma', 'no-cache'
|
||||
res.header 'Expires', 0
|
||||
res.send 200, data
|
||||
html.renderMain(req, res)
|
||||
|
||||
setupFacebookCrossDomainCommunicationRoute = (app) ->
|
||||
app.get '/channel.html', (req, res) ->
|
||||
|
@ -242,9 +224,11 @@ exports.setupMailchimp = ->
|
|||
|
||||
exports.setExpressConfigurationOptions = (app) ->
|
||||
app.set('port', config.port)
|
||||
app.set('views', __dirname + '/app/views')
|
||||
app.set('views', __dirname + '/app/templates')
|
||||
app.set('view engine', 'jade')
|
||||
app.set('view options', { layout: false })
|
||||
app.set('view options', { layout: false, pretty: true })
|
||||
app.locals.basedir = __dirname + '/app'
|
||||
app.locals.pretty = true
|
||||
app.set('env', if config.isProduction then 'production' else 'development')
|
||||
app.set('json spaces', 0) if config.isProduction
|
||||
|
||||
|
|
Reference in a new issue