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
@@ -0,0 +1,49 @@
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
+
11
+ if (typeof WeakMap === 'undefined') {
12
+ (function() {
13
+ var defineProperty = Object.defineProperty;
14
+ var counter = Date.now() % 1e9;
15
+
16
+ var WeakMap = function() {
17
+ this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__');
18
+ };
19
+
20
+ WeakMap.prototype = {
21
+ set: function(key, value) {
22
+ var entry = key[this.name];
23
+ if (entry && entry[0] === key)
24
+ entry[1] = value;
25
+ else
26
+ defineProperty(key, this.name, {value: [key, value], writable: true});
27
+ return this;
28
+ },
29
+ get: function(key) {
30
+ var entry;
31
+ return (entry = key[this.name]) && entry[0] === key ?
32
+ entry[1] : undefined;
33
+ },
34
+ delete: function(key) {
35
+ var entry = key[this.name];
36
+ if (!entry || entry[0] !== key) return false;
37
+ entry[0] = entry[1] = undefined;
38
+ return true;
39
+ },
40
+ has: function(key) {
41
+ var entry = key[this.name];
42
+ if (!entry) return false;
43
+ return entry[0] === key;
44
+ }
45
+ };
46
+
47
+ window.WeakMap = WeakMap;
48
+ })();
49
+ }
@@ -0,0 +1,10 @@
1
+ #= require './WeakMap/WeakMap'
2
+ #= require './MutationObserver/MutationObserver'
3
+ #= require './dom'
4
+ #= require './CustomElements/CustomElements'
5
+ #= require './CustomElements/base'
6
+ #= require './CustomElements/traverse'
7
+ #= require './CustomElements/observe'
8
+ #= require './CustomElements/upgrade'
9
+ #= require './CustomElements/register'
10
+ #= require './CustomElements/boot'
@@ -0,0 +1,104 @@
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
+
11
+ (function(scope) {
12
+
13
+ 'use strict';
14
+
15
+ // polyfill performance.now
16
+
17
+ if (!window.performance) {
18
+ var start = Date.now();
19
+ // only at millisecond precision
20
+ window.performance = {now: function(){ return Date.now() - start; }};
21
+ }
22
+
23
+ // polyfill for requestAnimationFrame
24
+
25
+ if (!window.requestAnimationFrame) {
26
+ window.requestAnimationFrame = (function() {
27
+ var nativeRaf = window.webkitRequestAnimationFrame ||
28
+ window.mozRequestAnimationFrame;
29
+
30
+ return nativeRaf ?
31
+ function(callback) {
32
+ return nativeRaf(function() {
33
+ callback(performance.now());
34
+ });
35
+ } :
36
+ function( callback ){
37
+ return window.setTimeout(callback, 1000 / 60);
38
+ };
39
+ })();
40
+ }
41
+
42
+ if (!window.cancelAnimationFrame) {
43
+ window.cancelAnimationFrame = (function() {
44
+ return window.webkitCancelAnimationFrame ||
45
+ window.mozCancelAnimationFrame ||
46
+ function(id) {
47
+ clearTimeout(id);
48
+ };
49
+ })();
50
+ }
51
+
52
+ // defaultPrevented is broken in IE.
53
+ // https://connect.microsoft.com/IE/feedback/details/790389/event-defaultprevented-returns-false-after-preventdefault-was-called
54
+ var workingDefaultPrevented = (function() {
55
+ var e = document.createEvent('Event');
56
+ e.initEvent('foo', true, true);
57
+ e.preventDefault();
58
+ return e.defaultPrevented;
59
+ })();
60
+
61
+ if (!workingDefaultPrevented) {
62
+ var origPreventDefault = Event.prototype.preventDefault;
63
+ Event.prototype.preventDefault = function() {
64
+ if (!this.cancelable) {
65
+ return;
66
+ }
67
+
68
+ origPreventDefault.call(this);
69
+
70
+ Object.defineProperty(this, 'defaultPrevented', {
71
+ get: function() {
72
+ return true;
73
+ },
74
+ configurable: true
75
+ });
76
+ };
77
+ }
78
+
79
+ var isIE = /Trident/.test(navigator.userAgent);
80
+
81
+ // CustomEvent constructor shim
82
+ if (!window.CustomEvent || isIE && (typeof window.CustomEvent !== 'function')) {
83
+ window.CustomEvent = function(inType, params) {
84
+ params = params || {};
85
+ var e = document.createEvent('CustomEvent');
86
+ e.initCustomEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable), params.detail);
87
+ return e;
88
+ };
89
+ window.CustomEvent.prototype = window.Event.prototype;
90
+ }
91
+
92
+ // Event constructor shim
93
+ if (!window.Event || isIE && (typeof window.Event !== 'function')) {
94
+ var origEvent = window.Event;
95
+ window.Event = function(inType, params) {
96
+ params = params || {};
97
+ var e = document.createEvent('Event');
98
+ e.initEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable));
99
+ return e;
100
+ };
101
+ window.Event.prototype = origEvent.prototype;
102
+ }
103
+
104
+ })(window.WebComponents);
@@ -0,0 +1,125 @@
1
+ class Written.Uploaders.AWS
2
+ constructor: (settings) ->
3
+ Images.settings = settings
4
+
5
+ initialize: (node, observer) =>
6
+ img = node.querySelector('img')
7
+ container = node.querySelector('div')
8
+
9
+ node.dataset.uploadable = true
10
+ node.addEventListener 'written:uploader:error', @error, true
11
+ node.addEventListener 'written:uploader:uploading', @progress, true
12
+ node.addEventListener 'written:uploader:completed', @uploaded, true
13
+ node.addEventListener 'change', @input.bind(this, node, observer), true
14
+
15
+ container.addEventListener 'click', @open.bind(this, node), true
16
+ image = Images.get(img.dataset.image)
17
+ if image
18
+ image.node = node
19
+
20
+ open: (node, e) =>
21
+ node.querySelector('input')?.click()
22
+
23
+ uploaded: (e) =>
24
+ caption = e.target.querySelector('figcaption')
25
+ img = e.target.querySelector('img')
26
+
27
+ text = caption.childNodes[caption.childNodes.length - 1]
28
+ text.textContent = "(#{e.image.url})"
29
+ img.src = e.image.url
30
+ selection = window.getSelection()
31
+ selection.removeAllRanges()
32
+ range = document.createRange()
33
+ range.setEnd(text, text.textContent.length - 1)
34
+ selection.addRange(range)
35
+
36
+ error: (e) =>
37
+ console.log(e)
38
+
39
+ input: (node, history, e) =>
40
+ file = e.target.files[0]
41
+ @process(file, node, history)
42
+
43
+ process: (file, node) =>
44
+ caption = node.querySelector('figcaption')
45
+ progress = node.querySelector('div.progress')
46
+ filename = [Images.settings.namespace, @hash(file.name)].join('/')
47
+
48
+ image = Images.get(filename)
49
+ if !image
50
+ image = Images.upload(filename, file)
51
+
52
+ image.node = node
53
+ progress.style.width = image.progress
54
+ node.dataset.status = image.status
55
+
56
+ text = caption.childNodes[caption.childNodes.length - 1]
57
+ text.textContent = "(#{filename})"
58
+ selection = window.getSelection()
59
+ selection.removeAllRanges()
60
+ range = document.createRange()
61
+ range.setEnd(text, text.textContent.length - 1)
62
+ selection.addRange(range)
63
+
64
+ hash: (filename) ->
65
+ hash = 0
66
+ for i in [0..filename.length - 1]
67
+ hash += filename.charCodeAt(i) ^ hash
68
+
69
+ hash.toString('16')
70
+
71
+ Images = new class
72
+ constructor: ->
73
+ @cache = {}
74
+
75
+ failed: (image, event) ->
76
+ image.status = 'failed'
77
+
78
+ if image.node?
79
+ event = new CustomEvent('written:uploader:error', {bubbles: true})
80
+ event.image = image
81
+ image.node.dispatchEvent(event)
82
+
83
+ update: (image, event) ->
84
+ image.status = 'completed'
85
+
86
+ if image.node?
87
+ event = new CustomEvent('written:uploader:completed', {bubbles: true})
88
+ event.image = image
89
+ image.node.dispatchEvent(event)
90
+
91
+ upload: (name, file) =>
92
+ image = {
93
+ url: "#{@settings.url}/#{name}"
94
+ name: name
95
+ status: 'uploading'
96
+ progress: 0
97
+ }
98
+
99
+ form = new FormData()
100
+ form.append('key', name)
101
+ form.append('acl', 'private')
102
+ form.append("AWSAccessKeyId", @settings.accessKey)
103
+ form.append('policy', @settings.policy)
104
+ form.append('signature', @settings.signature)
105
+ form.append('Content-Type', file.type)
106
+ form.append('file', file, name)
107
+
108
+
109
+ image.xhr = @send(form, @settings.url)
110
+ image.xhr.addEventListener('load', @update.bind(this, image))
111
+ image.xhr.addEventListener('error', @failed.bind(this, image))
112
+
113
+ @cache[image.name] = image
114
+
115
+
116
+ get: (name) ->
117
+ @cache[name]
118
+
119
+ send: (form, url) =>
120
+ xhr = new XMLHttpRequest()
121
+ xhr.open('POST', url)
122
+ xhr.send(form)
123
+ xhr
124
+
125
+
@@ -1,5 +1,6 @@
1
1
  [data-editor="written"] {
2
2
  white-space: pre-wrap;
3
+ padding: 1em;
3
4
 
4
5
  p {
5
6
  min-height: 1.2em;
@@ -13,20 +14,88 @@
13
14
  list-style-type: none;
14
15
  }
15
16
 
16
- figure {
17
- background: rgba(230,230,230,1);
17
+ blockquote {
18
+ background: #eee;
19
+ border-radius: 2px;
20
+ padding: 0.5em 1em;
18
21
 
19
- & > div {
20
- display: flex;
21
- min-height: 200px;
22
+ p {
23
+ margin: 0;
24
+ font-style: italic;
22
25
  }
26
+ }
27
+ }
23
28
 
24
- img {
25
- margin: auto;
26
- }
29
+ [data-editor="written"] figure > div {
30
+ position: relative;
31
+ display: flex;
32
+ padding: 4px;
27
33
 
28
- figcaption {
29
- padding: 4px;
30
- }
34
+ &[data-uploadable] {
35
+ cursor: pointer;
36
+ }
37
+ }
38
+
39
+ [data-editor="written"] figure > div > div.progress {
40
+ position: absolute;
41
+ top: 0;
42
+ left: 0;
43
+ transition: width 0.24s ease;
44
+ }
45
+
46
+ [data-editor="written"] figure[data-status='uploading'] div.progress {
47
+ background-color: green;
48
+ height: 1px;
49
+ }
50
+
51
+ [data-editor="written"] figure img {
52
+ border: 2px solid #eee;
53
+ min-height: 100px;
54
+ min-width: 100px;
55
+ max-width: 100%;
56
+ max-height: 100%;
57
+ margin: auto;
58
+
59
+ &.error {
60
+ border-color: #F32626
31
61
  }
32
62
  }
63
+
64
+ [data-editor="written"] figure figcaption {
65
+ text-align: center;
66
+ font-size: 0.8em;
67
+ padding: 4px;
68
+ }
69
+
70
+ [data-editor="written"] figure input[type='file'] {
71
+ display: none;
72
+ }
73
+
74
+ [data-editor="written"] written-pre {
75
+ background: #f5f2f0;
76
+ padding: 1em;
77
+ margin: .5em 0;
78
+ overflow: auto;
79
+ color: black;
80
+ background: none;
81
+ text-shadow: 0 1px white;
82
+ font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
83
+ text-align: left;
84
+ white-space: pre;
85
+ word-spacing: normal;
86
+ word-break: normal;
87
+ word-wrap: normal;
88
+ line-height: 1.5;
89
+ -moz-tab-size: 4;
90
+ -o-tab-size: 4;
91
+ tab-size: 4;
92
+ -webkit-hyphens: none;
93
+ -moz-hyphens: none;
94
+ -ms-hyphens: none;
95
+ hyphens: none;
96
+ }
97
+
98
+ [data-editor="written"] written-pre > code {
99
+ display: block;
100
+ white-space: pre-line;
101
+ }
@@ -1,3 +1,3 @@
1
1
  module Written
2
- VERSION = '0.0.5'
2
+ VERSION = '0.1.1'
3
3
  end
@@ -1,7 +1,25 @@
1
1
  #= require 'written'
2
2
  #= require 'prism'
3
3
 
4
- Written.Parsers.Block.get('Code').parseSyntax = (element) ->
4
+ code = Written.Parsers.Block.get('Code').prototype
5
+ code.highlight = (element) ->
5
6
  Prism.highlightElement(element, false)
6
7
 
7
- new Written(document.getElementById('Editor'))
8
+ code = Written.Parsers.Inline.get('Code').prototype
9
+ code.highlight = (element) ->
10
+ Prism.highlightElement(element, false)
11
+
12
+
13
+ if window.AWS?
14
+ uploader = new Written.Uploaders.AWS({
15
+ bucket: AWS.bucket,
16
+ accessKey: AWS.accessKey,
17
+ policy: AWS.policy,
18
+ signature: AWS.signature
19
+ })
20
+
21
+ Written.Parsers.Block.get('Image').uploader(uploader)
22
+
23
+ editor = new Written(document.getElementById('Editor'))
24
+ editor.initialize()
25
+
@@ -6,6 +6,6 @@
6
6
  min-height: 450px;
7
7
  border: 1px solid gray;
8
8
  outline: none;
9
-
10
- padding: 1em;
9
+
10
+ padding: 1em 1em 30vh 1em;
11
11
  }
@@ -106,7 +106,6 @@ pre[class*="language-"] {
106
106
  .language-css .token.string,
107
107
  .style .token.string {
108
108
  color: #a67f59;
109
- background: hsla(0, 0%, 100%, .5);
110
109
  }
111
110
 
112
111
  .token.atrule,