@themarioga/grid-editor 5.1.2 → 5.2.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.
Files changed (36) hide show
  1. package/BUILDING.md +6 -0
  2. package/CHANGELOG.md +25 -0
  3. package/README.md +17 -8
  4. package/UPGRADING.md +25 -0
  5. package/dist/jquery.grideditor.bundle.min.js +2 -2
  6. package/dist/jquery.grideditor.js +369 -223
  7. package/dist/jquery.grideditor.min.js +1 -1
  8. package/dist/jquery.grideditor.min.js.map +1 -1
  9. package/dist/plugins/grideditor.ckeditor.js +115 -0
  10. package/dist/plugins/grideditor.ckeditor.min.js +2 -0
  11. package/dist/plugins/grideditor.ckeditor.min.js.map +1 -0
  12. package/dist/plugins/grideditor.summernote.js +93 -0
  13. package/dist/plugins/grideditor.summernote.min.js +2 -0
  14. package/dist/plugins/grideditor.summernote.min.js.map +1 -0
  15. package/dist/plugins/grideditor.tinymce.js +157 -0
  16. package/dist/plugins/grideditor.tinymce.min.js +2 -0
  17. package/dist/plugins/grideditor.tinymce.min.js.map +1 -0
  18. package/docs/plugins.md +55 -3
  19. package/example/attributes.html +2 -0
  20. package/example/autosave.html +2 -0
  21. package/example/basic.html +2 -0
  22. package/example/ckeditor.html +2 -0
  23. package/example/clipboard.html +2 -0
  24. package/example/containers.html +2 -0
  25. package/example/elements.html +2 -0
  26. package/example/summernote.html +2 -0
  27. package/example/wrap_content.html +2 -0
  28. package/package.json +1 -1
  29. package/src/js/compat/bundled-texts.js +14 -0
  30. package/src/js/jquery.grideditor.js +68 -11
  31. package/src/js/plugins/grideditor.ckeditor.js +115 -0
  32. package/src/js/plugins/grideditor.summernote.js +93 -0
  33. package/src/js/plugins/grideditor.tinymce.js +157 -0
  34. package/src/js/jquery.grideditor.ckeditor.js +0 -77
  35. package/src/js/jquery.grideditor.summernote.js +0 -72
  36. package/src/js/jquery.grideditor.tinymce.js +0 -143
@@ -0,0 +1,157 @@
1
+ /**
2
+ * tinyMCE for grid-editor's content areas.
3
+ *
4
+ * A text editor plugin: load this file after the editor, and tinyMCE 6
5
+ * after or before it, and content_types: ['tinymce'] edits each content area
6
+ * with an inline tinyMCE. What it can ask the editor for is the handle its
7
+ * factory is called with, described in docs/plugins.md.
8
+ *
9
+ * <script src="tinymce/tinymce.min.js"></script>
10
+ * <script src="dist/jquery.grideditor.min.js"></script>
11
+ * <script src="dist/plugins/grideditor.tinymce.min.js"></script>
12
+ *
13
+ * Up to 6.0 the main bundle carries a copy of this file too.
14
+ */
15
+ (function($) {
16
+
17
+ // tinyMCE snapshots the target element's attributes when an inline editor is
18
+ // created and restores them on remove(), so this has to run *after* remove()
19
+ // to keep the grid editor's own class and tinyMCE's leftovers off the element.
20
+ function cleanUp(contentArea) {
21
+ contentArea
22
+ .removeClass('active')
23
+ .removeClass('ge-rte-active')
24
+ .removeAttr('id')
25
+ .removeAttr('style')
26
+ .removeAttr('spellcheck')
27
+ .removeAttr('contenteditable')
28
+ .removeAttr('data-mce-style')
29
+ ;
30
+ }
31
+
32
+ var INITIAL_CONTENT = '<p>Lorem ipsum dolores</p>';
33
+
34
+ $.fn.gridEditor.texts.tinymce = function(ge) {
35
+ return {
36
+ initialContent: INITIAL_CONTENT,
37
+ missingKey: 'error.tinymce_missing',
38
+
39
+ available: function() { return !!window.tinymce; },
40
+
41
+ start: function(contentAreas) {
42
+ var settings = ge.settings;
43
+ var userConfig = (settings.tinymce && settings.tinymce.config) ? settings.tinymce.config : {};
44
+
45
+ contentAreas.each(function() {
46
+ var contentArea = $(this);
47
+ if (contentArea.hasClass('active')) { return; }
48
+
49
+ if (contentArea.html() == INITIAL_CONTENT) {
50
+ contentArea.html('');
51
+ }
52
+ contentArea.addClass('active');
53
+
54
+ var configuration = $.extend({
55
+ // tinyMCE's own "Upgrade" badge in the menubar. Off by
56
+ // default because an inline editor here is a column of
57
+ // someone's page, not tinyMCE's own interface; a host that
58
+ // wants it back passes promotion: true.
59
+ promotion: false,
60
+ }, userConfig, {
61
+ target: this,
62
+ inline: true,
63
+ init_instance_callback: function(editor) {
64
+ // deinit ran before this init finished, so tear it down again
65
+ if (contentArea.data('ge-tinymce-pending-remove')) {
66
+ contentArea.removeData('ge-tinymce-pending-remove');
67
+ editor.remove();
68
+ // deinit already ran and cannot clean up after this
69
+ // late remove(), so do it here instead
70
+ cleanUp(contentArea);
71
+ return;
72
+ }
73
+
74
+ contentArea.data('ge-tinymce', editor);
75
+
76
+ // The editor rewrote what is inside this content area
77
+ // while it took it over, so whatever the grid editor
78
+ // had in there is gone. Saying so lets it put its own
79
+ // furniture back.
80
+ ge.textReady(contentArea);
81
+
82
+ // Undo and redo rewrite it too, from snapshots that
83
+ // leave out whatever is marked data-mce-bogus - an
84
+ // element's drawer among them - so it goes back in
85
+ // after each of those as well
86
+ editor.on('Undo Redo', function() {
87
+ ge.textReady(contentArea);
88
+ });
89
+
90
+ // The inline toolbar is laid out against the element's
91
+ // geometry at the moment tinyMCE draws it, and an
92
+ // element that has just appeared - a tab pane, an
93
+ // accordion body, a column whose width is still
94
+ // settling - may not have its own width yet. Asking
95
+ // for the ui again once the browser has laid the frame
96
+ // out measures it as it now is, instead of leaving a
97
+ // toolbar wrapped into a narrow column.
98
+ window.requestAnimationFrame(function() {
99
+ if (editor.removed || !editor.ui || !editor.ui.show) { return; }
100
+
101
+ editor.ui.show();
102
+ });
103
+
104
+ // And again whenever the user comes back to this
105
+ // editor: by then the element may have been resized,
106
+ // hidden and shown again - a tab switched away from
107
+ // and back, a column made narrower - and the toolbar
108
+ // is only ever as right as its last measurement.
109
+ editor.on('focus', function() {
110
+ window.requestAnimationFrame(function() {
111
+ if (editor.removed || !editor.ui || !editor.ui.show) { return; }
112
+
113
+ editor.ui.show();
114
+ });
115
+ });
116
+
117
+ // Bring focus to text field
118
+ editor.focus();
119
+
120
+ // Call the original callbacks, if any were passed in the config
121
+ if (userConfig.init_instance_callback) {
122
+ userConfig.init_instance_callback.call(this, editor);
123
+ }
124
+ // 'oninit' is the pre-6 name, honoured so existing configs keep working
125
+ if (userConfig.oninit) {
126
+ userConfig.oninit.call(this, editor);
127
+ }
128
+ }
129
+ });
130
+ // We always edit the element we were handed, so a selector in the
131
+ // user config would only fight with target
132
+ delete configuration.selector;
133
+
134
+ window.tinymce.init(configuration);
135
+ });
136
+ },
137
+
138
+ stop: function(contentAreas) {
139
+ contentAreas.filter('.active').each(function() {
140
+ var contentArea = $(this);
141
+ var editor = contentArea.data('ge-tinymce');
142
+
143
+ if (editor) {
144
+ contentArea.removeData('ge-tinymce');
145
+ editor.remove();
146
+ } else {
147
+ // tinymce.init() is asynchronous, so the editor may not exist
148
+ // yet. Leave a note for init_instance_callback to remove it.
149
+ contentArea.data('ge-tinymce-pending-remove', true);
150
+ }
151
+
152
+ cleanUp(contentArea);
153
+ });
154
+ },
155
+ };
156
+ };
157
+ })(jQuery);
@@ -0,0 +1,2 @@
1
+ (o=>{function a(e){e.removeClass("active").removeClass("ge-rte-active").removeAttr("id").removeAttr("style").removeAttr("spellcheck").removeAttr("contenteditable").removeAttr("data-mce-style")}var c="<p>Lorem ipsum dolores</p>";o.fn.gridEditor.texts.tinymce=function(n){return{initialContent:c,missingKey:"error.tinymce_missing",available:function(){return!!window.tinymce},start:function(e){var t=n.settings,i=t.tinymce&&t.tinymce.config?t.tinymce.config:{};e.each(function(){var e,t=o(this);t.hasClass("active")||(t.html()==c&&t.html(""),t.addClass("active"),delete(e=o.extend({promotion:!1},i,{target:this,inline:!0,init_instance_callback:function(e){t.data("ge-tinymce-pending-remove")?(t.removeData("ge-tinymce-pending-remove"),e.remove(),a(t)):(t.data("ge-tinymce",e),n.textReady(t),e.on("Undo Redo",function(){n.textReady(t)}),window.requestAnimationFrame(function(){!e.removed&&e.ui&&e.ui.show&&e.ui.show()}),e.on("focus",function(){window.requestAnimationFrame(function(){!e.removed&&e.ui&&e.ui.show&&e.ui.show()})}),e.focus(),i.init_instance_callback&&i.init_instance_callback.call(this,e),i.oninit&&i.oninit.call(this,e))}})).selector,window.tinymce.init(e))})},stop:function(e){e.filter(".active").each(function(){var e=o(this),t=e.data("ge-tinymce");t?(e.removeData("ge-tinymce"),t.remove()):e.data("ge-tinymce-pending-remove",!0),a(e)})}}}})(jQuery);
2
+ //# sourceMappingURL=grideditor.tinymce.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grideditor.tinymce.min.js","sources":["../../src/js/plugins/grideditor.tinymce.js"],"names":["$","cleanUp","contentArea","removeClass","removeAttr","INITIAL_CONTENT","fn","gridEditor","texts","tinymce","ge","initialContent","missingKey","available","window","start","contentAreas","settings","userConfig","config","each","configuration","this","hasClass","html","addClass","extend","promotion","target","inline","init_instance_callback","editor","data","removeData","remove","textReady","on","requestAnimationFrame","removed","ui","show","focus","call","oninit","selector","init","stop","filter","jQuery"],"mappings":"CAcUA,IAKN,SAASC,EAAQC,GACbA,EACKC,YAAY,QAAQ,EACpBA,YAAY,eAAe,EAC3BC,WAAW,IAAI,EACfA,WAAW,OAAO,EAClBA,WAAW,YAAY,EACvBA,WAAW,iBAAiB,EAC5BA,WAAW,gBAAgB,CAEpC,CAEA,IAAIC,EAAkB,6BAEtBL,EAAEM,GAAGC,WAAWC,MAAMC,QAAU,SAASC,GACrC,MAAO,CACHC,eAAgBN,EAChBO,WAAY,wBAEZC,UAAW,WAAa,MAAO,CAAC,CAACC,OAAOL,OAAS,EAEjDM,MAAO,SAASC,GACZ,IAAIC,EAAWP,EAAGO,SACdC,EAAcD,EAASR,SAAWQ,EAASR,QAAQU,OAAUF,EAASR,QAAQU,OAAS,GAE3FH,EAAaI,KAAK,WACd,IAQIC,EARAnB,EAAcF,EAAEsB,IAAI,EACpBpB,EAAYqB,SAAS,QAAQ,IAE7BrB,EAAYsB,KAAK,GAAKnB,GACtBH,EAAYsB,KAAK,EAAE,EAEvBtB,EAAYuB,SAAS,QAAQ,EAgF7B,OA9EIJ,EAAgBrB,EAAE0B,OAAO,CAKzBC,UAAW,CAAA,CACf,EAAGT,EAAY,CACXU,OAAQN,KACRO,OAAQ,CAAA,EACRC,uBAAwB,SAASC,GAEzB7B,EAAY8B,KAAK,2BAA2B,GAC5C9B,EAAY+B,WAAW,2BAA2B,EAClDF,EAAOG,OAAO,EAGdjC,EAAQC,CAAW,IAIvBA,EAAY8B,KAAK,aAAcD,CAAM,EAMrCrB,EAAGyB,UAAUjC,CAAW,EAMxB6B,EAAOK,GAAG,YAAa,WACnB1B,EAAGyB,UAAUjC,CAAW,CAC5B,CAAC,EAUDY,OAAOuB,sBAAsB,WACrBN,CAAAA,EAAOO,SAAYP,EAAOQ,IAAOR,EAAOQ,GAAGC,MAE/CT,EAAOQ,GAAGC,KAAK,CACnB,CAAC,EAODT,EAAOK,GAAG,QAAS,WACftB,OAAOuB,sBAAsB,WACrBN,CAAAA,EAAOO,SAAYP,EAAOQ,IAAOR,EAAOQ,GAAGC,MAE/CT,EAAOQ,GAAGC,KAAK,CACnB,CAAC,CACL,CAAC,EAGDT,EAAOU,MAAM,EAGTvB,EAAWY,wBACXZ,EAAWY,uBAAuBY,KAAKpB,KAAMS,CAAM,EAGnDb,EAAWyB,QACXzB,EAAWyB,OAAOD,KAAKpB,KAAMS,CAAM,EAE3C,CACJ,CAAC,GAGoBa,SAErB9B,OAAOL,QAAQoC,KAAKxB,CAAa,EACrC,CAAC,CACL,EAEAyB,KAAM,SAAS9B,GACXA,EAAa+B,OAAO,SAAS,EAAE3B,KAAK,WAChC,IAAIlB,EAAcF,EAAEsB,IAAI,EACpBS,EAAS7B,EAAY8B,KAAK,YAAY,EAEtCD,GACA7B,EAAY+B,WAAW,YAAY,EACnCF,EAAOG,OAAO,GAIdhC,EAAY8B,KAAK,4BAA6B,CAAA,CAAI,EAGtD/B,EAAQC,CAAW,CACvB,CAAC,CACL,CACJ,CACJ,CACH,GAAE8C,MAAM"}
package/docs/plugins.md CHANGED
@@ -14,13 +14,15 @@ the one to read first if you are about to write your own.
14
14
  <script src="dist/plugins/grideditor.elements.min.js"></script>
15
15
  ```
16
16
 
17
- There are three kinds. A **container plugin** builds a type of container — it
17
+ There are four kinds. A **container plugin** builds a type of container — it
18
18
  registers under `$.fn.gridEditor.containers`, and the toolbar offers a button
19
19
  for it. A **utility plugin** declares families of Bootstrap's responsive
20
20
  utility classes — `$.fn.gridEditor.utilities` — and the editor edits them per
21
- breakpoint. A **feature plugin** is anything else the editor can do —
21
+ breakpoint. A **text editor plugin** edits content areas with a rich text
22
+ editor — `$.fn.gridEditor.texts`, one per editor: tinyMCE, CKEditor,
23
+ summernote. A **feature plugin** is anything else the editor can do —
22
24
  `$.fn.gridEditor.features`, hooks into the canvas, and may contribute methods.
23
- All three are factories called once per editor with the same handle.
25
+ All four are factories called once per editor with the same handle.
24
26
 
25
27
  The `plugins` setting names which of the loaded ones to use:
26
28
 
@@ -124,6 +126,7 @@ not change without a major version.
124
126
  | `ge.setUtility(node, family, value, options?)` | Write one through the events. `options` is a view key or `{ view, source }` |
125
127
  | `ge.utilityField(node, family)` | A panel field for one family, for a plugin that builds its own panel |
126
128
  | `ge.rowFromLayout(layout)` | A detached row from a layout: `[8, 4]`, `['auto', 'equal']` or `{ row_cols, columns }` |
129
+ | `ge.textReady(block)` | A text editor has rewritten a content area: whatever the editor and its plugins put in there goes back in |
127
130
  | `ge.nodeHtml(node)` | One node's markup as `getHtml` would give it. The canvas leaves editing to read it and comes back, as it does for `getHtml` |
128
131
  | `ge.toolbarItems(name)` | The toolbar buttons the feature plugin `name` declared, for showing and hiding them |
129
132
  | `ge.bareStyle(node, family, property)` | A css property's value on the node with none of the family's classes: what a preview shows when no class applies and that is not a constant |
@@ -251,6 +254,55 @@ plugin knows which of its parts move: the tabs plugin makes its strip sortable
251
254
  and puts the panes back in strip order in `afterPaneMove`.
252
255
 
253
256
 
257
+ Text editor plugins
258
+ -------------------
259
+
260
+ A text editor plugin edits content areas with a rich text editor. It is
261
+ registered under the content type it edits - the name `content_types` uses -
262
+ and the three that ship, `grideditor.tinymce.js`, `grideditor.ckeditor.js` and
263
+ `grideditor.summernote.js`, are the ones to read:
264
+
265
+ ```javascript
266
+ $.fn.gridEditor.texts.mytext = function(ge) {
267
+ return {
268
+ initialContent: '<p>Write here</p>', // what a new column's content area holds
269
+ missingKey: 'error.mytext_missing', // the error when the library is not loaded, optional
270
+ available: function() { return !!window.MyText; }, // optional, true otherwise
271
+
272
+ start: function(block) { … }, // open the editor on this content area
273
+ stop: function(block) { … }, // close it, and leave the content area as it was
274
+ };
275
+ };
276
+ ```
277
+
278
+ - **`start(block)`** is called when the user clicks a content area of this
279
+ type. The editor marks it `ge-rte-active` first, and does not call `start`
280
+ again until it has been stopped. Once the library has taken the content
281
+ area over, call **`ge.textReady(block)`**: it rewrote what was inside, and
282
+ the editor and its plugins put their own furniture back - an element's
283
+ drawer, for one. Call it again whenever the library rewrites the content
284
+ area again, as tinyMCE's undo does.
285
+ - **`stop(block)`** is called for every content area of this type on each
286
+ `deinit`, which `getHtml` runs: a content area whose editor never started is
287
+ the plugin's to ignore. Whatever the library and `start` added - classes,
288
+ `contenteditable`, inline styles, ids - comes off here.
289
+ - **`available()`** false and the editor logs `missingKey`'s message and
290
+ starts nothing, without marking the content area, so a later click tries
291
+ again once the library is there.
292
+
293
+ A text editor is chosen by `content_types`, never by the `plugins` setting: a
294
+ page that names its containers there has named no editor.
295
+
296
+ Up to 6.0 the main bundle carries a copy of the three shipped ones, marked
297
+ `bundled`; editing with a copy warns once that 6.0 leaves them out. A plugin's
298
+ own file, loaded after, registers itself again and is the one used.
299
+
300
+ 5.x's `$.fn.gridEditor.RTEs.mytext = { init, deinit, initialContent }` still
301
+ works: `init(settings, contentAreas)` and `deinit(settings, contentAreas)` are
302
+ wrapped as `start` and `stop`, with a warning that `RTEs` is deprecated. It goes
303
+ in 7.0.
304
+
305
+
254
306
  Utility plugins
255
307
  ---------------
256
308
 
@@ -17,6 +17,8 @@
17
17
  <!-- Grid editor javascript -->
18
18
  <script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script>
19
19
  <script src="../dist/jquery.grideditor.min.js"></script>
20
+ <!-- The text editor is a plugin of its own, loaded after the editor -->
21
+ <script src="../dist/plugins/grideditor.tinymce.min.js"></script>
20
22
  <script src="../dist/plugins/grideditor.card.min.js"></script>
21
23
  <script src="../dist/plugins/grideditor.elements.min.js"></script>
22
24
 
@@ -18,6 +18,8 @@
18
18
  <!-- Grid editor javascript -->
19
19
  <script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script>
20
20
  <script src="../dist/jquery.grideditor.min.js"></script>
21
+ <!-- The text editor is a plugin of its own, loaded after the editor -->
22
+ <script src="../dist/plugins/grideditor.tinymce.min.js"></script>
21
23
  </head>
22
24
  <body>
23
25
  <!-- Back to the demo index -->
@@ -18,6 +18,8 @@
18
18
  <!-- The editor with SortableJS inside it. Every other example loads the
19
19
  two files separately; a page does one or the other, never both. -->
20
20
  <script src="../dist/jquery.grideditor.bundle.min.js"></script>
21
+ <!-- The text editor is a plugin of its own, loaded after the editor -->
22
+ <script src="../dist/plugins/grideditor.tinymce.min.js"></script>
21
23
 
22
24
  <script>
23
25
  $(function() {
@@ -19,6 +19,8 @@
19
19
  <!-- Grid editor javascript -->
20
20
  <script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script>
21
21
  <script src="../dist/jquery.grideditor.min.js"></script>
22
+ <!-- The text editor is a plugin of its own, loaded after the editor -->
23
+ <script src="../dist/plugins/grideditor.ckeditor.min.js"></script>
22
24
 
23
25
  <script>
24
26
  $(function() {
@@ -17,6 +17,8 @@
17
17
  <!-- Grid editor javascript -->
18
18
  <script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script>
19
19
  <script src="../dist/jquery.grideditor.min.js"></script>
20
+ <!-- The text editor is a plugin of its own, loaded after the editor -->
21
+ <script src="../dist/plugins/grideditor.tinymce.min.js"></script>
20
22
  <!-- Copy and paste is a plugin of its own. The others are here so there
21
23
  is a bit of everything to copy. -->
22
24
  <script src="../dist/plugins/grideditor.clipboard.min.js"></script>
@@ -17,6 +17,8 @@
17
17
  <!-- Grid editor javascript -->
18
18
  <script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script>
19
19
  <script src="../dist/jquery.grideditor.min.js"></script>
20
+ <!-- The text editor is a plugin of its own, loaded after the editor -->
21
+ <script src="../dist/plugins/grideditor.tinymce.min.js"></script>
20
22
  <!-- One file per container type: load the ones this page offers -->
21
23
  <script src="../dist/plugins/grideditor.tabs.min.js"></script>
22
24
  <script src="../dist/plugins/grideditor.accordion.min.js"></script>
@@ -17,6 +17,8 @@
17
17
  <!-- Grid editor javascript -->
18
18
  <script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script>
19
19
  <script src="../dist/jquery.grideditor.min.js"></script>
20
+ <!-- The text editor is a plugin of its own, loaded after the editor -->
21
+ <script src="../dist/plugins/grideditor.tinymce.min.js"></script>
20
22
  <script src="../dist/plugins/grideditor.elements.min.js"></script>
21
23
 
22
24
  <style>
@@ -21,6 +21,8 @@
21
21
  <!-- Grid editor javascript -->
22
22
  <script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script>
23
23
  <script src="../dist/jquery.grideditor.min.js"></script>
24
+ <!-- The text editor is a plugin of its own, loaded after the editor -->
25
+ <script src="../dist/plugins/grideditor.summernote.min.js"></script>
24
26
 
25
27
  <script>
26
28
  $(function() {
@@ -17,6 +17,8 @@
17
17
  <!-- Grid editor javascript -->
18
18
  <script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script>
19
19
  <script src="../dist/jquery.grideditor.min.js"></script>
20
+ <!-- The text editor is a plugin of its own, loaded after the editor -->
21
+ <script src="../dist/plugins/grideditor.tinymce.min.js"></script>
20
22
 
21
23
  <script>
22
24
  $(function() {
package/package.json CHANGED
@@ -34,7 +34,7 @@
34
34
  "comment": "A scoped package publishes restricted unless it is told otherwise.",
35
35
  "access": "public"
36
36
  },
37
- "version": "5.1.2",
37
+ "version": "5.2.0",
38
38
  "main": "dist/jquery.grideditor.js",
39
39
  "files": [
40
40
  "dist",
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Up to 6.0 the main bundle carries a copy of each text editor plugin, so a
3
+ * page that loaded only the editor keeps the editor it had. This marks those
4
+ * copies, and the editor warns once when a content area is edited with one:
5
+ * 6.0 leaves them out. Loaded on its own, a plugin's file registers itself
6
+ * again, unmarked, and is the one used.
7
+ *
8
+ * Only ever concatenated after the plugins it marks, never built on its own.
9
+ */
10
+ (function($) {
11
+ ['tinymce', 'ckeditor', 'summernote'].forEach(function(type) {
12
+ if ($.fn.gridEditor.texts[type]) { $.fn.gridEditor.texts[type].bundled = true; }
13
+ });
14
+ })(jQuery);
@@ -1404,11 +1404,25 @@ $.fn.gridEditor = function( optionsOrMethod ) {
1404
1404
  // its toolbar out against, and nothing anyone can type into
1405
1405
  if (!$(this).is(':visible')) { return; }
1406
1406
 
1407
- var rte = getRTE($(this).data('ge-content-type'));
1408
- if (rte) {
1409
- $(this).addClass('ge-rte-active', true);
1410
- rte.init(settings, $(this));
1407
+ var type = $(this).data('ge-content-type');
1408
+ var text = textFor(type);
1409
+ if (!text) { return; }
1410
+
1411
+ // Not marked active, so the next click tries again: the library
1412
+ // may be loaded by then
1413
+ if (text.available && !text.available()) {
1414
+ if (text.missingKey) { console.error(t(text.missingKey)); }
1415
+ return;
1416
+ }
1417
+
1418
+ if (text.bundled) {
1419
+ warnOnceHere('text-bundled:' + type, 'the ' + type + ' text editor is the copy in the ' +
1420
+ 'main bundle, which 6.0 will not include: load dist/plugins/grideditor.' + type +
1421
+ '.js after the editor');
1411
1422
  }
1423
+
1424
+ $(this).addClass('ge-rte-active');
1425
+ text.start($(this));
1412
1426
  }
1413
1427
 
1414
1428
  function reset() {
@@ -1436,9 +1450,9 @@ $.fn.gridEditor = function( optionsOrMethod ) {
1436
1450
  canvas.removeClass('ge-editing ge-drag-drawer ge-dropping');
1437
1451
  var contents = canvas.find('.ge-content').each(function() {
1438
1452
  var content = $(this);
1439
- var rte = getRTE(content.data('ge-content-type'));
1440
- if (rte) {
1441
- rte.deinit(settings, content);
1453
+ var text = textFor(content.data('ge-content-type'));
1454
+ if (text) {
1455
+ text.stop(content);
1442
1456
  }
1443
1457
  // Cleared after rte.deinit, not before: an editor can restore the
1444
1458
  // class attribute it snapshotted when it was created, which would
@@ -1555,6 +1569,23 @@ $.fn.gridEditor = function( optionsOrMethod ) {
1555
1569
  if (wanted(name)) { FEATURES[name] = factory(api); }
1556
1570
  });
1557
1571
 
1572
+ // Text editors are chosen by content_types rather than by the
1573
+ // plugins setting: a page that names its containers there has
1574
+ // named no editor, and still wants the one it uses
1575
+ $.each($.fn.gridEditor.texts, function(type, factory) {
1576
+ TEXTS[type] = $.extend({ bundled: !!factory.bundled }, factory(api));
1577
+ });
1578
+
1579
+ // 5.x's registry, for an integration a host wrote itself. One the
1580
+ // plugins above already provide is theirs.
1581
+ $.each($.fn.gridEditor.RTEs, function(type, rte) {
1582
+ if (TEXTS[type]) { return; }
1583
+
1584
+ warnOnceHere('rtes:' + type, '$.fn.gridEditor.RTEs is deprecated and will be removed ' +
1585
+ 'in 7.0: register "' + type + '" under $.fn.gridEditor.texts, see docs/plugins.md');
1586
+ TEXTS[type] = legacyText(rte);
1587
+ });
1588
+
1558
1589
  $.each($.fn.gridEditor.utilities, function(name, factory) {
1559
1590
  if (!wanted(name)) { return; }
1560
1591
 
@@ -1631,6 +1662,9 @@ $.fn.gridEditor = function( optionsOrMethod ) {
1631
1662
  bareStyle: bareStyle,
1632
1663
  rowFromLayout: rowFromLayoutValue,
1633
1664
  nodeHtml: nodeHtml,
1665
+ // A text editor has rewritten a content area, so whatever the
1666
+ // editor and its plugins had put in there goes back in
1667
+ textReady: function(block) { block.trigger('ge-rte-ready'); },
1634
1668
  toolbarItems: function(name) {
1635
1669
  return mainControls
1636
1670
  ? mainControls.find('[data-ge-toolbar="feature"][data-ge-feature="' + name + '"]')
@@ -2164,6 +2198,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
2164
2198
  * -------------------------------------------------------------- */
2165
2199
 
2166
2200
  var CONTAINERS = {}; // The container plugins in use, by the type each builds
2201
+ var TEXTS = {}; // The text editor plugins, by the content type each edits
2167
2202
  var FEATURES = {}; // The feature plugins in use, by name
2168
2203
  var UTILITIES = {}; // The utility plugins in use, by name
2169
2204
  var featureMethods = {}; // The methods those features contribute
@@ -3770,9 +3805,9 @@ $.fn.gridEditor = function( optionsOrMethod ) {
3770
3805
  * the all view. `offset` indents it, within the same 12 unit budget.
3771
3806
  */
3772
3807
  function createColumn(size, offset) {
3773
- var rte = getRTE(settings.content_types[0]);
3808
+ var text = textFor(settings.content_types[0]);
3774
3809
  var column = $('<div class="column"/>')
3775
- .append(createDefaultContentWrapper().html(rte ? rte.initialContent : ''))
3810
+ .append(createDefaultContentWrapper().html(text ? text.initialContent : ''))
3776
3811
  ;
3777
3812
 
3778
3813
  // The tier being edited, or the base class in the all view
@@ -3915,8 +3950,21 @@ $.fn.gridEditor = function( optionsOrMethod ) {
3915
3950
  return curView;
3916
3951
  }
3917
3952
 
3918
- function getRTE(type) {
3919
- return $.fn.gridEditor.RTEs[type];
3953
+ /** The text editor plugin for a content type, or null. */
3954
+ function textFor(type) {
3955
+ return TEXTS[type] || null;
3956
+ }
3957
+
3958
+ /**
3959
+ * An integration written to 5.x's contract - init and deinit over a
3960
+ * set of content areas - as a text editor plugin.
3961
+ */
3962
+ function legacyText(rte) {
3963
+ return {
3964
+ initialContent: rte.initialContent,
3965
+ start: function(block) { rte.init(settings, block); },
3966
+ stop: function(block) { rte.deinit(settings, block); },
3967
+ };
3920
3968
  }
3921
3969
 
3922
3970
  /**
@@ -3991,6 +4039,15 @@ $.fn.gridEditor = function( optionsOrMethod ) {
3991
4039
 
3992
4040
  };
3993
4041
 
4042
+ /**
4043
+ * Text editor plugins: tinyMCE, CKEditor, summernote, and whatever a host
4044
+ * writes. A factory registered under the content type it edits, called once
4045
+ * per editor with the handle described in docs/plugins.md, returning
4046
+ * { labelKey, initialContent, available, missingKey, start, stop }.
4047
+ */
4048
+ $.fn.gridEditor.texts = {};
4049
+
4050
+ /** 5.x's registry of text editors, adapted with a warning. Removed in 7.0. */
3994
4051
  $.fn.gridEditor.RTEs = {};
3995
4052
 
3996
4053
  /**
@@ -0,0 +1,115 @@
1
+ /**
2
+ * CKEditor for grid-editor's content areas.
3
+ *
4
+ * A text editor plugin: load this file after the editor, and CKEditor 4
5
+ * after or before it, and content_types: ['ckeditor'] edits each content area
6
+ * with an inline CKEditor. What it can ask the editor for is the handle its
7
+ * factory is called with, described in docs/plugins.md.
8
+ *
9
+ * <script src="ckeditor/ckeditor.js"></script>
10
+ * <script src="dist/jquery.grideditor.min.js"></script>
11
+ * <script src="dist/plugins/grideditor.ckeditor.min.js"></script>
12
+ *
13
+ * Up to 6.0 the main bundle carries a copy of this file too.
14
+ */
15
+ (function($) {
16
+
17
+ var INITIAL_CONTENT = '<p>Lorem initius... </p>';
18
+
19
+ $.fn.gridEditor.texts.ckeditor = function(ge) {
20
+
21
+ /**
22
+ * The instance editing this content area. Kept in jQuery data when it
23
+ * is made, and looked for among CKEditor's own as a fallback, since
24
+ * an instance made by another version of this file has no data.
25
+ */
26
+ function instanceOf(contentArea) {
27
+ var kept = contentArea.data('ge-ckeditor');
28
+ if (kept) { return kept; }
29
+
30
+ var found = null;
31
+ $.each(window.CKEDITOR.instances, function(name, instance) {
32
+ if (instance.element && instance.element.$ === contentArea[0]) { found = instance; }
33
+ });
34
+
35
+ return found;
36
+ }
37
+
38
+ return {
39
+ initialContent: INITIAL_CONTENT,
40
+ missingKey: 'error.ckeditor_missing',
41
+
42
+ available: function() { return !!window.CKEDITOR; },
43
+
44
+ start: function(contentAreas) {
45
+ var settings = ge.settings;
46
+
47
+ contentAreas.each(function() {
48
+ var contentArea = $(this);
49
+ if (contentArea.hasClass('active')) { return; }
50
+
51
+ if (contentArea.html() == INITIAL_CONTENT) {
52
+ // CKEditor kills this '&nbsp' creating a non usable box :/
53
+ contentArea.html('&nbsp;');
54
+ }
55
+
56
+ // Add the .attr('contenteditable',''true') or CKEditor loads readonly
57
+ contentArea.addClass('active').attr('contenteditable', 'true');
58
+
59
+ var configuration = $.extend(
60
+ {},
61
+ (settings.ckeditor && settings.ckeditor.config ? settings.ckeditor.config : {}),
62
+ {
63
+ // Focus editor on creation
64
+ on: {
65
+ instanceReady: function( evt ) {
66
+ // Call original instanceReady function, if one was passed in the config
67
+ var callback;
68
+ try {
69
+ callback = settings.ckeditor.config.on.instanceReady;
70
+ } catch (err) {
71
+ // No callback passed
72
+ }
73
+ if (callback) {
74
+ callback.call(this, evt);
75
+ }
76
+
77
+ // The editor owns what is inside the
78
+ // content area now, so the grid editor is
79
+ // told to put its own furniture back
80
+ ge.textReady(contentArea);
81
+
82
+ instance.focus();
83
+ }
84
+ }
85
+ }
86
+ );
87
+ var instance = window.CKEDITOR.inline(contentArea.get(0), configuration);
88
+ contentArea.data('ge-ckeditor', instance);
89
+ });
90
+ },
91
+
92
+ stop: function(contentAreas) {
93
+ contentAreas.filter('.active').each(function() {
94
+ var contentArea = $(this);
95
+
96
+ // This content area's instance, and no other: up to 5.x
97
+ // closing one content area destroyed every CKEditor on
98
+ // the page, other editors' and the host's own included
99
+ var instance = window.CKEDITOR ? instanceOf(contentArea) : null;
100
+ if (instance) { instance.destroy(); }
101
+ contentArea.removeData('ge-ckeditor');
102
+
103
+ // Cleanup
104
+ contentArea
105
+ .removeClass('active cke_focus')
106
+ .removeAttr('id')
107
+ .removeAttr('style')
108
+ .removeAttr('spellcheck')
109
+ .removeAttr('contenteditable')
110
+ ;
111
+ });
112
+ },
113
+ };
114
+ };
115
+ })(jQuery);