Add UI/models to allow uploading images

This commit is contained in:
Andrew Miner
2015-06-05 22:47:40 -07:00
parent 94cd133748
commit dd760018df
12 changed files with 361 additions and 10 deletions
@@ -0,0 +1,40 @@
###
Crafting Guide - markdown_image_controller.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
########################################################################################################################
module.exports = class MarkdownImageController extends BaseController
constructor: (options={})->
if not options.model? then throw new Error 'options.model is required'
options.templateName = 'markdown_image'
super
# BaseController Overrides #####################################################################
onDidRender: ->
@$image = @$('img')
@$fileName = @$('.fileName p')
@$button = @$('button')
super
refresh: ->
if @model.mimeType? and @model.encodedData?
@$image.attr 'src', "data:#{@model.mimeType};base64,#{@model.encodedData}"
else
@$image.attr 'src', '/images/unknown.png'
@$fileName.html @model.fileName
if @model.encodedData?
@$button.html 'Update'
else
@$button.html 'Choose'
super
@@ -0,0 +1,55 @@
###
Crafting Guide - markdown_image_list_controller.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
MarkdownImageController = require './markdown_image_controller'
MarkdownImageList = require '../models/markdown_image_list'
########################################################################################################################
module.exports = class MarkdownImageListController extends BaseController
constructor: (options={})->
options.model ?= new MarkdownImageList
options.templateName = 'markdown_image_list'
super options
# Property Methods #############################################################################
getMarkdownText: ->
return @model.markdownText
setMarkdownText: (markdownText)->
@model.markdownText = markdownText
Object.defineProperties @prototype,
markdownText: {get:@prototype.getMarkdownText, set:@prototype.setMarkdownText}
# BaseController Overrides #####################################################################
onDidRender: ->
@$imageContainer = @$('.image_container')
super
refresh: ->
@_controllers ?= []
index = 0
for image in @model.all
controller = @_controllers[index]
if not controller?
controller = new MarkdownImageController model:image
@_controllers.push controller
@$imageContainer.append controller.$el
controller.render()
else
controller.model = image
index += 1
while @_controllers.length > index
@_controllers.pop().remove()
@@ -5,12 +5,13 @@ Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
convertMarkdown = require 'marked'
_ = require 'underscore'
{Event} = require '../constants'
{Url} = require '../constants'
w = require 'when'
_ = require 'underscore'
BaseController = require './base_controller'
convertMarkdown = require 'marked'
{Event} = require '../constants'
MarkdownImageListController = require './markdown_image_list_controller'
{Url} = require '../constants'
w = require 'when'
########################################################################################################################
@@ -132,6 +133,8 @@ module.exports = class MarkdownSectionController extends BaseController
# BaseController Overrides #####################################################################
onDidRender: ->
@_imageListController = @addChild MarkdownImageListController, '.image_list'
@$errorText = @$('.error p')
@$markdownPanel = @$('.markdown')
@$sizer = @$('.sizer')
@@ -144,6 +147,7 @@ module.exports = class MarkdownSectionController extends BaseController
refresh: ->
@$title.html @title
@_imageListController.markdownText = @model
@_updateSizer()
@_updatePreview()
@@ -210,6 +214,7 @@ module.exports = class MarkdownSectionController extends BaseController
editButton: @$('button.edit')
editorPanel: @$('.editor')
errorPanel: @$('.error')
imageList: @$('.image_list')
markdownPanel: @$markdownPanel
previewButton: @$('button.preview')
returnButton: @$('button.return')
@@ -226,7 +231,7 @@ module.exports = class MarkdownSectionController extends BaseController
else if @state is State.creating
visible = buttonPanel:true, creatingPanel:true, editButton:true
else if @state is State.editing
visible = buttonPanel:true, cancelButton:true, editorPanel:true, previewButton:true
visible = buttonPanel:true, cancelButton:true, editorPanel:true, previewButton:true, imageList:true
else if @state is State.previewing
visible = buttonPanel:true, errorPanel:true, markdownPanel:true, returnButton:true, saveButton:true
errorText = "remember: your changes aren't saved yet!"
+46
View File
@@ -0,0 +1,46 @@
###
Crafting Guide - markdown_image.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
BaseModel = require './base_model'
{Event} = require '../constants'
########################################################################################################################
module.exports = class MarkdownImage extends BaseModel
@MIMETYPES = [
{patern:/\.png$/i, text:'image/png'}
{patern:/\.gif$/i, text:'image/gif'}
{patern:/\.jpg$/i, text:'image/jpeg'}
{patern:/\.jpeg$/i, text:'image/jpeg'}
]
constructor: (attributes={}, options={})->
attributes.encodedData ?= null
attributes.fileName ?= ''
attributes.path ?= ''
attributes.sha ?= null
super
@on Event.change + ':fileName', => @_mimeType = null
# Property Methods #############################################################################
getFullPath: ->
return "#{@path}/#{@fileName}"
getMimeType: ->
if not @_mimeType?
for mimeType in MarkdownImage.MIMETYPES
if mimeType.pattern.test @fileName
@_mimeType = mimeType.text
break
return @_mimeType
Object.defineProperties @prototype,
fullPath: {get:@prototype.getFullPath}
@@ -0,0 +1,66 @@
###
Crafting Guide - markdown_image_list.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
BaseModel = require './base_model'
{Event} = require '../constants'
MarkdownImage = require './markdown_image'
########################################################################################################################
module.exports = class MarkdownImageList extends BaseModel
constructor: (attributes={}, options={})->
attributes.markdownText ?= null
super attributes, options
@_images = {}
@on Event.change + ':markdownText', => @_analyzeMarkdownText()
@_analyzeMarkdownText()
# Property Methods #############################################################################
getAll: ->
fileNames = (fileName for fileName, image of @_images).sort()
result = []
for fileName in fileNames
result.push @_images[fileName]
return result
Object.defineProperties @prototype,
all: {get:@prototype.getAll}
# Private Methods ##############################################################################
_analyzeMarkdownText: ->
regex = /\!\[([^\]]*)\]\(([^\)]*)\)/g
newImages = {}
changed = false
if @markdownText?
while true
match = regex.exec @markdownText
break unless match?
fileName = match[2]
image = @_images[fileName]
if not image?
image = new MarkdownImage fileName:fileName
@trigger Event.add, this, image
changed = true
newImages[fileName] = image
for fileName, image of @_images
if not newImages[fileName]?
@trigger Event.remove, this, image
changed = true
if changed
@_images = newImages
@trigger Event.change, this
+11
View File
@@ -0,0 +1,11 @@
//-
//- Crafting Guide - markdown_image.jade
//-
//- Copyright (c) 2015 by Redwood Labs
//- All rights reserved.
//-
.view__markdown_image
img
.fileName: p
button
@@ -0,0 +1,10 @@
//-
//- Crafting Guide - markdown_image_list.jade
//-
//- Copyright (c) 2015 by Redwood Labs
//- All rights reserved.
//-
.view__markdown_image_list
h3 Images Referenced
.image_container
+3 -2
View File
@@ -10,6 +10,8 @@
.panel
.waiting.hideable: img(src='/images/wait.gif')
.markdown.hideable.hidden
.creating.hideable.hidden
p Please help by adding a description!
.editor.hideable.hidden
.instructions
p.
@@ -17,8 +19,7 @@
Flavored Markdown</a>. Use <tt>[[Item Name]]</tt> to link to another item.
textarea
.sizer
.creating.hideable.hidden
p Please help by adding a description!
.image_list
.buttons.hideable.hidden
.error.hideable.hidden: p
button.return.hideable.hidden return
+2
View File
@@ -19,6 +19,8 @@ All rights reserved.
@import 'item_selector';
@import 'item_selector_element';
@import 'login_page';
@import 'markdown_image';
@import 'markdown_image_list';
@import 'markdown_section';
@import 'minimal_recipe';
@import 'mod';
+32
View File
@@ -0,0 +1,32 @@
/*
Crafting Guide - markdown_image.scss
Copyright (C) 2015 by Redwood Labs
All rights reserved.
*/
.view__markdown_image {
position: relative; min-width: 33em;
display: inline-block;
margin-right: 2em;
vertical-align: top;
img {
position: absolute; height: 10em; width: 10em;
}
.fileName {
height: 3em;
margin: 1em 0 6em 11em;
p {
font-weight: bold;
}
}
button {
position: absolute; left: 7.33em; bottom: 0.66em;
}
}
@@ -0,0 +1,18 @@
/*
Crafting Guide - markdown_image_list.scss
Copyright (C) 2015 by Redwood Labs
All rights reserved.
*/
.view__markdown_image_list {
position: relative; min-width: 33em; width: 100%;
border-bottom: 0.1em solid $color-gray-medium;
margin: 2em 0;
padding: 0 0 2em 0;
h3 {
margin-bottom: 1em;
}
}
+65
View File
@@ -0,0 +1,65 @@
###
Crafting Guide - markdown_image_list_test.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
MarkdownImageList = require '../src/coffee/models/markdown_image_list'
########################################################################################################################
list = null
########################################################################################################################
describe 'markdown_image_list.coffee', ->
beforeEach ->
list = new MarkdownImageList
describe '_analyzeMarkdownText', ->
it 'finds nothing in a null string', ->
list.all.should.eql []
it 'finds nothing in an empty string', ->
list.markdownText = ''
list.all.should.eql []
it 'finds nothing when there are no images in the markdown', ->
list.markdownText = 'alpha bravo charlie delta'
list.all.should.eql []
it 'finds a single image alone', ->
list.markdownText = '![](alpha.png)'
(i.fileName for i in list.all).sort().should.eql ['alpha.png']
it 'finds a single image inside a markdown document', ->
list.markdownText = '# alpha\n bravo charlie\n\n| ![](alpha.png) |\n|:----------:|\ndelta'
(i.fileName for i in list.all).sort().should.eql ['alpha.png']
it 'finds multiple images inside a markdown document', ->
list.markdownText = '| Images |\n|:------:|\n| ![](alpha.png) |\n| ![](bravo.png) |\n| ![](charlie.png) |\n'
(i.fileName for i in list.all).sort().should.eql ['alpha.png', 'bravo.png', 'charlie.png']
it 'adds new images when markdown changes', ->
list.markdownText = '![](alpha.png)'
(i.fileName for i in list.all).sort().should.eql ['alpha.png']
list.markdownText = '![](alpha.png), ![](bravo.png)'
(i.fileName for i in list.all).sort().should.eql ['alpha.png', 'bravo.png']
it 'removes missing images when markdown changes', ->
list.markdownText = '![](alpha.png), ![](bravo.png)'
(i.fileName for i in list.all).sort().should.eql ['alpha.png', 'bravo.png']
list.markdownText = '![](alpha.png)'
(i.fileName for i in list.all).sort().should.eql ['alpha.png']
it 'does not change remaining images when still present in changed markdown', ->
list.markdownText = '![](alpha.png)'
list.all[0].testField = 'foo'
list.markdownText = '![](alpha.png) bravo'
list.all[0].testField.should.equal 'foo'