written 0.0.5 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/LICENSE +8 -0
  4. data/README.md +63 -0
  5. data/Rakefile +17 -27
  6. data/lib/written.rb +0 -8
  7. data/lib/written/app/assets/images/written/placeholder.png +0 -0
  8. data/lib/written/app/assets/javascripts/written.coffee +2 -0
  9. data/lib/written/app/assets/javascripts/written/core/content.coffee +53 -35
  10. data/lib/written/app/assets/javascripts/written/core/cursor.coffee +33 -12
  11. data/lib/written/app/assets/javascripts/written/core/document.coffee +16 -11
  12. data/lib/written/app/assets/javascripts/written/core/extensions/string.coffee +9 -0
  13. data/lib/written/app/assets/javascripts/written/core/extensions/text.coffee +2 -0
  14. data/lib/written/app/assets/javascripts/written/core/history.coffee +2 -0
  15. data/lib/written/app/assets/javascripts/written/core/observer.coffee +6 -2
  16. data/lib/written/app/assets/javascripts/written/core/stringify.coffee +15 -0
  17. data/lib/written/app/assets/javascripts/written/parsers/block.coffee +69 -0
  18. data/lib/written/app/assets/javascripts/written/parsers/block/code.coffee +79 -15
  19. data/lib/written/app/assets/javascripts/written/parsers/block/heading.coffee +60 -5
  20. data/lib/written/app/assets/javascripts/written/parsers/block/image.coffee +103 -9
  21. data/lib/written/app/assets/javascripts/written/parsers/block/olist.coffee +94 -12
  22. data/lib/written/app/assets/javascripts/written/parsers/block/paragraph.coffee +63 -5
  23. data/lib/written/app/assets/javascripts/written/parsers/block/quote.coffee +92 -0
  24. data/lib/written/app/assets/javascripts/written/parsers/block/ulist.coffee +93 -12
  25. data/lib/written/app/assets/javascripts/written/parsers/inline.coffee +81 -0
  26. data/lib/written/app/assets/javascripts/written/parsers/inline/code.coffee +57 -0
  27. data/lib/written/app/assets/javascripts/written/parsers/inline/italic.coffee +40 -7
  28. data/lib/written/app/assets/javascripts/written/parsers/inline/link.coffee +43 -13
  29. data/lib/written/app/assets/javascripts/written/parsers/inline/list.coffee +27 -0
  30. data/lib/written/app/assets/javascripts/written/parsers/inline/strong.coffee +41 -7
  31. data/lib/written/app/assets/javascripts/written/parsers/parsers.coffee +21 -107
  32. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/CustomElements.js +32 -0
  33. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/base.js +40 -0
  34. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/boot.js +124 -0
  35. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/observe.js +318 -0
  36. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/register.js +369 -0
  37. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/traverse.js +86 -0
  38. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/upgrade.js +130 -0
  39. data/lib/written/app/assets/javascripts/written/polyfills/MutationObserver/MutationObserver.js +575 -0
  40. data/lib/written/app/assets/javascripts/written/polyfills/WeakMap/WeakMap.js +49 -0
  41. data/lib/written/app/assets/javascripts/written/polyfills/base.coffee +10 -0
  42. data/lib/written/app/assets/javascripts/written/polyfills/dom.js +104 -0
  43. data/lib/written/app/assets/javascripts/written/uploaders/aws.coffee +125 -0
  44. data/lib/written/app/assets/stylesheets/written.scss +80 -11
  45. data/lib/written/version.rb +1 -1
  46. data/test/server/app/assets/javascripts/application.coffee +20 -2
  47. data/test/server/app/assets/stylesheets/application.scss +2 -2
  48. data/test/server/app/assets/stylesheets/prism.css +0 -1
  49. data/test/server/app/views/posts/show.html.erb +10 -3
  50. metadata +26 -20
  51. data/lib/written/app/assets/javascripts/written/core/ext.coffee +0 -109
  52. data/lib/written/app/assets/javascripts/written/core/extensions.coffee +0 -2
  53. data/lib/written/document.rb +0 -42
  54. data/lib/written/node.rb +0 -21
  55. data/lib/written/nodes/code.rb +0 -65
  56. data/lib/written/nodes/heading.rb +0 -15
  57. data/lib/written/nodes/image.rb +0 -14
  58. data/lib/written/nodes/ordered_list.rb +0 -18
  59. data/lib/written/nodes/unordered_list.rb +0 -19
  60. data/lib/written/parsers.rb +0 -11
  61. data/lib/written/parsers/base.rb +0 -26
  62. data/lib/written/parsers/code.rb +0 -60
  63. data/lib/written/parsers/heading.rb +0 -19
  64. data/lib/written/parsers/image.rb +0 -19
  65. data/lib/written/parsers/link.rb +0 -12
  66. data/lib/written/parsers/list.rb +0 -33
  67. 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
- render: (str) =>
7
- @node.textContent = str
8
- @node
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
- Written.Parsers.Block.register Paragraph, /.*/i, true
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
- rule: /^(-\s)(.+)/i
2
+ multiline: true
3
+
3
4
  constructor: (match) ->
4
- @match = match
5
- @node = "<ul data-status='opened'></ul>".toHTML()
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
- valid: (text) ->
8
- valid = UList::rule.test(text)
9
- if !valid
10
- @node.dataset.status = false
11
- valid
86
+ if walker.currentNode.length < offset
87
+ offset -= walker.currentNode.length
88
+ continue
89
+ range.setStart(walker.currentNode, offset)
90
+ break
12
91
 
13
- render: (text) =>
14
- @node.appendChild("<li>#{text}</li>".toHTML())
15
- @node
92
+ range.collapse(true)
93
+ range
16
94
 
17
- Written.Parsers.Block.register UList, UList::rule
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
- render: (textNode) =>
7
- italic = textNode.splitText(textNode.textContent.indexOf(@match[2]))
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
- Written.Parsers.Inline.register Italic, /(\s|^|\d)(\*{1}([^\*]+)\*{1})/gi
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
+ })