mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-12-13 01:01:34 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
bc0b3d0c29
10 changed files with 297 additions and 27 deletions
|
@ -49,6 +49,10 @@ toHex = (n) ->
|
|||
h = '0'+h if h.length is 1
|
||||
h
|
||||
|
||||
module.exports.pathToUrl = (path) ->
|
||||
base = location.protocol + '//' + location.hostname + (location.port && ":" + location.port)
|
||||
base + path
|
||||
|
||||
module.exports.i18n = (say, target, language=me.get('preferredLanguage', true), fallback='en') ->
|
||||
generalResult = null
|
||||
fallBackResult = null
|
||||
|
@ -184,6 +188,10 @@ if document?.createElement
|
|||
return
|
||||
)(document)
|
||||
|
||||
# So that we can stub out userAgent in tests
|
||||
module.exports.userAgent = ->
|
||||
window.navigator.userAgent
|
||||
|
||||
module.exports.getQueryVariable = getQueryVariable = (param, defaultValue) ->
|
||||
query = document.location.search.substring 1
|
||||
pairs = (pair.split('=') for pair in query.split '&')
|
||||
|
|
|
@ -1394,6 +1394,8 @@
|
|||
select_your_hero: "Select Your Hero"
|
||||
select_your_hero_description: "You can always change your hero by going to your Courses page and clicking \"Select Hero\""
|
||||
select_this_hero: "Select this Hero"
|
||||
current_hero: "Current Hero:"
|
||||
change_hero: "Change Hero"
|
||||
|
||||
teacher:
|
||||
course_solution: "Course Solution"
|
||||
|
@ -1527,7 +1529,18 @@
|
|||
|
||||
web_dev:
|
||||
image_gallery_title: "Image Gallery"
|
||||
image_gallery_description: "Copy these images into your webpage, or find your own image URLs online."
|
||||
select_an_image: "Select an image you want to use"
|
||||
scroll_down_for_more_images: "(Scroll down for more images)"
|
||||
copy_the_url: "Copy the URL below"
|
||||
copy_the_url_description: "Useful if you want to replace an existing image."
|
||||
copy_the_img_tag: "Copy the <img> tag"
|
||||
copy_the_img_tag_description: "Useful if you want to insert a new image."
|
||||
copy_url: "Copy URL"
|
||||
copy_img: "Copy <img>"
|
||||
how_to_copy_paste: "How to Copy/Paste"
|
||||
copy: "Copy"
|
||||
paste: "Paste"
|
||||
back_to_editing: "Back to Editing"
|
||||
|
||||
classes:
|
||||
archmage_title: "Archmage"
|
||||
|
|
|
@ -97,7 +97,7 @@
|
|||
justify-content: space-between
|
||||
|
||||
.btn
|
||||
// Undo .style-flat's .btn ~ .btn margin
|
||||
// Undo .style-flat's .btn + .btn margin
|
||||
margin: 0
|
||||
|
||||
&.just-one
|
||||
|
|
|
@ -1,11 +1,117 @@
|
|||
@import "app/styles/mixins"
|
||||
@import "app/styles/style-flat-variables"
|
||||
|
||||
#image-gallery-modal
|
||||
.modal-dialog
|
||||
width: 800px
|
||||
h3
|
||||
font-size: 20px
|
||||
|
||||
li
|
||||
font-size: 12px
|
||||
.subtitle
|
||||
font-size: 14px
|
||||
line-height: 14px
|
||||
|
||||
.modal-dialog
|
||||
width: 850px
|
||||
height: 550px
|
||||
max-height: 100vh
|
||||
|
||||
.modal-footer
|
||||
display: none
|
||||
|
||||
.modal-header
|
||||
padding: 0
|
||||
min-height: 0
|
||||
|
||||
.modal-body-content
|
||||
height: 485px
|
||||
|
||||
.no-select
|
||||
@include user-select(none)
|
||||
|
||||
.image-list
|
||||
overflow: -moz-scrollbars-vertical
|
||||
overflow-y: scroll
|
||||
margin: 0
|
||||
|
||||
// Force scrollbar visible
|
||||
&::-webkit-scrollbar
|
||||
// -webkit-appearance: none
|
||||
border: thin solid gray
|
||||
width: 14px
|
||||
&::-webkit-scrollbar-thumb
|
||||
background-color: rgba(0,0,0,.5)
|
||||
|
||||
.flex-col
|
||||
display: flex
|
||||
flex-direction: column
|
||||
height: 100%
|
||||
|
||||
.image-list
|
||||
height: 440px
|
||||
max-height: 100vh
|
||||
padding: 0
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
|
||||
.image-list-item
|
||||
img
|
||||
width: 72px
|
||||
height: 72px
|
||||
margin: 16px
|
||||
list-style-type: none
|
||||
background-color: #f8f8f8
|
||||
box-shadow: 0 0 0 1px gray
|
||||
|
||||
&.selected
|
||||
box-shadow: 0 0 0 6px $gold
|
||||
|
||||
//
|
||||
|
||||
.copy-row
|
||||
display: flex
|
||||
align-items: center
|
||||
|
||||
.copy-textarea-col
|
||||
flex-grow: 1
|
||||
|
||||
textarea
|
||||
width: 100%
|
||||
height: 55px
|
||||
|
||||
.copy-button-col
|
||||
padding-left: 10px
|
||||
|
||||
.copyable
|
||||
font-size: 10px
|
||||
line-height: 12px
|
||||
|
||||
.how-to-copy-paste
|
||||
font-size: 13px
|
||||
line-height: 16px
|
||||
font-style: italic
|
||||
color: gray
|
||||
|
||||
.close-button
|
||||
flex-grow: 1
|
||||
align-self: flex-end
|
||||
display: flex
|
||||
align-items: flex-end
|
||||
|
||||
// Fancy text inside horizontal rules
|
||||
|
||||
.hr-text
|
||||
position: relative
|
||||
hr
|
||||
width: 50%
|
||||
padding: 0
|
||||
border: none
|
||||
border-top: thin solid #444
|
||||
color: #444
|
||||
span
|
||||
position: absolute
|
||||
left: 50%
|
||||
top: 0.45em
|
||||
transform: translateX(-50%)
|
||||
padding: 0 0.75em
|
||||
font-weight: bold
|
||||
font-size: 10pt
|
||||
background: white
|
||||
|
|
|
@ -212,7 +212,7 @@ body[lang='ru'], body[lang='uk'], body[lang='bg'], body[lang^='mk'], body[lang='
|
|||
.disabled
|
||||
opacity: 50%
|
||||
|
||||
.btn ~ .btn
|
||||
.btn + .btn
|
||||
margin-left: 12px
|
||||
|
||||
.btn-primary, .btn-navy
|
||||
|
|
|
@ -45,12 +45,10 @@ block content
|
|||
img(src=view.hero.getPortraitURL())
|
||||
.current-hero-right-col
|
||||
.semibold.current-hero-text
|
||||
span.spr(data-i18n="TODO")
|
||||
| Current Hero:
|
||||
span.spr(data-i18n="courses.current_hero")
|
||||
span.current-hero-name= view.hero.getHeroShortName()
|
||||
button.change-hero-btn.btn.btn-lg.btn-forest
|
||||
span(data-i18n="TODO")
|
||||
| Change Hero
|
||||
span(data-i18n="courses.change_hero")
|
||||
|
||||
if view.classrooms.size()
|
||||
h3.text-uppercase(data-i18n="courses.my_classes")
|
||||
|
|
|
@ -1,23 +1,67 @@
|
|||
extends /templates/core/modal-base-flat
|
||||
|
||||
block modal-header-content
|
||||
h3(data-i18n="web_dev.image_gallery_title")
|
||||
span(data-i18n="web_dev.image_gallery_description")
|
||||
|
||||
block modal-body-content
|
||||
dl.dl-horizontal
|
||||
.row.modal-body-content
|
||||
div.image-list-container.col-sm-7
|
||||
h3
|
||||
='1. '
|
||||
span(data-i18n="web_dev.select_an_image")
|
||||
ul.image-list
|
||||
for image in view.images
|
||||
dt
|
||||
- var selectedState = state.get('selectedUrl') === image.portraitURL ? 'selected' : ''
|
||||
li.image-list-item.render(data-portrait-url=image.portraitURL, class=selectedState, selected=selectedState)
|
||||
img(src=image.portraitURL)
|
||||
dd
|
||||
ul.list-unstyled
|
||||
li
|
||||
span.no-select= 'URL: '
|
||||
kbd= image.portraitURL
|
||||
.small.text-center
|
||||
span(data-i18n="web_dev.scroll_down_for_more_images")
|
||||
|
||||
div.col-sm-5.flex-col.render
|
||||
h3
|
||||
='2. '
|
||||
span(data-i18n="web_dev.copy_the_url")
|
||||
.text-h3.subtitle(data-i18n="web_dev.copy_the_url_description")
|
||||
.copy-row.m-t-1
|
||||
.copy-textarea-col
|
||||
textarea.image-url.copyable
|
||||
if view.state.get('selectedUrl')
|
||||
= utils.pathToUrl(view.state.get('selectedUrl'))
|
||||
.copy-button-col
|
||||
button.btn.btn-forest.copy-url-button
|
||||
span(data-i18n="web_dev.copy_url")
|
||||
|
||||
.hr-text.m-t-1
|
||||
hr
|
||||
span(data-i18n="general.or")
|
||||
|
||||
h3(data-i18n="web_dev.copy_the_img_tag")
|
||||
.text-h3.subtitle(data-i18n="web_dev.copy_the_img_tag_description")
|
||||
.copy-row.m-t-1
|
||||
.copy-textarea-col
|
||||
textarea.image-tag.copyable
|
||||
if view.state.get('selectedUrl')
|
||||
= '<img src="' + utils.pathToUrl(view.state.get('selectedUrl')) + '"/>'
|
||||
.copy-button-col
|
||||
button.btn.btn-forest.copy-tag-button
|
||||
span(data-i18n="web_dev.copy_img")
|
||||
|
||||
.how-to-copy-paste.m-t-3
|
||||
div.m-b-1.text-center
|
||||
span(data-i18n="web_dev.how_to_copy_paste")
|
||||
.windows-only
|
||||
span(data-i18n="web_dev.copy")
|
||||
| : Control–C
|
||||
br
|
||||
li
|
||||
span.no-select= '<img>: '
|
||||
kbd= '<img src="' + image.portraitURL + '">'
|
||||
span(data-i18n="web_dev.paste")
|
||||
| : Control–V
|
||||
.mac-only.hidden
|
||||
span(data-i18n="web_dev.copy")
|
||||
| : Command ⌘–C
|
||||
br
|
||||
span(data-i18n="web_dev.paste")
|
||||
| : Command ⌘–V
|
||||
|
||||
.close-button
|
||||
a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="web_dev.back_to_editing").btn.btn-lg.btn-primary
|
||||
|
||||
block modal-footer-content
|
||||
a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="modal.close").btn.btn-primary Close
|
||||
|
|
|
@ -1,8 +1,42 @@
|
|||
ModalView = require 'views/core/ModalView'
|
||||
State = require 'models/State'
|
||||
utils = require 'core/utils'
|
||||
|
||||
module.exports = class ImageGalleryModal extends ModalView
|
||||
id: 'image-gallery-modal'
|
||||
template: require 'templates/play/level/modal/image-gallery-modal'
|
||||
|
||||
events:
|
||||
'click .image-list-item': 'onClickImageListItem'
|
||||
'click .copy-url-button': 'onClickCopyUrlButton'
|
||||
'click .copy-tag-button': 'onClickCopyTagButton'
|
||||
|
||||
getRenderData: ->
|
||||
_.merge super(arguments...), { utils }
|
||||
|
||||
initialize: ->
|
||||
@state = new State()
|
||||
@listenTo @state, 'all', =>
|
||||
@renderSelectors('.render')
|
||||
@afterRender()
|
||||
|
||||
afterRender: ->
|
||||
if utils.userAgent().indexOf("Mac") > -1
|
||||
@$('.windows-only').addClass('hidden')
|
||||
@$('.mac-only').removeClass('hidden')
|
||||
|
||||
onClickImageListItem: (e) ->
|
||||
selectedUrl = $(e.currentTarget).data('portrait-url')
|
||||
@state.set { selectedUrl }
|
||||
|
||||
onClickCopyUrlButton: (e) ->
|
||||
$('.image-url').select()
|
||||
@tryCopy()
|
||||
|
||||
onClickCopyTagButton: (e) ->
|
||||
$('.image-tag').select()
|
||||
@tryCopy()
|
||||
|
||||
# Top most useful Thang portraits
|
||||
images: [
|
||||
{slug: 'archer-f', name: 'Archer F', original: '529ab1a24b67a988ad000002', portraitURL: '/file/db/thang.type/529ab1a24b67a988ad000002/portrait.png', kind: 'Unit'}
|
||||
|
|
|
@ -431,7 +431,6 @@ describe 'POST /db/course_instance/-/recent', ->
|
|||
startDay = utils.createDay(1)
|
||||
endDay = utils.createDay(2)
|
||||
[res, body] = yield request.postAsync(url, { json: { startDay, endDay } })
|
||||
console.log startDay, endDay, res.body.courseInstances.length
|
||||
expect(res.body.courseInstances.length).toBe(0)
|
||||
|
||||
startDay = utils.createDay(-2)
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
Course = require 'models/Course'
|
||||
Level = require 'models/Level'
|
||||
LevelSession = require 'models/LevelSession'
|
||||
ImageGalleryModal = require 'views/play/level/modal/ImageGalleryModal'
|
||||
ProgressView = require 'views/play/level/modal/ProgressView'
|
||||
factories = require 'test/app/factories'
|
||||
utils = require 'core/utils'
|
||||
|
||||
describe 'ImageGalleryModal', ->
|
||||
modal = null
|
||||
|
||||
beforeEach (done) ->
|
||||
modal = new ImageGalleryModal()
|
||||
modal.render()
|
||||
_.defer done
|
||||
|
||||
it '(demo)', ->
|
||||
jasmine.demoModal(modal)
|
||||
|
||||
it 'shows a list of images', ->
|
||||
expect(modal.$('img').length).toBeGreaterThan(16)
|
||||
|
||||
describe 'clicking an image', ->
|
||||
beforeEach (done) ->
|
||||
@clickedImage = modal.$('li:nth-child(5)').click()
|
||||
@clickedImagePath = @clickedImage.data('portrait-url')
|
||||
@clickedImageUrl = utils.pathToUrl(@clickedImagePath)
|
||||
@clickedImageTag = '<img src="' + @clickedImageUrl + '"/>'
|
||||
_.defer done
|
||||
|
||||
it 'highlights the chosen image', ->
|
||||
expect(modal.$('li:nth-child(5)').hasClass('selected')).toBe(true)
|
||||
|
||||
it 'displays the URL/image tags in the Copy section', ->
|
||||
expect(modal.$('.image-url').text()).toBe(@clickedImageUrl)
|
||||
expect(modal.$('.image-tag').text()).toBe(@clickedImageTag)
|
||||
|
||||
describe "How to Copy/Paste section", ->
|
||||
userAgents = {
|
||||
windows: 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko'
|
||||
mac: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'
|
||||
}
|
||||
|
||||
it 'Shows Windows shortcuts to Windows users', (done) ->
|
||||
spyOn(utils, 'userAgent').and.callFake ->
|
||||
userAgents.windows
|
||||
modal.render()
|
||||
# This test is a little fragile — Only works if the text node is an immediate child to .windows-only
|
||||
expect(modal.$('.how-to-copy-paste :not(.hidden)').text()).toMatch(/Control|Ctrl/i)
|
||||
expect(modal.$('.how-to-copy-paste :not(.hidden)').text()).not.toMatch(/Command|Cmd/i)
|
||||
@clickedImage = modal.$('li:nth-child(5)').click()
|
||||
_.defer ->
|
||||
expect(modal.$('.how-to-copy-paste :not(.hidden)').text()).toMatch(/Control|Ctrl/i)
|
||||
expect(modal.$('.how-to-copy-paste :not(.hidden)').text()).not.toMatch(/Command|Cmd/i)
|
||||
done()
|
||||
|
||||
it 'Shows Mac shortcuts to Mac users', (done) ->
|
||||
spyOn(utils, 'userAgent').and.callFake ->
|
||||
userAgents.mac
|
||||
modal.render()
|
||||
# This test is a little fragile — Only works if the text node is an immediate child to .mac-only
|
||||
expect(modal.$('.how-to-copy-paste :not(.hidden)').text()).toMatch(/Command|Cmd/i)
|
||||
expect(modal.$('.how-to-copy-paste :not(.hidden)').text()).not.toMatch(/Control|Ctrl/i)
|
||||
@clickedImage = modal.$('li:nth-child(5)').click()
|
||||
_.defer ->
|
||||
expect(modal.$('.how-to-copy-paste :not(.hidden)').text()).toMatch(/Command|Cmd/i)
|
||||
expect(modal.$('.how-to-copy-paste :not(.hidden)').text()).not.toMatch(/Control|Ctrl/i)
|
||||
done()
|
Loading…
Reference in a new issue