@richscripts/richtexteditor 2.11.0 → 2.12.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.
@@ -10,6 +10,7 @@
10
10
  * - a chrome-less editing surface (toolbar / sub-toolbar / status bar hidden)
11
11
  * - run(command, value) -> dispatch any RTE command
12
12
  * - isActive(name) / state() -> query active formatting for your buttons
13
+ * - onFormatChange(cmds, cb) -> subscribe to state changes; returns unsubscribe
13
14
  * - can(command) -> queryCommandEnabled (undo/redo/...)
14
15
  * - on("update" | "change" | "selectionchange" | "focus" | "blur", cb)
15
16
  * - getHTML / setHTML / getText / setText / getMarkdown / setMarkdown / getJSON
@@ -266,6 +267,36 @@
266
267
  return String(this.getText() || "").replace(/\s+/g, "").length === 0;
267
268
  };
268
269
 
270
+ // Subscribe to formatting-state changes instead of polling isActive() on a
271
+ // timer or on every keystroke. This is the piece a bring-your-own-UI
272
+ // consumer actually needs: without it, a custom toolbar has to guess when to
273
+ // re-read state, which is why TinyMCE exposes formatChanged().
274
+ //
275
+ // Returns an unsubscribe function. Call it when your component unmounts - a
276
+ // framework component that leaks the callback keeps firing into a dead tree.
277
+ HeadlessController.prototype.onFormatChange = function (commands, callback) {
278
+ var noop = function () { };
279
+ if (typeof callback !== "function") return noop;
280
+ // Fall back to the destroy-aware no-op rather than throwing when the
281
+ // core predates this API, so a consumer on an older rte.js degrades to
282
+ // "no live updates" instead of a crash on construction.
283
+ if (!this.editor || typeof this.editor.formatChanged !== "function") return noop;
284
+ var self = this;
285
+ var off = this.editor.formatChanged(commands, function (state, info) {
286
+ if (self._destroyed) return;
287
+ callback(state, info);
288
+ });
289
+ this._formatOffs = this._formatOffs || [];
290
+ this._formatOffs.push(off);
291
+ return function () {
292
+ try { off(); } catch (e) { /* ignore */ }
293
+ if (self._formatOffs) {
294
+ var ix = self._formatOffs.indexOf(off);
295
+ if (ix !== -1) self._formatOffs.splice(ix, 1);
296
+ }
297
+ };
298
+ };
299
+
269
300
  HeadlessController.prototype.focus = function () {
270
301
  try { this.editor.focus(); } catch (e) { /* ignore */ }
271
302
  return this;
@@ -274,6 +305,15 @@
274
305
  HeadlessController.prototype.destroy = function () {
275
306
  this._destroyed = true;
276
307
  this._handlers = {};
308
+ // Drop format subscriptions here too: _destroyed alone only silences the
309
+ // callback, it does not stop the core from evaluating the commands on
310
+ // every toolbar refresh for a controller nobody is using any more.
311
+ if (this._formatOffs) {
312
+ for (var fi = 0; fi < this._formatOffs.length; fi++) {
313
+ try { this._formatOffs[fi](); } catch (e) { /* ignore */ }
314
+ }
315
+ this._formatOffs = null;
316
+ }
277
317
  if (this._boundDoc && this._docListeners) {
278
318
  for (var i = 0; i < this._docListeners.length; i++) {
279
319
  try { this._boundDoc.removeEventListener(this._docListeners[i][0], this._docListeners[i][1], true); } catch (e) {}