written 0.1.2 → 0.1.3

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/written/app/assets/javascripts/written/core/content.coffee +10 -10
  3. data/lib/written/app/assets/javascripts/written/core/cursor.coffee +6 -6
  4. data/lib/written/app/assets/javascripts/written/core/document.coffee +39 -6
  5. data/lib/written/app/assets/javascripts/written/parsers/block/code.coffee +34 -30
  6. data/lib/written/app/assets/javascripts/written/parsers/block/heading.coffee +28 -30
  7. data/lib/written/app/assets/javascripts/written/parsers/block/image.coffee +30 -49
  8. data/lib/written/app/assets/javascripts/written/parsers/block/olist.coffee +46 -49
  9. data/lib/written/app/assets/javascripts/written/parsers/block/paragraph.coffee +28 -32
  10. data/lib/written/app/assets/javascripts/written/parsers/block/quote.coffee +30 -32
  11. data/lib/written/app/assets/javascripts/written/parsers/block/ulist.coffee +43 -45
  12. data/lib/written/app/assets/javascripts/written/parsers/inline/code.coffee +28 -30
  13. data/lib/written/app/assets/javascripts/written/parsers/inline/italic.coffee +21 -25
  14. data/lib/written/app/assets/javascripts/written/parsers/inline/link.coffee +21 -25
  15. data/lib/written/app/assets/javascripts/written/parsers/inline/strong.coffee +21 -25
  16. data/lib/written/app/assets/javascripts/written/parsers/parsers.coffee +87 -19
  17. data/lib/written/app/assets/javascripts/written.coffee +0 -1
  18. data/lib/written/version.rb +1 -1
  19. data/test/server/app/assets/javascripts/application.coffee +5 -15
  20. metadata +2 -18
  21. data/lib/written/app/assets/javascripts/written/core/extensions/text.coffee +0 -2
  22. data/lib/written/app/assets/javascripts/written/core/stringify.coffee +0 -15
  23. data/lib/written/app/assets/javascripts/written/parsers/block.coffee +0 -69
  24. data/lib/written/app/assets/javascripts/written/parsers/inline/list.coffee +0 -27
  25. data/lib/written/app/assets/javascripts/written/parsers/inline.coffee +0 -81
  26. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/CustomElements.js +0 -32
  27. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/base.js +0 -40
  28. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/boot.js +0 -124
  29. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/observe.js +0 -318
  30. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/register.js +0 -369
  31. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/traverse.js +0 -86
  32. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/upgrade.js +0 -130
  33. data/lib/written/app/assets/javascripts/written/polyfills/MutationObserver/MutationObserver.js +0 -575
  34. data/lib/written/app/assets/javascripts/written/polyfills/WeakMap/WeakMap.js +0 -49
  35. data/lib/written/app/assets/javascripts/written/polyfills/base.coffee +0 -10
  36. data/lib/written/app/assets/javascripts/written/polyfills/dom.js +0 -104
@@ -1,22 +1,20 @@
1
1
  class Paragraph
2
- @parserName: 'Paragraph'
3
2
  multiline: false
4
3
 
5
4
  constructor: (match) ->
6
5
  @match = match
7
6
 
8
- processContent: (callback) =>
9
- if @content?
10
- throw "Content Error: The content was already processed"
11
- return
7
+ equals: (current, rendered) ->
8
+ current.outerHTML == rendered.outerHTML
12
9
 
13
- @content = callback(@match[0])
10
+ text: =>
11
+ @match[0]
14
12
 
15
- identical: (current, rendered) ->
16
- current.outerHTML == rendered.outerHTML
13
+ raw: =>
14
+ @match[0]
17
15
 
18
16
  markdown: =>
19
- node = "<p is='written-p'>".toHTML()
17
+ node = "<p>".toHTML()
20
18
  for text in @content
21
19
  if text.markdown?
22
20
  node.appendChild(text.markdown())
@@ -39,31 +37,29 @@ class Paragraph
39
37
 
40
38
  Paragraph.rule = /.*/i
41
39
 
42
- Written.Parsers.Block.register Paragraph, true
43
-
44
- prototype = Object.create(HTMLParagraphElement.prototype)
45
-
46
- prototype.getRange = (offset, walker) ->
47
- range = document.createRange()
40
+ Written.Parsers.register {
41
+ parser: Paragraph
42
+ node: 'p'
43
+ type: 'block'
44
+ default: true
45
+ getRange: (node, offset, walker) ->
46
+ range = document.createRange()
48
47
 
49
- if !@firstChild?
50
- range.setStart(this, 0)
51
- else
52
- while walker.nextNode()
53
- if walker.currentNode.length < offset
54
- offset -= walker.currentNode.length
55
- continue
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
56
55
 
57
- range.setStart(walker.currentNode, offset)
58
- break
56
+ range.setStart(walker.currentNode, offset)
57
+ break
59
58
 
60
- range.collapse(true)
61
- range
59
+ range.collapse(true)
60
+ range
62
61
 
63
- prototype.toString = ->
64
- @textContent
62
+ toString: (node) ->
63
+ node.textContent
65
64
 
66
- document.registerElement('written-p', {
67
- prototype: prototype
68
- extends: 'p'
69
- })
65
+ }
@@ -1,5 +1,4 @@
1
1
  class Quote
2
- @parserName: 'Quote'
3
2
  multiline: true
4
3
 
5
4
  constructor: (match) ->
@@ -12,21 +11,23 @@ class Quote
12
11
  append: (text) ->
13
12
  @matches.push(Quote.rule.exec(text))
14
13
 
15
- processContent: (callback) =>
16
- if @content?
17
- throw "Content Error: The content was already processed"
18
- return
14
+ raw: ->
15
+ lines = @matches.map (match) ->
16
+ match[0]
17
+
18
+ lines.join('\n')
19
19
 
20
+ text: =>
20
21
  lines = @matches.map (match) ->
21
22
  match[2]
22
-
23
- @content = callback(lines)
24
23
 
25
- identical: (current, rendered) ->
24
+ lines.join('\n')
25
+
26
+ equals: (current, rendered) ->
26
27
  current.outerHTML == rendered.outerHTML
27
28
 
28
29
  markdown: =>
29
- node = "<blockquote is='written-blockquote'></blockquote>".toHTML()
30
+ node = "<blockquote></blockquote>".toHTML()
30
31
  for line, index in @content
31
32
  p = "<p>".toHTML()
32
33
  p.appendChild(document.createTextNode(@matches[index][1]))
@@ -63,31 +64,28 @@ class Quote
63
64
 
64
65
  Quote.rule = /^(>\s)(.*)/i
65
66
 
66
- Written.Parsers.Block.register Quote
67
-
68
- prototype = Object.create(HTMLQuoteElement.prototype)
69
-
70
- prototype.getRange = (offset, walker) ->
71
- range = document.createRange()
67
+ Written.Parsers.register {
68
+ parser: Quote
69
+ node: 'blockquote'
70
+ type: 'block'
71
+ getRange: (node, offset, walker) ->
72
+ range = document.createRange()
72
73
 
73
- if !@firstChild?
74
- range.setStart(this, 0)
75
- else
76
- while walker.nextNode()
77
- if walker.currentNode.length < offset
78
- offset -= walker.currentNode.length
79
- continue
74
+ if !node.firstChild?
75
+ range.setStart(this, 0)
76
+ else
77
+ while walker.nextNode()
78
+ if walker.currentNode.length < offset
79
+ offset -= walker.currentNode.length
80
+ continue
80
81
 
81
- range.setStart(walker.currentNode, offset)
82
- break
82
+ range.setStart(walker.currentNode, offset)
83
+ break
83
84
 
84
- range.collapse(true)
85
- range
85
+ range.collapse(true)
86
+ range
86
87
 
87
- prototype.toString = ->
88
- @textContent
88
+ toString: (node) ->
89
+ node.textContent
89
90
 
90
- document.registerElement('written-blockquote', {
91
- prototype: prototype
92
- extends: 'blockquote'
93
- })
91
+ }
@@ -1,5 +1,4 @@
1
1
  class UList
2
- @parserName: 'UList'
3
2
  multiline: true
4
3
 
5
4
  constructor: (match) ->
@@ -10,26 +9,28 @@ class UList
10
9
 
11
10
  @opened
12
11
 
13
- append: (text) ->
14
- @matches.push(UList.rule.exec(text))
12
+ raw: ->
13
+ texts = @matches.map (match) ->
14
+ match[0]
15
15
 
16
- processContent: (callback) =>
17
- if @content?
18
- throw "Content Error: The content was already processed"
19
- return
16
+ texts.join('\n')
20
17
 
21
- lines = @matches.map (match) ->
18
+ text: ->
19
+ texts = @matches.map (match) ->
22
20
  match[2]
23
-
24
- @content = callback(lines)
25
21
 
26
- identical: (current, rendered) ->
22
+ texts.join('\n')
23
+
24
+ append: (text) ->
25
+ @matches.push(UList.rule.exec(text))
26
+
27
+ equals: (current, rendered) ->
27
28
  current.outerHTML == rendered.outerHTML
28
29
 
29
30
  markdown: =>
30
- node = "<ul is='written-ul'></ul>".toHTML()
31
+ node = "<ul></ul>".toHTML()
31
32
  for line, index in @content
32
- li = "<li is='written-li'>".toHTML()
33
+ li = "<li>".toHTML()
33
34
  li.appendChild(document.createTextNode(@matches[index][1]))
34
35
 
35
36
  for text in line
@@ -59,41 +60,38 @@ class UList
59
60
 
60
61
  UList.rule = /^(-\s)(.*)/i
61
62
 
62
- Written.Parsers.Block.register UList
63
-
64
- prototype = Object.create(HTMLUListElement.prototype)
65
-
66
- prototype.toString = ->
67
- texts = Array.prototype.slice.call(@children).map (li) ->
68
- li.toString()
69
- texts.join("\n")
63
+ Written.Parsers.register {
64
+ parser: UList
65
+ node: 'ul'
66
+ type: 'block'
67
+ getRange: (node, offset, walker) ->
68
+ range = document.createRange()
69
+ if !node.firstChild?
70
+ range.setStart(node, 0)
71
+ return
70
72
 
71
- prototype.getRange = (offset, walker) ->
72
- range = document.createRange()
73
- if !@firstChild?
74
- range.setStart(this, 0)
75
- return
73
+ li = node.firstElementChild
76
74
 
77
- li = this.firstElementChild
75
+ while walker.nextNode()
76
+ if !li.contains(walker.currentNode)
77
+ newList = walker.currentNode
78
+ while newList? && !(newList instanceof HTMLLIElement)
79
+ newList = newList.parentElement
80
+ li = newList
81
+ offset--
78
82
 
79
- while walker.nextNode()
80
- if !li.contains(walker.currentNode)
81
- newList = walker.currentNode
82
- while newList? && !(newList instanceof HTMLLIElement)
83
- newList = newList.parentElement
84
- li = newList
85
- offset--
83
+ if walker.currentNode.length < offset
84
+ offset -= walker.currentNode.length
85
+ continue
86
+ range.setStart(walker.currentNode, offset)
87
+ break
86
88
 
87
- if walker.currentNode.length < offset
88
- offset -= walker.currentNode.length
89
- continue
90
- range.setStart(walker.currentNode, offset)
91
- break
89
+ range.collapse(true)
90
+ range
92
91
 
93
- range.collapse(true)
94
- range
92
+ toString: (node) ->
93
+ texts = Array.prototype.slice.call(node.children).map (li) ->
94
+ li.textContent
95
+ texts.join("\n")
95
96
 
96
- document.registerElement('written-ul', {
97
- prototype: prototype
98
- extends: 'ul'
99
- })
97
+ }
@@ -1,5 +1,4 @@
1
1
  class Code
2
- @parserName: 'Code'
3
2
  constructor: (match) ->
4
3
  @match = match
5
4
 
@@ -10,7 +9,7 @@ class Code
10
9
  @match[0].length
11
10
 
12
11
  markdown: =>
13
- node = "<code is='written-code'>#{this.match[0]}</code>".toHTML()
12
+ node = "<code>#{this.match[0]}</code>".toHTML()
14
13
  if @match[3]?
15
14
  node.classList.add("language-#{@match[3]}")
16
15
 
@@ -28,31 +27,30 @@ class Code
28
27
 
29
28
  Code.rule = /((~{3})([a-z]+)?)\s(.+)?(~{3})/gi
30
29
 
31
- Written.Parsers.Inline.register Code
32
-
33
- prototype = Object.create(HTMLElement.prototype)
34
-
35
- prototype.getRange = (offset, walker) ->
36
- range = document.createRange()
37
-
38
- if !@firstChild?
39
- range.setStart(this, 0)
40
- else
41
- while walker.nextNode()
42
- if walker.currentNode.length < offset
43
- offset -= walker.currentNode.length
44
- continue
45
-
46
- range.setStart(walker.currentNode, offset)
47
- break
48
-
49
- range.collapse(true)
50
- range
51
-
52
- prototype.toString = ->
53
- @textContent
54
-
55
- document.registerElement('written-code', {
56
- prototype: prototype
57
- extends: 'code'
58
- })
30
+ Written.Parsers.register {
31
+ parser: Code
32
+ node: 'code'
33
+ type: 'inline'
34
+ getRange: (node, 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
+ toString: (node) ->
52
+ node.textContent
53
+
54
+ highlightWith: (callback) ->
55
+ Code.prototype.highlight = callback
56
+ }
@@ -1,5 +1,4 @@
1
1
  class Italic
2
- @parserName: 'Italic'
3
2
  constructor: (match) ->
4
3
  @match = match
5
4
 
@@ -10,38 +9,35 @@ class Italic
10
9
  @match[0].length
11
10
 
12
11
  markdown: =>
13
- "<em is='written-em'>#{this.match[1]}</em>".toHTML()
12
+ "<em>#{this.match[1]}</em>".toHTML()
14
13
 
15
14
  html: ->
16
15
  "<em>#{this.match[3]}</em>".toHTML()
17
16
 
18
17
  Italic.rule = /((_{1})([^_]+)(_{1}))/gi
19
18
 
20
- Written.Parsers.Inline.register Italic
21
19
 
22
- prototype = Object.create(HTMLElement.prototype)
20
+ Written.Parsers.register {
21
+ parser: Italic
22
+ node: 'em'
23
+ type: 'inline'
24
+ getRange: (node, offset, walker) ->
25
+ range = document.createRange()
23
26
 
24
- prototype.getRange = (offset, walker) ->
25
- range = document.createRange()
27
+ if !node.firstChild?
28
+ range.setStart(this, 0)
29
+ else
30
+ while walker.nextNode()
31
+ if walker.currentNode.length < offset
32
+ offset -= walker.currentNode.length
33
+ continue
26
34
 
27
- if !@firstChild?
28
- range.setStart(this, 0)
29
- else
30
- while walker.nextNode()
31
- if walker.currentNode.length < offset
32
- offset -= walker.currentNode.length
33
- continue
35
+ range.setStart(walker.currentNode, offset)
36
+ break
34
37
 
35
- range.setStart(walker.currentNode, offset)
36
- break
38
+ range.collapse(true)
39
+ range
37
40
 
38
- range.collapse(true)
39
- range
40
-
41
- prototype.toString = ->
42
- @textContent
43
-
44
- document.registerElement('written-em', {
45
- prototype: prototype
46
- extends: 'em'
47
- })
41
+ toString: (node) ->
42
+ node.textContent
43
+ }
@@ -1,5 +1,4 @@
1
1
  class Link
2
- @parserName: 'Link'
3
2
  constructor: (match) ->
4
3
  @match = match
5
4
 
@@ -10,38 +9,35 @@ class Link
10
9
  @match[0].length
11
10
 
12
11
  markdown: =>
13
- "<a is='written-a' href='javascript:void(0)'><strong>#{@match[1]}</strong>#{@match[3]}</a>".toHTML()
12
+ "<a href='javascript:void(0)'><strong>#{@match[1]}</strong>#{@match[3]}</a>".toHTML()
14
13
 
15
14
  html: =>
16
15
  "<a href='#{@match[4]}'>#{@match[2]}</a>".toHTML()
17
16
 
18
17
  Link.rule = /!{0}(\[([^\]]+)\])(\(([^\)]+)\))/gi
19
18
 
20
- Written.Parsers.Inline.register Link
21
19
 
22
- prototype = Object.create(HTMLAnchorElement.prototype)
20
+ Written.Parsers.register {
21
+ parser: Link
22
+ node: 'a'
23
+ type: 'inline'
24
+ getRange: (node, offset, walker) ->
25
+ range = document.createRange()
23
26
 
24
- prototype.getRange = (offset, walker) ->
25
- range = document.createRange()
27
+ if !node.firstChild?
28
+ range.setStart(this, 0)
29
+ else
30
+ while walker.nextNode()
31
+ if walker.currentNode.length < offset
32
+ offset -= walker.currentNode.length
33
+ continue
26
34
 
27
- if !@firstChild?
28
- range.setStart(this, 0)
29
- else
30
- while walker.nextNode()
31
- if walker.currentNode.length < offset
32
- offset -= walker.currentNode.length
33
- continue
35
+ range.setStart(walker.currentNode, offset)
36
+ break
34
37
 
35
- range.setStart(walker.currentNode, offset)
36
- break
38
+ range.collapse(true)
39
+ range
37
40
 
38
- range.collapse(true)
39
- range
40
-
41
- prototype.toString = ->
42
- @textContent
43
-
44
- document.registerElement('written-a', {
45
- prototype: prototype
46
- extends: 'a'
47
- })
41
+ toString: (node) ->
42
+ node.textContent
43
+ }
@@ -1,5 +1,4 @@
1
1
  class Strong
2
- @parserName: 'Strong'
3
2
  constructor: (match) ->
4
3
  @match = match
5
4
 
@@ -10,38 +9,35 @@ class Strong
10
9
  @match[0].length
11
10
 
12
11
  markdown: =>
13
- "<strong is='written-strong'>#{@match[0]}</strong>".toHTML()
12
+ "<strong>#{@match[0]}</strong>".toHTML()
14
13
 
15
14
  html: =>
16
15
  "<strong>#{@match[3]}</strong>".toHTML()
17
16
 
18
17
  Strong.rule = /((\*{1})([^\*]+)(\*{1}))/gi
19
18
 
20
- Written.Parsers.Inline.register Strong
19
+ Written.Parsers.register {
20
+ parser: Strong
21
+ node: 'strong'
22
+ type: 'inline'
23
+ getRange: (node, offset, walker) ->
24
+ range = document.createRange()
21
25
 
22
- prototype = Object.create(HTMLElement.prototype)
26
+ if !node.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
23
33
 
24
- prototype.getRange = (offset, walker) ->
25
- range = document.createRange()
34
+ range.setStart(walker.currentNode, offset)
35
+ break
26
36
 
27
- if !@firstChild?
28
- range.setStart(this, 0)
29
- else
30
- while walker.nextNode()
31
- if walker.currentNode.length < offset
32
- offset -= walker.currentNode.length
33
- continue
37
+ range.collapse(true)
38
+ range
34
39
 
35
- range.setStart(walker.currentNode, offset)
36
- break
40
+ toString: (node) ->
41
+ node.textContent
37
42
 
38
- range.collapse(true)
39
- range
40
-
41
- prototype.toString = ->
42
- @textContent
43
-
44
- document.registerElement('written-strong', {
45
- prototype: prototype
46
- extends: 'strong'
47
- })
43
+ }
@@ -1,27 +1,95 @@
1
- class @Written.Parsers
1
+ @Written.Parsers = new class
2
2
  constructor: ->
3
- @block = new Written.Parsers.Block()
4
- @inline = new Written.Parsers.Inline()
3
+ @blocks = []
4
+ @inlines = []
5
+ @nodes = {}
5
6
 
6
- use: (type, name) ->
7
- if type != 'block' && type != 'inline'
7
+ @blocks.parse = @parseBlocks.bind(this, @blocks)
8
+ @inlines.parse = @parseInlines.bind(this, @inlines)
9
+
10
+ register: (struct) ->
11
+ type = undefined
12
+ if struct.type == 'block'
13
+ type = @blocks
14
+ else if struct.type == 'inline'
15
+ type = @inlines
16
+ else
8
17
  raise 'error: Written.Parsers can either be "block" or "inline"'
9
18
  return
10
19
 
11
- @[type].use(name)
20
+ if struct.default
21
+ type.default = struct.parser
22
+ else
23
+ type.push struct.parser
24
+
25
+ @nodes[struct.node] = struct
26
+
27
+ parse: (parsers, text) ->
28
+ parsers.parse(text)
29
+
30
+ parseBlocks: (parsers, text) ->
31
+ parsers = [parsers.default].concat(parsers).reverse()
32
+ blocks = []
33
+ lines = text.split('\n').reverse()
34
+ while (line = lines.pop()) != undefined
35
+ str = line
36
+ block = blocks[blocks.length - 1]
37
+
38
+ if block? && block.multiline && block.accepts(line)
39
+ block.append(line)
40
+ continue
41
+
42
+ blocks.push(@find(parsers, str))
43
+
44
+ blocks
45
+
46
+ parseInlines: (parsers, text) ->
47
+ buffer = ''
48
+ content = []
49
+ matches = []
50
+ index = 0
51
+
52
+ for p in parsers
53
+ p.rule.lastIndex = 0
54
+
55
+ while match = p.rule.exec(text)
56
+ parser = new p(match)
57
+ matches[parser.index()] = parser
58
+
59
+ while text[index]?
60
+ if parser = matches[index]
61
+ content.push buffer.slice(0)
62
+ content.push parser
63
+ buffer = ''
64
+ index += parser.length()
65
+ else
66
+ buffer += text[index]
67
+ index += 1
68
+
69
+ if buffer.length > 0
70
+ content.push buffer
71
+
72
+ content
73
+
74
+ find: (parsers, str) ->
75
+ parser = undefined
76
+ for p in parsers
77
+ if match = p.rule.exec(str)
78
+ parser = new p(match)
79
+ break
80
+
81
+ return parser
82
+
83
+ get: (name) ->
84
+ @nodes[name]
12
85
 
13
- available: ->
14
- {
15
- block: Written.Parsers.Block.parsers.available,
16
- inline: Written.Parsers.Inline.parsers.available
17
- }
86
+ getRange: (node, offset, walker) ->
87
+ @nodes[node.nodeName.toLowerCase()].getRange(node, offset, walker)
18
88
 
19
- freeze: ->
20
- @block.freeze()
21
- @inline.freeze()
89
+ toString: (node) ->
90
+ struct = @nodes[node.nodeName.toLowerCase()]
91
+ if struct?
92
+ struct.toString(node)
93
+ else
94
+ node.textContent
22
95
 
23
- @Written.Parsers.default = ->
24
- parsers = new Written.Parsers()
25
- parsers.use('inline', 'all')
26
- parsers.use('block', 'all')
27
- parsers
@@ -1,4 +1,3 @@
1
- #= require ./written/polyfills/base
2
1
  #= require_tree ./written/core
3
2
  #= require_tree ./written/parsers
4
3
  #= require_tree ./written/uploaders
@@ -1,3 +1,3 @@
1
1
  module Written
2
- VERSION = '0.1.2'
2
+ VERSION = '0.1.3'
3
3
  end