written 0.1.2 → 0.1.3

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