written 0.0.5 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/LICENSE +8 -0
- data/README.md +63 -0
- data/Rakefile +17 -27
- data/lib/written.rb +0 -8
- data/lib/written/app/assets/images/written/placeholder.png +0 -0
- data/lib/written/app/assets/javascripts/written.coffee +2 -0
- data/lib/written/app/assets/javascripts/written/core/content.coffee +53 -35
- data/lib/written/app/assets/javascripts/written/core/cursor.coffee +33 -12
- data/lib/written/app/assets/javascripts/written/core/document.coffee +16 -11
- data/lib/written/app/assets/javascripts/written/core/extensions/string.coffee +9 -0
- data/lib/written/app/assets/javascripts/written/core/extensions/text.coffee +2 -0
- data/lib/written/app/assets/javascripts/written/core/history.coffee +2 -0
- data/lib/written/app/assets/javascripts/written/core/observer.coffee +6 -2
- data/lib/written/app/assets/javascripts/written/core/stringify.coffee +15 -0
- data/lib/written/app/assets/javascripts/written/parsers/block.coffee +69 -0
- data/lib/written/app/assets/javascripts/written/parsers/block/code.coffee +79 -15
- data/lib/written/app/assets/javascripts/written/parsers/block/heading.coffee +60 -5
- data/lib/written/app/assets/javascripts/written/parsers/block/image.coffee +103 -9
- data/lib/written/app/assets/javascripts/written/parsers/block/olist.coffee +94 -12
- data/lib/written/app/assets/javascripts/written/parsers/block/paragraph.coffee +63 -5
- data/lib/written/app/assets/javascripts/written/parsers/block/quote.coffee +92 -0
- data/lib/written/app/assets/javascripts/written/parsers/block/ulist.coffee +93 -12
- data/lib/written/app/assets/javascripts/written/parsers/inline.coffee +81 -0
- data/lib/written/app/assets/javascripts/written/parsers/inline/code.coffee +57 -0
- data/lib/written/app/assets/javascripts/written/parsers/inline/italic.coffee +40 -7
- data/lib/written/app/assets/javascripts/written/parsers/inline/link.coffee +43 -13
- data/lib/written/app/assets/javascripts/written/parsers/inline/list.coffee +27 -0
- data/lib/written/app/assets/javascripts/written/parsers/inline/strong.coffee +41 -7
- data/lib/written/app/assets/javascripts/written/parsers/parsers.coffee +21 -107
- data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/CustomElements.js +32 -0
- data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/base.js +40 -0
- data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/boot.js +124 -0
- data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/observe.js +318 -0
- data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/register.js +369 -0
- data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/traverse.js +86 -0
- data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/upgrade.js +130 -0
- data/lib/written/app/assets/javascripts/written/polyfills/MutationObserver/MutationObserver.js +575 -0
- data/lib/written/app/assets/javascripts/written/polyfills/WeakMap/WeakMap.js +49 -0
- data/lib/written/app/assets/javascripts/written/polyfills/base.coffee +10 -0
- data/lib/written/app/assets/javascripts/written/polyfills/dom.js +104 -0
- data/lib/written/app/assets/javascripts/written/uploaders/aws.coffee +125 -0
- data/lib/written/app/assets/stylesheets/written.scss +80 -11
- data/lib/written/version.rb +1 -1
- data/test/server/app/assets/javascripts/application.coffee +20 -2
- data/test/server/app/assets/stylesheets/application.scss +2 -2
- data/test/server/app/assets/stylesheets/prism.css +0 -1
- data/test/server/app/views/posts/show.html.erb +10 -3
- metadata +26 -20
- data/lib/written/app/assets/javascripts/written/core/ext.coffee +0 -109
- data/lib/written/app/assets/javascripts/written/core/extensions.coffee +0 -2
- data/lib/written/document.rb +0 -42
- data/lib/written/node.rb +0 -21
- data/lib/written/nodes/code.rb +0 -65
- data/lib/written/nodes/heading.rb +0 -15
- data/lib/written/nodes/image.rb +0 -14
- data/lib/written/nodes/ordered_list.rb +0 -18
- data/lib/written/nodes/unordered_list.rb +0 -19
- data/lib/written/parsers.rb +0 -11
- data/lib/written/parsers/base.rb +0 -26
- data/lib/written/parsers/code.rb +0 -60
- data/lib/written/parsers/heading.rb +0 -19
- data/lib/written/parsers/image.rb +0 -19
- data/lib/written/parsers/link.rb +0 -12
- data/lib/written/parsers/list.rb +0 -33
- data/lib/written/parsers/word.rb +0 -16
@@ -1,10 +1,68 @@
|
|
1
1
|
class Paragraph
|
2
|
+
multiline: false
|
3
|
+
|
2
4
|
constructor: (match) ->
|
3
5
|
@match = match
|
4
|
-
@node = "<p>".toHTML()
|
5
6
|
|
6
|
-
|
7
|
-
@
|
8
|
-
|
7
|
+
processContent: (callback) =>
|
8
|
+
if @content?
|
9
|
+
throw "Content Error: The content was already processed"
|
10
|
+
return
|
11
|
+
|
12
|
+
@content = callback(@match[0])
|
13
|
+
|
14
|
+
identical: (current, rendered) ->
|
15
|
+
current.outerHTML == rendered.outerHTML
|
16
|
+
|
17
|
+
markdown: =>
|
18
|
+
node = "<p is='written-p'>".toHTML()
|
19
|
+
for text in @content
|
20
|
+
if text.markdown?
|
21
|
+
node.appendChild(text.markdown())
|
22
|
+
else
|
23
|
+
node.appendChild(document.createTextNode(text))
|
24
|
+
|
25
|
+
node
|
26
|
+
|
27
|
+
html: =>
|
28
|
+
node = "<p>".toHTML()
|
29
|
+
for text in @content
|
30
|
+
if text.html?
|
31
|
+
node.appendChild(text.html())
|
32
|
+
else
|
33
|
+
node.appendChild(document.createTextNode(text))
|
34
|
+
|
35
|
+
node
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
Paragraph.rule = /.*/i
|
40
|
+
|
41
|
+
Written.Parsers.Block.register Paragraph, true
|
42
|
+
|
43
|
+
prototype = Object.create(HTMLParagraphElement.prototype)
|
44
|
+
|
45
|
+
prototype.getRange = (offset, walker) ->
|
46
|
+
range = document.createRange()
|
47
|
+
|
48
|
+
if !@firstChild?
|
49
|
+
range.setStart(this, 0)
|
50
|
+
else
|
51
|
+
while walker.nextNode()
|
52
|
+
if walker.currentNode.length < offset
|
53
|
+
offset -= walker.currentNode.length
|
54
|
+
continue
|
55
|
+
|
56
|
+
range.setStart(walker.currentNode, offset)
|
57
|
+
break
|
58
|
+
|
59
|
+
range.collapse(true)
|
60
|
+
range
|
61
|
+
|
62
|
+
prototype.toString = ->
|
63
|
+
@textContent
|
9
64
|
|
10
|
-
|
65
|
+
document.registerElement('written-p', {
|
66
|
+
prototype: prototype
|
67
|
+
extends: 'p'
|
68
|
+
})
|
@@ -0,0 +1,92 @@
|
|
1
|
+
class Quote
|
2
|
+
multiline: true
|
3
|
+
|
4
|
+
constructor: (match) ->
|
5
|
+
@matches = [match]
|
6
|
+
@opened = true
|
7
|
+
|
8
|
+
accepts: (text) ->
|
9
|
+
@opened = Quote.rule.test(text)
|
10
|
+
|
11
|
+
append: (text) ->
|
12
|
+
@matches.push(Quote.rule.exec(text))
|
13
|
+
|
14
|
+
processContent: (callback) =>
|
15
|
+
if @content?
|
16
|
+
throw "Content Error: The content was already processed"
|
17
|
+
return
|
18
|
+
|
19
|
+
lines = @matches.map (match) ->
|
20
|
+
match[2]
|
21
|
+
|
22
|
+
@content = callback(lines)
|
23
|
+
|
24
|
+
identical: (current, rendered) ->
|
25
|
+
current.outerHTML == rendered.outerHTML
|
26
|
+
|
27
|
+
markdown: =>
|
28
|
+
node = "<blockquote is='written-blockquote'></blockquote>".toHTML()
|
29
|
+
for line, index in @content
|
30
|
+
p = "<p>".toHTML()
|
31
|
+
p.appendChild(document.createTextNode(@matches[index][1]))
|
32
|
+
|
33
|
+
for text in line
|
34
|
+
if text.markdown?
|
35
|
+
p.appendChild(text.markdown())
|
36
|
+
else
|
37
|
+
p.appendChild(document.createTextNode(text.toString()))
|
38
|
+
|
39
|
+
if index < @content.length - 1
|
40
|
+
p.insertAdjacentHTML('beforeend', '\n')
|
41
|
+
node.appendChild(p)
|
42
|
+
|
43
|
+
node
|
44
|
+
|
45
|
+
html: =>
|
46
|
+
node = "<blockquote></blockquote>".toHTML()
|
47
|
+
for line, index in @content
|
48
|
+
p = "<p>".toHTML()
|
49
|
+
|
50
|
+
for text in line
|
51
|
+
if text.html?
|
52
|
+
p.appendChild(text.html())
|
53
|
+
else
|
54
|
+
p.appendChild(document.createTextNode(text.toString()))
|
55
|
+
|
56
|
+
if index < @content.length - 1
|
57
|
+
p.insertAdjacentHTML('beforeend', '\n')
|
58
|
+
node.appendChild(p)
|
59
|
+
|
60
|
+
node
|
61
|
+
|
62
|
+
|
63
|
+
Quote.rule = /^(>\s)(.*)/i
|
64
|
+
|
65
|
+
Written.Parsers.Block.register Quote
|
66
|
+
|
67
|
+
prototype = Object.create(HTMLQuoteElement.prototype)
|
68
|
+
|
69
|
+
prototype.getRange = (offset, walker) ->
|
70
|
+
range = document.createRange()
|
71
|
+
|
72
|
+
if !@firstChild?
|
73
|
+
range.setStart(this, 0)
|
74
|
+
else
|
75
|
+
while walker.nextNode()
|
76
|
+
if walker.currentNode.length < offset
|
77
|
+
offset -= walker.currentNode.length
|
78
|
+
continue
|
79
|
+
|
80
|
+
range.setStart(walker.currentNode, offset)
|
81
|
+
break
|
82
|
+
|
83
|
+
range.collapse(true)
|
84
|
+
range
|
85
|
+
|
86
|
+
prototype.toString = ->
|
87
|
+
@textContent
|
88
|
+
|
89
|
+
document.registerElement('written-blockquote', {
|
90
|
+
prototype: prototype
|
91
|
+
extends: 'blockquote'
|
92
|
+
})
|
@@ -1,17 +1,98 @@
|
|
1
1
|
class UList
|
2
|
-
|
2
|
+
multiline: true
|
3
|
+
|
3
4
|
constructor: (match) ->
|
4
|
-
@
|
5
|
-
|
5
|
+
@matches = [match]
|
6
|
+
|
7
|
+
accepts: (text) ->
|
8
|
+
@opened = UList.rule.test(text)
|
9
|
+
|
10
|
+
@opened
|
11
|
+
|
12
|
+
append: (text) ->
|
13
|
+
@matches.push(UList.rule.exec(text))
|
14
|
+
|
15
|
+
processContent: (callback) =>
|
16
|
+
if @content?
|
17
|
+
throw "Content Error: The content was already processed"
|
18
|
+
return
|
19
|
+
|
20
|
+
lines = @matches.map (match) ->
|
21
|
+
match[2]
|
22
|
+
|
23
|
+
@content = callback(lines)
|
24
|
+
|
25
|
+
identical: (current, rendered) ->
|
26
|
+
current.outerHTML == rendered.outerHTML
|
27
|
+
|
28
|
+
markdown: =>
|
29
|
+
node = "<ul is='written-ul'></ul>".toHTML()
|
30
|
+
for line, index in @content
|
31
|
+
li = "<li is='written-li'>".toHTML()
|
32
|
+
li.appendChild(document.createTextNode(@matches[index][1]))
|
33
|
+
|
34
|
+
for text in line
|
35
|
+
if text.markdown?
|
36
|
+
li.appendChild(text.markdown())
|
37
|
+
else
|
38
|
+
li.appendChild(document.createTextNode(text.toString()))
|
39
|
+
|
40
|
+
node.appendChild(li)
|
41
|
+
|
42
|
+
node
|
43
|
+
|
44
|
+
html: =>
|
45
|
+
node = "<ul></ul>".toHTML()
|
46
|
+
for line, index in @content
|
47
|
+
li = "<li>".toHTML()
|
48
|
+
|
49
|
+
for text in line
|
50
|
+
if text.html?
|
51
|
+
li.appendChild(text.html())
|
52
|
+
else
|
53
|
+
li.appendChild(document.createTextNode(text.toString()))
|
54
|
+
|
55
|
+
node.appendChild(li)
|
56
|
+
|
57
|
+
node
|
58
|
+
|
59
|
+
UList.rule = /^(-\s)(.*)/i
|
60
|
+
|
61
|
+
Written.Parsers.Block.register UList
|
62
|
+
|
63
|
+
prototype = Object.create(HTMLUListElement.prototype)
|
64
|
+
|
65
|
+
prototype.toString = ->
|
66
|
+
texts = Array.prototype.slice.call(@children).map (li) ->
|
67
|
+
li.toString()
|
68
|
+
texts.join("\n")
|
69
|
+
|
70
|
+
prototype.getRange = (offset, walker) ->
|
71
|
+
range = document.createRange()
|
72
|
+
if !@firstChild?
|
73
|
+
range.setStart(this, 0)
|
74
|
+
return
|
75
|
+
|
76
|
+
li = this.firstElementChild
|
77
|
+
|
78
|
+
while walker.nextNode()
|
79
|
+
if !li.contains(walker.currentNode)
|
80
|
+
newList = walker.currentNode
|
81
|
+
while newList? && !(newList instanceof HTMLLIElement)
|
82
|
+
newList = newList.parentElement
|
83
|
+
li = newList
|
84
|
+
offset--
|
6
85
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
86
|
+
if walker.currentNode.length < offset
|
87
|
+
offset -= walker.currentNode.length
|
88
|
+
continue
|
89
|
+
range.setStart(walker.currentNode, offset)
|
90
|
+
break
|
12
91
|
|
13
|
-
|
14
|
-
|
15
|
-
@node
|
92
|
+
range.collapse(true)
|
93
|
+
range
|
16
94
|
|
17
|
-
|
95
|
+
document.registerElement('written-ul', {
|
96
|
+
prototype: prototype
|
97
|
+
extends: 'ul'
|
98
|
+
})
|
@@ -0,0 +1,81 @@
|
|
1
|
+
class @Written.Parsers.Inline
|
2
|
+
constructor: ->
|
3
|
+
@parsers = []
|
4
|
+
|
5
|
+
freeze: ->
|
6
|
+
Object.freeze(this)
|
7
|
+
Object.freeze(@parsers)
|
8
|
+
|
9
|
+
use: (names) ->
|
10
|
+
if typeof names == 'string' && names == 'all'
|
11
|
+
@parsers = Written.Parsers.Inline.parsers.available.slice()
|
12
|
+
return this
|
13
|
+
|
14
|
+
if typeof names == 'string'
|
15
|
+
names = names.split(',').map (name) ->
|
16
|
+
name.trim()
|
17
|
+
|
18
|
+
parser = Written.Parsers.Inline.parsers.available.find (parser) ->
|
19
|
+
names.contains(parser.name)
|
20
|
+
|
21
|
+
if !parser?
|
22
|
+
throw "Couldn't find parser #{name}."
|
23
|
+
return this
|
24
|
+
|
25
|
+
@parsers.push parser
|
26
|
+
return this
|
27
|
+
|
28
|
+
parse: (text) =>
|
29
|
+
if Array.isArray(text)
|
30
|
+
return text.map(@parse)
|
31
|
+
|
32
|
+
parsers = []
|
33
|
+
for p in @parsers
|
34
|
+
if text.length is 0
|
35
|
+
break
|
36
|
+
|
37
|
+
p.rule.lastIndex = 0
|
38
|
+
|
39
|
+
while match = p.rule.exec(text)
|
40
|
+
parser = new p(match)
|
41
|
+
parsers[parser.index()] = parser
|
42
|
+
|
43
|
+
@merge(parsers, text)
|
44
|
+
|
45
|
+
|
46
|
+
merge: (parsers, text) ->
|
47
|
+
content = []
|
48
|
+
buffer = ''
|
49
|
+
index = 0
|
50
|
+
|
51
|
+
while text[index]?
|
52
|
+
if parser = parsers[index]
|
53
|
+
content.push buffer.slice(0)
|
54
|
+
content.push parser
|
55
|
+
buffer = ''
|
56
|
+
index += parser.length()
|
57
|
+
else
|
58
|
+
buffer += text[index]
|
59
|
+
index += 1
|
60
|
+
|
61
|
+
if buffer.length > 0
|
62
|
+
content.push buffer
|
63
|
+
|
64
|
+
content
|
65
|
+
|
66
|
+
splice: (text, parser) ->
|
67
|
+
start = parser.index()
|
68
|
+
end = start + parser.length()
|
69
|
+
text.slice(0, start) + text.slice(end, text.length)
|
70
|
+
|
71
|
+
|
72
|
+
@Written.Parsers.Inline.get = (name) ->
|
73
|
+
Written.Parsers.Inline.parsers.available.find (p) ->
|
74
|
+
p.name.localeCompare(name) == 0
|
75
|
+
|
76
|
+
@Written.Parsers.Inline.parsers = {
|
77
|
+
available: []
|
78
|
+
}
|
79
|
+
|
80
|
+
@Written.Parsers.Inline.register = (parser) ->
|
81
|
+
Written.Parsers.Inline.parsers.available.push(parser)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class Code
|
2
|
+
constructor: (match) ->
|
3
|
+
@match = match
|
4
|
+
|
5
|
+
index: =>
|
6
|
+
@match.index
|
7
|
+
|
8
|
+
length: =>
|
9
|
+
@match[0].length
|
10
|
+
|
11
|
+
markdown: =>
|
12
|
+
node = "<code is='written-code'>#{this.match[0]}</code>".toHTML()
|
13
|
+
if @match[3]?
|
14
|
+
node.classList.add("language-#{@match[3]}")
|
15
|
+
|
16
|
+
if @highlight?
|
17
|
+
@highlight(node)
|
18
|
+
node
|
19
|
+
|
20
|
+
html: =>
|
21
|
+
node = "<code>#{this.match[4]}</code>".toHTML()
|
22
|
+
if @match[3]?
|
23
|
+
node.classList.add("language-#{@match[3]}")
|
24
|
+
|
25
|
+
node
|
26
|
+
|
27
|
+
|
28
|
+
Code.rule = /((~{3})([a-z]+)?)\s(.+)?(~{3})/gi
|
29
|
+
|
30
|
+
Written.Parsers.Inline.register Code
|
31
|
+
|
32
|
+
prototype = Object.create(HTMLElement.prototype)
|
33
|
+
|
34
|
+
prototype.getRange = (offset, walker) ->
|
35
|
+
range = document.createRange()
|
36
|
+
|
37
|
+
if !@firstChild?
|
38
|
+
range.setStart(this, 0)
|
39
|
+
else
|
40
|
+
while walker.nextNode()
|
41
|
+
if walker.currentNode.length < offset
|
42
|
+
offset -= walker.currentNode.length
|
43
|
+
continue
|
44
|
+
|
45
|
+
range.setStart(walker.currentNode, offset)
|
46
|
+
break
|
47
|
+
|
48
|
+
range.collapse(true)
|
49
|
+
range
|
50
|
+
|
51
|
+
prototype.toString = ->
|
52
|
+
@textContent
|
53
|
+
|
54
|
+
document.registerElement('written-code', {
|
55
|
+
prototype: prototype
|
56
|
+
extends: 'code'
|
57
|
+
})
|
@@ -1,13 +1,46 @@
|
|
1
1
|
class Italic
|
2
2
|
constructor: (match) ->
|
3
3
|
@match = match
|
4
|
-
@node = "<em>".toHTML()
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
italic.splitText(@match[2].length)
|
9
|
-
@node.appendChild(document.createTextNode(@match[2]))
|
10
|
-
textNode.parentElement.replaceChild(@node, italic)
|
5
|
+
index: =>
|
6
|
+
@match.index
|
11
7
|
|
12
|
-
|
8
|
+
length: =>
|
9
|
+
@match[0].length
|
13
10
|
|
11
|
+
markdown: =>
|
12
|
+
"<em is='written-em'>#{this.match[1]}</em>".toHTML()
|
13
|
+
|
14
|
+
html: ->
|
15
|
+
"<em>#{this.match[3]}</em>".toHTML()
|
16
|
+
|
17
|
+
Italic.rule = /((_{1})([^_]+)(_{1}))/gi
|
18
|
+
|
19
|
+
Written.Parsers.Inline.register Italic
|
20
|
+
|
21
|
+
prototype = Object.create(HTMLElement.prototype)
|
22
|
+
|
23
|
+
prototype.getRange = (offset, walker) ->
|
24
|
+
range = document.createRange()
|
25
|
+
|
26
|
+
if !@firstChild?
|
27
|
+
range.setStart(this, 0)
|
28
|
+
else
|
29
|
+
while walker.nextNode()
|
30
|
+
if walker.currentNode.length < offset
|
31
|
+
offset -= walker.currentNode.length
|
32
|
+
continue
|
33
|
+
|
34
|
+
range.setStart(walker.currentNode, offset)
|
35
|
+
break
|
36
|
+
|
37
|
+
range.collapse(true)
|
38
|
+
range
|
39
|
+
|
40
|
+
prototype.toString = ->
|
41
|
+
@textContent
|
42
|
+
|
43
|
+
document.registerElement('written-em', {
|
44
|
+
prototype: prototype
|
45
|
+
extends: 'em'
|
46
|
+
})
|