@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
@@ -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
  /**
@@ -4101,30 +4158,225 @@ $.fn.gridEditor.locales = {
4101
4158
  };
4102
4159
 
4103
4160
  })( jQuery );
4161
+ /**
4162
+ * tinyMCE for grid-editor's content areas.
4163
+ *
4164
+ * A text editor plugin: load this file after the editor, and tinyMCE 6
4165
+ * after or before it, and content_types: ['tinymce'] edits each content area
4166
+ * with an inline tinyMCE. What it can ask the editor for is the handle its
4167
+ * factory is called with, described in docs/plugins.md.
4168
+ *
4169
+ * <script src="tinymce/tinymce.min.js"></script>
4170
+ * <script src="dist/jquery.grideditor.min.js"></script>
4171
+ * <script src="dist/plugins/grideditor.tinymce.min.js"></script>
4172
+ *
4173
+ * Up to 6.0 the main bundle carries a copy of this file too.
4174
+ */
4104
4175
  (function($) {
4105
- $.fn.gridEditor.RTEs.ckeditor = {
4106
4176
 
4107
- init: function(settings, contentAreas) {
4177
+ // tinyMCE snapshots the target element's attributes when an inline editor is
4178
+ // created and restores them on remove(), so this has to run *after* remove()
4179
+ // to keep the grid editor's own class and tinyMCE's leftovers off the element.
4180
+ function cleanUp(contentArea) {
4181
+ contentArea
4182
+ .removeClass('active')
4183
+ .removeClass('ge-rte-active')
4184
+ .removeAttr('id')
4185
+ .removeAttr('style')
4186
+ .removeAttr('spellcheck')
4187
+ .removeAttr('contenteditable')
4188
+ .removeAttr('data-mce-style')
4189
+ ;
4190
+ }
4108
4191
 
4109
- if (!window.CKEDITOR) {
4110
- console.error($.fn.gridEditor.t(settings, 'error.ckeditor_missing'));
4111
- }
4192
+ var INITIAL_CONTENT = '<p>Lorem ipsum dolores</p>';
4193
+
4194
+ $.fn.gridEditor.texts.tinymce = function(ge) {
4195
+ return {
4196
+ initialContent: INITIAL_CONTENT,
4197
+ missingKey: 'error.tinymce_missing',
4198
+
4199
+ available: function() { return !!window.tinymce; },
4112
4200
 
4113
- var self = this;
4114
- contentAreas.each(function() {
4115
- var contentArea = $(this);
4116
- if (!contentArea.hasClass('active')) {
4117
- if (contentArea.html() == self.initialContent) {
4118
- // CKEditor kills this '&nbsp' creating a non usable box :/
4119
- contentArea.html('&nbsp;');
4201
+ start: function(contentAreas) {
4202
+ var settings = ge.settings;
4203
+ var userConfig = (settings.tinymce && settings.tinymce.config) ? settings.tinymce.config : {};
4204
+
4205
+ contentAreas.each(function() {
4206
+ var contentArea = $(this);
4207
+ if (contentArea.hasClass('active')) { return; }
4208
+
4209
+ if (contentArea.html() == INITIAL_CONTENT) {
4210
+ contentArea.html('');
4120
4211
  }
4121
-
4212
+ contentArea.addClass('active');
4213
+
4214
+ var configuration = $.extend({
4215
+ // tinyMCE's own "Upgrade" badge in the menubar. Off by
4216
+ // default because an inline editor here is a column of
4217
+ // someone's page, not tinyMCE's own interface; a host that
4218
+ // wants it back passes promotion: true.
4219
+ promotion: false,
4220
+ }, userConfig, {
4221
+ target: this,
4222
+ inline: true,
4223
+ init_instance_callback: function(editor) {
4224
+ // deinit ran before this init finished, so tear it down again
4225
+ if (contentArea.data('ge-tinymce-pending-remove')) {
4226
+ contentArea.removeData('ge-tinymce-pending-remove');
4227
+ editor.remove();
4228
+ // deinit already ran and cannot clean up after this
4229
+ // late remove(), so do it here instead
4230
+ cleanUp(contentArea);
4231
+ return;
4232
+ }
4233
+
4234
+ contentArea.data('ge-tinymce', editor);
4235
+
4236
+ // The editor rewrote what is inside this content area
4237
+ // while it took it over, so whatever the grid editor
4238
+ // had in there is gone. Saying so lets it put its own
4239
+ // furniture back.
4240
+ ge.textReady(contentArea);
4241
+
4242
+ // Undo and redo rewrite it too, from snapshots that
4243
+ // leave out whatever is marked data-mce-bogus - an
4244
+ // element's drawer among them - so it goes back in
4245
+ // after each of those as well
4246
+ editor.on('Undo Redo', function() {
4247
+ ge.textReady(contentArea);
4248
+ });
4249
+
4250
+ // The inline toolbar is laid out against the element's
4251
+ // geometry at the moment tinyMCE draws it, and an
4252
+ // element that has just appeared - a tab pane, an
4253
+ // accordion body, a column whose width is still
4254
+ // settling - may not have its own width yet. Asking
4255
+ // for the ui again once the browser has laid the frame
4256
+ // out measures it as it now is, instead of leaving a
4257
+ // toolbar wrapped into a narrow column.
4258
+ window.requestAnimationFrame(function() {
4259
+ if (editor.removed || !editor.ui || !editor.ui.show) { return; }
4260
+
4261
+ editor.ui.show();
4262
+ });
4263
+
4264
+ // And again whenever the user comes back to this
4265
+ // editor: by then the element may have been resized,
4266
+ // hidden and shown again - a tab switched away from
4267
+ // and back, a column made narrower - and the toolbar
4268
+ // is only ever as right as its last measurement.
4269
+ editor.on('focus', function() {
4270
+ window.requestAnimationFrame(function() {
4271
+ if (editor.removed || !editor.ui || !editor.ui.show) { return; }
4272
+
4273
+ editor.ui.show();
4274
+ });
4275
+ });
4276
+
4277
+ // Bring focus to text field
4278
+ editor.focus();
4279
+
4280
+ // Call the original callbacks, if any were passed in the config
4281
+ if (userConfig.init_instance_callback) {
4282
+ userConfig.init_instance_callback.call(this, editor);
4283
+ }
4284
+ // 'oninit' is the pre-6 name, honoured so existing configs keep working
4285
+ if (userConfig.oninit) {
4286
+ userConfig.oninit.call(this, editor);
4287
+ }
4288
+ }
4289
+ });
4290
+ // We always edit the element we were handed, so a selector in the
4291
+ // user config would only fight with target
4292
+ delete configuration.selector;
4293
+
4294
+ window.tinymce.init(configuration);
4295
+ });
4296
+ },
4297
+
4298
+ stop: function(contentAreas) {
4299
+ contentAreas.filter('.active').each(function() {
4300
+ var contentArea = $(this);
4301
+ var editor = contentArea.data('ge-tinymce');
4302
+
4303
+ if (editor) {
4304
+ contentArea.removeData('ge-tinymce');
4305
+ editor.remove();
4306
+ } else {
4307
+ // tinymce.init() is asynchronous, so the editor may not exist
4308
+ // yet. Leave a note for init_instance_callback to remove it.
4309
+ contentArea.data('ge-tinymce-pending-remove', true);
4310
+ }
4311
+
4312
+ cleanUp(contentArea);
4313
+ });
4314
+ },
4315
+ };
4316
+ };
4317
+ })(jQuery);
4318
+
4319
+ /**
4320
+ * CKEditor for grid-editor's content areas.
4321
+ *
4322
+ * A text editor plugin: load this file after the editor, and CKEditor 4
4323
+ * after or before it, and content_types: ['ckeditor'] edits each content area
4324
+ * with an inline CKEditor. What it can ask the editor for is the handle its
4325
+ * factory is called with, described in docs/plugins.md.
4326
+ *
4327
+ * <script src="ckeditor/ckeditor.js"></script>
4328
+ * <script src="dist/jquery.grideditor.min.js"></script>
4329
+ * <script src="dist/plugins/grideditor.ckeditor.min.js"></script>
4330
+ *
4331
+ * Up to 6.0 the main bundle carries a copy of this file too.
4332
+ */
4333
+ (function($) {
4334
+
4335
+ var INITIAL_CONTENT = '<p>Lorem initius... </p>';
4336
+
4337
+ $.fn.gridEditor.texts.ckeditor = function(ge) {
4338
+
4339
+ /**
4340
+ * The instance editing this content area. Kept in jQuery data when it
4341
+ * is made, and looked for among CKEditor's own as a fallback, since
4342
+ * an instance made by another version of this file has no data.
4343
+ */
4344
+ function instanceOf(contentArea) {
4345
+ var kept = contentArea.data('ge-ckeditor');
4346
+ if (kept) { return kept; }
4347
+
4348
+ var found = null;
4349
+ $.each(window.CKEDITOR.instances, function(name, instance) {
4350
+ if (instance.element && instance.element.$ === contentArea[0]) { found = instance; }
4351
+ });
4352
+
4353
+ return found;
4354
+ }
4355
+
4356
+ return {
4357
+ initialContent: INITIAL_CONTENT,
4358
+ missingKey: 'error.ckeditor_missing',
4359
+
4360
+ available: function() { return !!window.CKEDITOR; },
4361
+
4362
+ start: function(contentAreas) {
4363
+ var settings = ge.settings;
4364
+
4365
+ contentAreas.each(function() {
4366
+ var contentArea = $(this);
4367
+ if (contentArea.hasClass('active')) { return; }
4368
+
4369
+ if (contentArea.html() == INITIAL_CONTENT) {
4370
+ // CKEditor kills this '&nbsp' creating a non usable box :/
4371
+ contentArea.html('&nbsp;');
4372
+ }
4373
+
4122
4374
  // Add the .attr('contenteditable',''true') or CKEditor loads readonly
4123
4375
  contentArea.addClass('active').attr('contenteditable', 'true');
4124
-
4376
+
4125
4377
  var configuration = $.extend(
4126
4378
  {},
4127
- (settings.ckeditor && settings.ckeditor.config ? settings.ckeditor.config : {}),
4379
+ (settings.ckeditor && settings.ckeditor.config ? settings.ckeditor.config : {}),
4128
4380
  {
4129
4381
  // Focus editor on creation
4130
4382
  on: {
@@ -4143,56 +4395,81 @@ $.fn.gridEditor.locales = {
4143
4395
  // The editor owns what is inside the
4144
4396
  // content area now, so the grid editor is
4145
4397
  // told to put its own furniture back
4146
- contentArea.trigger('ge-rte-ready');
4398
+ ge.textReady(contentArea);
4147
4399
 
4148
4400
  instance.focus();
4149
4401
  }
4150
4402
  }
4151
4403
  }
4152
4404
  );
4153
- var instance = CKEDITOR.inline(contentArea.get(0), configuration);
4154
- }
4155
- });
4156
- },
4157
-
4158
- deinit: function(settings, contentAreas) {
4159
- contentAreas.filter('.active').each(function() {
4160
- var contentArea = $(this);
4161
-
4162
- // Destroy all CKEditor instances
4163
- $.each(CKEDITOR.instances, function(_, instance) {
4164
- instance.destroy();
4405
+ var instance = window.CKEDITOR.inline(contentArea.get(0), configuration);
4406
+ contentArea.data('ge-ckeditor', instance);
4165
4407
  });
4408
+ },
4166
4409
 
4167
- // Cleanup
4168
- contentArea
4169
- .removeClass('active cke_focus')
4170
- .removeAttr('id')
4171
- .removeAttr('style')
4172
- .removeAttr('spellcheck')
4173
- .removeAttr('contenteditable')
4174
- ;
4175
- });
4176
- },
4177
-
4178
- initialContent: '<p>Lorem initius... </p>',
4410
+ stop: function(contentAreas) {
4411
+ contentAreas.filter('.active').each(function() {
4412
+ var contentArea = $(this);
4413
+
4414
+ // This content area's instance, and no other: up to 5.x
4415
+ // closing one content area destroyed every CKEditor on
4416
+ // the page, other editors' and the host's own included
4417
+ var instance = window.CKEDITOR ? instanceOf(contentArea) : null;
4418
+ if (instance) { instance.destroy(); }
4419
+ contentArea.removeData('ge-ckeditor');
4420
+
4421
+ // Cleanup
4422
+ contentArea
4423
+ .removeClass('active cke_focus')
4424
+ .removeAttr('id')
4425
+ .removeAttr('style')
4426
+ .removeAttr('spellcheck')
4427
+ .removeAttr('contenteditable')
4428
+ ;
4429
+ });
4430
+ },
4431
+ };
4179
4432
  };
4180
4433
  })(jQuery);
4434
+
4435
+ /**
4436
+ * Summernote for grid-editor's content areas.
4437
+ *
4438
+ * A text editor plugin: load this file after the editor, and summernote
4439
+ * after or before it, and content_types: ['summernote'] edits each content
4440
+ * area with summernote in air mode. What it can ask the editor for is the
4441
+ * handle its factory is called with, described in docs/plugins.md.
4442
+ *
4443
+ * <script src="summernote/summernote-bs5.min.js"></script>
4444
+ * <script src="dist/jquery.grideditor.min.js"></script>
4445
+ * <script src="dist/plugins/grideditor.summernote.min.js"></script>
4446
+ *
4447
+ * Up to 6.0 the main bundle carries a copy of this file too.
4448
+ */
4181
4449
  (function($) {
4182
4450
 
4183
- $.fn.gridEditor.RTEs.summernote = {
4451
+ var INITIAL_CONTENT = '<p>Lorem ipsum dolores</p>';
4184
4452
 
4185
- init: function(settings, contentAreas) {
4186
-
4187
- if (!jQuery().summernote) {
4188
- console.error($.fn.gridEditor.t(settings, 'error.summernote_missing'));
4189
- }
4453
+ $.fn.gridEditor.texts.summernote = function(ge) {
4454
+ return {
4455
+ initialContent: INITIAL_CONTENT,
4456
+ missingKey: 'error.summernote_missing',
4457
+
4458
+ available: function() { return !!$.fn.summernote; },
4459
+
4460
+ start: function(contentAreas) {
4461
+ var settings = ge.settings;
4190
4462
 
4191
- var self = this;
4192
- contentAreas.each(function() {
4193
- var contentArea = $(this);
4194
- if (!contentArea.hasClass('active')) {
4195
- if (contentArea.html() == self.initialContent) {
4463
+ // Summernote 0.9.1 calls $.now(), which jQuery 4 removed, and
4464
+ // cannot open without it. Given back only where it is missing,
4465
+ // and only once summernote is actually used.
4466
+ if (!$.now) { $.now = Date.now; }
4467
+
4468
+ contentAreas.each(function() {
4469
+ var contentArea = $(this);
4470
+ if (contentArea.hasClass('active')) { return; }
4471
+
4472
+ if (contentArea.html() == INITIAL_CONTENT) {
4196
4473
  contentArea.html('');
4197
4474
  }
4198
4475
  contentArea.addClass('active');
@@ -4207,7 +4484,7 @@ $.fn.gridEditor.locales = {
4207
4484
  // Focus editor on creation
4208
4485
  callbacks: {
4209
4486
  onInit: function() {
4210
-
4487
+
4211
4488
  // Call original oninit function, if one was passed in the config
4212
4489
  var callback;
4213
4490
  try {
@@ -4222,175 +4499,44 @@ $.fn.gridEditor.locales = {
4222
4499
  // The editor owns what is inside the
4223
4500
  // content area now, so the grid editor is
4224
4501
  // told to put its own furniture back
4225
- contentArea.trigger('ge-rte-ready');
4226
-
4502
+ ge.textReady(contentArea);
4503
+
4227
4504
  contentArea.summernote('focus');
4228
4505
  }
4229
4506
  }
4230
4507
  }
4231
4508
  );
4232
4509
  contentArea.summernote(configuration);
4233
- }
4234
- });
4235
- },
4236
-
4237
- deinit: function(settings, contentAreas) {
4238
- contentAreas.filter('.active').each(function() {
4239
- var contentArea = $(this);
4240
- contentArea.summernote('destroy');
4241
- contentArea
4242
- .removeClass('active')
4243
- .removeAttr('id')
4244
- .removeAttr('style')
4245
- .removeAttr('spellcheck')
4246
- ;
4247
- });
4248
- },
4510
+ });
4511
+ },
4249
4512
 
4250
- initialContent: '<p>Lorem ipsum dolores</p>',
4513
+ stop: function(contentAreas) {
4514
+ contentAreas.filter('.active').each(function() {
4515
+ var contentArea = $(this);
4516
+ contentArea.summernote('destroy');
4517
+ contentArea
4518
+ .removeClass('active')
4519
+ .removeAttr('id')
4520
+ .removeAttr('style')
4521
+ .removeAttr('spellcheck')
4522
+ ;
4523
+ });
4524
+ },
4525
+ };
4251
4526
  };
4252
4527
  })(jQuery);
4253
4528
 
4529
+ /**
4530
+ * Up to 6.0 the main bundle carries a copy of each text editor plugin, so a
4531
+ * page that loaded only the editor keeps the editor it had. This marks those
4532
+ * copies, and the editor warns once when a content area is edited with one:
4533
+ * 6.0 leaves them out. Loaded on its own, a plugin's file registers itself
4534
+ * again, unmarked, and is the one used.
4535
+ *
4536
+ * Only ever concatenated after the plugins it marks, never built on its own.
4537
+ */
4254
4538
  (function($) {
4255
-
4256
- // tinyMCE snapshots the target element's attributes when an inline editor is
4257
- // created and restores them on remove(), so this has to run *after* remove()
4258
- // to keep the grid editor's own class and tinyMCE's leftovers off the element.
4259
- function cleanUp(contentArea) {
4260
- contentArea
4261
- .removeClass('active')
4262
- .removeClass('ge-rte-active')
4263
- .removeAttr('id')
4264
- .removeAttr('style')
4265
- .removeAttr('spellcheck')
4266
- .removeAttr('contenteditable')
4267
- .removeAttr('data-mce-style')
4268
- ;
4269
- }
4270
-
4271
- $.fn.gridEditor.RTEs.tinymce = {
4272
-
4273
- init: function(settings, contentAreas) {
4274
-
4275
- if (!window.tinymce) {
4276
- console.error($.fn.gridEditor.t(settings, 'error.tinymce_missing'));
4277
- return;
4278
- }
4279
-
4280
- var self = this;
4281
- var userConfig = (settings.tinymce && settings.tinymce.config) ? settings.tinymce.config : {};
4282
-
4283
- contentAreas.each(function() {
4284
- var contentArea = $(this);
4285
- if (contentArea.hasClass('active')) { return; }
4286
-
4287
- if (contentArea.html() == self.initialContent) {
4288
- contentArea.html('');
4289
- }
4290
- contentArea.addClass('active');
4291
-
4292
- var configuration = $.extend({
4293
- // tinyMCE's own "Upgrade" badge in the menubar. Off by
4294
- // default because an inline editor here is a column of
4295
- // someone's page, not tinyMCE's own interface; a host that
4296
- // wants it back passes promotion: true.
4297
- promotion: false,
4298
- }, userConfig, {
4299
- target: this,
4300
- inline: true,
4301
- init_instance_callback: function(editor) {
4302
- // deinit ran before this init finished, so tear it down again
4303
- if (contentArea.data('ge-tinymce-pending-remove')) {
4304
- contentArea.removeData('ge-tinymce-pending-remove');
4305
- editor.remove();
4306
- // deinit already ran and cannot clean up after this
4307
- // late remove(), so do it here instead
4308
- cleanUp(contentArea);
4309
- return;
4310
- }
4311
-
4312
- contentArea.data('ge-tinymce', editor);
4313
-
4314
- // The editor rewrote what is inside this content area
4315
- // while it took it over, so whatever the grid editor
4316
- // had in there is gone. Saying so lets it put its own
4317
- // furniture back (see RTE_READY in the core).
4318
- contentArea.trigger('ge-rte-ready');
4319
-
4320
- // Undo and redo rewrite it too, from snapshots that
4321
- // leave out whatever is marked data-mce-bogus - an
4322
- // element's drawer among them - so it goes back in
4323
- // after each of those as well
4324
- editor.on('Undo Redo', function() {
4325
- contentArea.trigger('ge-rte-ready');
4326
- });
4327
-
4328
- // The inline toolbar is laid out against the element's
4329
- // geometry at the moment tinyMCE draws it, and an
4330
- // element that has just appeared - a tab pane, an
4331
- // accordion body, a column whose width is still
4332
- // settling - may not have its own width yet. Asking
4333
- // for the ui again once the browser has laid the frame
4334
- // out measures it as it now is, instead of leaving a
4335
- // toolbar wrapped into a narrow column.
4336
- window.requestAnimationFrame(function() {
4337
- if (editor.removed || !editor.ui || !editor.ui.show) { return; }
4338
-
4339
- editor.ui.show();
4340
- });
4341
-
4342
- // And again whenever the user comes back to this
4343
- // editor: by then the element may have been resized,
4344
- // hidden and shown again - a tab switched away from
4345
- // and back, a column made narrower - and the toolbar
4346
- // is only ever as right as its last measurement.
4347
- editor.on('focus', function() {
4348
- window.requestAnimationFrame(function() {
4349
- if (editor.removed || !editor.ui || !editor.ui.show) { return; }
4350
-
4351
- editor.ui.show();
4352
- });
4353
- });
4354
-
4355
- // Bring focus to text field
4356
- editor.focus();
4357
-
4358
- // Call the original callbacks, if any were passed in the config
4359
- if (userConfig.init_instance_callback) {
4360
- userConfig.init_instance_callback.call(this, editor);
4361
- }
4362
- // 'oninit' is the pre-6 name, honoured so existing configs keep working
4363
- if (userConfig.oninit) {
4364
- userConfig.oninit.call(this, editor);
4365
- }
4366
- }
4367
- });
4368
- // We always edit the element we were handed, so a selector in the
4369
- // user config would only fight with target
4370
- delete configuration.selector;
4371
-
4372
- window.tinymce.init(configuration);
4373
- });
4374
- },
4375
-
4376
- deinit: function(settings, contentAreas) {
4377
- contentAreas.filter('.active').each(function() {
4378
- var contentArea = $(this);
4379
- var editor = contentArea.data('ge-tinymce');
4380
-
4381
- if (editor) {
4382
- contentArea.removeData('ge-tinymce');
4383
- editor.remove();
4384
- } else {
4385
- // tinymce.init() is asynchronous, so the editor may not exist
4386
- // yet. Leave a note for init_instance_callback to remove it.
4387
- contentArea.data('ge-tinymce-pending-remove', true);
4388
- }
4389
-
4390
- cleanUp(contentArea);
4391
- });
4392
- },
4393
-
4394
- initialContent: '<p>Lorem ipsum dolores</p>',
4395
- };
4539
+ ['tinymce', 'ckeditor', 'summernote'].forEach(function(type) {
4540
+ if ($.fn.gridEditor.texts[type]) { $.fn.gridEditor.texts[type].bundled = true; }
4541
+ });
4396
4542
  })(jQuery);