written 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/written/app/assets/javascripts/written/attachments/image.coffee +113 -0
- data/lib/written/app/assets/javascripts/written/core/attachments.coffee +37 -0
- data/lib/written/app/assets/javascripts/written/core/content.coffee +12 -26
- data/lib/written/app/assets/javascripts/written/parsers/block/image.coffee +1 -1
- data/lib/written/app/assets/javascripts/written.coffee +1 -1
- data/lib/written/app/assets/stylesheets/written.scss +116 -16
- data/lib/written/version.rb +1 -1
- data/test/server/app/assets/javascripts/application.coffee +3 -1
- data/test/server/app/views/posts/show.html.erb +4 -4
- metadata +4 -3
- data/lib/written/app/assets/javascripts/written/uploaders/aws.coffee +0 -125
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3feabb75dd783639d084dd9b58cb552d195a8a38
|
4
|
+
data.tar.gz: cf8fbadf5551a7d453904460bc4ba4cb5b7f0676
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 43c5f5f0d01986530cdbbba47fcfecdafc6c07b367027b3413603aba5cb33fda21adfb34dff6f1263488eb9a6f4ca50d38ac1194a3eabfe5cef39556535c8370
|
7
|
+
data.tar.gz: 10722bca8267b18cf3a268aefe0ec7bd82afb4445918b8f65c801ddf5484e0c8e212c1f7b0222351032b63fd643df4ac57c9b821e6488ac0b732d8916de65ea2
|
@@ -0,0 +1,113 @@
|
|
1
|
+
class Image extends Written.Attachments.Base
|
2
|
+
constructor: (@node) ->
|
3
|
+
@input = document.createElement('input')
|
4
|
+
@input.type = 'file'
|
5
|
+
@input.addEventListener 'change', @confirm.bind(this, @node)
|
6
|
+
@node.querySelector('img').addEventListener 'click', @select.bind(this, @node)
|
7
|
+
|
8
|
+
template: ->
|
9
|
+
"<div id='WrittenOverlay' contenteditable=false>
|
10
|
+
<div id='WrittenDialog'>
|
11
|
+
<header>
|
12
|
+
<div class='progress'></div>
|
13
|
+
<button data-action='cancel'>Cancel</button>
|
14
|
+
<h3>Use this image?</h3>
|
15
|
+
<button data-action='upload'>Use</button>
|
16
|
+
</header>
|
17
|
+
<figure>
|
18
|
+
<img />
|
19
|
+
</figure>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
".toHTML()
|
23
|
+
|
24
|
+
select: (node) =>
|
25
|
+
@selection = @getSelection()
|
26
|
+
@input.click()
|
27
|
+
|
28
|
+
confirm: (node, event) =>
|
29
|
+
@pause()
|
30
|
+
@overlay = @template()
|
31
|
+
@editor().element().appendChild(@overlay)
|
32
|
+
|
33
|
+
@image(event, @preview.bind(this, @overlay.querySelector('img')))
|
34
|
+
@overlay.querySelector('button[data-action=cancel]').addEventListener('click', @cancel)
|
35
|
+
|
36
|
+
preview: (node, event) ->
|
37
|
+
node.src = event.target.result
|
38
|
+
@overlay.querySelector('button[data-action=upload]').addEventListener('click', @upload)
|
39
|
+
|
40
|
+
image: (event, callback) ->
|
41
|
+
@file = event.target.files[0]
|
42
|
+
|
43
|
+
reader = new FileReader()
|
44
|
+
reader.addEventListener('load', callback)
|
45
|
+
reader.addEventListener('error', @failed)
|
46
|
+
reader.readAsDataURL(@file)
|
47
|
+
|
48
|
+
cancel: =>
|
49
|
+
@overlay.remove()
|
50
|
+
@resume()
|
51
|
+
|
52
|
+
complete: (name) ->
|
53
|
+
figcaption = @node.querySelector('figcaption')
|
54
|
+
url = figcaption.childNodes[figcaption.childNodes.length - 1]
|
55
|
+
|
56
|
+
@overlay.addEventListener 'transitionend', =>
|
57
|
+
@overlay.remove()
|
58
|
+
@resume =>
|
59
|
+
figcaption.replaceChild(document.createTextNode("(#{window.AWS.bucket}/#{name})"), url)
|
60
|
+
|
61
|
+
@overlay.classList.add('fade')
|
62
|
+
|
63
|
+
failed: (e) =>
|
64
|
+
dialog = @overlay.querySelector('#WrittenDialog')
|
65
|
+
dialog.classList.add 'failed'
|
66
|
+
dialog.querySelector('h3').textContent = "Failed"
|
67
|
+
dialog.querySelector('figure').remove()
|
68
|
+
dialog.appendChild("
|
69
|
+
<section>
|
70
|
+
<p>An error occured while trying to process your image.</p>
|
71
|
+
<button>Close</button>
|
72
|
+
</section>
|
73
|
+
".toHTML())
|
74
|
+
dialog.querySelector('button').addEventListener('click', @cancel)
|
75
|
+
|
76
|
+
upload: (e) =>
|
77
|
+
@overlay.querySelector('h3').textContent = "Uploading"
|
78
|
+
Array.prototype.slice.call(@overlay.querySelectorAll('button')).forEach (button) ->
|
79
|
+
button.remove()
|
80
|
+
|
81
|
+
name = @name(@file.name)
|
82
|
+
|
83
|
+
form = new FormData()
|
84
|
+
form.append('key', name)
|
85
|
+
form.append('acl', 'private')
|
86
|
+
form.append("AWSAccessKeyId", window.AWS.accessKey)
|
87
|
+
form.append('policy', window.AWS.policy)
|
88
|
+
form.append('signature', window.AWS.signature)
|
89
|
+
form.append('Content-Type', @file.type)
|
90
|
+
form.append('file', @file, name)
|
91
|
+
|
92
|
+
xhr = new XMLHttpRequest()
|
93
|
+
xhr.addEventListener('load', @complete.bind(this, name))
|
94
|
+
xhr.addEventListener('error', @failed)
|
95
|
+
xhr.upload.addEventListener('progress', @progress)
|
96
|
+
xhr.open('POST', window.AWS.bucket)
|
97
|
+
xhr.send(form)
|
98
|
+
xhr
|
99
|
+
|
100
|
+
|
101
|
+
progress: (e) =>
|
102
|
+
value = e.loaded / e.total * 100
|
103
|
+
@overlay.querySelector('div.progress').style.width = "#{value}%"
|
104
|
+
|
105
|
+
name: (filename) ->
|
106
|
+
hash = 0
|
107
|
+
for i in [0..filename.length - 1]
|
108
|
+
hash += filename.charCodeAt(i) ^ hash
|
109
|
+
|
110
|
+
hash.toString('16')
|
111
|
+
|
112
|
+
|
113
|
+
Written.Attachments.bind 'figure', Image
|
@@ -0,0 +1,37 @@
|
|
1
|
+
@Written.Attachments = new class Attachments
|
2
|
+
constructor: ->
|
3
|
+
@attachments = {}
|
4
|
+
@nodes = []
|
5
|
+
|
6
|
+
bind: (nodeName, attachment) ->
|
7
|
+
@attachments[nodeName] = attachment
|
8
|
+
|
9
|
+
attach: (editor) =>
|
10
|
+
for name, attachment of @attachments
|
11
|
+
nodes = editor.element().querySelectorAll(name)
|
12
|
+
for node in nodes
|
13
|
+
if @nodes.includes(node)
|
14
|
+
continue
|
15
|
+
|
16
|
+
attachment = new attachment(node)
|
17
|
+
attachment.editor(editor)
|
18
|
+
@nodes.push(node)
|
19
|
+
|
20
|
+
|
21
|
+
@Written.Attachments.Base = class Attachment
|
22
|
+
editor: (editor) ->
|
23
|
+
@editor = ->
|
24
|
+
editor
|
25
|
+
|
26
|
+
@editor()
|
27
|
+
|
28
|
+
pause: =>
|
29
|
+
@editor().observer.pause()
|
30
|
+
|
31
|
+
resume: (callback) =>
|
32
|
+
@editor().observer.resume()
|
33
|
+
if callback?
|
34
|
+
callback()
|
35
|
+
|
36
|
+
getSelection: ->
|
37
|
+
window.getSelection()
|
@@ -38,7 +38,8 @@ class @Written
|
|
38
38
|
document.cursor = cursor
|
39
39
|
|
40
40
|
|
41
|
-
|
41
|
+
@render(document)
|
42
|
+
|
42
43
|
document.cursor.focus(document.toString().length)
|
43
44
|
|
44
45
|
@history = new Written.History(document)
|
@@ -54,7 +55,7 @@ class @Written
|
|
54
55
|
changeTo: (text) =>
|
55
56
|
document = new Written.Document(text, @parsers)
|
56
57
|
@history.push(document)
|
57
|
-
|
58
|
+
@render(document)
|
58
59
|
|
59
60
|
changed: (e) =>
|
60
61
|
oldDocument = @history.current
|
@@ -63,33 +64,14 @@ class @Written
|
|
63
64
|
if @element().children.length > 0 && oldDocument.toString().localeCompare(newDocument.toString()) == 0
|
64
65
|
return
|
65
66
|
|
66
|
-
newDocument.applyTo(@element())
|
67
67
|
@history.push(newDocument)
|
68
|
+
@render(newDocument)
|
69
|
+
|
68
70
|
@dispatch('written:changed', document: newDocument)
|
69
71
|
|
70
72
|
cursor: =>
|
71
73
|
@history.current.cursor = new Written.Cursor(@element(), window.getSelection())
|
72
74
|
|
73
|
-
update: (document) =>
|
74
|
-
elements = Array.prototype.slice.call(@element().children)
|
75
|
-
|
76
|
-
for block, index in document.blocks
|
77
|
-
node = block.markdown()
|
78
|
-
element = elements[index]
|
79
|
-
|
80
|
-
if element?
|
81
|
-
if !block.identical(element, node)
|
82
|
-
@element().replaceChild(node, element)
|
83
|
-
else
|
84
|
-
@element().appendChild(node)
|
85
|
-
|
86
|
-
if elements.length > index
|
87
|
-
for i in [index..elements.length - 1]
|
88
|
-
elements[i].remove()
|
89
|
-
|
90
|
-
document.cursor.focus()
|
91
|
-
|
92
|
-
|
93
75
|
linefeed: (e) =>
|
94
76
|
return unless e.which == 13
|
95
77
|
e.preventDefault()
|
@@ -117,10 +99,14 @@ class @Written
|
|
117
99
|
if cursor.offset < document.toString().length
|
118
100
|
cursor.offset += 1
|
119
101
|
|
120
|
-
|
102
|
+
@render(document)
|
121
103
|
@history.push(document)
|
122
104
|
|
123
105
|
|
106
|
+
render: (document) =>
|
107
|
+
document.applyTo(@element())
|
108
|
+
Written.Attachments.attach(this)
|
109
|
+
|
124
110
|
undo: (e) =>
|
125
111
|
if e.code == 'KeyZ' && e.metaKey && !e.shiftKey
|
126
112
|
e.preventDefault()
|
@@ -130,7 +116,7 @@ class @Written
|
|
130
116
|
|
131
117
|
if document = @history.previous()
|
132
118
|
@history.current = document
|
133
|
-
|
119
|
+
@render(document)
|
134
120
|
|
135
121
|
redo: (e) =>
|
136
122
|
if e.code == 'KeyZ' && e.metaKey && e.shiftKey
|
@@ -141,7 +127,7 @@ class @Written
|
|
141
127
|
|
142
128
|
if document = @history.next()
|
143
129
|
@history.current = document
|
144
|
-
@history.current
|
130
|
+
@render(@history.current)
|
145
131
|
|
146
132
|
toString: =>
|
147
133
|
texts = []
|
@@ -18,7 +18,7 @@ class Image
|
|
18
18
|
rendered.querySelector('img').src == img.src
|
19
19
|
|
20
20
|
markdown: =>
|
21
|
-
figure = "<figure><div contenteditable='false'><
|
21
|
+
figure = "<figure><div contenteditable='false'><img/></div><figcaption /></figure>".toHTML()
|
22
22
|
caption = figure.querySelector('figcaption')
|
23
23
|
container = figure.querySelector('div')
|
24
24
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
[data-editor="written"] {
|
2
|
+
position: relative;
|
2
3
|
white-space: pre-wrap;
|
3
4
|
padding: 1em;
|
4
5
|
|
@@ -29,23 +30,9 @@
|
|
29
30
|
[data-editor="written"] figure > div {
|
30
31
|
position: relative;
|
31
32
|
display: flex;
|
33
|
+
align-items: center;
|
34
|
+
justify-content: center;
|
32
35
|
padding: 4px;
|
33
|
-
|
34
|
-
&[data-uploadable] {
|
35
|
-
cursor: pointer;
|
36
|
-
}
|
37
|
-
}
|
38
|
-
|
39
|
-
[data-editor="written"] figure > div > div.progress {
|
40
|
-
position: absolute;
|
41
|
-
top: 0;
|
42
|
-
left: 0;
|
43
|
-
transition: width 0.24s ease;
|
44
|
-
}
|
45
|
-
|
46
|
-
[data-editor="written"] figure[data-status='uploading'] div.progress {
|
47
|
-
background-color: green;
|
48
|
-
height: 1px;
|
49
36
|
}
|
50
37
|
|
51
38
|
[data-editor="written"] figure img {
|
@@ -99,3 +86,116 @@
|
|
99
86
|
display: block;
|
100
87
|
white-space: pre-line;
|
101
88
|
}
|
89
|
+
|
90
|
+
[data-editor="written"] > #WrittenOverlay {
|
91
|
+
position: absolute;
|
92
|
+
top: 0;
|
93
|
+
left: 0;
|
94
|
+
right: 0;
|
95
|
+
bottom: 0;
|
96
|
+
|
97
|
+
display: flex;
|
98
|
+
align-items: center;
|
99
|
+
justify-content: center;
|
100
|
+
|
101
|
+
background-color: rgba(230, 230, 230, 0.8);
|
102
|
+
|
103
|
+
transition: opacity 0.2s ease-out;
|
104
|
+
|
105
|
+
&.fade {
|
106
|
+
opacity: 0;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
[data-editor="written"] > #WrittenOverlay > #WrittenDialog {
|
111
|
+
display: flex;
|
112
|
+
flex-direction: column;
|
113
|
+
|
114
|
+
min-width: 300px;
|
115
|
+
width: 100vw;
|
116
|
+
max-width: 700px;
|
117
|
+
|
118
|
+
max-height: 800px;
|
119
|
+
|
120
|
+
background: white;
|
121
|
+
|
122
|
+
box-shadow: 0 0 3px rgba(150, 150, 150, 0.5);
|
123
|
+
|
124
|
+
overflow: hidden;
|
125
|
+
|
126
|
+
button {
|
127
|
+
outline: none;
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
[data-editor="written"] > #WrittenOverlay > #WrittenDialog figure {
|
132
|
+
img {
|
133
|
+
border: none;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
[data-editor="written"] > #WrittenOverlay > #WrittenDialog div.progress {
|
138
|
+
position: absolute;
|
139
|
+
top: 0;
|
140
|
+
left: 0;
|
141
|
+
bottom: 0;
|
142
|
+
|
143
|
+
transition: width 0.2s ease;
|
144
|
+
background-color: #2B99E8;
|
145
|
+
box-shadow: 0 0 2px rgba(0, 147, 255, 0.41);
|
146
|
+
}
|
147
|
+
|
148
|
+
[data-editor="written"] > #WrittenOverlay > #WrittenDialog header {
|
149
|
+
position: relative;
|
150
|
+
display: flex;
|
151
|
+
justify-content: space-between;
|
152
|
+
border-bottom: 1px solid rgba(200, 200, 200, 200);
|
153
|
+
box-shadow: 0 2px whitesmoke;
|
154
|
+
background-color: whitesmoke;
|
155
|
+
|
156
|
+
h3 {
|
157
|
+
flex: 1;
|
158
|
+
margin: 0;
|
159
|
+
text-align: center;
|
160
|
+
z-index: 1;
|
161
|
+
line-height: 2em;
|
162
|
+
}
|
163
|
+
|
164
|
+
button {
|
165
|
+
border: none;
|
166
|
+
text-transform: uppercase;
|
167
|
+
color: white;
|
168
|
+
width: 100px;
|
169
|
+
|
170
|
+
&[data-action=cancel] {
|
171
|
+
background-color: red;
|
172
|
+
}
|
173
|
+
|
174
|
+
&[data-action=upload] {
|
175
|
+
background-color: green;
|
176
|
+
}
|
177
|
+
}
|
178
|
+
|
179
|
+
}
|
180
|
+
|
181
|
+
[data-editor="written"] > #WrittenOverlay > #WrittenDialog.failed header {
|
182
|
+
background-color: #CA1414;
|
183
|
+
color: white;
|
184
|
+
}
|
185
|
+
|
186
|
+
[data-editor="written"] > #WrittenOverlay > #WrittenDialog.failed section {
|
187
|
+
display: flex;
|
188
|
+
flex-direction: column;
|
189
|
+
padding: 12px;
|
190
|
+
text-align: center;
|
191
|
+
}
|
192
|
+
|
193
|
+
[data-editor="written"] > #WrittenOverlay > #WrittenDialog.failed button {
|
194
|
+
background-color: #CA1414;
|
195
|
+
text-align: center;
|
196
|
+
color: white;
|
197
|
+
width: 80%;
|
198
|
+
margin: 12px auto;
|
199
|
+
padding: 4px;
|
200
|
+
border: none;
|
201
|
+
}
|
data/lib/written/version.rb
CHANGED
@@ -3,12 +3,12 @@
|
|
3
3
|
1. this is a list
|
4
4
|
2. this is another list
|
5
5
|
3. this is another list
|
6
|
-
- this is
|
7
|
-
3. this is [a](http://google.com)
|
8
|
-
test *boo* and them *some* or
|
6
|
+
- this is *a* list
|
7
|
+
3. this is [a](http://google.com) _list_. It's special, isn't *it*?
|
8
|
+
test *boo* and them *some* or *some!!* a
|
9
9
|
|
10
10
|
|
11
|
-
![an
|
11
|
+
![an _image_ is worth a *thousand* words](image)
|
12
12
|
|
13
13
|
~~~javascript
|
14
14
|
var test = "hello"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: written
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pier-Olivier Thibault
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-05-
|
11
|
+
date: 2016-05-20 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Written is a rich Markdown editor for the web.
|
14
14
|
email: pothibo@gmail.com
|
@@ -25,6 +25,8 @@ files:
|
|
25
25
|
- lib/written.rb
|
26
26
|
- lib/written/app/assets/images/written/placeholder.png
|
27
27
|
- lib/written/app/assets/javascripts/written.coffee
|
28
|
+
- lib/written/app/assets/javascripts/written/attachments/image.coffee
|
29
|
+
- lib/written/app/assets/javascripts/written/core/attachments.coffee
|
28
30
|
- lib/written/app/assets/javascripts/written/core/content.coffee
|
29
31
|
- lib/written/app/assets/javascripts/written/core/cursor.coffee
|
30
32
|
- lib/written/app/assets/javascripts/written/core/document.coffee
|
@@ -43,7 +45,6 @@ files:
|
|
43
45
|
- lib/written/app/assets/javascripts/written/parsers/inline/link.coffee
|
44
46
|
- lib/written/app/assets/javascripts/written/parsers/inline/strong.coffee
|
45
47
|
- lib/written/app/assets/javascripts/written/parsers/parsers.coffee
|
46
|
-
- lib/written/app/assets/javascripts/written/uploaders/aws.coffee
|
47
48
|
- lib/written/app/assets/stylesheets/written.scss
|
48
49
|
- lib/written/railtie.rb
|
49
50
|
- lib/written/version.rb
|
@@ -1,125 +0,0 @@
|
|
1
|
-
class Written.Uploaders.AWS
|
2
|
-
constructor: (settings) ->
|
3
|
-
Images.settings = settings
|
4
|
-
|
5
|
-
initialize: (node, observer) =>
|
6
|
-
img = node.querySelector('img')
|
7
|
-
container = node.querySelector('div')
|
8
|
-
|
9
|
-
node.dataset.uploadable = true
|
10
|
-
node.addEventListener 'written:uploader:error', @error, true
|
11
|
-
node.addEventListener 'written:uploader:uploading', @progress, true
|
12
|
-
node.addEventListener 'written:uploader:completed', @uploaded, true
|
13
|
-
node.addEventListener 'change', @input.bind(this, node, observer), true
|
14
|
-
|
15
|
-
container.addEventListener 'click', @open.bind(this, node), true
|
16
|
-
image = Images.get(img.dataset.image)
|
17
|
-
if image
|
18
|
-
image.node = node
|
19
|
-
|
20
|
-
open: (node, e) =>
|
21
|
-
node.querySelector('input')?.click()
|
22
|
-
|
23
|
-
uploaded: (e) =>
|
24
|
-
caption = e.target.querySelector('figcaption')
|
25
|
-
img = e.target.querySelector('img')
|
26
|
-
|
27
|
-
text = caption.childNodes[caption.childNodes.length - 1]
|
28
|
-
text.textContent = "(#{e.image.url})"
|
29
|
-
img.src = e.image.url
|
30
|
-
selection = window.getSelection()
|
31
|
-
selection.removeAllRanges()
|
32
|
-
range = document.createRange()
|
33
|
-
range.setEnd(text, text.textContent.length - 1)
|
34
|
-
selection.addRange(range)
|
35
|
-
|
36
|
-
error: (e) =>
|
37
|
-
console.log(e)
|
38
|
-
|
39
|
-
input: (node, history, e) =>
|
40
|
-
file = e.target.files[0]
|
41
|
-
@process(file, node, history)
|
42
|
-
|
43
|
-
process: (file, node) =>
|
44
|
-
caption = node.querySelector('figcaption')
|
45
|
-
progress = node.querySelector('div.progress')
|
46
|
-
filename = [Images.settings.namespace, @hash(file.name)].join('/')
|
47
|
-
|
48
|
-
image = Images.get(filename)
|
49
|
-
if !image
|
50
|
-
image = Images.upload(filename, file)
|
51
|
-
|
52
|
-
image.node = node
|
53
|
-
progress.style.width = image.progress
|
54
|
-
node.dataset.status = image.status
|
55
|
-
|
56
|
-
text = caption.childNodes[caption.childNodes.length - 1]
|
57
|
-
text.textContent = "(#{filename})"
|
58
|
-
selection = window.getSelection()
|
59
|
-
selection.removeAllRanges()
|
60
|
-
range = document.createRange()
|
61
|
-
range.setEnd(text, text.textContent.length - 1)
|
62
|
-
selection.addRange(range)
|
63
|
-
|
64
|
-
hash: (filename) ->
|
65
|
-
hash = 0
|
66
|
-
for i in [0..filename.length - 1]
|
67
|
-
hash += filename.charCodeAt(i) ^ hash
|
68
|
-
|
69
|
-
hash.toString('16')
|
70
|
-
|
71
|
-
Images = new class
|
72
|
-
constructor: ->
|
73
|
-
@cache = {}
|
74
|
-
|
75
|
-
failed: (image, event) ->
|
76
|
-
image.status = 'failed'
|
77
|
-
|
78
|
-
if image.node?
|
79
|
-
event = new CustomEvent('written:uploader:error', {bubbles: true})
|
80
|
-
event.image = image
|
81
|
-
image.node.dispatchEvent(event)
|
82
|
-
|
83
|
-
update: (image, event) ->
|
84
|
-
image.status = 'completed'
|
85
|
-
|
86
|
-
if image.node?
|
87
|
-
event = new CustomEvent('written:uploader:completed', {bubbles: true})
|
88
|
-
event.image = image
|
89
|
-
image.node.dispatchEvent(event)
|
90
|
-
|
91
|
-
upload: (name, file) =>
|
92
|
-
image = {
|
93
|
-
url: "#{@settings.url}/#{name}"
|
94
|
-
name: name
|
95
|
-
status: 'uploading'
|
96
|
-
progress: 0
|
97
|
-
}
|
98
|
-
|
99
|
-
form = new FormData()
|
100
|
-
form.append('key', name)
|
101
|
-
form.append('acl', 'private')
|
102
|
-
form.append("AWSAccessKeyId", @settings.accessKey)
|
103
|
-
form.append('policy', @settings.policy)
|
104
|
-
form.append('signature', @settings.signature)
|
105
|
-
form.append('Content-Type', file.type)
|
106
|
-
form.append('file', file, name)
|
107
|
-
|
108
|
-
|
109
|
-
image.xhr = @send(form, @settings.url)
|
110
|
-
image.xhr.addEventListener('load', @update.bind(this, image))
|
111
|
-
image.xhr.addEventListener('error', @failed.bind(this, image))
|
112
|
-
|
113
|
-
@cache[image.name] = image
|
114
|
-
|
115
|
-
|
116
|
-
get: (name) ->
|
117
|
-
@cache[name]
|
118
|
-
|
119
|
-
send: (form, url) =>
|
120
|
-
xhr = new XMLHttpRequest()
|
121
|
-
xhr.open('POST', url)
|
122
|
-
xhr.send(form)
|
123
|
-
xhr
|
124
|
-
|
125
|
-
|