Add UI/models to allow uploading images
This commit is contained in:
@@ -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.
|
All rights reserved.
|
||||||
###
|
###
|
||||||
|
|
||||||
BaseController = require './base_controller'
|
_ = require 'underscore'
|
||||||
convertMarkdown = require 'marked'
|
BaseController = require './base_controller'
|
||||||
_ = require 'underscore'
|
convertMarkdown = require 'marked'
|
||||||
{Event} = require '../constants'
|
{Event} = require '../constants'
|
||||||
{Url} = require '../constants'
|
MarkdownImageListController = require './markdown_image_list_controller'
|
||||||
w = require 'when'
|
{Url} = require '../constants'
|
||||||
|
w = require 'when'
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
@@ -132,6 +133,8 @@ module.exports = class MarkdownSectionController extends BaseController
|
|||||||
# BaseController Overrides #####################################################################
|
# BaseController Overrides #####################################################################
|
||||||
|
|
||||||
onDidRender: ->
|
onDidRender: ->
|
||||||
|
@_imageListController = @addChild MarkdownImageListController, '.image_list'
|
||||||
|
|
||||||
@$errorText = @$('.error p')
|
@$errorText = @$('.error p')
|
||||||
@$markdownPanel = @$('.markdown')
|
@$markdownPanel = @$('.markdown')
|
||||||
@$sizer = @$('.sizer')
|
@$sizer = @$('.sizer')
|
||||||
@@ -144,6 +147,7 @@ module.exports = class MarkdownSectionController extends BaseController
|
|||||||
|
|
||||||
refresh: ->
|
refresh: ->
|
||||||
@$title.html @title
|
@$title.html @title
|
||||||
|
@_imageListController.markdownText = @model
|
||||||
|
|
||||||
@_updateSizer()
|
@_updateSizer()
|
||||||
@_updatePreview()
|
@_updatePreview()
|
||||||
@@ -210,6 +214,7 @@ module.exports = class MarkdownSectionController extends BaseController
|
|||||||
editButton: @$('button.edit')
|
editButton: @$('button.edit')
|
||||||
editorPanel: @$('.editor')
|
editorPanel: @$('.editor')
|
||||||
errorPanel: @$('.error')
|
errorPanel: @$('.error')
|
||||||
|
imageList: @$('.image_list')
|
||||||
markdownPanel: @$markdownPanel
|
markdownPanel: @$markdownPanel
|
||||||
previewButton: @$('button.preview')
|
previewButton: @$('button.preview')
|
||||||
returnButton: @$('button.return')
|
returnButton: @$('button.return')
|
||||||
@@ -226,7 +231,7 @@ module.exports = class MarkdownSectionController extends BaseController
|
|||||||
else if @state is State.creating
|
else if @state is State.creating
|
||||||
visible = buttonPanel:true, creatingPanel:true, editButton:true
|
visible = buttonPanel:true, creatingPanel:true, editButton:true
|
||||||
else if @state is State.editing
|
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
|
else if @state is State.previewing
|
||||||
visible = buttonPanel:true, errorPanel:true, markdownPanel:true, returnButton:true, saveButton:true
|
visible = buttonPanel:true, errorPanel:true, markdownPanel:true, returnButton:true, saveButton:true
|
||||||
errorText = "remember: your changes aren't saved yet!"
|
errorText = "remember: your changes aren't saved yet!"
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -10,6 +10,8 @@
|
|||||||
.panel
|
.panel
|
||||||
.waiting.hideable: img(src='/images/wait.gif')
|
.waiting.hideable: img(src='/images/wait.gif')
|
||||||
.markdown.hideable.hidden
|
.markdown.hideable.hidden
|
||||||
|
.creating.hideable.hidden
|
||||||
|
p Please help by adding a description!
|
||||||
.editor.hideable.hidden
|
.editor.hideable.hidden
|
||||||
.instructions
|
.instructions
|
||||||
p.
|
p.
|
||||||
@@ -17,8 +19,7 @@
|
|||||||
Flavored Markdown</a>. Use <tt>[[Item Name]]</tt> to link to another item.
|
Flavored Markdown</a>. Use <tt>[[Item Name]]</tt> to link to another item.
|
||||||
textarea
|
textarea
|
||||||
.sizer
|
.sizer
|
||||||
.creating.hideable.hidden
|
.image_list
|
||||||
p Please help by adding a description!
|
|
||||||
.buttons.hideable.hidden
|
.buttons.hideable.hidden
|
||||||
.error.hideable.hidden: p
|
.error.hideable.hidden: p
|
||||||
button.return.hideable.hidden return
|
button.return.hideable.hidden return
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ All rights reserved.
|
|||||||
@import 'item_selector';
|
@import 'item_selector';
|
||||||
@import 'item_selector_element';
|
@import 'item_selector_element';
|
||||||
@import 'login_page';
|
@import 'login_page';
|
||||||
|
@import 'markdown_image';
|
||||||
|
@import 'markdown_image_list';
|
||||||
@import 'markdown_section';
|
@import 'markdown_section';
|
||||||
@import 'minimal_recipe';
|
@import 'minimal_recipe';
|
||||||
@import 'mod';
|
@import 'mod';
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 = ''
|
||||||
|
(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|  |\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|  |\n|  |\n|  |\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 = ''
|
||||||
|
(i.fileName for i in list.all).sort().should.eql ['alpha.png']
|
||||||
|
|
||||||
|
list.markdownText = ', '
|
||||||
|
(i.fileName for i in list.all).sort().should.eql ['alpha.png', 'bravo.png']
|
||||||
|
|
||||||
|
it 'removes missing images when markdown changes', ->
|
||||||
|
list.markdownText = ', '
|
||||||
|
(i.fileName for i in list.all).sort().should.eql ['alpha.png', 'bravo.png']
|
||||||
|
|
||||||
|
list.markdownText = ''
|
||||||
|
(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 = ''
|
||||||
|
list.all[0].testField = 'foo'
|
||||||
|
|
||||||
|
list.markdownText = ' bravo'
|
||||||
|
list.all[0].testField.should.equal 'foo'
|
||||||
Reference in New Issue
Block a user