written 0.0.5 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ })