written 0.0.2

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 (65) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/Gemfile +15 -0
  4. data/Gemfile.lock +128 -0
  5. data/Rakefile +83 -0
  6. data/lib/written/app/assets/javascripts/vendors/prism.js +1411 -0
  7. data/lib/written/app/assets/javascripts/written/core/content.coffee +106 -0
  8. data/lib/written/app/assets/javascripts/written/core/cursor.coffee +59 -0
  9. data/lib/written/app/assets/javascripts/written/core/document.coffee +19 -0
  10. data/lib/written/app/assets/javascripts/written/core/ext.coffee +109 -0
  11. data/lib/written/app/assets/javascripts/written/core/extensions.coffee +2 -0
  12. data/lib/written/app/assets/javascripts/written/core/history.coffee +16 -0
  13. data/lib/written/app/assets/javascripts/written/core/observer.coffee +29 -0
  14. data/lib/written/app/assets/javascripts/written/extensions/clipboard.coffee +114 -0
  15. data/lib/written/app/assets/javascripts/written/extensions/image.coffee +91 -0
  16. data/lib/written/app/assets/javascripts/written/parsers/block/code.coffee +25 -0
  17. data/lib/written/app/assets/javascripts/written/parsers/block/heading.coffee +10 -0
  18. data/lib/written/app/assets/javascripts/written/parsers/block/image.coffee +18 -0
  19. data/lib/written/app/assets/javascripts/written/parsers/block/olist.coffee +18 -0
  20. data/lib/written/app/assets/javascripts/written/parsers/block/paragraph.coffee +10 -0
  21. data/lib/written/app/assets/javascripts/written/parsers/block/ulist.coffee +17 -0
  22. data/lib/written/app/assets/javascripts/written/parsers/inline/italic.coffee +13 -0
  23. data/lib/written/app/assets/javascripts/written/parsers/inline/link.coffee +16 -0
  24. data/lib/written/app/assets/javascripts/written/parsers/inline/strong.coffee +12 -0
  25. data/lib/written/app/assets/javascripts/written/parsers/parsers.coffee +98 -0
  26. data/lib/written/app/assets/javascripts/written.coffee +4 -0
  27. data/lib/written/app/assets/stylesheets/vendors/prism.css +138 -0
  28. data/lib/written/app/assets/stylesheets/written.scss +21 -0
  29. data/lib/written/document.rb +42 -0
  30. data/lib/written/node.rb +21 -0
  31. data/lib/written/nodes/code.rb +65 -0
  32. data/lib/written/nodes/heading.rb +15 -0
  33. data/lib/written/nodes/image.rb +14 -0
  34. data/lib/written/nodes/ordered_list.rb +18 -0
  35. data/lib/written/nodes/unordered_list.rb +19 -0
  36. data/lib/written/parsers/base.rb +26 -0
  37. data/lib/written/parsers/code.rb +60 -0
  38. data/lib/written/parsers/heading.rb +19 -0
  39. data/lib/written/parsers/image.rb +19 -0
  40. data/lib/written/parsers/link.rb +12 -0
  41. data/lib/written/parsers/list.rb +33 -0
  42. data/lib/written/parsers/word.rb +16 -0
  43. data/lib/written/parsers.rb +11 -0
  44. data/lib/written/railtie.rb +20 -0
  45. data/lib/written/version.rb +3 -0
  46. data/lib/written.rb +14 -0
  47. data/test/javascript/assertions/assert.coffee +3 -0
  48. data/test/javascript/polyfills/HTMLULListElement.coffee +0 -0
  49. data/test/javascript/polyfills/Text.coffee +0 -0
  50. data/test/javascript/polyfills.coffee +2 -0
  51. data/test/javascript/runner.coffee +46 -0
  52. data/test/javascript/tests/initialization.coffee +16 -0
  53. data/test/javascript/tests/parsing.coffee +9 -0
  54. data/test/ruby/blank_test.rb +83 -0
  55. data/test/server/app/assets/javascripts/application.coffee +3 -0
  56. data/test/server/app/assets/stylesheets/application.scss +10 -0
  57. data/test/server/app/controllers/application_controller.rb +2 -0
  58. data/test/server/app/controllers/posts_controller.rb +4 -0
  59. data/test/server/app/views/layouts/application.html.erb +14 -0
  60. data/test/server/app/views/posts/show.html.erb +14 -0
  61. data/test/server/application.rb +12 -0
  62. data/test/server/config.ru +5 -0
  63. data/test/server/log/test.log +570 -0
  64. data/written.gemspec +17 -0
  65. metadata +106 -0
@@ -0,0 +1,106 @@
1
+ class @Written
2
+ constructor: (el) ->
3
+ el.instance = this
4
+ el.dataset.editor = "written"
5
+ @element = ->
6
+ return el
7
+
8
+ @observer = new Written.Observer(@element(), @changed)
9
+ @observer.pause @initialize
10
+
11
+ @element().addEventListener('keypress', @linefeed)
12
+
13
+ cursor = new Written.Cursor(@element(), window.getSelection())
14
+ cursor.focus(0, @element())
15
+
16
+ initialize: =>
17
+ if @parsers?
18
+ return
19
+
20
+ Written.Parsers.freeze()
21
+
22
+ @history = new Written.History(new Written.Document(@toString(), Written.Parsers))
23
+
24
+ node = @history.current().head
25
+
26
+ @element().textContent = ''
27
+
28
+ while node
29
+ @element().appendChild(node)
30
+ node = node.nextDocumentNode
31
+
32
+
33
+ changed: (e) =>
34
+ oldDocument = @history.current()
35
+ newDocument = new Written.Document(@toString())
36
+ if oldDocument.toString().localeCompare(newDocument.toString()) == 0
37
+ return
38
+
39
+ @update(newDocument, new Written.Cursor(@element(), window.getSelection()))
40
+ @history.push(newDocument)
41
+
42
+ update: (document, cursor) =>
43
+ node = document.head
44
+ current = @element().firstElementChild
45
+
46
+ while node
47
+ if !current?
48
+ @element().appendChild(node.cloneNode(true))
49
+ else if node.outerHTML.localeCompare(current.outerHTML) != 0
50
+ clonedNodeDocument = node.cloneNode(true)
51
+ @element().replaceChild(clonedNodeDocument, current)
52
+ current = clonedNodeDocument
53
+
54
+ node = node.nextDocumentNode
55
+ if current?
56
+ current = current.nextElementSibling
57
+
58
+ if current?
59
+ node = current.nextElementSibling
60
+ while node
61
+ nextNode = node.nextElementSibling
62
+ node.remove()
63
+ node = nextNode
64
+
65
+ cursor.focus()
66
+
67
+
68
+ linefeed: (e) =>
69
+ return unless e.which == 13
70
+ e.preventDefault()
71
+ e.stopPropagation()
72
+
73
+ cursor = new Written.Cursor(@element(), window.getSelection())
74
+ @observer.pause =>
75
+
76
+ offset = cursor.offset
77
+ lines = @history.current().toString().split('\n').map (line) ->
78
+ if line.length < offset
79
+ offset -= line.length
80
+ else if offset >= 0
81
+ line = [line.slice(0, offset), '\n', line.slice(offset)].join('')
82
+ offset -= line.length
83
+ offset -= 1
84
+ line
85
+
86
+ if offset == 0
87
+ lines.push('')
88
+ cursor.offset += 1
89
+
90
+ document = new Written.Document(lines.join('\n'))
91
+ if cursor.offset < document.toString().length
92
+ cursor.offset += 1
93
+
94
+ @update(document, cursor)
95
+ @history.push(document)
96
+
97
+
98
+ toString: =>
99
+ texts = []
100
+ for node in @element().childNodes
101
+ content = node.toString().split('\n')
102
+ texts.push content.join('\n')
103
+
104
+ texts.join '\n'
105
+
106
+
@@ -0,0 +1,59 @@
1
+ class Written.Cursor
2
+ constructor: (element, selection) ->
3
+ @element = ->
4
+ element
5
+ @selection = selection
6
+ children = Array.prototype.slice.call(@element().children, 0)
7
+ @offset = selection.focusOffset
8
+
9
+ node = selection.focusNode
10
+
11
+ while node && !children.includes(node)
12
+ parent = node.parentElement
13
+
14
+ if parent
15
+ for child in (parent.childNodes || [])
16
+ if child == node
17
+ break
18
+ else
19
+ @offset += child.textContent.length
20
+
21
+ node = parent
22
+
23
+
24
+ for child in @element().children
25
+ if child == node
26
+ break
27
+ @offset += child.textContent.length
28
+ @offset += 1
29
+
30
+ @currentNode = ->
31
+ node
32
+
33
+
34
+ offsetAt: (node) ->
35
+ offset = @offset
36
+
37
+ element = @element().firstElementChild
38
+ while element && element != node
39
+ offset -= element.textContent.length
40
+ element = element.nextElementSibling
41
+
42
+ offset
43
+
44
+ focus: (offset, node) =>
45
+ if offset is undefined
46
+ offset = @offset
47
+
48
+ if node is undefined
49
+ node = @element().firstElementChild
50
+
51
+ while node.nextElementSibling && node.toString().length < offset
52
+ offset -= node.textContent.length + 1
53
+ node = node.nextElementSibling
54
+
55
+
56
+ range = node.getRange(offset, document.createTreeWalker(node, NodeFilter.SHOW_TEXT))
57
+ @selection.removeAllRanges()
58
+ @selection.addRange(range)
59
+
@@ -0,0 +1,19 @@
1
+ #= require ../parsers/parsers
2
+
3
+ Written.Document = class
4
+ constructor: (textContent, parsers) ->
5
+ @texts = new Array()
6
+
7
+ @head = Written.Parsers.Block.parse(textContent.split('\n').reverse())
8
+
9
+ node = @head
10
+
11
+ while node
12
+ Written.Parsers.Inline.parse(node)
13
+ text = node.toString()
14
+ @texts.push(text)
15
+ node = node.nextDocumentNode
16
+
17
+
18
+ toString: =>
19
+ @texts.join('\n')
@@ -0,0 +1,109 @@
1
+ String::toHTML = ->
2
+ el = document.createElement('div')
3
+ el.innerHTML = this
4
+ if el.children.length > 1
5
+ el.children
6
+ else
7
+ el.children[0]
8
+
9
+ Text::toString = ->
10
+ @textContent
11
+
12
+ HTMLOListElement::toString = HTMLUListElement::toString = ->
13
+ texts = Array.prototype.slice.call(@children).map (li) ->
14
+ li.textContent
15
+ texts.join("\n")
16
+
17
+ HTMLDivElement::toString = ->
18
+ if @classList.contains('image')
19
+ @lastChild.textContent
20
+ else
21
+ @textContent
22
+
23
+ HTMLPreElement::toString = ->
24
+ if @textContent[@textContent.length - 1] == '\n'
25
+ @textContent.substr(0, @textContent.length - 1)
26
+ else
27
+ @textContent
28
+
29
+ HTMLElement::toString = ->
30
+ if @nodeName == 'FIGURE'
31
+ (@querySelector('figcaption') || this).textContent
32
+ else
33
+ @textContent
34
+
35
+ HTMLHeadingElement::toString = HTMLParagraphElement::toString = ->
36
+ @textContent
37
+
38
+ HTMLElement::offset = (node, walker) ->
39
+ offset = 0
40
+
41
+ while walker.nextNode()
42
+ if walker.currentNode != node
43
+ offset += walker.currentNode.length
44
+ else
45
+ break
46
+
47
+ offset
48
+
49
+ HTMLOListElement::offset = HTMLUListElement::offset = (node, walker) ->
50
+ offset = 0
51
+ li = this.firstElementChild
52
+
53
+ while walker.nextNode()
54
+ if !li.contains(walker.currentNode)
55
+ newList = walker.currentNode
56
+ while newList? && !(newList instanceof HTMLLIElement)
57
+ newList = newList.parentElement
58
+ li = newList
59
+ offset++
60
+
61
+ if walker.currentNode != node
62
+ offset += walker.currentNode.length
63
+ else
64
+ break
65
+
66
+ offset
67
+
68
+ HTMLElement::getRange = (offset, walker) ->
69
+ range = document.createRange()
70
+
71
+ if !@firstChild?
72
+ range.setStart(this, 0)
73
+ else
74
+ while walker.nextNode()
75
+ if walker.currentNode.length < offset
76
+ offset -= walker.currentNode.length
77
+ continue
78
+
79
+ range.setStart(walker.currentNode, offset)
80
+ break
81
+
82
+ range.collapse(true)
83
+ range
84
+
85
+
86
+ HTMLOListElement::getRange = HTMLUListElement::getRange = (offset, walker) ->
87
+ range = document.createRange()
88
+ if !@firstChild?
89
+ range.setStart(this, 0)
90
+ return
91
+
92
+ li = this.firstElementChild
93
+
94
+ while walker.nextNode()
95
+ if !li.contains(walker.currentNode)
96
+ newList = walker.currentNode
97
+ while newList? && !(newList instanceof HTMLLIElement)
98
+ newList = newList.parentElement
99
+ li = newList
100
+ offset--
101
+
102
+ if walker.currentNode.length < offset
103
+ offset -= walker.currentNode.length
104
+ continue
105
+ range.setStart(walker.currentNode, offset)
106
+ break
107
+
108
+ range.collapse(true)
109
+ range
@@ -0,0 +1,2 @@
1
+ Written.Extensions = []
2
+
@@ -0,0 +1,16 @@
1
+ Written.History = class History
2
+ constructor: (currentDocument) ->
3
+ @documents = [currentDocument]
4
+
5
+ current: =>
6
+ @documents[0]
7
+
8
+ push: (document) =>
9
+ @documents.splice(0, 0, document)
10
+ while @documents.length > 50
11
+ @documents.pop()
12
+
13
+ pop: =>
14
+ documents.pop()
15
+
16
+
@@ -0,0 +1,29 @@
1
+ class Written.Observer
2
+ constructor: (element, callback) ->
3
+ @element = ->
4
+ element
5
+
6
+ @settings = ->
7
+ {
8
+ childList: true
9
+ subtree: true
10
+ characterData: true
11
+ }
12
+
13
+
14
+ @mutations = new MutationObserver(@normalize.bind(this, callback))
15
+ @mutations.observe @element(), @settings()
16
+
17
+ normalize: (callback, events) =>
18
+ @pause =>
19
+ for event in events
20
+ if event.target instanceof HTMLElement
21
+ for br in event.target.querySelectorAll('br')
22
+ br.remove()
23
+
24
+ @pause(callback)
25
+
26
+ pause: (callback) =>
27
+ @mutations.disconnect()
28
+ callback()
29
+ @mutations.observe @element(), @settings()
@@ -0,0 +1,114 @@
1
+ class ClipBoard
2
+ constructor: (editor, hooks) ->
3
+ @editor = editor
4
+ @editor.element().addEventListener 'paste', @paste
5
+
6
+ paste: (e) =>
7
+ e.preventDefault()
8
+ e.stopPropagation()
9
+
10
+ data = e.clipboardData.getData('text/plain')
11
+
12
+ texts = data.split('\n').map (text) ->
13
+ document.createTextNode(text)
14
+ return unless texts.length > 0
15
+
16
+ _sel = window.getSelection()
17
+ sel = {
18
+ type: _sel.type
19
+ }
20
+ position = _sel.anchorNode.compareDocumentPosition(_sel.extentNode)
21
+ if _sel.anchorOffset <= _sel.extentOffset || position == document.DOCUMENT_POSITION_PRECEDING
22
+ sel.anchorNode = _sel.anchorNode
23
+ sel.anchorOffset = _sel.anchorOffset
24
+ sel.extentNode = _sel.extentNode
25
+ sel.extentOffset = _sel.extentOffset
26
+ else
27
+ sel.anchorNode = _sel.extentNode
28
+ sel.anchorOffset = _sel.extentOffset
29
+ sel.extentNode = _sel.anchorNode
30
+ sel.extentOffset = _sel.anchorOffset
31
+
32
+ if sel.anchorNode == @editor.element()
33
+ line = @editor.line()
34
+ @editor.element().appendChild(line)
35
+ sel.anchorNode = sel.extentNode = line
36
+
37
+ @editor.observer.hold =>
38
+ if sel.type == 'Range'
39
+ @replace(texts, sel)
40
+ else
41
+ @insert(texts, sel)
42
+
43
+
44
+ replace: (texts, sel) =>
45
+
46
+ node = sel.extentNode
47
+ line = node
48
+ nodes = [node]
49
+
50
+ while line.parentElement != @editor.element()
51
+ line = line.parentElement
52
+
53
+ offsets = {
54
+ start: sel.anchorOffset
55
+ end: sel.extentOffset
56
+ }
57
+
58
+ offsets.start = @editor.lineOffset(line, sel.anchorNode, offsets.start)
59
+ offsets.end = @editor.lineOffset(line, sel.extentNode, offsets.end)
60
+
61
+ text = texts.map((t) ->
62
+ t.textContent
63
+ ).join()
64
+
65
+
66
+ line.textContent = line.textContent.substr(0, offsets.start) + text + line.textContent.substr(offsets.end)
67
+
68
+ fragment = @editor.parse(@editor.cloneNodesFrom(line))
69
+
70
+ @editor.updateDOM(line, fragment)
71
+
72
+ cursor = new Written.Cursor(offsets.start + text.length)
73
+ cursor.update(@editor.walker(cursor.focus(line)), true)
74
+
75
+
76
+ insert: (texts, sel) =>
77
+ node = sel.anchorNode
78
+ str = node.textContent
79
+ text = texts.map((t) ->
80
+ t.textContent
81
+ ).join("\n")
82
+
83
+ node.textContent = str.substr(0, sel.anchorOffset)
84
+ node.textContent += text
85
+ node.textContent += str.substr(sel.anchorOffset)
86
+
87
+ line = node
88
+ while line.parentElement != @editor.element()
89
+ line = line.parentElement
90
+
91
+ offset = sel.anchorOffset + text.length
92
+ if node != line
93
+ offset += @nodeOffset(node, line)
94
+
95
+ fragment = @editor.parse(@editor.cloneNodesFrom(line))
96
+
97
+ lines = @editor.updateDOM(line, fragment)
98
+
99
+ cursor = new Written.Cursor(offset)
100
+ cursor.update(@editor.walker(cursor.focus(lines[0])), true)
101
+
102
+ nodeOffset: (node, line) ->
103
+ offset = 0
104
+
105
+ for n in line.childNodes
106
+ if n == node
107
+ break
108
+ else if n.contains(node)
109
+ offset += @nodeOffset(node, n)
110
+ break
111
+ else
112
+ offset += n.textContent.length
113
+
114
+ offset
@@ -0,0 +1,91 @@
1
+ class
2
+ loaded: =>
3
+ @element().addEventListener 'dragover', @over
4
+ @element().addEventListener 'dragleave', @cancel
5
+ @element().addEventListener 'drop', @drop
6
+ @on 'click', @container(), @open
7
+ @on 'change', @element().querySelector('input'), @upload
8
+ if @element().querySelector('figcaption').hasAttribute('name')
9
+ @show()
10
+ else
11
+ @placehold()
12
+
13
+ over: (e) =>
14
+ e.preventDefault()
15
+ @element().classList.add 'dropping'
16
+
17
+ cancel: (e) =>
18
+ e.preventDefault()
19
+ @element().classList.remove 'dropping'
20
+
21
+ drop: (e) =>
22
+ e.preventDefault()
23
+ @element().classList.remove 'dropping'
24
+ e.file = e.dataTransfer.files[0]
25
+ @upload(e)
26
+
27
+ show: =>
28
+ img = @retrieve('img')
29
+ img.src = @element().querySelector('figcaption').getAttribute('name')
30
+ @container().appendChild(img)
31
+
32
+ placehold: =>
33
+ placeholder = @retrieve('.placeholder')
34
+ @container().appendChild(placeholder)
35
+
36
+ container: =>
37
+ @container.element ||= @element().querySelector('div[contenteditable=false]')
38
+
39
+ open: =>
40
+ @container().querySelector('input').click()
41
+
42
+ uploaded: (e) =>
43
+ xml = new DOMParser().parseFromString(e.target.response, 'text/xml')
44
+ url = xml.querySelector('Location').textContent
45
+ img = @retrieve('img')
46
+ img.src = url
47
+ progressBar = @retrieve('.progressbar')
48
+ @container().insertBefore(img, progressBar)
49
+ progressBar.remove()
50
+ @retrieve('.placeholder').remove()
51
+ @element().querySelector('figcaption').lastChild.textContent = "(#{url})"
52
+
53
+ upload: (e) =>
54
+ @container().appendChild(@retrieve('.progressbar'))
55
+ unless e.file?
56
+ e.file = e.target.files[0]
57
+ id = PostBody.getAttribute('postid')
58
+ policy = PostBody.getAttribute('policy')
59
+ signature = PostBody.getAttribute('signature')
60
+ bucket = PostBody.getAttribute('bucket')
61
+ namespace = PostBody.getAttribute('namespace')
62
+ access_key = PostBody.getAttribute('access_key')
63
+
64
+ url = "https://s3.amazonaws.com/#{bucket}/"
65
+ dir = [id]
66
+ if namespace?
67
+ dir.splice(0,0, namespace)
68
+
69
+ dir = dir.join '/'
70
+
71
+ data = new FormData()
72
+ data.append 'AWSAccessKeyId', access_key
73
+ data.append 'success_action_status', 201
74
+ data.append 'acl', 'private'
75
+ data.append 'policy', policy
76
+ data.append 'signature', signature
77
+ data.append 'key', "#{dir}/#{e.file.name}"
78
+ data.append 'Content-Type', e.file.type
79
+ data.append 'file', e.file
80
+
81
+ xhr = new XMLHttpRequest()
82
+ xhr.open('POST', url, true)
83
+ xhr.onload = @uploaded
84
+ xhr.onprogress = @progress
85
+ xhr.send(data)
86
+
87
+ progress: (e) =>
88
+ return unless e.lengthComputable
89
+ percentComplete = e.loaded / e.total * 100.0;
90
+ progressBar = @retrieve('.progressbar')
91
+ progressBar.firstElementChild.style.width = "#{percentComplete}%";
@@ -0,0 +1,25 @@
1
+ class Code
2
+ constructor: (match) ->
3
+ @match = match
4
+ @node = "<pre data-status='opened' data-multiline='true'><code as='Written.Code'></code></pre>".toHTML()
5
+
6
+
7
+ valid: ->
8
+ true
9
+
10
+ render: (text) =>
11
+ if @match[3]?
12
+ @node.querySelector('code').classList.add("language-#{@match[3]}")
13
+
14
+ code = @node.querySelector('code')
15
+ code.appendChild(document.createTextNode(text + "\n"))
16
+
17
+ if /(~{3})$/i.test(text)
18
+ @node.dataset.status = 'closed'
19
+
20
+ Prism.highlightElement(code, false)
21
+
22
+ @node
23
+
24
+
25
+ Written.Parsers.Block.register Code, /((~{3})([a-z]+)?)(.+)?/i
@@ -0,0 +1,10 @@
1
+ class Header
2
+ constructor: (match) ->
3
+ @match = match
4
+
5
+ render: (text) =>
6
+ size = @match[1].length
7
+ return "<h#{size}>#{text}</h#{size}>".toHTML()
8
+
9
+
10
+ Written.Parsers.Block.register Header, /^(#{1,6}) /i
@@ -0,0 +1,18 @@
1
+ class Image
2
+ constructor: (match) ->
3
+ @match = match
4
+ @figure = "<figure><div contenteditable='false'><img/></div><figcaption /></figure>".toHTML()
5
+
6
+ render: =>
7
+ caption = @figure.querySelector('figcaption')
8
+ caption.appendChild document.createTextNode(@match[1])
9
+ caption.appendChild document.createTextNode(@match[3])
10
+ if @match[4]?
11
+ caption.setAttribute('name', @match[4])
12
+
13
+ img = @figure.querySelector('img')
14
+ img.src = @match[4]
15
+
16
+ @figure
17
+
18
+ Written.Parsers.Block.register Image, /^(!{1}\[([^\]]+)\])(\(([^\s]+)?\))$/i
@@ -0,0 +1,18 @@
1
+ class OList
2
+ rule: /^(\d\.\s)(.+)/i
3
+ constructor: (match) ->
4
+ @match = match
5
+ @node = "<ol data-status='opened'></ol>".toHTML()
6
+
7
+ valid: (text) ->
8
+ valid = OList::rule.test(text)
9
+ if !valid
10
+ @node.dataset.status = false
11
+ valid
12
+
13
+ render: (text) =>
14
+ @node.appendChild("<li>#{text}</li>".toHTML())
15
+ @node
16
+
17
+ Written.Parsers.Block.register OList, OList::rule
18
+
@@ -0,0 +1,10 @@
1
+ class Paragraph
2
+ constructor: (match) ->
3
+ @match = match
4
+ @node = "<p>".toHTML()
5
+
6
+ render: (str) =>
7
+ @node.textContent = str
8
+ @node
9
+
10
+ Written.Parsers.Block.register Paragraph, /.*/i, true
@@ -0,0 +1,17 @@
1
+ class UList
2
+ rule: /^(-\s)(.+)/i
3
+ constructor: (match) ->
4
+ @match = match
5
+ @node = "<ul data-status='opened'></ul>".toHTML()
6
+
7
+ valid: (text) ->
8
+ valid = UList::rule.test(text)
9
+ if !valid
10
+ @node.dataset.status = false
11
+ valid
12
+
13
+ render: (text) =>
14
+ @node.appendChild("<li>#{text}</li>".toHTML())
15
+ @node
16
+
17
+ Written.Parsers.Block.register UList, UList::rule
@@ -0,0 +1,13 @@
1
+ class Italic
2
+ constructor: (match) ->
3
+ @match = match
4
+ @node = "<em>".toHTML()
5
+
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)
11
+
12
+ Written.Parsers.Inline.register Italic, /(\s|^|\d)(\*{1}([^\*]+)\*{1})/gi
13
+
@@ -0,0 +1,16 @@
1
+ class Link
2
+ constructor: (match) ->
3
+ @match = match
4
+ @node = "<a>".toHTML()
5
+
6
+ render: (textNode) =>
7
+ @node.href = @match[4]
8
+ name = "<strong>".toHTML()
9
+ anchor = textNode.splitText(textNode.textContent.indexOf(@match[0]))
10
+ anchor.splitText(@match[0].length)
11
+ name.textContent = @match[1]
12
+ @node.appendChild(name)
13
+ @node.appendChild(document.createTextNode(@match[3]))
14
+ textNode.parentElement.replaceChild(@node, anchor)
15
+
16
+ Written.Parsers.Inline.register Link, /!{0}(\[([^\]]+)\])(\(([^\)]+)\))/gi