written 0.2.0 → 0.4.0

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/.babelrc +3 -0
  3. data/.gitignore +2 -0
  4. data/.ruby-version +1 -0
  5. data/README.md +18 -8
  6. data/Rakefile +1 -0
  7. data/compile-coffee.js +14 -0
  8. data/lib/written/app/assets/javascripts/written/attachments/image.coffee +4 -4
  9. data/lib/written/app/assets/javascripts/written/core/content.coffee +18 -29
  10. data/lib/written/app/assets/javascripts/written/core/cursor.coffee +8 -7
  11. data/lib/written/app/assets/javascripts/written/core/document.coffee +9 -8
  12. data/lib/written/app/assets/javascripts/written/core/{extensions/string.coffee → html.coffee} +3 -2
  13. data/lib/written/app/assets/javascripts/written/core/parsers.coffee +179 -0
  14. data/lib/written/app/assets/javascripts/written/parsers/block/code.coffee +11 -28
  15. data/lib/written/app/assets/javascripts/written/parsers/block/heading.coffee +37 -39
  16. data/lib/written/app/assets/javascripts/written/parsers/block/image.coffee +15 -39
  17. data/lib/written/app/assets/javascripts/written/parsers/block/olist.coffee +23 -20
  18. data/lib/written/app/assets/javascripts/written/parsers/block/paragraph.coffee +14 -34
  19. data/lib/written/app/assets/javascripts/written/parsers/block/quote.coffee +19 -38
  20. data/lib/written/app/assets/javascripts/written/parsers/block/ulist.coffee +20 -18
  21. data/lib/written/app/assets/javascripts/written/parsers/inline/bold.coffee +23 -0
  22. data/lib/written/app/assets/javascripts/written/parsers/inline/code.coffee +8 -27
  23. data/lib/written/app/assets/javascripts/written/parsers/inline/italic.coffee +8 -28
  24. data/lib/written/app/assets/javascripts/written/parsers/inline/link.coffee +8 -27
  25. data/lib/written/app/assets/stylesheets/written.scss +4 -0
  26. data/lib/written/version.rb +1 -1
  27. data/package.json +25 -0
  28. data/test/javascript/parsers/block/code.js +58 -0
  29. data/test/javascript/parsers/block/header.js +40 -0
  30. data/test/javascript/parsers/block/image.js +39 -0
  31. data/test/javascript/parsers/block/olist.js +41 -0
  32. data/test/javascript/parsers/block/paragraph.js +42 -0
  33. data/test/javascript/parsers/block/quote.js +41 -0
  34. data/test/javascript/parsers/block/ulist.js +40 -0
  35. data/test/javascript/parsers/inline/code.js +41 -0
  36. data/test/javascript/parsers/inline/italic.js +42 -0
  37. data/test/javascript/parsers/inline/link.js +42 -0
  38. data/test/javascript/parsers/inline/strong.js +41 -0
  39. data/test/server/app/assets/javascripts/application.coffee +8 -9
  40. data/test/server/app/assets/javascripts/secret.coffee +6 -0
  41. data/test/server/app/views/posts/show.html.erb +4 -4
  42. metadata +21 -12
  43. data/lib/written/app/assets/javascripts/written/parsers/inline/strong.coffee +0 -43
  44. data/lib/written/app/assets/javascripts/written/parsers/parsers.coffee +0 -95
  45. data/test/javascript/assertions/assert.coffee +0 -3
  46. data/test/javascript/polyfills.coffee +0 -2
  47. data/test/javascript/polyfills/HTMLULListElement.coffee +0 -0
  48. data/test/javascript/polyfills/Text.coffee +0 -0
  49. data/test/javascript/runner.coffee +0 -46
  50. data/test/javascript/tests/initialization.coffee +0 -16
  51. data/test/javascript/tests/parsing.coffee +0 -9
@@ -1,4 +1,4 @@
1
- class Code
1
+ class Code extends Written.Parsers.Block
2
2
  multiline: true
3
3
 
4
4
  constructor: (match) ->
@@ -7,7 +7,7 @@ class Code
7
7
  @opened = true
8
8
 
9
9
 
10
- raw: ->
10
+ outerText: ->
11
11
  texts = @matches.map (m) ->
12
12
  m[0]
13
13
  texts.join('\n')
@@ -16,7 +16,7 @@ class Code
16
16
  @opened
17
17
 
18
18
  append: (text) ->
19
- match = /(~{3})$/i.exec(text)
19
+ match = /^(~{3})$/i.exec(text)
20
20
  if match?
21
21
  @matches.push(match)
22
22
  @opened = false
@@ -30,8 +30,8 @@ class Code
30
30
  equals: (current, rendered) ->
31
31
  current.outerHTML == rendered.outerHTML
32
32
 
33
- markdown: =>
34
- node = "<pre><code></code></pre>".toHTML()
33
+ toEditor: =>
34
+ node = Written.toHTML("<pre><code></code></pre>")
35
35
  code = node.querySelector('code')
36
36
 
37
37
  if @matches[0][3]?
@@ -47,8 +47,8 @@ class Code
47
47
 
48
48
  node
49
49
 
50
- html: =>
51
- node = "<pre><code></code></pre>".toHTML()
50
+ toHTML: =>
51
+ node = Written.toHTML("<pre><code></code></pre>")
52
52
  code = node.querySelector('code')
53
53
 
54
54
  if @matches[0][3]?
@@ -57,33 +57,16 @@ class Code
57
57
  if @matches[0][4]?
58
58
  code.insertAdjacentHTML('beforebegin', "<header>#{@matches[0][4]}</header>")
59
59
 
60
- code.appendChild(document.createTextNode(@content))
60
+ code.appendChild(document.createTextNode(@content.slice(1, -1)))
61
61
 
62
62
  node
63
63
 
64
- Code.rule = /^((~{3})([a-z]+)?)(?:\s(.*))?/i
65
-
66
64
  Written.Parsers.register {
67
65
  parser: Code
68
- node: 'pre'
66
+ name: 'code'
67
+ nodes: ['pre']
69
68
  type: 'block'
70
- getRange: (node, offset, walker) ->
71
- range = document.createRange()
72
-
73
- if !node.firstChild?
74
- range.setStart(node, 0)
75
- else
76
- while walker.nextNode()
77
- if walker.currentNode.length < offset
78
- offset -= walker.currentNode.length
79
- continue
80
-
81
- range.setStart(walker.currentNode, offset)
82
- break
83
-
84
- range.collapse(true)
85
- range
86
-
69
+ rule: /^((~{3})([a-z]+)?)(?:\s(.*))?/i
87
70
  toString: (node) ->
88
71
  if node.textContent[node.textContent.length - 1] == '\n'
89
72
  node.textContent.substr(0, node.textContent.length - 1)
@@ -1,4 +1,4 @@
1
- class Header
1
+ class Header extends Written.Parsers.Block
2
2
  multiline: false
3
3
 
4
4
  constructor: (match) ->
@@ -7,58 +7,56 @@ class Header
7
7
  equals: (current, rendered) ->
8
8
  current.outerHTML == rendered.outerHTML
9
9
 
10
- text: ->
10
+ innerText: ->
11
11
  @match[3]
12
12
 
13
- raw: ->
13
+ outerText: ->
14
14
  @match[0]
15
15
 
16
- markdown: =>
17
- node = "<h#{@match[2].length}>".toHTML()
16
+ toEditor: =>
17
+ node = Written.toHTML("<h#{@match[2].length}>")
18
18
 
19
19
  for text in @content
20
- if text.markdown?
21
- node.appendChild(text.markdown())
20
+ if text.toEditor?
21
+ node.appendChild(text.toEditor())
22
22
  else
23
23
  node.appendChild(document.createTextNode(text))
24
24
 
25
25
  node.insertAdjacentHTML('afterbegin', @match[1])
26
26
  node
27
27
 
28
- html: =>
29
- node = "<h#{@match[2].length}>".toHTML()
28
+ toHTML: =>
29
+ node = Written.toHTML("<h#{@match[2].length}>")
30
30
  for text in @content
31
- if text.html?
32
- node.appendChild(text.html())
31
+ if text.toHTML?
32
+ node.appendChild(text.toHTML())
33
33
  else
34
34
  node.appendChild(document.createTextNode(text))
35
35
  node
36
36
 
37
- Header.rule = /^((#{1,6})\s)(.*)$/i
38
-
39
-
40
- [1,2,3,4,5,6].forEach (size) ->
41
- Written.Parsers.register {
42
- parser: Header
43
- node: "h#{size}"
44
- type: 'block'
45
- getRange: (node, offset, walker) ->
46
- range = document.createRange()
47
-
48
- if !node.firstChild?
49
- range.setStart(node, 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
- toString: (node) ->
63
- node.textContent
64
- }
37
+ Written.Parsers.register {
38
+ parser: Header
39
+ name: 'header'
40
+ nodes: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
41
+ type: 'block'
42
+ rule: /^((#{1,6})\s)(.*)$/i
43
+ getRange: (node, offset, walker) ->
44
+ range = document.createRange()
45
+
46
+ if !node.firstChild?
47
+ range.setStart(node, 0)
48
+ else
49
+ while walker.nextNode()
50
+ if walker.currentNode.length < offset
51
+ offset -= walker.currentNode.length
52
+ continue
53
+
54
+ range.setStart(walker.currentNode, offset)
55
+ break
56
+
57
+ range.collapse(true)
58
+ range
59
+
60
+ toString: (node) ->
61
+ node.textContent
62
+ }
@@ -1,13 +1,13 @@
1
- class Image
1
+ class Image extends Written.Parsers.Block
2
2
  multiline: false
3
3
 
4
4
  constructor: (match) ->
5
5
  @match = match
6
6
 
7
- raw: ->
7
+ outerText: ->
8
8
  @match[0]
9
9
 
10
- text: ->
10
+ innerText: ->
11
11
  @match[2]
12
12
 
13
13
  equals: (current, rendered) ->
@@ -17,14 +17,14 @@ class Image
17
17
  rendered.querySelector('figcaption').outerHTML == figcaption.outerHTML &&
18
18
  rendered.querySelector('img').src == img.src
19
19
 
20
- markdown: =>
21
- figure = "<figure><div contenteditable='false'><img/></div><figcaption /></figure>".toHTML()
20
+ toEditor: =>
21
+ figure = Written.toHTML("<figure><div contenteditable='false'><img/></div><figcaption /></figure>")
22
22
  caption = figure.querySelector('figcaption')
23
23
  container = figure.querySelector('div')
24
24
 
25
25
  for text in @content
26
- if text.markdown?
27
- caption.appendChild(text.markdown())
26
+ if text.toEditor?
27
+ caption.appendChild(text.toEditor())
28
28
  else
29
29
  caption.appendChild(document.createTextNode(text))
30
30
 
@@ -34,25 +34,22 @@ class Image
34
34
 
35
35
  img = figure.querySelector('img')
36
36
  if @match[4]?
37
- img.src = img.dataset.image = @match[4]
37
+ img.src = @match[4]
38
38
  else
39
39
  img.src = '/assets/written/placeholder.png'
40
40
 
41
- if @configure?
42
- @configure(figure)
43
-
44
41
  figure
45
42
 
46
- html: ->
47
- figure = "<figure><div><img/></div><figcaption /></figure>".toHTML()
43
+ toHTML: ->
44
+ figure = Written.toHTML("<figure><img/><figcaption /></figure>")
48
45
  img = figure.querySelector('img')
49
46
  caption = figure.querySelector('figcaption')
50
47
 
51
48
  img.src = @match[4]
52
49
 
53
50
  for text in @content
54
- if text.html?
55
- caption.appendChild(text.html())
51
+ if text.toHTML?
52
+ caption.appendChild(text.toHTML())
56
53
  else
57
54
  caption.appendChild(document.createTextNode(text))
58
55
 
@@ -62,33 +59,12 @@ class Image
62
59
  img.src = '/assets/written/placeholder.png'
63
60
  img.onerror = undefined
64
61
 
65
- Image.rule = /^(!{1}\[([^\]]*)\])(\(([^\s]*)?\))$/i
66
-
67
- Image.uploader = (uploader) ->
68
- Image::configure = uploader.initialize
69
-
70
-
71
62
  Written.Parsers.register {
72
63
  parser: Image
73
- node: 'figure'
64
+ name: 'image'
65
+ nodes: ['figure']
74
66
  type: 'block'
75
- getRange: (node, offset, walker) ->
76
- range = document.createRange()
77
-
78
- if !node.firstChild?
79
- range.setStart(this, 0)
80
- else
81
- while walker.nextNode()
82
- if walker.currentNode.length < offset
83
- offset -= walker.currentNode.length
84
- continue
85
-
86
- range.setStart(walker.currentNode, offset)
87
- break
88
-
89
- range.collapse(true)
90
- range
91
-
67
+ rule: /^(!{1}\[([^\]]*)\])(\(([^\s]*)?\))$/i
92
68
  toString: (node) ->
93
69
  (node.querySelector('figcaption') || node).textContent
94
70
  }
@@ -1,4 +1,6 @@
1
- class OList
1
+ RULE = /^(\d+\.\s)(.*)/i
2
+
3
+ class OList extends Written.Parsers.Block
2
4
  multiline: true
3
5
 
4
6
  constructor: (match) ->
@@ -6,37 +8,38 @@ class OList
6
8
  @opened = true
7
9
 
8
10
  accepts: (text) ->
9
- @opened = OList.rule.test(text)
11
+ @opened = RULE.test(text)
10
12
 
11
13
  @opened
12
14
 
13
15
  append: (text) ->
14
- @matches.push(OList.rule.exec(text))
16
+ @matches.push(RULE.exec(text))
15
17
 
16
18
  equals: (current, rendered) ->
17
19
  current.outerHTML == rendered.outerHTML
18
20
 
19
- raw: ->
21
+ innerText: ->
20
22
  texts = @matches.map (match) ->
21
- match[0]
23
+ match[2]
22
24
 
23
25
  texts.join('\n')
24
26
 
25
- text: ->
27
+ outerText: ->
26
28
  texts = @matches.map (match) ->
27
- match[2]
29
+ match[0]
28
30
 
29
31
  texts.join('\n')
30
32
 
31
- markdown: =>
32
- node = "<ol></ol>".toHTML()
33
+
34
+ toEditor: =>
35
+ node = Written.toHTML("<ol></ol>")
33
36
  for line, index in @content
34
- li = "<li>".toHTML()
37
+ li = Written.toHTML("<li>")
35
38
  li.appendChild(document.createTextNode(@matches[index][1]))
36
39
 
37
40
  for text in line
38
- if text.markdown?
39
- li.appendChild(text.markdown())
41
+ if text.toEditor?
42
+ li.appendChild(text.toEditor())
40
43
  else
41
44
  li.appendChild(document.createTextNode(text.toString()))
42
45
 
@@ -44,14 +47,14 @@ class OList
44
47
 
45
48
  node
46
49
 
47
- html: =>
48
- node = "<ol></ol>".toHTML()
50
+ toHTML: =>
51
+ node = Written.toHTML("<ol></ol>")
49
52
  for line, index in @content
50
- li = "<li>".toHTML()
53
+ li = Written.toHTML("<li>")
51
54
 
52
55
  for text in line
53
- if text.html?
54
- li.appendChild(text.html())
56
+ if text.toHTML?
57
+ li.appendChild(text.toHTML())
55
58
  else
56
59
  li.appendChild(document.createTextNode(text.toString()))
57
60
 
@@ -60,12 +63,12 @@ class OList
60
63
  node
61
64
 
62
65
 
63
- OList.rule = /^(\d+\.\s)(.*)/i
64
-
65
66
  Written.Parsers.register {
66
67
  parser: OList
67
- node: 'ol'
68
+ name: 'olist'
69
+ nodes: ['ol']
68
70
  type: 'block'
71
+ rule: RULE
69
72
  getRange: (node, offset, walker) ->
70
73
  range = document.createRange()
71
74
  if !node.firstChild?
@@ -1,4 +1,4 @@
1
- class Paragraph
1
+ class Paragraph extends Written.Parsers.Block
2
2
  multiline: false
3
3
 
4
4
  constructor: (match) ->
@@ -7,27 +7,27 @@ class Paragraph
7
7
  equals: (current, rendered) ->
8
8
  current.outerHTML == rendered.outerHTML
9
9
 
10
- text: =>
10
+ innerText: =>
11
11
  @match[0]
12
12
 
13
- raw: =>
13
+ outerText: =>
14
14
  @match[0]
15
15
 
16
- markdown: =>
17
- node = "<p>".toHTML()
16
+ toEditor: =>
17
+ node = Written.toHTML("<p>")
18
18
  for text in @content
19
- if text.markdown?
20
- node.appendChild(text.markdown())
19
+ if text.toEditor?
20
+ node.appendChild(text.toEditor())
21
21
  else
22
22
  node.appendChild(document.createTextNode(text))
23
23
 
24
24
  node
25
25
 
26
- html: =>
27
- node = "<p>".toHTML()
26
+ toHTML: =>
27
+ node = Written.toHTML("<p>")
28
28
  for text in @content
29
- if text.html?
30
- node.appendChild(text.html())
29
+ if text.toHTML?
30
+ node.appendChild(text.toHTML())
31
31
  else
32
32
  node.appendChild(document.createTextNode(text))
33
33
 
@@ -35,31 +35,11 @@ class Paragraph
35
35
 
36
36
 
37
37
 
38
- Paragraph.rule = /.*/i
39
-
40
38
  Written.Parsers.register {
41
39
  parser: Paragraph
42
- node: 'p'
40
+ name: 'paragraph'
41
+ nodes: ['p']
43
42
  type: 'block'
43
+ rule: /.*/i
44
44
  default: true
45
- getRange: (node, offset, walker) ->
46
- range = document.createRange()
47
-
48
- if !node.firstChild?
49
- range.setStart(node, 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
- toString: (node) ->
63
- node.textContent
64
-
65
45
  }