written 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }