editable_components 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (188) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +64 -0
  4. data/Rakefile +34 -0
  5. data/app/assets/config/editable_components_manifest.js +2 -0
  6. data/app/assets/fonts/editable_components/icomoon.eot +0 -0
  7. data/app/assets/fonts/editable_components/icomoon.svg +24 -0
  8. data/app/assets/fonts/editable_components/icomoon.ttf +0 -0
  9. data/app/assets/fonts/editable_components/icomoon.woff +0 -0
  10. data/app/assets/javascripts/editable_components/application.js +13 -0
  11. data/app/assets/javascripts/editable_components/dragula/dragula.css +22 -0
  12. data/app/assets/javascripts/editable_components/dragula/dragula.js +908 -0
  13. data/app/assets/javascripts/editable_components/dragula/dragula.min.css +1 -0
  14. data/app/assets/javascripts/editable_components/dragula/dragula.min.js +1 -0
  15. data/app/assets/javascripts/editable_components/dropzone/CONTRIBUTING.md +78 -0
  16. data/app/assets/javascripts/editable_components/dropzone/LICENSE +12 -0
  17. data/app/assets/javascripts/editable_components/dropzone/README.md +134 -0
  18. data/app/assets/javascripts/editable_components/dropzone/basic.css +39 -0
  19. data/app/assets/javascripts/editable_components/dropzone/dropzone-amd-module.js +1782 -0
  20. data/app/assets/javascripts/editable_components/dropzone/dropzone.css +388 -0
  21. data/app/assets/javascripts/editable_components/dropzone/dropzone.js +1767 -0
  22. data/app/assets/javascripts/editable_components/dropzone/min/basic.min.css +1 -0
  23. data/app/assets/javascripts/editable_components/dropzone/min/dropzone-amd-module.min.js +2 -0
  24. data/app/assets/javascripts/editable_components/dropzone/min/dropzone.min.css +1 -0
  25. data/app/assets/javascripts/editable_components/dropzone/min/dropzone.min.js +2 -0
  26. data/app/assets/javascripts/editable_components/main.js +280 -0
  27. data/app/assets/javascripts/editable_components/medium-editor/API.md +520 -0
  28. data/app/assets/javascripts/editable_components/medium-editor/CHANGES.md +1043 -0
  29. data/app/assets/javascripts/editable_components/medium-editor/CODE_OF_CONDUCT.md +13 -0
  30. data/app/assets/javascripts/editable_components/medium-editor/CONTRIBUTING.md +56 -0
  31. data/app/assets/javascripts/editable_components/medium-editor/CUSTOM-EVENTS.md +223 -0
  32. data/app/assets/javascripts/editable_components/medium-editor/LICENSE +36 -0
  33. data/app/assets/javascripts/editable_components/medium-editor/MAINTAINERS.md +33 -0
  34. data/app/assets/javascripts/editable_components/medium-editor/OPTIONS.md +663 -0
  35. data/app/assets/javascripts/editable_components/medium-editor/README.md +725 -0
  36. data/app/assets/javascripts/editable_components/medium-editor/UPGRADE-5.md +146 -0
  37. data/app/assets/javascripts/editable_components/medium-editor/medium-editor.css +230 -0
  38. data/app/assets/javascripts/editable_components/medium-editor/medium-editor.js +7876 -0
  39. data/app/assets/javascripts/editable_components/medium-editor/medium-editor.min.css +1 -0
  40. data/app/assets/javascripts/editable_components/medium-editor/medium-editor.min.js +4 -0
  41. data/app/assets/javascripts/editable_components/medium-editor/themes/beagle.css +78 -0
  42. data/app/assets/javascripts/editable_components/medium-editor/themes/beagle.min.css +1 -0
  43. data/app/assets/javascripts/editable_components/medium-editor/themes/bootstrap.css +67 -0
  44. data/app/assets/javascripts/editable_components/medium-editor/themes/bootstrap.min.css +1 -0
  45. data/app/assets/javascripts/editable_components/medium-editor/themes/default.css +63 -0
  46. data/app/assets/javascripts/editable_components/medium-editor/themes/default.min.css +1 -0
  47. data/app/assets/javascripts/editable_components/medium-editor/themes/flat.css +58 -0
  48. data/app/assets/javascripts/editable_components/medium-editor/themes/flat.min.css +1 -0
  49. data/app/assets/javascripts/editable_components/medium-editor/themes/mani.css +57 -0
  50. data/app/assets/javascripts/editable_components/medium-editor/themes/mani.min.css +1 -0
  51. data/app/assets/javascripts/editable_components/medium-editor/themes/roman.css +58 -0
  52. data/app/assets/javascripts/editable_components/medium-editor/themes/roman.min.css +1 -0
  53. data/app/assets/javascripts/editable_components/medium-editor/themes/tim.css +67 -0
  54. data/app/assets/javascripts/editable_components/medium-editor/themes/tim.min.css +1 -0
  55. data/app/assets/javascripts/editable_components/sizzle/sizzle.js +2272 -0
  56. data/app/assets/javascripts/editable_components/sizzle/sizzle.min.js +3 -0
  57. data/app/assets/javascripts/editable_components/sizzle/sizzle.min.map +1 -0
  58. data/app/assets/javascripts/editable_components/vue/README.md +124 -0
  59. data/app/assets/javascripts/editable_components/vue/vue.common.js +9320 -0
  60. data/app/assets/javascripts/editable_components/vue/vue.common.min.js +7 -0
  61. data/app/assets/javascripts/editable_components/vue/vue.esm.js +9318 -0
  62. data/app/assets/javascripts/editable_components/vue/vue.js +9316 -0
  63. data/app/assets/javascripts/editable_components/vue/vue.min.js +8 -0
  64. data/app/assets/javascripts/editable_components/vue/vue.runtime.common.js +6831 -0
  65. data/app/assets/javascripts/editable_components/vue/vue.runtime.esm.js +6829 -0
  66. data/app/assets/javascripts/editable_components/vue/vue.runtime.js +6827 -0
  67. data/app/assets/javascripts/editable_components/vue/vue.runtime.min.js +7 -0
  68. data/app/assets/stylesheets/editable_components/application.sass +65 -0
  69. data/app/assets/stylesheets/editable_components/blaze/blaze.colors.min.css +1 -0
  70. data/app/assets/stylesheets/editable_components/blaze/blaze.min.css +1 -0
  71. data/app/assets/stylesheets/editable_components/blaze/components.addresses.min.css +1 -0
  72. data/app/assets/stylesheets/editable_components/blaze/components.alerts.min.css +1 -0
  73. data/app/assets/stylesheets/editable_components/blaze/components.avatars.min.css +1 -0
  74. data/app/assets/stylesheets/editable_components/blaze/components.badges.min.css +1 -0
  75. data/app/assets/stylesheets/editable_components/blaze/components.breadcrumbs.min.css +1 -0
  76. data/app/assets/stylesheets/editable_components/blaze/components.bubbles.min.css +1 -0
  77. data/app/assets/stylesheets/editable_components/blaze/components.buttons.min.css +1 -0
  78. data/app/assets/stylesheets/editable_components/blaze/components.calendars.min.css +1 -0
  79. data/app/assets/stylesheets/editable_components/blaze/components.cards.min.css +1 -0
  80. data/app/assets/stylesheets/editable_components/blaze/components.headings.min.css +1 -0
  81. data/app/assets/stylesheets/editable_components/blaze/components.hints.min.css +1 -0
  82. data/app/assets/stylesheets/editable_components/blaze/components.input-groups.min.css +1 -0
  83. data/app/assets/stylesheets/editable_components/blaze/components.inputs.min.css +1 -0
  84. data/app/assets/stylesheets/editable_components/blaze/components.links.min.css +1 -0
  85. data/app/assets/stylesheets/editable_components/blaze/components.lists.min.css +1 -0
  86. data/app/assets/stylesheets/editable_components/blaze/components.navs.min.css +1 -0
  87. data/app/assets/stylesheets/editable_components/blaze/components.overlays.min.css +1 -0
  88. data/app/assets/stylesheets/editable_components/blaze/components.pagination.min.css +1 -0
  89. data/app/assets/stylesheets/editable_components/blaze/components.progress.min.css +1 -0
  90. data/app/assets/stylesheets/editable_components/blaze/components.ranges.min.css +1 -0
  91. data/app/assets/stylesheets/editable_components/blaze/components.tables.min.css +1 -0
  92. data/app/assets/stylesheets/editable_components/blaze/components.tabs.min.css +1 -0
  93. data/app/assets/stylesheets/editable_components/blaze/components.tags.min.css +1 -0
  94. data/app/assets/stylesheets/editable_components/blaze/components.toggles.min.css +1 -0
  95. data/app/assets/stylesheets/editable_components/blaze/components.tooltips.min.css +1 -0
  96. data/app/assets/stylesheets/editable_components/blaze/components.trees.min.css +1 -0
  97. data/app/assets/stylesheets/editable_components/blaze/components.typography.min.css +1 -0
  98. data/app/assets/stylesheets/editable_components/blaze/generics.global.min.css +1 -0
  99. data/app/assets/stylesheets/editable_components/blaze/objects.containers.min.css +1 -0
  100. data/app/assets/stylesheets/editable_components/blaze/objects.drawers.min.css +1 -0
  101. data/app/assets/stylesheets/editable_components/blaze/objects.forms.min.css +1 -0
  102. data/app/assets/stylesheets/editable_components/blaze/objects.grid.min.css +1 -0
  103. data/app/assets/stylesheets/editable_components/blaze/objects.grid.responsive.min.css +1 -0
  104. data/app/assets/stylesheets/editable_components/blaze/objects.images.min.css +1 -0
  105. data/app/assets/stylesheets/editable_components/blaze/objects.media.min.css +1 -0
  106. data/app/assets/stylesheets/editable_components/blaze/objects.modals.min.css +1 -0
  107. data/app/assets/stylesheets/editable_components/blaze/objects.panels.min.css +1 -0
  108. data/app/assets/stylesheets/editable_components/blaze/themes/blaze.example.min.css +1 -0
  109. data/app/assets/stylesheets/editable_components/blaze/utilities.alignment.min.css +1 -0
  110. data/app/assets/stylesheets/editable_components/blaze/utilities.boxing.min.css +1 -0
  111. data/app/assets/stylesheets/editable_components/blaze/utilities.elevation.min.css +1 -0
  112. data/app/assets/stylesheets/editable_components/blaze/utilities.sizes.min.css +1 -0
  113. data/app/assets/stylesheets/editable_components/dragula/dragula.css +22 -0
  114. data/app/assets/stylesheets/editable_components/dragula/dragula.js +908 -0
  115. data/app/assets/stylesheets/editable_components/dragula/dragula.min.css +1 -0
  116. data/app/assets/stylesheets/editable_components/dragula/dragula.min.js +1 -0
  117. data/app/assets/stylesheets/editable_components/main.sass +225 -0
  118. data/app/assets/stylesheets/editable_components/medium-editor/API.md +520 -0
  119. data/app/assets/stylesheets/editable_components/medium-editor/CHANGES.md +1043 -0
  120. data/app/assets/stylesheets/editable_components/medium-editor/CODE_OF_CONDUCT.md +13 -0
  121. data/app/assets/stylesheets/editable_components/medium-editor/CONTRIBUTING.md +56 -0
  122. data/app/assets/stylesheets/editable_components/medium-editor/CUSTOM-EVENTS.md +223 -0
  123. data/app/assets/stylesheets/editable_components/medium-editor/LICENSE +36 -0
  124. data/app/assets/stylesheets/editable_components/medium-editor/MAINTAINERS.md +33 -0
  125. data/app/assets/stylesheets/editable_components/medium-editor/OPTIONS.md +663 -0
  126. data/app/assets/stylesheets/editable_components/medium-editor/README.md +725 -0
  127. data/app/assets/stylesheets/editable_components/medium-editor/UPGRADE-5.md +146 -0
  128. data/app/assets/stylesheets/editable_components/medium-editor/medium-editor.css +230 -0
  129. data/app/assets/stylesheets/editable_components/medium-editor/medium-editor.js +7876 -0
  130. data/app/assets/stylesheets/editable_components/medium-editor/medium-editor.min.css +1 -0
  131. data/app/assets/stylesheets/editable_components/medium-editor/medium-editor.min.js +4 -0
  132. data/app/assets/stylesheets/editable_components/medium-editor/themes/beagle.css +78 -0
  133. data/app/assets/stylesheets/editable_components/medium-editor/themes/beagle.min.css +1 -0
  134. data/app/assets/stylesheets/editable_components/medium-editor/themes/bootstrap.css +67 -0
  135. data/app/assets/stylesheets/editable_components/medium-editor/themes/bootstrap.min.css +1 -0
  136. data/app/assets/stylesheets/editable_components/medium-editor/themes/default.css +63 -0
  137. data/app/assets/stylesheets/editable_components/medium-editor/themes/default.min.css +1 -0
  138. data/app/assets/stylesheets/editable_components/medium-editor/themes/flat.css +58 -0
  139. data/app/assets/stylesheets/editable_components/medium-editor/themes/flat.min.css +1 -0
  140. data/app/assets/stylesheets/editable_components/medium-editor/themes/mani.css +57 -0
  141. data/app/assets/stylesheets/editable_components/medium-editor/themes/mani.min.css +1 -0
  142. data/app/assets/stylesheets/editable_components/medium-editor/themes/roman.css +58 -0
  143. data/app/assets/stylesheets/editable_components/medium-editor/themes/roman.min.css +1 -0
  144. data/app/assets/stylesheets/editable_components/medium-editor/themes/tim.css +67 -0
  145. data/app/assets/stylesheets/editable_components/medium-editor/themes/tim.min.css +1 -0
  146. data/app/controllers/editable_components/blocks_controller.rb +36 -0
  147. data/app/helpers/editable_components/application_helper.rb +4 -0
  148. data/app/jobs/editable_components/application_job.rb +4 -0
  149. data/app/mailers/editable_components/application_mailer.rb +6 -0
  150. data/app/models/editable_components/application_record.rb +5 -0
  151. data/app/models/editable_components/block.rb +176 -0
  152. data/app/models/editable_components/concerns/editable.rb +34 -0
  153. data/app/models/editable_components/item.rb +50 -0
  154. data/app/models/editable_components/item_boolean.rb +23 -0
  155. data/app/models/editable_components/item_datetime.rb +14 -0
  156. data/app/models/editable_components/item_file.rb +46 -0
  157. data/app/models/editable_components/item_float.rb +23 -0
  158. data/app/models/editable_components/item_hash.rb +16 -0
  159. data/app/models/editable_components/item_integer.rb +23 -0
  160. data/app/models/editable_components/item_string.rb +29 -0
  161. data/app/models/editable_components/item_text.rb +19 -0
  162. data/app/views/editable_components/_admin_toolbar.html.erb +105 -0
  163. data/app/views/editable_components/_block.html.erb +6 -0
  164. data/app/views/editable_components/_block_image.html.erb +6 -0
  165. data/app/views/editable_components/_block_multi_text.html.erb +7 -0
  166. data/app/views/editable_components/_block_slide.html.erb +6 -0
  167. data/app/views/editable_components/_block_slider.html.erb +7 -0
  168. data/app/views/editable_components/_block_text.html.erb +5 -0
  169. data/app/views/editable_components/_block_text_with_image.html.erb +12 -0
  170. data/app/views/editable_components/_blocks.html.erb +12 -0
  171. data/config/initializers/editable_components.rb +72 -0
  172. data/config/routes.rb +4 -0
  173. data/db/migrate/20170414173603_create_editable_components_blocks.rb +18 -0
  174. data/db/migrate/20170414173610_create_editable_components_items.rb +11 -0
  175. data/db/migrate/20170414173611_add_items_booleans.rb +5 -0
  176. data/db/migrate/20170414173612_add_items_datetimes.rb +5 -0
  177. data/db/migrate/20170414173613_add_items_files.rb +5 -0
  178. data/db/migrate/20170414173614_add_items_floats.rb +5 -0
  179. data/db/migrate/20170414173615_add_items_hashes.rb +5 -0
  180. data/db/migrate/20170414173616_add_items_integers.rb +5 -0
  181. data/db/migrate/20170414173617_add_items_strings.rb +5 -0
  182. data/db/migrate/20170414173618_add_items_texts.rb +5 -0
  183. data/lib/data/img1.jpg +0 -0
  184. data/lib/editable_components.rb +87 -0
  185. data/lib/editable_components/engine.rb +21 -0
  186. data/lib/editable_components/version.rb +3 -0
  187. data/lib/tasks/editable_components_tasks.rake +4 -0
  188. metadata +264 -0
@@ -0,0 +1,725 @@
1
+ # MediumEditor
2
+
3
+ This is a clone of [medium.com](https://medium.com) inline editor toolbar.
4
+
5
+ MediumEditor has been written using vanilla JavaScript, no additional frameworks required.
6
+
7
+ [![screenshot](https://raw.github.com/yabwe/medium-editor/master/demo/img/medium-editor.jpg)](http://yabwe.github.io/medium-editor/)
8
+
9
+ [![Join the chat at https://gitter.im/yabwe/medium-editor](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/yabwe/medium-editor?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
10
+
11
+ ## Browser Support
12
+
13
+ [![Saucelabs Build Status](https://saucelabs.com/browser-matrix/mediumeditor.svg)](https://saucelabs.com/beta/dashboard/builds)
14
+
15
+ ![Supported Browsers](https://cloud.githubusercontent.com/assets/2444240/12874138/d3960a04-cd9b-11e5-8cc5-8136d82cf5f6.png)
16
+
17
+ [![NPM info](https://nodei.co/npm/medium-editor.png?downloads=true)](https://www.npmjs.com/package/medium-editor)
18
+
19
+ [![Travis build status](https://travis-ci.org/yabwe/medium-editor.svg?branch=master)](https://travis-ci.org/yabwe/medium-editor)
20
+ [![Dependency Status](https://david-dm.org/yabwe/medium-editor.svg)](https://david-dm.org/yabwe/medium-editor)
21
+ [![devDependency Status](https://david-dm.org/yabwe/medium-editor/dev-status.svg)](https://david-dm.org/yabwe/medium-editor#info=devDependencies)
22
+ [![Coverage Status](https://coveralls.io/repos/yabwe/medium-editor/badge.svg?branch=master&service=github)](https://coveralls.io/github/yabwe/medium-editor?branch=master)
23
+
24
+ # Basic usage
25
+
26
+ ### Demo
27
+
28
+ __demo__: [http://yabwe.github.io/medium-editor/](http://yabwe.github.io/medium-editor/)
29
+
30
+ ### Installation
31
+
32
+ **Via npm:**
33
+
34
+ Run in your console: `npm install medium-editor`
35
+
36
+ **Via bower:**
37
+
38
+ `bower install medium-editor`
39
+
40
+ **Via an external CDN**
41
+
42
+ * Using [jsDelivr](http://www.jsdelivr.com/#!medium-editor).
43
+
44
+ For the latest version:
45
+
46
+ ```html
47
+ <script src="//cdn.jsdelivr.net/medium-editor/latest/js/medium-editor.min.js"></script>
48
+ <link rel="stylesheet" href="//cdn.jsdelivr.net/medium-editor/latest/css/medium-editor.min.css" type="text/css" media="screen" charset="utf-8">
49
+ ```
50
+
51
+ For a custom one:
52
+
53
+ ```html
54
+ <script src="//cdn.jsdelivr.net/medium-editor/4.11.1/js/medium-editor.min.js"></script>
55
+ <link rel="stylesheet" href="//cdn.jsdelivr.net/medium-editor/4.11.1/css/medium-editor.min.css" type="text/css" media="screen" charset="utf-8">
56
+ ```
57
+
58
+ * Using [CDNJS](https://cdnjs.com/libraries/medium-editor).
59
+
60
+ **Manual installation:**
61
+
62
+ Download the [latest release](https://github.com/yabwe/medium-editor/releases) and attach medium editor's stylesheets to your page:
63
+
64
+ Find the files to below mentioned linking in the dist folder. (./medium-editor/dist/...)
65
+
66
+ ```html
67
+ <link rel="stylesheet" href="css/medium-editor.css"> <!-- Core -->
68
+ <link rel="stylesheet" href="css/themes/default.css"> <!-- or any other theme -->
69
+ ```
70
+
71
+ ### Usage
72
+
73
+ The next step is to reference the editor's script
74
+
75
+ ```html
76
+ <script src="js/medium-editor.js"></script>
77
+ ```
78
+
79
+ You can now instantiate a new MediumEditor object:
80
+ ```html
81
+ <script>var editor = new MediumEditor('.editable');</script>
82
+ ```
83
+
84
+ The above code will transform all the elements with the .editable class into HTML5 editable contents and add the medium editor toolbar to them.
85
+
86
+ You can also pass a list of HTML elements:
87
+
88
+ ```javascript
89
+ var elements = document.querySelectorAll('.editable'),
90
+ editor = new MediumEditor(elements);
91
+ ```
92
+
93
+ MediumEditor also supports textarea. If you provide a textarea element, the script will create a new div with `contentEditable=true`, hide the textarea and link the textarea value to the div HTML content.
94
+
95
+ ##### Integrating with various frameworks
96
+
97
+ People have contributed wrappers around MediumEditor for integrating with different frameworks and tech stacks. Take a look at the list of existing [Wrappers and Integrations](https://github.com/yabwe/medium-editor/wiki/Wrappers-and-Integration) that have already been written for MediumEditor!
98
+
99
+ ## MediumEditor Options
100
+
101
+ View the [MediumEditor Options documentation](OPTIONS.md) on all the various options for MediumEditor.
102
+
103
+ Options to customize medium-editor are passed as the second argument to the [MediumEditor constructor](API.md#mediumeditorelements-options). Example:
104
+
105
+ ```js
106
+ var editor = new MediumEditor('.editor', {
107
+ // options go here
108
+ });
109
+ ```
110
+
111
+ ### Core options
112
+ * __activeButtonClass__: CSS class added to active buttons in the toolbar. Default: `'medium-editor-button-active'`
113
+ * __buttonLabels__: type of labels on the buttons. Values: `false` | 'fontawesome'. Default: `false`
114
+
115
+ #### NOTE:
116
+ Using `'fontawesome'` as the buttonLabels requires version 4.1.0 of the fontawesome css to be on the page to ensure all icons will be displayed correctly
117
+
118
+ * __delay__: time in milliseconds to show the toolbar or anchor tag preview. Default: `0`
119
+ * __disableReturn__: enables/disables the use of the return-key. You can also set specific element behavior by using setting a data-disable-return attribute. Default: `false`
120
+ * __disableDoubleReturn__: allows/disallows two (or more) empty new lines. You can also set specific element behavior by using setting a data-disable-double-return attribute. Default: `false`
121
+ * __disableExtraSpaces__: when set to true, it disallows spaces at the beginning and end of the element. Also it disallows entering 2 consecutive spaces between 2 words. Default: `false`
122
+ * __disableEditing__: enables/disables adding the contenteditable behavior. Useful for using the toolbar with customized buttons/actions. You can also set specific element behavior by using setting a data-disable-editing attribute. Default: `false`
123
+ * __elementsContainer__: specifies a DOM node to contain MediumEditor's toolbar and anchor preview elements. Default: `document.body`
124
+ * __extensions__: extension to use (see [Custom Buttons and Extensions](src/js/extensions)) for more. Default: `{}`
125
+ * __spellcheck__: Enable/disable native contentEditable automatic spellcheck. Default: `true`
126
+ * __targetBlank__: enables/disables target="\_blank" for anchor tags. Default: `false`
127
+
128
+ ### Toolbar options
129
+
130
+ The toolbar for MediumEditor is implemented as a built-in extension which automatically displays whenever the user selects some text. The toolbar can hold any set of defined built-in buttons, but can also hold any custom buttons passed in as extensions.
131
+
132
+ Options for the toolbar are passed as an object that is a member of the outer options object. Example:
133
+ ```javascript
134
+ var editor = new MediumEditor('.editable', {
135
+ toolbar: {
136
+ /* These are the default options for the toolbar,
137
+ if nothing is passed this is what is used */
138
+ allowMultiParagraphSelection: true,
139
+ buttons: ['bold', 'italic', 'underline', 'anchor', 'h2', 'h3', 'quote'],
140
+ diffLeft: 0,
141
+ diffTop: -10,
142
+ firstButtonClass: 'medium-editor-button-first',
143
+ lastButtonClass: 'medium-editor-button-last',
144
+ relativeContainer: null,
145
+ standardizeSelectionStart: false,
146
+ static: false,
147
+ /* options which only apply when static is true */
148
+ align: 'center',
149
+ sticky: false,
150
+ updateOnEmptySelection: false
151
+ }
152
+ });
153
+ ```
154
+
155
+ * __allowMultiParagraphSelection__: enables/disables whether the toolbar should be displayed when selecting multiple paragraphs/block elements. Default: `true`
156
+ * __buttons__: the set of buttons to display on the toolbar. Default: `['bold', 'italic', 'underline', 'anchor', 'h2', 'h3', 'quote']`
157
+ * See [Button Options](#button-options) for details on more button options
158
+ * __diffLeft__: value in pixels to be added to the X axis positioning of the toolbar. Default: `0`
159
+ * __diffTop__: value in pixels to be added to the Y axis positioning of the toolbar. Default: `-10`
160
+ * __firstButtonClass__: CSS class added to the first button in the toolbar. Default: `'medium-editor-button-first'`
161
+ * __lastButtonClass__: CSS class added to the last button in the toolbar. Default: `'medium-editor-button-last'`
162
+ * __relativeContainer__: DOMElement to append the toolbar to instead of the body. When passed, the toolbar will also be positioned `relative` instead of `absolute`. Default: `null`
163
+ * __standardizeSelectionStart__: enables/disables standardizing how the beginning of a range is decided between browsers whenever the selected text is analyzed for updating toolbar buttons status. Default: `false`
164
+ * __static__: enable/disable the toolbar always displaying in the same location relative to the medium-editor element. Default: `false`
165
+
166
+ ##### Options which only apply when the `static` option is being used:
167
+ * __align__: `left`|`center`|`right` - When the __static__ option is `true`, this aligns the static toolbar relative to the medium-editor element. Default: `center`
168
+ * __sticky__: When the __static__ option is `true`, this enables/disables the toolbar "sticking" to the viewport and staying visible on the screen while the page scrolls. Default: `false`
169
+ * __updateOnEmptySelection__: When the __static__ option is `true`, this enables/disables updating the state of the toolbar buttons even when the selection is collapsed (there is no selection, just a cursor). Default: `false`
170
+
171
+ To disable the toolbar (which also disables the anchor-preview extension), set the value of the `toolbar` option to `false`:
172
+ ```javascript
173
+ var editor = new MediumEditor('.editable', {
174
+ toolbar: false
175
+ });
176
+ ```
177
+
178
+ #### Button Options
179
+
180
+ Button behavior can be modified by passing an object into the buttons array instead of a string. This allow for overriding some of the default behavior of buttons. The following options are some of the basic parts of buttons that you may override, but any part of the `MediumEditor.Extension.prototype` can be overridden via these button options. (Check out the [source code for buttons](src/js/extensions/button.js) to see what all can be overridden).
181
+
182
+ * __name__: name of the button being overridden
183
+ * __action__: argument to pass to `MediumEditor.execAction()` when the button is clicked.
184
+ * __aria__: value to add as the aria-label attribute of the button element displayed in the toolbar. This is also used as the tooltip for the button.
185
+ * __tagNames__: array of element tag names that would indicate that this button has already been applied. If this action has already been applied, the button will be displayed as 'active' in the toolbar.
186
+ * _Example_: For 'bold', if the text is ever within a `<b>` or `<strong>` tag that indicates the text is already bold. So the array of tagNames for bold would be: `['b', 'strong']`
187
+ * __NOTE__: This is not used if `useQueryState` is set to `true`.
188
+ * __style__: A pair of css property & value(s) that indicate that this button has already been applied. If this action has already been applied, the button will be displayed as 'active' in the toolbar.
189
+ * _Example_: For 'bold', if the text is ever within an element with a `'font-weight'` style property set to `700` or `'bold'`, that indicates the text is already bold. So the style object for bold would be `{ prop: 'font-weight', value: '700|bold' }`
190
+ * __NOTE__: This is not used if `useQueryState` is set to `true`.
191
+ * Properties of the __style__ object:
192
+ * __prop__: name of the css property
193
+ * __value__: value(s) of the css property (multiple values can be separated by a `'|'`)
194
+ * __useQueryState__: Enables/disables whether this button should use the built-in `document.queryCommandState()` method to determine whether the action has already been applied. If the action has already been applied, the button will be displayed as 'active' in the toolbar
195
+ * _Example_: For 'bold', if this is set to true, the code will call `document.queryCommandState('bold')` which will return true if the browser thinks the text is already bold, and false otherwise
196
+ * __contentDefault__: Default `innerHTML` to put inside the button
197
+ * __contentFA__: The `innerHTML` to use for the content of the button if the __buttonLabels__ option for MediumEditor is set to `'fontawesome'`
198
+ * __classList__: An array of classNames (strings) to be added to the button
199
+ * __attrs__: A set of key-value pairs to add to the button as custom attributes to the button element.
200
+
201
+ Example of overriding buttons (here, the goal is to mimic medium by having <kbd>H1</kbd> and <kbd>H2</kbd> buttons which actually produce `<h2>` and `<h3>` tags respectively):
202
+ ```javascript
203
+ var editor = new MediumEditor('.editable', {
204
+ toolbar: {
205
+ buttons: [
206
+ 'bold',
207
+ 'italic',
208
+ {
209
+ name: 'h1',
210
+ action: 'append-h2',
211
+ aria: 'header type 1',
212
+ tagNames: ['h2'],
213
+ contentDefault: '<b>H1</b>',
214
+ classList: ['custom-class-h1'],
215
+ attrs: {
216
+ 'data-custom-attr': 'attr-value-h1'
217
+ }
218
+ },
219
+ {
220
+ name: 'h2',
221
+ action: 'append-h3',
222
+ aria: 'header type 2',
223
+ tagNames: ['h3'],
224
+ contentDefault: '<b>H2</b>',
225
+ classList: ['custom-class-h2'],
226
+ attrs: {
227
+ 'data-custom-attr': 'attr-value-h2'
228
+ }
229
+ },
230
+ 'justifyCenter',
231
+ 'quote',
232
+ 'anchor'
233
+ ]
234
+ }
235
+ });
236
+ ```
237
+
238
+ ### Anchor Preview options
239
+
240
+ The anchor preview is a built-in extension which automatically displays a 'tooltip' when the user is hovering over a link in the editor. The tooltip will display the `href` of the link, and when click, will open the anchor editing form in the toolbar.
241
+
242
+ Options for the anchor preview 'tooltip' are passed as an object that is a member of the outer options object. Example:
243
+ ```javascript
244
+ var editor = new MediumEditor('.editable', {
245
+ anchorPreview: {
246
+ /* These are the default options for anchor preview,
247
+ if nothing is passed this is what it used */
248
+ hideDelay: 500,
249
+ previewValueSelector: 'a'
250
+ }
251
+ }
252
+ });
253
+ ```
254
+
255
+ * __hideDelay__: time in milliseconds to show the anchor tag preview after the mouse has left the anchor tag. Default: `500`
256
+ * __previewValueSelector__: the default selector to locate where to put the activeAnchor value in the preview. You should only need to override this if you've modified the way in which the anchor-preview extension renders. Default: `'a'`
257
+ * __showWhenToolbarIsVisible__: determines whether the anchor tag preview shows up when the toolbar is visible. You should set this value to true if the static option for the toolbar is true and you want the preview to show at the same time. Default: `false`
258
+ * __showOnEmptyLinks__: determines whether the anchor tag preview shows up on link with href as '' or '#something'. You should set this value to false if you do not want the preview to show up in such use cases. Default: `true`
259
+
260
+ To disable the anchor preview, set the value of the `anchorPreview` option to `false`:
261
+ ```javascript
262
+ var editor = new MediumEditor('.editable', {
263
+ anchorPreview: false
264
+ });
265
+ ```
266
+ ##### NOTE:
267
+ * If the toolbar is disabled (via `toolbar: false` option or `data-disable-toolbar` attribute) the anchor-preview is automatically disabled.
268
+ * If the anchor editing form is not enabled, clicking on the anchor-preview will not allow the href of the link to be edited
269
+
270
+ ### Placeholder Options
271
+
272
+ The placeholder handler is a built-in extension which displays placeholder text when the editor is empty.
273
+
274
+ Options for placeholder are passed as an object that is a member of the outer options object. Example:
275
+ ```javascript
276
+ var editor = new MediumEditor('.editable', {
277
+ placeholder: {
278
+ /* This example includes the default options for placeholder,
279
+ if nothing is passed this is what it used */
280
+ text: 'Type your text',
281
+ hideOnClick: true
282
+ }
283
+ });
284
+ ```
285
+
286
+ * __text__: Defines the default placeholder for empty contenteditables when __placeholder__ is not set to false. You can overwrite it by setting a `data-placeholder` attribute on the editor elements. Default: `'Type your text'`
287
+
288
+ * __hideOnClick__: Causes the placeholder to disappear as soon as the field gains focus. Default: `true`.
289
+ To hide the placeholder only after starting to type, and to show it again as soon as field is empty, set this option to `false`.
290
+
291
+
292
+ To disable the placeholder, set the value of the `placeholder` option to `false`:
293
+ ```javascript
294
+ var editor = new MediumEditor('.editable', {
295
+ placeholder: false
296
+ });
297
+ ```
298
+
299
+ ### Anchor Form options
300
+
301
+ The anchor form is a built-in button extension which allows the user to add/edit/remove links from within the editor. When 'anchor' is passed in as a button in the list of buttons, this extension will be enabled and can be triggered by clicking the corresponding button in the toolbar.
302
+
303
+ Options for the anchor form are passed as an object that is a member of the outer options object. Example:
304
+ ```javascript
305
+ var editor = new MediumEditor('.editable', {
306
+ toolbar: {
307
+ buttons: ['bold', 'italic', 'underline', 'anchor']
308
+ },
309
+ anchor: {
310
+ /* These are the default options for anchor form,
311
+ if nothing is passed this is what it used */
312
+ customClassOption: null,
313
+ customClassOptionText: 'Button',
314
+ linkValidation: false,
315
+ placeholderText: 'Paste or type a link',
316
+ targetCheckbox: false,
317
+ targetCheckboxText: 'Open in new window'
318
+ }
319
+ }
320
+ });
321
+ ```
322
+
323
+ * __customClassOption__: custom class name the user can optionally have added to their created links (ie 'button'). If passed as a non-empty string, a checkbox will be displayed allowing the user to choose whether to have the class added to the created link or not. Default: `null`
324
+ * __customClassOptionText__: text to be shown in the checkbox when the __customClassOption__ is being used. Default: `'Button'`
325
+ * __linkValidation__: enables/disables check for common URL protocols on anchor links. Converts invalid url characters (ie spaces) to valid characters using `encodeURI`. Default: `false`
326
+ * __placeholderText__: text to be shown as placeholder of the anchor input. Default: `'Paste or type a link'`
327
+ * __targetCheckbox__: enables/disables displaying a "Open in new window" checkbox, which when checked changes the `target` attribute of the created link. Default: `false`
328
+ * __targetCheckboxText__: text to be shown in the checkbox enabled via the __targetCheckbox__ option. Default: `'Open in new window'`
329
+
330
+ ### Paste Options
331
+
332
+ The paste handler is a built-in extension which attempts to filter the content when the user pastes. How the paste handler filters is configurable via specific options.
333
+
334
+ Options for paste handling are passed as an object that is a member of the outer options object. Example:
335
+ ```javascript
336
+ var editor = new MediumEditor('.editable', {
337
+ paste: {
338
+ /* This example includes the default options for paste,
339
+ if nothing is passed this is what it used */
340
+ forcePlainText: true,
341
+ cleanPastedHTML: false,
342
+ cleanReplacements: [],
343
+ cleanAttrs: ['class', 'style', 'dir'],
344
+ cleanTags: ['meta'],
345
+ unwrapTags: []
346
+ }
347
+ });
348
+ ```
349
+
350
+ * __forcePlainText__: Forces pasting as plain text. Default: `true`
351
+ * __cleanPastedHTML__: cleans pasted content from different sources, like google docs etc. Default: `false`
352
+ * __preCleanReplacements__: custom pairs (2 element arrays) of RegExp and replacement text to use during paste when __forcePlainText__ or __cleanPastedHTML__ are `true` OR when calling `cleanPaste(text)` helper method. These replacements are executed _before_ builtin replacements. Default: `[]`
353
+ * __cleanReplacements__: custom pairs (2 element arrays) of RegExp and replacement text to use during paste when __forcePlainText__ or __cleanPastedHTML__ are `true` OR when calling `cleanPaste(text)` helper method. These replacements are executed _after_ builtin replacements. Default: `[]`
354
+ * __cleanAttrs__: list of element attributes to remove during paste when __cleanPastedHTML__ is `true` or when calling `cleanPaste(text)` or `pasteHTML(html,options)` helper methods. Default: `['class', 'style', 'dir']`
355
+ * __cleanTags__: list of element tag names to remove during paste when __cleanPastedHTML__ is `true` or when calling `cleanPaste(text)` or `pasteHTML(html,options)` helper methods. Default: `['meta']`
356
+ * __unwrapTags__: list of element tag names to unwrap (remove the element tag but retain its child elements) during paste when __cleanPastedHTML__ is `true` or when calling `cleanPaste(text)` or `pasteHTML(html,options)` helper methods. Default: `[]`
357
+
358
+ ### KeyboardCommands Options
359
+
360
+ The keyboard commands handler is a built-in extension for mapping key-combinations to actions to execute in the editor.
361
+
362
+ Options for KeyboardCommands are passed as an object that is a member of the outer options object. Example:
363
+ ```javascript
364
+ var editor = new MediumEditor('.editable', {
365
+ keyboardCommands: {
366
+ /* This example includes the default options for keyboardCommands,
367
+ if nothing is passed this is what it used */
368
+ commands: [
369
+ {
370
+ command: 'bold',
371
+ key: 'B',
372
+ meta: true,
373
+ shift: false,
374
+ alt: false
375
+ },
376
+ {
377
+ command: 'italic',
378
+ key: 'I',
379
+ meta: true,
380
+ shift: false,
381
+ alt: false
382
+ },
383
+ {
384
+ command: 'underline',
385
+ key: 'U',
386
+ meta: true,
387
+ shift: false,
388
+ alt: false
389
+ }
390
+ ],
391
+ }
392
+ });
393
+ ```
394
+
395
+ * __commands__: Array of objects describing each command and the combination of keys that will trigger it. Required for each object:
396
+ * _command_: argument passed to `editor.execAction()` when key-combination is used
397
+ * if defined as `false`, the shortcut will be disabled
398
+ * _key_: keyboard character that triggers this command
399
+ * _meta_: whether the ctrl/meta key has to be active or inactive
400
+ * _shift_: whether the shift key has to be active or inactive
401
+ * _alt_: whether the alt key has to be active or inactive
402
+
403
+ To disable the keyboard commands, set the value of the `keyboardCommands` option to `false`:
404
+ ```javascript
405
+ var editor = new MediumEditor('.editable', {
406
+ keyboardCommands: false
407
+ });
408
+ ```
409
+
410
+ ### Auto Link Options
411
+
412
+ The auto-link handler is a built-in extension which automatically turns URLs entered into the text field into HTML anchor tags (similar to the functionality of Markdown). This feature is OFF by default.
413
+
414
+ To enable built-in auto-link support, set the value of the `autoLink` option to `true`:
415
+
416
+ ```javascript
417
+ var editor = new MediumEditor('.editable', {
418
+ autoLink: true
419
+ });
420
+ ```
421
+
422
+ ### Image Dragging Options
423
+
424
+ The image dragging handler is a built-in extension for handling dragging & dropping images into the contenteditable. This feature is ON by default.
425
+
426
+ To disable built-in image dragging, set the value of the `imageDragging` option to `false`:
427
+ ```javascript
428
+ var editor = new MediumEditor('.editable', {
429
+ imageDragging: false
430
+ });
431
+ ```
432
+
433
+ #### Disable File Dragging
434
+ To stop preventing drag & drop events and disable file dragging in general, provide a dummy ImageDragging extension.
435
+ ```javascript
436
+ var editor = new MediumEditor('.editor', {
437
+ extensions: {
438
+ 'imageDragging': {}
439
+ }
440
+ });
441
+ ```
442
+ Due to the [state of code](https://github.com/yabwe/medium-editor/issues/966) in 5.0.0, the editor *ALWAYS* prevented any drag and drop actions.
443
+ We will have a better way to disable file dragging in 6.*
444
+
445
+ ### Options Example:
446
+
447
+ ```javascript
448
+ var editor = new MediumEditor('.editable', {
449
+ delay: 1000,
450
+ targetBlank: true,
451
+ toolbar: {
452
+ buttons: ['bold', 'italic', 'quote'],
453
+ diffLeft: 25,
454
+ diffTop: 10,
455
+ },
456
+ anchor: {
457
+ placeholderText: 'Type a link',
458
+ customClassOption: 'btn',
459
+ customClassOptionText: 'Create Button'
460
+ },
461
+ paste: {
462
+ cleanPastedHTML: true,
463
+ cleanAttrs: ['style', 'dir'],
464
+ cleanTags: ['label', 'meta'],
465
+ unwrapTags: ['sub', 'sup']
466
+ },
467
+ anchorPreview: {
468
+ hideDelay: 300
469
+ },
470
+ placeholder: {
471
+ text: 'Click to edit'
472
+ }
473
+ });
474
+ ```
475
+
476
+ ## Buttons
477
+
478
+ By default, MediumEditor supports buttons for most of the commands for `document.execCommand()` that are well-supported across all its supported browsers.
479
+
480
+ ### Default buttons.
481
+
482
+ MediumEditor, by default, will show only the buttons listed here to avoid a huge toolbar:
483
+
484
+ * __bold__
485
+ * __italic__
486
+ * __underline__
487
+ * __anchor__ _(built-in support for collecting a url via the anchor extension)_
488
+ * __h2__
489
+ * __h3__
490
+ * __quote__
491
+
492
+ ### All buttons.
493
+
494
+ These are all the built-in buttons supported by MediumEditor.
495
+
496
+ * __bold__
497
+ * __italic__
498
+ * __underline__
499
+ * __strikethrough__
500
+ * __subscript__
501
+ * __superscript__
502
+ * __anchor__
503
+ * __image__ (this simply converts selected text to an image tag)
504
+ * __quote__
505
+ * __pre__
506
+ * __orderedlist__
507
+ * __unorderedlist__
508
+ * __indent__ (moves the selected text up one level)
509
+ * __outdent__ (moves the selected text down one level)
510
+ * __justifyLeft__
511
+ * __justifyCenter__
512
+ * __justifyRight__
513
+ * __justifyFull__
514
+ * __h1__
515
+ * __h2__
516
+ * __h3__
517
+ * __h4__
518
+ * __h5__
519
+ * __h6__
520
+ * __removeFormat__ (clears inline style formatting, preserves blocks)
521
+ * __html__ (parses selected html and converts into actual html elements)
522
+
523
+ ## Themes
524
+
525
+ Check out the Wiki page for a list of available themes: [https://github.com/yabwe/medium-editor/wiki/Themes](https://github.com/yabwe/medium-editor/wiki/Themes)
526
+
527
+ ## API
528
+
529
+ View the [MediumEditor Object API documentation](API.md) on the Wiki for details on all the methods supported on the MediumEditor object.
530
+
531
+ ### Initialization methods
532
+ * __MediumEditor(elements, options)__: Creates an instance of MediumEditor
533
+ * __.destroy()__: tears down the editor if already setup, removing all DOM elements and event handlers
534
+ * __.setup()__: rebuilds the editor if it has already been destroyed, recreating DOM elements and attaching event handlers
535
+ * __.addElements()__: add elements to an already initialized instance of MediumEditor
536
+ * __.removeElements()__: remove elements from an already initialized instance of MediumEditor
537
+
538
+ ### Event Methods
539
+ * __.on(target, event, listener, useCapture)__: attach a listener to a DOM event which will be detached when MediumEditor is deactivated
540
+ * __.off(target, event, listener, useCapture)__: detach a listener to a DOM event that was attached via `on()`
541
+ * __.subscribe(event, listener)__: attaches a listener to a custom medium-editor event
542
+ * __.unsubscribe(event, listener)__: detaches a listener from a custom medium-editor event
543
+ * __.trigger(name, data, editable)__: manually triggers a custom medium-editor event
544
+
545
+ ### Selection Methods
546
+ * __.checkSelection()__: manually trigger an update of the toolbar and extensions based on the current selection
547
+ * __.exportSelection()__: return a data representation of the selected text, which can be applied via `importSelection()`
548
+ * __.importSelection(selectionState)__: restore the selection using a data representation of previously selected text (ie value returned by `exportSelection()`)
549
+ * __.getFocusedElement()__: returns an element if any contenteditable element monitored by MediumEditor currently has focused
550
+ * __.getSelectedParentElement(range)__: get the parent contenteditable element that contains the current selection
551
+ * __.restoreSelection()__: restore the selection to what was selected when `saveSelection()` was called
552
+ * __.saveSelection()__: internally store the set of selected text
553
+ * __.selectAllContents()__: expands the selection to contain all text within the focused contenteditable
554
+ * __.selectElement(element)__: change selection to be a specific element and update the toolbar to reflect the selection
555
+ * __.stopSelectionUpdates()__: stop the toolbar from updating to reflect the state of the selected text
556
+ * __.startSelectionUpdates()__: put the toolbar back into its normal updating state
557
+
558
+ ### Editor Action Methods
559
+ * __.cleanPaste(text)__: convert text to plaintext and replace current selection with result
560
+ * __.createLink(opts)__: creates a link via the native `document.execCommand('createLink')` command
561
+ * __.execAction(action, opts)__: executes an built-in action via `document.execCommand`
562
+ * __.pasteHTML(html, options)__: replace the current selection with html
563
+ * __.queryCommandState(action)__: wrapper around the browser's built in `document.queryCommandState(action)` for checking whether a specific action has already been applied to the selection.
564
+
565
+ ### Helper Methods
566
+ * __.delay(fn)__: delay any function from being executed by the amount of time passed as the `delay` option
567
+ * __.getContent(index)__: gets the trimmed `innerHTML` of the element at `index`
568
+ * __.getExtensionByName(name)__: get a reference to an extension with the specified name
569
+ * __.resetContent(element)__: reset the content of all elements or a specific element to its value when added to the editor initially
570
+ * __.serialize()__: returns a JSON object with elements contents
571
+ * __.setContent(html, index)__: sets the `innerHTML` to `html` of the element at `index`
572
+
573
+ ### Static Methods/Properties
574
+ * __.getEditorFromElement(element)__: retrieve the instance of MediumEditor that is monitoring the provided editor element
575
+ * __.version__: the version information for the MediumEditor library
576
+
577
+ ## Dynamically add/remove elements to your instance
578
+
579
+ It is possible to dynamically add new elements to your existing MediumEditor instance:
580
+
581
+ ```javascript
582
+ var editor = new MediumEditor('.editable');
583
+ editor.subscribe('editableInput', this._handleEditableInput.bind(this));
584
+
585
+ // imagine an ajax fetch/any other dynamic functionality which will add new '.editable' elements to the DOM
586
+
587
+ editor.addElements('.editable');
588
+ // OR editor.addElements(document.getElementsByClassName('editable'));
589
+ // OR editor.addElements(document.querySelectorAll('.editable'));
590
+ ```
591
+
592
+ Passing an elements or array of elements to `addElements(elements)` will:
593
+ * Add the given element or array of elements to the internal `this.elements` array.
594
+ * Ensure the element(s) are initialized with the proper attributes and event handlers as if the element had been passed during instantiation of the editor.
595
+ * For any `<textarea>` elements:
596
+ * Hide the `<textarea>`
597
+ * Create a new `<div contenteditable=true>` element and add it to the elements array.
598
+ * Ensure the 2 elements remain sync'd.
599
+ * Be intelligent enough to run the necessary code only once per element, no matter how often you will call it.
600
+
601
+ ### Removing elements dynamically
602
+
603
+ Straight forward, just call `removeElements` with the element or array of elements you to want to tear down. Each element itself will remain a contenteditable - it will just remove all event handlers and all references to it so you can safely remove it from DOM.
604
+
605
+ ```javascript
606
+ editor.removeElements(document.querySelector('#myElement'));
607
+ // OR editor.removeElements(document.getElementById('myElement'));
608
+ // OR editor.removeElements('#myElement');
609
+
610
+ // in case you have jQuery and don't exactly know when an element was removed, for example after routing state change
611
+ var removedElements = [];
612
+ editor.elements.forEach(function (element) {
613
+ // check if the element is still available in current DOM
614
+ if (!$(element).parents('body').length) {
615
+ removedElements.push(element);
616
+ }
617
+ });
618
+
619
+ editor.removeElements(removedElements);
620
+ ```
621
+
622
+ ## Capturing DOM changes
623
+
624
+ For observing any changes on contentEditable, use the custom `'editableInput'` event exposed via the `subscribe()` method:
625
+
626
+ ```js
627
+ var editor = new MediumEditor('.editable');
628
+ editor.subscribe('editableInput', function (event, editable) {
629
+ // Do some work
630
+ });
631
+ ```
632
+
633
+ This event is supported in all browsers supported by MediumEditor (including IE9+ and Edge)! To help with cases when one instance of MediumEditor is monitoring multiple elements, the 2nd argument passed to the event handler (`editable` in the example above) will be a reference to the contenteditable element that has actually changed.
634
+
635
+ This is handy when you need to capture any modifications to the contenteditable element including:
636
+ * Typing
637
+ * Cutting/Pasting
638
+ * Changes from clicking on buttons in the toolbar
639
+ * Undo/Redo
640
+
641
+ Why is this interesting and why should you use this event instead of just attaching to the `input` event on the contenteditable element?
642
+
643
+ So for most modern browsers (Chrome, Firefox, Safari, etc.), the `input` event works just fine. In fact, `editableInput` is just a proxy for the `input` event in those browsers. However, the `input` event [is not supported for contenteditable elements in IE 9-11](https://connect.microsoft.com/IE/feedback/details/794285/ie10-11-input-event-does-not-fire-on-div-with-contenteditable-set) and is _mostly_ supported in Microsoft Edge, but not fully.
644
+
645
+ So, to properly support the `editableInput` event in Internet Explorer and Microsoft Edge, MediumEditor uses a combination of the `selectionchange` and `keypress` events, as well as monitoring calls to `document.execCommand`.
646
+
647
+ ## Extensions & Plugins
648
+
649
+ Check the [documentation](src/js/extensions) in order to learn how to develop extensions for MediumEditor.
650
+
651
+ A list of existing extensions and plugins, such as [Images and Media embeds](http://orthes.github.io/medium-editor-insert-plugin/), [Tables](https://github.com/yabwe/medium-editor-tables) and [Markdown](https://github.com/IonicaBizau/medium-editor-markdown) can be found [here](https://github.com/yabwe/medium-editor/wiki/Extensions-Plugins).
652
+
653
+ ## Development
654
+
655
+ To run the demo locally:
656
+
657
+ 1. Clone this repo locally
658
+ 2. Run `npm install` from your console at the root
659
+ 3. Run `node index.js` from the root
660
+ 4. Navigate to `http://localhost:8088/demo/index.html` to view the demo
661
+
662
+ MediumEditor development tasks are managed by Grunt. To install all the necessary packages, just invoke:
663
+
664
+ ```bash
665
+ npm install
666
+ ```
667
+
668
+ To run all the test and build the dist files for testing on demo pages, just invoke:
669
+ ```bash
670
+ grunt
671
+ ```
672
+
673
+ These are the other available grunt tasks:
674
+
675
+ * __js__: runs jslint and jasmine tests and creates minified and concatenated versions of the script;
676
+ * __css__: runs autoprefixer and csslint
677
+ * __test__: runs jasmine tests, jslint and csslint
678
+ * __watch__: watch for modifications on script/scss files
679
+ * __spec__: runs a task against a specified file
680
+
681
+ The source files are located inside the __src__ directory. Be sure to make changes to these files and not files in the dist directory.
682
+
683
+ ## Contributing
684
+
685
+ [Kill some bugs :)](https://github.com/yabwe/medium-editor/issues?q=is%3Aopen+is%3Aissue+label%3Abug)
686
+
687
+ 1. Fork it
688
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
689
+ 3. Test your changes to the best of your ability.
690
+ 4. Update the documentation to reflect your changes if they add or changes current functionality.
691
+ 5. Commit your changes (`git commit -am 'Added some feature'`) **without files from the _dist_ directory**.
692
+ 6. Push to the branch (`git push origin my-new-feature`)
693
+ 7. Create new Pull Request
694
+
695
+ ### Code Consistency
696
+
697
+ To help create consistent looking code throughout the project, we use a few tools to help us. They have plugins for most popular editors/IDEs to make coding for our project, but you should use them in your project as well!
698
+
699
+ #### JSHint
700
+
701
+ We use [JSHint](http://jshint.com/) on each build to find easy-to-catch errors and potential problems in our js. You can find our JSHint settings in the `.jshintrc` file in the root of the project.
702
+
703
+ #### jscs
704
+
705
+ We use [jscs](http://jscs.info/) on each build to enforce some code style rules we have for our project. You can find our jscs settings in the `.jscsrc` file in the root of the project.
706
+
707
+ #### EditorConfig
708
+
709
+ We use [EditorConfig](http://EditorConfig.org) to maintain consistent coding styles between various editors and IDEs. You can find our settings in the `.editorconfig` file in the root of the project.
710
+
711
+ ### Easy First Bugs
712
+
713
+ Looking for something simple for a first contribution? Try fixing an [easy first bug](https://github.com/yabwe/medium-editor/issues?q=is%3Aopen+is%3Aissue+label%3A%22easy+first+bug%22)!
714
+
715
+ ## Contributors (100+ and counting!)
716
+
717
+ [https://github.com/yabwe/medium-editor/graphs/contributors](https://github.com/yabwe/medium-editor/graphs/contributors)
718
+
719
+ ## Is Your Org Using MediumEditor?
720
+
721
+ Add your org [here](https://github.com/yabwe/medium-editor/issues/828) and we can add you to our [landing page](https://yabwe.github.io/medium-editor/#who-is-using-it)!
722
+
723
+ ## License
724
+
725
+ MIT: https://github.com/yabwe/medium-editor/blob/master/LICENSE