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.
- checksums.yaml +4 -4
- data/.babelrc +3 -0
- data/.gitignore +2 -0
- data/.ruby-version +1 -0
- data/README.md +18 -8
- data/Rakefile +1 -0
- data/compile-coffee.js +14 -0
- data/lib/written/app/assets/javascripts/written/attachments/image.coffee +4 -4
- data/lib/written/app/assets/javascripts/written/core/content.coffee +18 -29
- data/lib/written/app/assets/javascripts/written/core/cursor.coffee +8 -7
- data/lib/written/app/assets/javascripts/written/core/document.coffee +9 -8
- data/lib/written/app/assets/javascripts/written/core/{extensions/string.coffee → html.coffee} +3 -2
- data/lib/written/app/assets/javascripts/written/core/parsers.coffee +179 -0
- data/lib/written/app/assets/javascripts/written/parsers/block/code.coffee +11 -28
- data/lib/written/app/assets/javascripts/written/parsers/block/heading.coffee +37 -39
- data/lib/written/app/assets/javascripts/written/parsers/block/image.coffee +15 -39
- data/lib/written/app/assets/javascripts/written/parsers/block/olist.coffee +23 -20
- data/lib/written/app/assets/javascripts/written/parsers/block/paragraph.coffee +14 -34
- data/lib/written/app/assets/javascripts/written/parsers/block/quote.coffee +19 -38
- data/lib/written/app/assets/javascripts/written/parsers/block/ulist.coffee +20 -18
- data/lib/written/app/assets/javascripts/written/parsers/inline/bold.coffee +23 -0
- data/lib/written/app/assets/javascripts/written/parsers/inline/code.coffee +8 -27
- data/lib/written/app/assets/javascripts/written/parsers/inline/italic.coffee +8 -28
- data/lib/written/app/assets/javascripts/written/parsers/inline/link.coffee +8 -27
- data/lib/written/app/assets/stylesheets/written.scss +4 -0
- data/lib/written/version.rb +1 -1
- data/package.json +25 -0
- data/test/javascript/parsers/block/code.js +58 -0
- data/test/javascript/parsers/block/header.js +40 -0
- data/test/javascript/parsers/block/image.js +39 -0
- data/test/javascript/parsers/block/olist.js +41 -0
- data/test/javascript/parsers/block/paragraph.js +42 -0
- data/test/javascript/parsers/block/quote.js +41 -0
- data/test/javascript/parsers/block/ulist.js +40 -0
- data/test/javascript/parsers/inline/code.js +41 -0
- data/test/javascript/parsers/inline/italic.js +42 -0
- data/test/javascript/parsers/inline/link.js +42 -0
- data/test/javascript/parsers/inline/strong.js +41 -0
- data/test/server/app/assets/javascripts/application.coffee +8 -9
- data/test/server/app/assets/javascripts/secret.coffee +6 -0
- data/test/server/app/views/posts/show.html.erb +4 -4
- metadata +21 -12
- data/lib/written/app/assets/javascripts/written/parsers/inline/strong.coffee +0 -43
- data/lib/written/app/assets/javascripts/written/parsers/parsers.coffee +0 -95
- data/test/javascript/assertions/assert.coffee +0 -3
- data/test/javascript/polyfills.coffee +0 -2
- data/test/javascript/polyfills/HTMLULListElement.coffee +0 -0
- data/test/javascript/polyfills/Text.coffee +0 -0
- data/test/javascript/runner.coffee +0 -46
- data/test/javascript/tests/initialization.coffee +0 -16
- data/test/javascript/tests/parsing.coffee +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b65dbcbd89c707b5459e63d6e3126bd5e60dc62
|
4
|
+
data.tar.gz: 18f572157a4c5ba0335de9cfc0b7af2e80ff770e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 966a36308d380f0d119795a462e5d2c1c6c87f1cb6902707df6143ca6ba5f2dab1d60f4f95d9b19c4f301a19ae6cc3ba8afce49f2fca3f48e83e39fa7c89e945
|
7
|
+
data.tar.gz: b8ba365832818f9c86c43eeb628fc6822421257114e68eeb633b713fd7568a5dfb3d91edd48c99921a2390ceb7fee5fff38da17743aa664f75f7167df696a18e
|
data/.babelrc
ADDED
data/.gitignore
CHANGED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.0
|
data/README.md
CHANGED
@@ -16,8 +16,7 @@ The editor also allows you to cherry-pick the markdown feature you wish to suppo
|
|
16
16
|
To start using Written, you just have to create a new editor.
|
17
17
|
|
18
18
|
~~~javascript
|
19
|
-
|
20
|
-
editor.initialize()
|
19
|
+
new Written(document.querySelector('#Editor'))
|
21
20
|
~~~
|
22
21
|
|
23
22
|
You can retrieve the document either as a markdown text, or a HTML string. For storage purposes, you might want to store the HTML string from the current document
|
@@ -34,13 +33,13 @@ Written allows you to enable the parsers you wish. The parsers are split into tw
|
|
34
33
|
The editor needs to be configured before it is initialized. Here's how you customize the parsers.
|
35
34
|
|
36
35
|
~~~javascript
|
37
|
-
var
|
38
|
-
|
39
|
-
|
40
|
-
editor.
|
36
|
+
var parsers = new Written.Parsers({
|
37
|
+
blocks: ['header', 'code', 'ulist', 'olist']
|
38
|
+
})
|
39
|
+
var editor = new Written(document.querySelector('#Editor'), {parsers: parsers})
|
41
40
|
~~~
|
42
41
|
|
43
|
-
|
42
|
+
Not specifying parsers will enable *all parsers*.
|
44
43
|
|
45
44
|
### Document
|
46
45
|
|
@@ -52,7 +51,7 @@ Those documents are then stored in a history that Written then use to implement
|
|
52
51
|
|
53
52
|
### On Change Events
|
54
53
|
|
55
|
-
Written dispatch an event whenever text changes on the Editor. To receive update when the editor change, just add an event listener to
|
54
|
+
Written dispatch an event whenever text changes on the Editor. To receive update when the editor change, just add an event listener to ```written:changed``` event.
|
56
55
|
|
57
56
|
~~~javascript
|
58
57
|
document.addEventListener('written:changed', function(event) {
|
@@ -61,3 +60,14 @@ Written dispatch an event whenever text changes on the Editor. To receive update
|
|
61
60
|
document.toString() // Markdown version
|
62
61
|
})
|
63
62
|
~~~
|
63
|
+
|
64
|
+
## Test the editor locally
|
65
|
+
|
66
|
+
If you want to test the editor on your machine, the easiest way is with [Ruby](httsp://www.ruby-lang.org).
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
$ bundle install
|
70
|
+
$ bundle exec rake server
|
71
|
+
```
|
72
|
+
|
73
|
+
This will launch a server that runs the Editor with on ```localhost:3000```.
|
data/Rakefile
CHANGED
@@ -62,6 +62,7 @@ task :compile do
|
|
62
62
|
Bundler.require(:default, ENV['RAILS_ENV'])
|
63
63
|
|
64
64
|
environment = Sprockets::Environment.new
|
65
|
+
environment.js_compressor = :uglifier
|
65
66
|
|
66
67
|
environment.append_path 'lib/written/app/assets/javascripts'
|
67
68
|
environment.append_path 'lib/written/app/assets/stylesheets'
|
data/compile-coffee.js
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
var coffee = require('coffee-script')
|
2
|
+
var babelJest = require("babel-jest");
|
3
|
+
|
4
|
+
module.exports = {
|
5
|
+
process: function(src, path) {
|
6
|
+
if (coffee.helpers.isCoffee(path)) {
|
7
|
+
return coffee.compile(src, {
|
8
|
+
'bare': true
|
9
|
+
});
|
10
|
+
} else {
|
11
|
+
return babelJest.process(src, path);
|
12
|
+
}
|
13
|
+
}
|
14
|
+
};
|
@@ -6,7 +6,7 @@ class Image extends Written.Attachments.Base
|
|
6
6
|
@node.querySelector('img').addEventListener 'click', @select.bind(this, @node)
|
7
7
|
|
8
8
|
template: ->
|
9
|
-
"<div id='WrittenOverlay' contenteditable=false>
|
9
|
+
Written.toHTML("<div id='WrittenOverlay' contenteditable=false>
|
10
10
|
<div id='WrittenDialog'>
|
11
11
|
<header>
|
12
12
|
<div class='progress'></div>
|
@@ -19,7 +19,7 @@ class Image extends Written.Attachments.Base
|
|
19
19
|
</figure>
|
20
20
|
</div>
|
21
21
|
</div>
|
22
|
-
"
|
22
|
+
")
|
23
23
|
|
24
24
|
select: (node) =>
|
25
25
|
@selection = @getSelection()
|
@@ -65,12 +65,12 @@ class Image extends Written.Attachments.Base
|
|
65
65
|
dialog.classList.add 'failed'
|
66
66
|
dialog.querySelector('h3').textContent = "Failed"
|
67
67
|
dialog.querySelector('figure').remove()
|
68
|
-
dialog.appendChild("
|
68
|
+
dialog.appendChild(Written.toHTML("
|
69
69
|
<section>
|
70
70
|
<p>An error occured while trying to process your image.</p>
|
71
71
|
<button>Close</button>
|
72
72
|
</section>
|
73
|
-
"
|
73
|
+
"))
|
74
74
|
dialog.querySelector('button').addEventListener('click', @cancel)
|
75
75
|
|
76
76
|
upload: (e) =>
|
@@ -1,16 +1,10 @@
|
|
1
1
|
class @Written
|
2
|
-
constructor: (el) ->
|
2
|
+
constructor: (el, options = {}) ->
|
3
3
|
el.instance = this
|
4
4
|
el.dataset.editor = "written"
|
5
5
|
@element = ->
|
6
6
|
return el
|
7
7
|
|
8
|
-
text = @toString()
|
9
|
-
@element().textContent = ''
|
10
|
-
|
11
|
-
@observer = new Written.Observer(@element(), @changed)
|
12
|
-
@initialize = @initialize.bind(this, text)
|
13
|
-
|
14
8
|
@element().addEventListener 'dragover', @over
|
15
9
|
@element().addEventListener('drop', @preventDefaults)
|
16
10
|
|
@@ -19,35 +13,33 @@ class @Written
|
|
19
13
|
@element().addEventListener('keydown', @redo)
|
20
14
|
@element().addEventListener('keydown', @cursor)
|
21
15
|
|
22
|
-
|
23
|
-
preventDefaults: (e) ->
|
24
|
-
e.preventDefault()
|
25
|
-
|
26
|
-
initialize: (text, parsers) ->
|
27
|
-
@observer.pause()
|
16
|
+
parsers = options.parsers
|
28
17
|
if !parsers?
|
29
|
-
parsers = Written.Parsers
|
18
|
+
parsers = new Written.Parsers()
|
30
19
|
|
31
20
|
@parsers = parsers
|
32
21
|
|
22
|
+
text = @toString()
|
23
|
+
@element().textContent = ''
|
24
|
+
|
33
25
|
if @element().contentEditable != 'true'
|
34
26
|
@element().contentEditable = 'true'
|
35
27
|
|
36
|
-
|
37
|
-
|
38
|
-
document.cursor = cursor
|
28
|
+
cursor = new Written.Cursor(@element(), window.getSelection(), @parsers)
|
29
|
+
document = new Written.Document(text, @parsers, cursor)
|
39
30
|
|
40
|
-
|
41
31
|
@render(document)
|
42
32
|
|
43
33
|
document.cursor.focus(document.toString().length)
|
44
34
|
|
45
35
|
@history = new Written.History(document)
|
46
|
-
|
36
|
+
@observer = new Written.Observer(@element(), @changed)
|
47
37
|
@dispatch('written:initialized')
|
48
|
-
@observer.resume()
|
49
38
|
|
50
39
|
|
40
|
+
preventDefaults: (e) ->
|
41
|
+
e.preventDefault()
|
42
|
+
|
51
43
|
dispatch: (name, data = {}) =>
|
52
44
|
event = new CustomEvent(name, bubbles: true, detail: data)
|
53
45
|
@element().dispatchEvent(event)
|
@@ -59,8 +51,8 @@ class @Written
|
|
59
51
|
|
60
52
|
changed: (e) =>
|
61
53
|
oldDocument = @history.current
|
62
|
-
|
63
|
-
newDocument
|
54
|
+
cursor = new Written.Cursor(@element(), window.getSelection(), @parsers)
|
55
|
+
newDocument = new Written.Document(@toString(), @parsers, cursor)
|
64
56
|
if @element().children.length > 0 && oldDocument.toString().localeCompare(newDocument.toString()) == 0
|
65
57
|
return
|
66
58
|
|
@@ -70,14 +62,14 @@ class @Written
|
|
70
62
|
@dispatch('written:changed', document: newDocument)
|
71
63
|
|
72
64
|
cursor: =>
|
73
|
-
@history.current.cursor = new Written.Cursor(@element(), window.getSelection())
|
65
|
+
@history.current.cursor = new Written.Cursor(@element(), window.getSelection(), @parsers)
|
74
66
|
|
75
67
|
linefeed: (e) =>
|
76
68
|
return unless e.which == 13
|
77
69
|
e.preventDefault()
|
78
70
|
e.stopPropagation()
|
79
71
|
|
80
|
-
cursor = new Written.Cursor(@element(), window.getSelection())
|
72
|
+
cursor = new Written.Cursor(@element(), window.getSelection(), @parsers)
|
81
73
|
@observer.pause =>
|
82
74
|
|
83
75
|
offset = cursor.offset
|
@@ -94,8 +86,7 @@ class @Written
|
|
94
86
|
lines.push('')
|
95
87
|
cursor.offset += 1
|
96
88
|
|
97
|
-
document = new Written.Document(lines.join('\n'), @parsers)
|
98
|
-
document.cursor = cursor
|
89
|
+
document = new Written.Document(lines.join('\n'), @parsers, cursor)
|
99
90
|
if cursor.offset < document.toString().length
|
100
91
|
cursor.offset += 1
|
101
92
|
|
@@ -132,10 +123,8 @@ class @Written
|
|
132
123
|
toString: =>
|
133
124
|
texts = []
|
134
125
|
for node in @element().childNodes
|
135
|
-
content =
|
126
|
+
content = @parsers.toString(node).split('\n')
|
136
127
|
texts.push content.join('\n')
|
137
128
|
|
138
129
|
texts.join '\n'
|
139
130
|
|
140
|
-
|
141
|
-
Written.Uploaders = {}
|
@@ -1,8 +1,9 @@
|
|
1
1
|
class Written.Cursor
|
2
|
-
constructor: (element, selection) ->
|
2
|
+
constructor: (element, selection, parsers) ->
|
3
3
|
@element = ->
|
4
4
|
element
|
5
5
|
@selection = selection
|
6
|
+
@parsers = parsers
|
6
7
|
children = Array.prototype.slice.call(@element().children, 0)
|
7
8
|
@offset = selection.focusOffset
|
8
9
|
|
@@ -15,7 +16,7 @@ class Written.Cursor
|
|
15
16
|
child = node.previousSibling
|
16
17
|
|
17
18
|
while child
|
18
|
-
@offset +=
|
19
|
+
@offset += @parsers.toString(child).length
|
19
20
|
child = child.previousSibling
|
20
21
|
|
21
22
|
if node instanceof HTMLLIElement
|
@@ -27,7 +28,7 @@ class Written.Cursor
|
|
27
28
|
for child in @element().children
|
28
29
|
if child == node
|
29
30
|
break
|
30
|
-
@offset +=
|
31
|
+
@offset += @parsers.toString(child).length
|
31
32
|
@offset += 1
|
32
33
|
|
33
34
|
@currentNode = ->
|
@@ -39,7 +40,7 @@ class Written.Cursor
|
|
39
40
|
|
40
41
|
element = @element().firstElementChild
|
41
42
|
while element && element != node
|
42
|
-
offset -=
|
43
|
+
offset -= @parsers.toString(element).length
|
43
44
|
element = element.nextElementSibling
|
44
45
|
|
45
46
|
offset
|
@@ -53,12 +54,12 @@ class Written.Cursor
|
|
53
54
|
if node is undefined
|
54
55
|
node = @element().firstElementChild
|
55
56
|
|
56
|
-
while node.nextElementSibling &&
|
57
|
-
offset -=
|
57
|
+
while node.nextElementSibling && @parsers.toString(node).length < offset
|
58
|
+
offset -= @parsers.toString(node).length + 1
|
58
59
|
node = node.nextElementSibling
|
59
60
|
|
60
61
|
|
61
|
-
range =
|
62
|
+
range = @parsers.getRange(node, Math.min(offset, @parsers.toString(node).length), document.createTreeWalker(node, NodeFilter.SHOW_TEXT))
|
62
63
|
|
63
64
|
if @offsetDiffersBetween(@selection, range)
|
64
65
|
@selection.removeAllRanges()
|
@@ -1,16 +1,17 @@
|
|
1
|
-
#= require
|
1
|
+
#= require ./parsers
|
2
2
|
|
3
3
|
class Written.Document
|
4
|
-
constructor: (text, parsers) ->
|
4
|
+
constructor: (text, parsers, cursor) ->
|
5
|
+
@cursor = cursor
|
5
6
|
@blocks = parsers.parse(parsers.blocks, text)
|
6
7
|
|
7
8
|
@blocks.forEach (block) =>
|
8
|
-
if block.
|
9
|
+
if block.innerText?
|
9
10
|
if block.multiline
|
10
|
-
block.content = block.
|
11
|
+
block.content = block.innerText().split('\n').map (text) ->
|
11
12
|
parsers.parse(parsers.inlines, text)
|
12
13
|
else
|
13
|
-
block.content = parsers.parse(parsers.inlines, block.
|
14
|
+
block.content = parsers.parse(parsers.inlines, block.innerText())
|
14
15
|
|
15
16
|
freeze: =>
|
16
17
|
Object.freeze(@blocks)
|
@@ -20,7 +21,7 @@ class Written.Document
|
|
20
21
|
text = ''
|
21
22
|
|
22
23
|
@blocks.forEach (node) ->
|
23
|
-
text += node.
|
24
|
+
text += node.toHTML().outerHTML + "\n"
|
24
25
|
|
25
26
|
text
|
26
27
|
|
@@ -40,7 +41,7 @@ class Written.Document
|
|
40
41
|
@cursor.focus()
|
41
42
|
|
42
43
|
findNodeFor: (block, remaining) ->
|
43
|
-
node = block.
|
44
|
+
node = block.toEditor()
|
44
45
|
|
45
46
|
found = remaining.find (existing) ->
|
46
47
|
block.equals(existing, node)
|
@@ -52,7 +53,7 @@ class Written.Document
|
|
52
53
|
return @toString.cache
|
53
54
|
|
54
55
|
texts = @blocks.map (block) ->
|
55
|
-
block.
|
56
|
+
block.outerText()
|
56
57
|
|
57
58
|
texts.join('\n')
|
58
59
|
|
@@ -0,0 +1,179 @@
|
|
1
|
+
class @Written.Parsers
|
2
|
+
constructor: (parsers = {}) ->
|
3
|
+
if parsers.blocks?
|
4
|
+
@blocks = Written.Parsers.Blocks.select(parsers.blocks)
|
5
|
+
else
|
6
|
+
@blocks = [Written.Parsers.Blocks.default].concat(Written.Parsers.Blocks)
|
7
|
+
|
8
|
+
if parsers.inlines?
|
9
|
+
@inlines = Written.Parsers.Inlines.select(parsers.inlines)
|
10
|
+
else
|
11
|
+
@inlines = Written.Parsers.Inlines
|
12
|
+
|
13
|
+
@nodes = {}
|
14
|
+
|
15
|
+
for struct in @blocks.concat(@inlines)
|
16
|
+
for node in struct.nodes
|
17
|
+
@nodes[node] = struct
|
18
|
+
|
19
|
+
@blocks.parse = @parseBlocks.bind(this, @blocks)
|
20
|
+
@inlines.parse = @parseInlines.bind(this, @inlines)
|
21
|
+
|
22
|
+
parse: (parsers, text) ->
|
23
|
+
parsers.parse(text)
|
24
|
+
|
25
|
+
parseBlocks: (parsers, text) ->
|
26
|
+
parsers = [parsers.default].concat(parsers).reverse()
|
27
|
+
blocks = []
|
28
|
+
lines = text.split('\n').reverse()
|
29
|
+
while (line = lines.pop()) != undefined
|
30
|
+
str = line
|
31
|
+
block = blocks[blocks.length - 1]
|
32
|
+
|
33
|
+
if block? && block.multiline && block.accepts(line)
|
34
|
+
block.append(line)
|
35
|
+
continue
|
36
|
+
|
37
|
+
blocks.push(@find(parsers, str))
|
38
|
+
|
39
|
+
blocks
|
40
|
+
|
41
|
+
parseInlines: (parsers, text) ->
|
42
|
+
buffer = ''
|
43
|
+
content = []
|
44
|
+
matches = []
|
45
|
+
index = 0
|
46
|
+
|
47
|
+
for struct in parsers
|
48
|
+
struct.rule.lastIndex = 0
|
49
|
+
|
50
|
+
while match = struct.rule.exec(text)
|
51
|
+
parser = new struct.parser(match)
|
52
|
+
matches[parser.index()] = parser
|
53
|
+
|
54
|
+
while text[index]?
|
55
|
+
if parser = matches[index]
|
56
|
+
content.push buffer.slice(0)
|
57
|
+
content.push parser
|
58
|
+
buffer = ''
|
59
|
+
index += parser.length()
|
60
|
+
else
|
61
|
+
buffer += text[index]
|
62
|
+
index += 1
|
63
|
+
|
64
|
+
if buffer.length > 0
|
65
|
+
content.push buffer
|
66
|
+
|
67
|
+
content
|
68
|
+
|
69
|
+
|
70
|
+
find: (parsers, str) ->
|
71
|
+
parser = undefined
|
72
|
+
for struct in parsers
|
73
|
+
if match = struct.rule.exec(str)
|
74
|
+
parser = new struct.parser(match)
|
75
|
+
break
|
76
|
+
|
77
|
+
return parser
|
78
|
+
|
79
|
+
get: (name) ->
|
80
|
+
@nodes[name]
|
81
|
+
|
82
|
+
getRange: (node, offset, walker) ->
|
83
|
+
@nodes[node.nodeName.toLowerCase()].getRange(node, offset, walker)
|
84
|
+
|
85
|
+
toString: (node) ->
|
86
|
+
struct = @nodes[node.nodeName.toLowerCase()]
|
87
|
+
if struct?
|
88
|
+
struct.toString(node)
|
89
|
+
else
|
90
|
+
node.textContent
|
91
|
+
|
92
|
+
Written.Parsers.Blocks = []
|
93
|
+
Written.Parsers.Inlines = []
|
94
|
+
|
95
|
+
Written.Parsers.Blocks.select = (nodes) ->
|
96
|
+
selected = []
|
97
|
+
nodes.map (name) ->
|
98
|
+
struct = Written.Parsers.Blocks.find (struct) ->
|
99
|
+
struct.name == name
|
100
|
+
if struct?
|
101
|
+
selected.push struct
|
102
|
+
|
103
|
+
[Written.Parsers.Blocks.default].concat(selected)
|
104
|
+
|
105
|
+
Written.Parsers.Inlines.select = (nodes) ->
|
106
|
+
selected = []
|
107
|
+
nodes.map (name) ->
|
108
|
+
struct = Written.Parsers.Inlines.find (struct) ->
|
109
|
+
struct.name == name
|
110
|
+
if struct?
|
111
|
+
selected.push struct
|
112
|
+
|
113
|
+
selected
|
114
|
+
|
115
|
+
Written.Parsers.register = (struct) ->
|
116
|
+
type = undefined
|
117
|
+
if struct.type == 'block'
|
118
|
+
type = Written.Parsers.Blocks
|
119
|
+
else if struct.type == 'inline'
|
120
|
+
type = Written.Parsers.Inlines
|
121
|
+
else
|
122
|
+
raise 'error: Written.Parsers can either be "block" or "inline"'
|
123
|
+
return
|
124
|
+
|
125
|
+
Written.Parsers.normalize(struct)
|
126
|
+
if struct.default
|
127
|
+
type.default = struct
|
128
|
+
else
|
129
|
+
type.push struct
|
130
|
+
|
131
|
+
|
132
|
+
Written.Parsers.normalize = (struct) ->
|
133
|
+
if !struct.getRange
|
134
|
+
struct.getRange = (node, offset, walker) ->
|
135
|
+
range = document.createRange()
|
136
|
+
|
137
|
+
if !node.firstChild?
|
138
|
+
range.setStart(node, 0)
|
139
|
+
else
|
140
|
+
while walker.nextNode()
|
141
|
+
if walker.currentNode.length < offset
|
142
|
+
offset -= walker.currentNode.length
|
143
|
+
continue
|
144
|
+
|
145
|
+
range.setStart(walker.currentNode, offset)
|
146
|
+
break
|
147
|
+
|
148
|
+
range.collapse(true)
|
149
|
+
range
|
150
|
+
|
151
|
+
if Object.prototype.toString == struct.toString
|
152
|
+
struct.toString = (node) ->
|
153
|
+
node.textContent
|
154
|
+
|
155
|
+
class Written.Parsers.Block
|
156
|
+
outerText: ->
|
157
|
+
throw "method implementation: #{this.name}.outerText() is missing."
|
158
|
+
|
159
|
+
equals: ->
|
160
|
+
throw "method implementation: #{this.name}.equals(current, rendered) is missing."
|
161
|
+
|
162
|
+
toEditor: ->
|
163
|
+
throw "method implementation: #{this.name}.toEditor() is missing."
|
164
|
+
|
165
|
+
toHTML: ->
|
166
|
+
throw "method implementation: #{this.name}.toHTML() is missing."
|
167
|
+
|
168
|
+
class Written.Parsers.Inline
|
169
|
+
index: ->
|
170
|
+
throw "method implementation: #{this.name}.index() is missing."
|
171
|
+
|
172
|
+
length: ->
|
173
|
+
throw "method implementation: #{this.name}.length() is missing."
|
174
|
+
|
175
|
+
toEditor: ->
|
176
|
+
throw "method implementation: #{this.name}.toEditor() is missing."
|
177
|
+
|
178
|
+
toHTML: ->
|
179
|
+
throw "method implementation: #{this.name}.toHTML() is missing."
|