Merge branch 'master' into production

This commit is contained in:
Phoenix Eliot 2016-08-12 13:30:23 -07:00
commit bc0b3d0c29
10 changed files with 297 additions and 27 deletions

View file

@ -49,6 +49,10 @@ toHex = (n) ->
h = '0'+h if h.length is 1 h = '0'+h if h.length is 1
h 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') -> module.exports.i18n = (say, target, language=me.get('preferredLanguage', true), fallback='en') ->
generalResult = null generalResult = null
fallBackResult = null fallBackResult = null
@ -184,6 +188,10 @@ if document?.createElement
return return
)(document) )(document)
# So that we can stub out userAgent in tests
module.exports.userAgent = ->
window.navigator.userAgent
module.exports.getQueryVariable = getQueryVariable = (param, defaultValue) -> module.exports.getQueryVariable = getQueryVariable = (param, defaultValue) ->
query = document.location.search.substring 1 query = document.location.search.substring 1
pairs = (pair.split('=') for pair in query.split '&') pairs = (pair.split('=') for pair in query.split '&')

View file

@ -1394,6 +1394,8 @@
select_your_hero: "Select Your Hero" 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_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" select_this_hero: "Select this Hero"
current_hero: "Current Hero:"
change_hero: "Change Hero"
teacher: teacher:
course_solution: "Course Solution" course_solution: "Course Solution"
@ -1527,7 +1529,18 @@
web_dev: web_dev:
image_gallery_title: "Image Gallery" 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: classes:
archmage_title: "Archmage" archmage_title: "Archmage"

View file

@ -97,7 +97,7 @@
justify-content: space-between justify-content: space-between
.btn .btn
// Undo .style-flat's .btn ~ .btn margin // Undo .style-flat's .btn + .btn margin
margin: 0 margin: 0
&.just-one &.just-one

View file

@ -1,11 +1,117 @@
@import "app/styles/mixins" @import "app/styles/mixins"
@import "app/styles/style-flat-variables"
#image-gallery-modal #image-gallery-modal
.modal-dialog h3
width: 800px font-size: 20px
li .subtitle
font-size: 12px 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 .no-select
@include user-select(none) @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

View file

@ -212,7 +212,7 @@ body[lang='ru'], body[lang='uk'], body[lang='bg'], body[lang^='mk'], body[lang='
.disabled .disabled
opacity: 50% opacity: 50%
.btn ~ .btn .btn + .btn
margin-left: 12px margin-left: 12px
.btn-primary, .btn-navy .btn-primary, .btn-navy

View file

@ -45,12 +45,10 @@ block content
img(src=view.hero.getPortraitURL()) img(src=view.hero.getPortraitURL())
.current-hero-right-col .current-hero-right-col
.semibold.current-hero-text .semibold.current-hero-text
span.spr(data-i18n="TODO") span.spr(data-i18n="courses.current_hero")
| Current Hero:
span.current-hero-name= view.hero.getHeroShortName() span.current-hero-name= view.hero.getHeroShortName()
button.change-hero-btn.btn.btn-lg.btn-forest button.change-hero-btn.btn.btn-lg.btn-forest
span(data-i18n="TODO") span(data-i18n="courses.change_hero")
| Change Hero
if view.classrooms.size() if view.classrooms.size()
h3.text-uppercase(data-i18n="courses.my_classes") h3.text-uppercase(data-i18n="courses.my_classes")

View file

@ -1,23 +1,67 @@
extends /templates/core/modal-base-flat extends /templates/core/modal-base-flat
block modal-header-content block modal-header-content
h3(data-i18n="web_dev.image_gallery_title")
span(data-i18n="web_dev.image_gallery_description")
block modal-body-content 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 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) img(src=image.portraitURL)
dd .small.text-center
ul.list-unstyled span(data-i18n="web_dev.scroll_down_for_more_images")
li
span.no-select= 'URL: ' div.col-sm-5.flex-col.render
kbd= image.portraitURL 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")
| : ControlC
br br
li span(data-i18n="web_dev.paste")
span.no-select= '<img>: ' | : ControlV
kbd= '<img src="' + image.portraitURL + '">' .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 block modal-footer-content
a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="modal.close").btn.btn-primary Close

View file

@ -1,8 +1,42 @@
ModalView = require 'views/core/ModalView' ModalView = require 'views/core/ModalView'
State = require 'models/State'
utils = require 'core/utils'
module.exports = class ImageGalleryModal extends ModalView module.exports = class ImageGalleryModal extends ModalView
id: 'image-gallery-modal' id: 'image-gallery-modal'
template: require 'templates/play/level/modal/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 # Top most useful Thang portraits
images: [ images: [
{slug: 'archer-f', name: 'Archer F', original: '529ab1a24b67a988ad000002', portraitURL: '/file/db/thang.type/529ab1a24b67a988ad000002/portrait.png', kind: 'Unit'} {slug: 'archer-f', name: 'Archer F', original: '529ab1a24b67a988ad000002', portraitURL: '/file/db/thang.type/529ab1a24b67a988ad000002/portrait.png', kind: 'Unit'}

View file

@ -431,7 +431,6 @@ describe 'POST /db/course_instance/-/recent', ->
startDay = utils.createDay(1) startDay = utils.createDay(1)
endDay = utils.createDay(2) endDay = utils.createDay(2)
[res, body] = yield request.postAsync(url, { json: { startDay, endDay } }) [res, body] = yield request.postAsync(url, { json: { startDay, endDay } })
console.log startDay, endDay, res.body.courseInstances.length
expect(res.body.courseInstances.length).toBe(0) expect(res.body.courseInstances.length).toBe(0)
startDay = utils.createDay(-2) startDay = utils.createDay(-2)

View file

@ -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()