written 0.0.5 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/LICENSE +8 -0
  4. data/README.md +63 -0
  5. data/Rakefile +17 -27
  6. data/lib/written.rb +0 -8
  7. data/lib/written/app/assets/images/written/placeholder.png +0 -0
  8. data/lib/written/app/assets/javascripts/written.coffee +2 -0
  9. data/lib/written/app/assets/javascripts/written/core/content.coffee +53 -35
  10. data/lib/written/app/assets/javascripts/written/core/cursor.coffee +33 -12
  11. data/lib/written/app/assets/javascripts/written/core/document.coffee +16 -11
  12. data/lib/written/app/assets/javascripts/written/core/extensions/string.coffee +9 -0
  13. data/lib/written/app/assets/javascripts/written/core/extensions/text.coffee +2 -0
  14. data/lib/written/app/assets/javascripts/written/core/history.coffee +2 -0
  15. data/lib/written/app/assets/javascripts/written/core/observer.coffee +6 -2
  16. data/lib/written/app/assets/javascripts/written/core/stringify.coffee +15 -0
  17. data/lib/written/app/assets/javascripts/written/parsers/block.coffee +69 -0
  18. data/lib/written/app/assets/javascripts/written/parsers/block/code.coffee +79 -15
  19. data/lib/written/app/assets/javascripts/written/parsers/block/heading.coffee +60 -5
  20. data/lib/written/app/assets/javascripts/written/parsers/block/image.coffee +103 -9
  21. data/lib/written/app/assets/javascripts/written/parsers/block/olist.coffee +94 -12
  22. data/lib/written/app/assets/javascripts/written/parsers/block/paragraph.coffee +63 -5
  23. data/lib/written/app/assets/javascripts/written/parsers/block/quote.coffee +92 -0
  24. data/lib/written/app/assets/javascripts/written/parsers/block/ulist.coffee +93 -12
  25. data/lib/written/app/assets/javascripts/written/parsers/inline.coffee +81 -0
  26. data/lib/written/app/assets/javascripts/written/parsers/inline/code.coffee +57 -0
  27. data/lib/written/app/assets/javascripts/written/parsers/inline/italic.coffee +40 -7
  28. data/lib/written/app/assets/javascripts/written/parsers/inline/link.coffee +43 -13
  29. data/lib/written/app/assets/javascripts/written/parsers/inline/list.coffee +27 -0
  30. data/lib/written/app/assets/javascripts/written/parsers/inline/strong.coffee +41 -7
  31. data/lib/written/app/assets/javascripts/written/parsers/parsers.coffee +21 -107
  32. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/CustomElements.js +32 -0
  33. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/base.js +40 -0
  34. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/boot.js +124 -0
  35. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/observe.js +318 -0
  36. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/register.js +369 -0
  37. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/traverse.js +86 -0
  38. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/upgrade.js +130 -0
  39. data/lib/written/app/assets/javascripts/written/polyfills/MutationObserver/MutationObserver.js +575 -0
  40. data/lib/written/app/assets/javascripts/written/polyfills/WeakMap/WeakMap.js +49 -0
  41. data/lib/written/app/assets/javascripts/written/polyfills/base.coffee +10 -0
  42. data/lib/written/app/assets/javascripts/written/polyfills/dom.js +104 -0
  43. data/lib/written/app/assets/javascripts/written/uploaders/aws.coffee +125 -0
  44. data/lib/written/app/assets/stylesheets/written.scss +80 -11
  45. data/lib/written/version.rb +1 -1
  46. data/test/server/app/assets/javascripts/application.coffee +20 -2
  47. data/test/server/app/assets/stylesheets/application.scss +2 -2
  48. data/test/server/app/assets/stylesheets/prism.css +0 -1
  49. data/test/server/app/views/posts/show.html.erb +10 -3
  50. metadata +26 -20
  51. data/lib/written/app/assets/javascripts/written/core/ext.coffee +0 -109
  52. data/lib/written/app/assets/javascripts/written/core/extensions.coffee +0 -2
  53. data/lib/written/document.rb +0 -42
  54. data/lib/written/node.rb +0 -21
  55. data/lib/written/nodes/code.rb +0 -65
  56. data/lib/written/nodes/heading.rb +0 -15
  57. data/lib/written/nodes/image.rb +0 -14
  58. data/lib/written/nodes/ordered_list.rb +0 -18
  59. data/lib/written/nodes/unordered_list.rb +0 -19
  60. data/lib/written/parsers.rb +0 -11
  61. data/lib/written/parsers/base.rb +0 -26
  62. data/lib/written/parsers/code.rb +0 -60
  63. data/lib/written/parsers/heading.rb +0 -19
  64. data/lib/written/parsers/image.rb +0 -19
  65. data/lib/written/parsers/link.rb +0 -12
  66. data/lib/written/parsers/list.rb +0 -33
  67. data/lib/written/parsers/word.rb +0 -16
@@ -1,16 +1,46 @@
1
1
  class Link
2
2
  constructor: (match) ->
3
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
4
+
5
+ index: =>
6
+ @match.index
7
+
8
+ length: =>
9
+ @match[0].length
10
+
11
+ markdown: =>
12
+ "<a is='written-a' href='javascript:void(0)'><strong>#{@match[1]}</strong>#{@match[3]}</a>".toHTML()
13
+
14
+ html: =>
15
+ "<a href='#{@match[4]}'>#{@match[2]}</a>".toHTML()
16
+
17
+ Link.rule = /!{0}(\[([^\]]+)\])(\(([^\)]+)\))/gi
18
+
19
+ Written.Parsers.Inline.register Link
20
+
21
+ prototype = Object.create(HTMLAnchorElement.prototype)
22
+
23
+ prototype.getRange = (offset, walker) ->
24
+ range = document.createRange()
25
+
26
+ if !@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
33
+
34
+ range.setStart(walker.currentNode, offset)
35
+ break
36
+
37
+ range.collapse(true)
38
+ range
39
+
40
+ prototype.toString = ->
41
+ @textContent
42
+
43
+ document.registerElement('written-a', {
44
+ prototype: prototype
45
+ extends: 'a'
46
+ })
@@ -0,0 +1,27 @@
1
+ prototype = Object.create(HTMLLIElement.prototype)
2
+
3
+ prototype.getRange = (offset, walker) ->
4
+ range = document.createRange()
5
+
6
+ if !@firstChild?
7
+ range.setStart(this, 0)
8
+ else
9
+ while walker.nextNode()
10
+ if walker.currentNode.length < offset
11
+ offset -= walker.currentNode.length
12
+ continue
13
+
14
+ range.setStart(walker.currentNode, offset)
15
+ break
16
+
17
+ range.collapse(true)
18
+ range
19
+
20
+ prototype.toString = ->
21
+ @textContent
22
+
23
+ document.registerElement('written-li', {
24
+ prototype: prototype
25
+ extends: 'li'
26
+ })
27
+
@@ -1,12 +1,46 @@
1
1
  class Strong
2
2
  constructor: (match) ->
3
3
  @match = match
4
- @node = "<strong>".toHTML()
5
4
 
6
- render: (textNode) =>
7
- strong = textNode.splitText(textNode.textContent.indexOf(@match[0]))
8
- strong.splitText(@match[0].length)
9
- @node.appendChild(document.createTextNode(@match[0]))
10
- textNode.parentElement.replaceChild(@node, strong)
5
+ index: =>
6
+ @match.index
11
7
 
12
- Written.Parsers.Inline.register Strong, /((\*{2})[^\*]+(\*{2}))/gi
8
+ length: =>
9
+ @match[0].length
10
+
11
+ markdown: =>
12
+ "<strong is='written-strong'>#{@match[0]}</strong>".toHTML()
13
+
14
+ html: =>
15
+ "<strong>#{@match[3]}</strong>".toHTML()
16
+
17
+ Strong.rule = /((\*{1})([^\*]+)(\*{1}))/gi
18
+
19
+ Written.Parsers.Inline.register Strong
20
+
21
+ prototype = Object.create(HTMLElement.prototype)
22
+
23
+ prototype.getRange = (offset, walker) ->
24
+ range = document.createRange()
25
+
26
+ if !@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
33
+
34
+ range.setStart(walker.currentNode, offset)
35
+ break
36
+
37
+ range.collapse(true)
38
+ range
39
+
40
+ prototype.toString = ->
41
+ @textContent
42
+
43
+ document.registerElement('written-strong', {
44
+ prototype: prototype
45
+ extends: 'strong'
46
+ })
@@ -1,113 +1,27 @@
1
- Written.Parsers = {
2
- freeze: ->
3
- Written.Parsers.Block.freeze()
4
- Written.Parsers.Inline.freeze()
5
- }
6
-
7
- Written.Parsers.Block = new class
8
- constructor: ->
9
- @parsers = []
10
-
11
- freeze: ->
12
- @parsers.push this.defaultParser
13
-
14
- Object.freeze(this)
15
- Object.freeze(@parsers)
16
-
17
- get: (name) ->
18
- parser = @parsers.find (parser) ->
19
- parser.parser.name.localeCompare(name) == 0
20
-
21
- if parser
22
- parser.parser
23
-
24
-
25
-
26
- register: (parser, rule, defaultParser = false) ->
27
- if defaultParser
28
- this.defaultParser = {
29
- rule: rule,
30
- parser: parser
31
- }
32
- else
33
- @parsers.push {
34
- rule: rule,
35
- parser: parser
36
- }
37
-
38
- parse: (lines) =>
39
- elements = []
40
- currentNode = undefined
41
- while (line = lines.pop()) != undefined
42
- str = line.toString()
43
- if !currentNode
44
- parser = @find(str)
45
- currentNode = parser.render(str)
46
- elements.push(currentNode)
47
- continue
48
-
49
- if currentNode.dataset.status != 'opened'
50
- parser = @find(str)
51
- currentNode.nextDocumentNode = parser.render(str)
52
- currentNode = currentNode.nextDocumentNode
53
- currentNode.writtenNodeParser = parser
54
- elements.push(currentNode)
55
- continue
56
- else if currentNode.writtenNodeParser.valid(str)
57
- currentNode.writtenNodeParser.render(str)
58
- continue
59
- else
60
- parser = @find(str)
61
- currentNode.nextDocumentNode = parser.render(str)
62
- currentNode = currentNode.nextDocumentNode
63
- currentNode.writtenNodeParser = parser
64
- elements.push(currentNode)
65
-
66
- elements[0]
67
-
68
- find: (str) ->
69
- parser = undefined
70
- for p in @parsers
71
- if match = p.rule.exec(str)
72
- parser = new p.parser(match)
73
- break
74
-
75
- return parser
76
-
77
-
78
- Written.Parsers.Inline = new class
1
+ class @Written.Parsers
79
2
  constructor: ->
80
- @parsers = []
81
-
82
- freeze: ->
83
- Object.freeze(this)
84
- Object.freeze(@parsers)
3
+ @block = new Written.Parsers.Block()
4
+ @inline = new Written.Parsers.Inline()
85
5
 
86
- get: (name) ->
87
- parser = @parsers.find (parser) ->
88
- parser.parser.name.localeCompare(name) == 0
6
+ use: (type, name) ->
7
+ if type != 'block' && type != 'inline'
8
+ raise 'error: Written.Parsers can either be "block" or "inline"'
9
+ return
89
10
 
90
- if parser
91
- parser.parser
11
+ @[type].use(name)
92
12
 
93
-
94
- register: (parser, rule) ->
95
- @parsers.push {
96
- rule: rule,
97
- parser: parser
13
+ available: ->
14
+ {
15
+ block: Written.Parsers.Block.parsers.available,
16
+ inline: Written.Parsers.Inline.parsers.available
98
17
  }
99
18
 
100
- parse: (block) =>
101
- walker = document.createTreeWalker(block, NodeFilter.SHOW_TEXT)
102
- for p in @parsers
103
- walker.currentNode = walker.root
104
-
105
- while walker.nextNode()
106
- if match = p.rule.exec(walker.currentNode.textContent)
107
- new p.parser(match).render(walker.currentNode)
108
-
109
-
110
- isLeafNode: (node) ->
111
- if node.children.length == 0
112
- return NodeFilter.FILTER_ACCEPT
113
-
19
+ freeze: ->
20
+ @block.freeze()
21
+ @inline.freeze()
22
+
23
+ @Written.Parsers.default = ->
24
+ parsers = new Written.Parsers()
25
+ parsers.use('inline', 'all')
26
+ parsers.use('block', 'all')
27
+ parsers
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
4
+ * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
5
+ * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6
+ * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
7
+ * Code distributed by Google as part of the polymer project is also
8
+ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
9
+ */
10
+ (function() {
11
+
12
+ // Establish polyfill scope. We do this here to store flags. Flags are not
13
+ // supported in the build.
14
+ window.CustomElements = window.CustomElements || {flags:{}};
15
+
16
+ // Flags. Convert url arguments to flags
17
+ var flags = {};
18
+ if (!flags.noOpts) {
19
+ location.search.slice(1).split('&').forEach(function(option) {
20
+ var parts = option.split('=');
21
+ var match;
22
+ if (parts[0] && (match = parts[0].match(/wc-(.+)/))) {
23
+ flags[match[1]] = parts[1] || true;
24
+ }
25
+ });
26
+ }
27
+
28
+
29
+ // exports
30
+ window.CustomElements.flags = flags;
31
+
32
+ })();
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
4
+ * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
5
+ * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6
+ * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
7
+ * Code distributed by Google as part of the polymer project is also
8
+ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
9
+ */
10
+ window.CustomElements = window.CustomElements || {flags:{}};
11
+
12
+ (function(scope) {
13
+
14
+ // imports
15
+ var flags = scope.flags;
16
+
17
+ // world's simplest module initializer
18
+ var modules = [];
19
+ var addModule = function(module) {
20
+ modules.push(module);
21
+ };
22
+
23
+ var initializeModules = function() {
24
+ modules.forEach(function(module) {
25
+ module(scope);
26
+ });
27
+ };
28
+
29
+ // exports
30
+ scope.addModule = addModule;
31
+ scope.initializeModules = initializeModules;
32
+ scope.hasNative = Boolean(document.registerElement);
33
+ scope.isIE = /Trident/.test(navigator.userAgent);
34
+
35
+ // NOTE: For consistent timing, use native custom elements only when not
36
+ // polyfilling other key related web components features.
37
+ scope.useNative = !flags.register && scope.hasNative &&
38
+ !window.ShadowDOMPolyfill && (!window.HTMLImports || window.HTMLImports.useNative);
39
+
40
+ })(window.CustomElements);
@@ -0,0 +1,124 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
4
+ * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
5
+ * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6
+ * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
7
+ * Code distributed by Google as part of the polymer project is also
8
+ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
9
+ */
10
+ (function(scope){
11
+
12
+ // imports
13
+ var useNative = scope.useNative;
14
+ var initializeModules = scope.initializeModules;
15
+
16
+ var isIE = scope.isIE;
17
+
18
+ // If native, setup stub api and bail.
19
+ // NOTE: we fire `WebComponentsReady` under native for api compatibility
20
+ if (useNative) {
21
+ // stub
22
+ var nop = function() {};
23
+
24
+ // exports
25
+ scope.watchShadow = nop;
26
+ scope.upgrade = nop;
27
+ scope.upgradeAll = nop;
28
+ scope.upgradeDocumentTree = nop;
29
+ scope.upgradeSubtree = nop;
30
+ scope.takeRecords = nop;
31
+
32
+ scope.instanceof = function(obj, base) {
33
+ return obj instanceof base;
34
+ };
35
+
36
+ } else {
37
+ // Initialize polyfill modules. Note, polyfill modules are loaded but not
38
+ // executed; this is a convenient way to control which modules run when
39
+ // the polyfill is required and allows the polyfill to load even when it's
40
+ // not needed.
41
+ initializeModules();
42
+ }
43
+
44
+ // imports
45
+ var upgradeDocumentTree = scope.upgradeDocumentTree;
46
+ var upgradeDocument = scope.upgradeDocument;
47
+
48
+ // ShadowDOM polyfill wraps elements but some elements like `document`
49
+ // cannot be wrapped so we help the polyfill by wrapping some elements.
50
+ if (!window.wrap) {
51
+ if (window.ShadowDOMPolyfill) {
52
+ window.wrap = window.ShadowDOMPolyfill.wrapIfNeeded;
53
+ window.unwrap = window.ShadowDOMPolyfill.unwrapIfNeeded;
54
+ } else {
55
+ window.wrap = window.unwrap = function(node) {
56
+ return node;
57
+ };
58
+ }
59
+ }
60
+
61
+ // eagarly upgrade imported documents
62
+ if (window.HTMLImports) {
63
+ window.HTMLImports.__importsParsingHook = function(elt) {
64
+ if (elt.import) {
65
+ upgradeDocument(wrap(elt.import));
66
+ }
67
+ };
68
+ }
69
+
70
+ // bootstrap parsing
71
+ function bootstrap() {
72
+ // one more upgrade to catch out of order registrations
73
+ upgradeDocumentTree(window.wrap(document));
74
+ // install upgrade hook if HTMLImports are available
75
+ // set internal 'ready' flag, now document.registerElement will trigger
76
+ // synchronous upgrades
77
+ window.CustomElements.ready = true;
78
+ // async to ensure *native* custom elements upgrade prior to this
79
+ // DOMContentLoaded can fire before elements upgrade (e.g. when there's
80
+ // an external script)
81
+ // Delay doubly to help workaround
82
+ // https://code.google.com/p/chromium/issues/detail?id=516550.
83
+ // CustomElements must use requestAnimationFrame in attachedCallback
84
+ // to query style/layout data. The WebComponentsReady event is intended
85
+ // to convey overall readiness, which ideally should be after elements
86
+ // are attached. Adding a slight extra delay to WebComponentsReady
87
+ // helps preserve this guarantee.
88
+ var requestAnimationFrame = window.requestAnimationFrame || function(f) {
89
+ setTimeout(f, 16);
90
+ };
91
+ requestAnimationFrame(function() {
92
+ setTimeout(function() {
93
+ // capture blunt profiling data
94
+ window.CustomElements.readyTime = Date.now();
95
+ if (window.HTMLImports) {
96
+ window.CustomElements.elapsed = window.CustomElements.readyTime - window.HTMLImports.readyTime;
97
+ }
98
+ // notify the system that we are bootstrapped
99
+ document.dispatchEvent(
100
+ new CustomEvent('WebComponentsReady', {bubbles: true})
101
+ );
102
+ });
103
+ });
104
+ }
105
+
106
+ // When loading at readyState complete time (or via flag), boot custom elements
107
+ // immediately.
108
+ // If relevant, HTMLImports must already be loaded.
109
+ if (document.readyState === 'complete' || scope.flags.eager) {
110
+ bootstrap();
111
+ // When loading at readyState interactive time, bootstrap only if HTMLImports
112
+ // are not pending. Also avoid IE as the semantics of this state are unreliable.
113
+ } else if (document.readyState === 'interactive' && !window.attachEvent &&
114
+ (!window.HTMLImports || window.HTMLImports.ready)) {
115
+ bootstrap();
116
+ // When loading at other readyStates, wait for the appropriate DOM event to
117
+ // bootstrap.
118
+ } else {
119
+ var loadEvent = window.HTMLImports && !window.HTMLImports.ready ?
120
+ 'HTMLImportsLoaded' : 'DOMContentLoaded';
121
+ window.addEventListener(loadEvent, bootstrap);
122
+ }
123
+
124
+ })(window.CustomElements);