@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,93 @@
1
+ /**
2
+ * Summernote for grid-editor's content areas.
3
+ *
4
+ * A text editor plugin: load this file after the editor, and summernote
5
+ * after or before it, and content_types: ['summernote'] edits each content
6
+ * area with summernote in air mode. What it can ask the editor for is the
7
+ * handle its factory is called with, described in docs/plugins.md.
8
+ *
9
+ * <script src="summernote/summernote-bs5.min.js"></script>
10
+ * <script src="dist/jquery.grideditor.min.js"></script>
11
+ * <script src="dist/plugins/grideditor.summernote.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 ipsum dolores</p>';
18
+
19
+ $.fn.gridEditor.texts.summernote = function(ge) {
20
+ return {
21
+ initialContent: INITIAL_CONTENT,
22
+ missingKey: 'error.summernote_missing',
23
+
24
+ available: function() { return !!$.fn.summernote; },
25
+
26
+ start: function(contentAreas) {
27
+ var settings = ge.settings;
28
+
29
+ // Summernote 0.9.1 calls $.now(), which jQuery 4 removed, and
30
+ // cannot open without it. Given back only where it is missing,
31
+ // and only once summernote is actually used.
32
+ if (!$.now) { $.now = Date.now; }
33
+
34
+ contentAreas.each(function() {
35
+ var contentArea = $(this);
36
+ if (contentArea.hasClass('active')) { return; }
37
+
38
+ if (contentArea.html() == INITIAL_CONTENT) {
39
+ contentArea.html('');
40
+ }
41
+ contentArea.addClass('active');
42
+
43
+ var configuration = $.extend(
44
+ true, // deep copy
45
+ {},
46
+ (settings.summernote && settings.summernote.config ? settings.summernote.config : {}),
47
+ {
48
+ tabsize: 2,
49
+ airMode: true,
50
+ // Focus editor on creation
51
+ callbacks: {
52
+ onInit: function() {
53
+
54
+ // Call original oninit function, if one was passed in the config
55
+ var callback;
56
+ try {
57
+ callback = settings.summernote.config.callbacks.onInit;
58
+ } catch (err) {
59
+ // No callback passed
60
+ }
61
+ if (callback) {
62
+ callback.call(this);
63
+ }
64
+
65
+ // The editor owns what is inside the
66
+ // content area now, so the grid editor is
67
+ // told to put its own furniture back
68
+ ge.textReady(contentArea);
69
+
70
+ contentArea.summernote('focus');
71
+ }
72
+ }
73
+ }
74
+ );
75
+ contentArea.summernote(configuration);
76
+ });
77
+ },
78
+
79
+ stop: function(contentAreas) {
80
+ contentAreas.filter('.active').each(function() {
81
+ var contentArea = $(this);
82
+ contentArea.summernote('destroy');
83
+ contentArea
84
+ .removeClass('active')
85
+ .removeAttr('id')
86
+ .removeAttr('style')
87
+ .removeAttr('spellcheck')
88
+ ;
89
+ });
90
+ },
91
+ };
92
+ };
93
+ })(jQuery);
@@ -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);
@@ -1,77 +0,0 @@
1
- (function($) {
2
- $.fn.gridEditor.RTEs.ckeditor = {
3
-
4
- init: function(settings, contentAreas) {
5
-
6
- if (!window.CKEDITOR) {
7
- console.error($.fn.gridEditor.t(settings, 'error.ckeditor_missing'));
8
- }
9
-
10
- var self = this;
11
- contentAreas.each(function() {
12
- var contentArea = $(this);
13
- if (!contentArea.hasClass('active')) {
14
- if (contentArea.html() == self.initialContent) {
15
- // CKEditor kills this '&nbsp' creating a non usable box :/
16
- contentArea.html('&nbsp;');
17
- }
18
-
19
- // Add the .attr('contenteditable',''true') or CKEditor loads readonly
20
- contentArea.addClass('active').attr('contenteditable', 'true');
21
-
22
- var configuration = $.extend(
23
- {},
24
- (settings.ckeditor && settings.ckeditor.config ? settings.ckeditor.config : {}),
25
- {
26
- // Focus editor on creation
27
- on: {
28
- instanceReady: function( evt ) {
29
- // Call original instanceReady function, if one was passed in the config
30
- var callback;
31
- try {
32
- callback = settings.ckeditor.config.on.instanceReady;
33
- } catch (err) {
34
- // No callback passed
35
- }
36
- if (callback) {
37
- callback.call(this, evt);
38
- }
39
-
40
- // The editor owns what is inside the
41
- // content area now, so the grid editor is
42
- // told to put its own furniture back
43
- contentArea.trigger('ge-rte-ready');
44
-
45
- instance.focus();
46
- }
47
- }
48
- }
49
- );
50
- var instance = CKEDITOR.inline(contentArea.get(0), configuration);
51
- }
52
- });
53
- },
54
-
55
- deinit: function(settings, contentAreas) {
56
- contentAreas.filter('.active').each(function() {
57
- var contentArea = $(this);
58
-
59
- // Destroy all CKEditor instances
60
- $.each(CKEDITOR.instances, function(_, instance) {
61
- instance.destroy();
62
- });
63
-
64
- // Cleanup
65
- contentArea
66
- .removeClass('active cke_focus')
67
- .removeAttr('id')
68
- .removeAttr('style')
69
- .removeAttr('spellcheck')
70
- .removeAttr('contenteditable')
71
- ;
72
- });
73
- },
74
-
75
- initialContent: '<p>Lorem initius... </p>',
76
- };
77
- })(jQuery);
@@ -1,72 +0,0 @@
1
- (function($) {
2
-
3
- $.fn.gridEditor.RTEs.summernote = {
4
-
5
- init: function(settings, contentAreas) {
6
-
7
- if (!jQuery().summernote) {
8
- console.error($.fn.gridEditor.t(settings, 'error.summernote_missing'));
9
- }
10
-
11
- var self = this;
12
- contentAreas.each(function() {
13
- var contentArea = $(this);
14
- if (!contentArea.hasClass('active')) {
15
- if (contentArea.html() == self.initialContent) {
16
- contentArea.html('');
17
- }
18
- contentArea.addClass('active');
19
-
20
- var configuration = $.extend(
21
- true, // deep copy
22
- {},
23
- (settings.summernote && settings.summernote.config ? settings.summernote.config : {}),
24
- {
25
- tabsize: 2,
26
- airMode: true,
27
- // Focus editor on creation
28
- callbacks: {
29
- onInit: function() {
30
-
31
- // Call original oninit function, if one was passed in the config
32
- var callback;
33
- try {
34
- callback = settings.summernote.config.callbacks.onInit;
35
- } catch (err) {
36
- // No callback passed
37
- }
38
- if (callback) {
39
- callback.call(this);
40
- }
41
-
42
- // The editor owns what is inside the
43
- // content area now, so the grid editor is
44
- // told to put its own furniture back
45
- contentArea.trigger('ge-rte-ready');
46
-
47
- contentArea.summernote('focus');
48
- }
49
- }
50
- }
51
- );
52
- contentArea.summernote(configuration);
53
- }
54
- });
55
- },
56
-
57
- deinit: function(settings, contentAreas) {
58
- contentAreas.filter('.active').each(function() {
59
- var contentArea = $(this);
60
- contentArea.summernote('destroy');
61
- contentArea
62
- .removeClass('active')
63
- .removeAttr('id')
64
- .removeAttr('style')
65
- .removeAttr('spellcheck')
66
- ;
67
- });
68
- },
69
-
70
- initialContent: '<p>Lorem ipsum dolores</p>',
71
- };
72
- })(jQuery);
@@ -1,143 +0,0 @@
1
- (function($) {
2
-
3
- // tinyMCE snapshots the target element's attributes when an inline editor is
4
- // created and restores them on remove(), so this has to run *after* remove()
5
- // to keep the grid editor's own class and tinyMCE's leftovers off the element.
6
- function cleanUp(contentArea) {
7
- contentArea
8
- .removeClass('active')
9
- .removeClass('ge-rte-active')
10
- .removeAttr('id')
11
- .removeAttr('style')
12
- .removeAttr('spellcheck')
13
- .removeAttr('contenteditable')
14
- .removeAttr('data-mce-style')
15
- ;
16
- }
17
-
18
- $.fn.gridEditor.RTEs.tinymce = {
19
-
20
- init: function(settings, contentAreas) {
21
-
22
- if (!window.tinymce) {
23
- console.error($.fn.gridEditor.t(settings, 'error.tinymce_missing'));
24
- return;
25
- }
26
-
27
- var self = this;
28
- var userConfig = (settings.tinymce && settings.tinymce.config) ? settings.tinymce.config : {};
29
-
30
- contentAreas.each(function() {
31
- var contentArea = $(this);
32
- if (contentArea.hasClass('active')) { return; }
33
-
34
- if (contentArea.html() == self.initialContent) {
35
- contentArea.html('');
36
- }
37
- contentArea.addClass('active');
38
-
39
- var configuration = $.extend({
40
- // tinyMCE's own "Upgrade" badge in the menubar. Off by
41
- // default because an inline editor here is a column of
42
- // someone's page, not tinyMCE's own interface; a host that
43
- // wants it back passes promotion: true.
44
- promotion: false,
45
- }, userConfig, {
46
- target: this,
47
- inline: true,
48
- init_instance_callback: function(editor) {
49
- // deinit ran before this init finished, so tear it down again
50
- if (contentArea.data('ge-tinymce-pending-remove')) {
51
- contentArea.removeData('ge-tinymce-pending-remove');
52
- editor.remove();
53
- // deinit already ran and cannot clean up after this
54
- // late remove(), so do it here instead
55
- cleanUp(contentArea);
56
- return;
57
- }
58
-
59
- contentArea.data('ge-tinymce', editor);
60
-
61
- // The editor rewrote what is inside this content area
62
- // while it took it over, so whatever the grid editor
63
- // had in there is gone. Saying so lets it put its own
64
- // furniture back (see RTE_READY in the core).
65
- contentArea.trigger('ge-rte-ready');
66
-
67
- // Undo and redo rewrite it too, from snapshots that
68
- // leave out whatever is marked data-mce-bogus - an
69
- // element's drawer among them - so it goes back in
70
- // after each of those as well
71
- editor.on('Undo Redo', function() {
72
- contentArea.trigger('ge-rte-ready');
73
- });
74
-
75
- // The inline toolbar is laid out against the element's
76
- // geometry at the moment tinyMCE draws it, and an
77
- // element that has just appeared - a tab pane, an
78
- // accordion body, a column whose width is still
79
- // settling - may not have its own width yet. Asking
80
- // for the ui again once the browser has laid the frame
81
- // out measures it as it now is, instead of leaving a
82
- // toolbar wrapped into a narrow column.
83
- window.requestAnimationFrame(function() {
84
- if (editor.removed || !editor.ui || !editor.ui.show) { return; }
85
-
86
- editor.ui.show();
87
- });
88
-
89
- // And again whenever the user comes back to this
90
- // editor: by then the element may have been resized,
91
- // hidden and shown again - a tab switched away from
92
- // and back, a column made narrower - and the toolbar
93
- // is only ever as right as its last measurement.
94
- editor.on('focus', function() {
95
- window.requestAnimationFrame(function() {
96
- if (editor.removed || !editor.ui || !editor.ui.show) { return; }
97
-
98
- editor.ui.show();
99
- });
100
- });
101
-
102
- // Bring focus to text field
103
- editor.focus();
104
-
105
- // Call the original callbacks, if any were passed in the config
106
- if (userConfig.init_instance_callback) {
107
- userConfig.init_instance_callback.call(this, editor);
108
- }
109
- // 'oninit' is the pre-6 name, honoured so existing configs keep working
110
- if (userConfig.oninit) {
111
- userConfig.oninit.call(this, editor);
112
- }
113
- }
114
- });
115
- // We always edit the element we were handed, so a selector in the
116
- // user config would only fight with target
117
- delete configuration.selector;
118
-
119
- window.tinymce.init(configuration);
120
- });
121
- },
122
-
123
- deinit: function(settings, contentAreas) {
124
- contentAreas.filter('.active').each(function() {
125
- var contentArea = $(this);
126
- var editor = contentArea.data('ge-tinymce');
127
-
128
- if (editor) {
129
- contentArea.removeData('ge-tinymce');
130
- editor.remove();
131
- } else {
132
- // tinymce.init() is asynchronous, so the editor may not exist
133
- // yet. Leave a note for init_instance_callback to remove it.
134
- contentArea.data('ge-tinymce-pending-remove', true);
135
- }
136
-
137
- cleanUp(contentArea);
138
- });
139
- },
140
-
141
- initialContent: '<p>Lorem ipsum dolores</p>',
142
- };
143
- })(jQuery);