Skip to content

Commit

Permalink
Item9427: Only update button state if cursor idle more than 500ms
Browse files Browse the repository at this point in the history
Improves cursor responsiveness on large documents (>250KiB), especially
on slow PCs and IE users.

Tested on IE6, IE7, IE8, Opera 10.61, Chrome 6.0.490.1, Safari 5.0.1, FF3.6.7

PLEASE HELP TEST and provide feedback. Maybe the 500ms lag between
cursor position and button state updates is too unsettling.

If we decide that this is the case, I will make this latency a pref
var and we can default it to 100ms or so, and then users/sites with large
docs can increase the number.

git-svn-id: http://svn.foswiki.org/trunk@8610 0b4bb1d4-4e5a-0410-9cc4-b2b747904278
  • Loading branch information
PaulHarvey authored and PaulHarvey committed Aug 20, 2010
1 parent bb385a8 commit a79e3f6
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 16 deletions.
15 changes: 10 additions & 5 deletions TinyMCEPlugin/data/System/TinyMCEPlugin.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,21 @@ Below is the default configuration. If it is to be modified, it should be copied
},
"theme_advanced_toolbar_align" : "left",
"foswikibuttons_formats" : {
/* Use 'attributes : { class : "foo" }' instead of 'classes: "foo"'
* because this is how it's done in the default advanced theme shipped
* with TinyMCE which seems to be a few ms faster on cursor movement
*/
"Normal" : { "remove" : "all" },
"Heading 1" : { "block" : "h1", "remove" : "all" },
"Heading 2" : { "block" : "h2", "remove" : "all" },
"Heading 3" : { "block" : "h3", "remove" : "all" },
"Heading 4" : { "block" : "h4", "remove" : "all" },
"Heading 5" : { "block" : "h5", "remove" : "all" },
"Heading 6" : { "block" : "h6", "remove" : "all" },
"VERBATIM" : { "block" : "pre", "remove" : "all", "classes" : "TMLverbatim" },
"LITERAL" : { "block" : "div", "remove" : "all", "classes" : "WYSIWYG_LITERAL" },
"Protect on save" : { "block" : "div", "remove" : "all", "classes" : "WYSIWYG_PROTECTED" },
"Protect forever" : { "block" : "div", "remove" : "all", "classes" : "WYSIWYG_STICKY" }
"VERBATIM" : { "block" : "pre", "remove" : "all", "attributes" : { "class" : "TMLverbatim" } },
"LITERAL" : { "block" : "div", "remove" : "all", "attributes" : { "class" : "WYSIWYG_LITERAL" } },
"Protect on save" : { "block" : "div", "remove" : "all", "attributes" : { "class" : "WYSIWYG_PROTECTED" }, "selector" : "*" },
"Protect forever" : { "block" : "div", "remove" : "all", "attributes" : { "class" : "WYSIWYG_STICKY" }, "selector" : "*" }
},
"paste_create_paragraphs" : true,
"paste_create_linebreaks" : false,
Expand Down Expand Up @@ -285,7 +289,8 @@ Another great Foswiki extension from the <a style="text-decoration:none" href="h
| Change History: | <!-- versions below in reverse order -->&nbsp; |
| ?? Aug 2010 | Foswikitask:Item9236: Enable contextmenu !TinyMCE plugin, allows table row/column copy & paste<br/>\
Foswikitask:Item9263: Fix autosave breaking on IECollections' IE6<br/>\
Foswikitask:Item9424: Use non-jQuery build to avoid IE8 resizing quirks on jquery-1.3.2 |
Foswikitask:Item9424: Use non-jQuery build to avoid IE8 resizing quirks on jquery-1.3.2<br/>\
Foswikitask:Item9427: Only update button state if cursor has been idle &gt; 500ms, improves cursor movement responsiveness in &gt;250KiB documents on IE/slow PCs |
| 01 Jul 2010 | Foswikitask:Item9221: Fix colour formatting problem and JS error transitioning to fullscreen<br/>\
Foswikitask:Item9222: Fix autosave implementation compatibility with Foswiki 1.0.x JQueryPlugin<br/>\
Foswikitask:Item9234: Upgrade to !TinyMCE 3.3.8 |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
Copyright (C) 2007-2009 Crawford Currie http://c-dot.co.uk
Copyright (C) 2010 Foswiki Contributors http://foswiki.org
All Rights Reserved.
This program is free software; you can redistribute it and/or
Expand All @@ -21,6 +22,19 @@
tinymce.create('tinymce.plugins.FoswikiButtons', {
/* Foswiki formats listbox */
formats_listbox: null,
/* Remembers which node was last calculated for button state */
_lastButtonUpdateNode: null,
/* Flag to indicate there's a setTimeout waiting to fire a
** _tryNodeChangeEvent() */
_nodeChangeEventScheduled: null,
/* Flag to indicate that the pending setTimeout waiting to fire a
** _tryNodeChangeEvent(), should be deferred */
_deferNodeChangeEvent: null,
/* setTimeout interval governing cursor idle time required to fire a
** button state update*/
nodeChangeEventFrequency: 500,

_lastButtonUpdateNode: null,

init: function (ed, url) {
this.formats = ed.getParam('foswikibuttons_formats');
Expand Down Expand Up @@ -254,16 +268,140 @@
return;
},

/*
* *IF YOU MAKE CHANGES HERE*, consider netbook/mobile users who need
* every spare CPU cycle.
*
* _nodeChange() is fired *very* frequently, on every cursor movement
* for example. So expensive operations are deferred until the cursor
* has settled for some time period this.nodeChangeEventFrequency (500ms)
*/
_nodeChange: function (ed, cm, node, collapsed) {
var selectedFormats = ed.formatter.matchAll(
ed.plugins.foswikibuttons.format_names),
listbox = cm.get(ed.id + '_foswikiformat');

var selectedFormats, listbox;

if (typeof(node) !== 'object') {
return;
} else if (!collapsed) {
// !collapsed means a selection; always update button state if
// there is a selection.
//this._updateButtonState(ed, cm, node, collapsed);
} else if (node !== this._lastButtonUpdateNode) {
// Only update button state if it wasn't already calculated for
// this node already on a previous call.
//this._updateButtonState(ed, cm, node, collapsed);

// Remember the node
this._lastButtonUpdateNode = node;
}
/* comment the following line and un-comment the line after that to
** do reliable performance analysis of _updateButtonState(). See
** Item9427 */
this._scheduleNodeChangeEvent(ed, cm, node, collapsed);
//_updateButtonState(ed, cm, node, collapsed);

return true;

},

/* Schedule a _tryNodeChangeEvent() call, unless one is already
* scheduled. In that case, defer that next call, because this means
* the cursor hasn't settled for long enough. _tryNodeChangeEvent()
* will re-schedule itself instead of calling _doUpdateButtonState()
* when it does eventually fire.
*/
_scheduleNodeChangeEvent: function (ed, cm, node, collapsed) {
var that = this;

if (this._nodeChangeEventScheduled) {
// defer the next update and keep blocking; cursor is still moving
this._deferNodeChangeEvent = true;
} else {
this._deferNodeChangeEvent = false;
this._nodeChangeEventScheduled = true;
setTimeout(function () {
that._tryNodeChangeEvent(that, ed, cm, node, collapsed);

return;
}, this.nodeChangeEventFrequency);
}

return;
},

/* If this event is to be deferred, "re-set the clock and wait
** another 500ms" - otherwise, finally, just do it
*/
_tryNodeChangeEvent: function (that, ed, cm, node, collapsed) {
if (that._deferNodeChangeEvent) {
that._deferNodeChangeEvent = false;
that._nodeChangeEventScheduled = false;
that._scheduleNodeChangeEvent(ed, cm, node, collapsed);
} else {
/* Call expensive nodeChange stuff from here
* If we got to here, the cursor has been idle for > 500ms
*
* Additionally, the node and collapsed args are no longer
* relevant since the setTimeout was set. Use
* ed.selection.getNode() and ed.selection.isCollapsed() instead
*/
that._doUpdateButtonState(ed, cm);
that._nodeChangeEventScheduled = false;
}

if (collapsed) { // Disable the buttons
return;
},

/* This is a wrapper to _updateButtonState(). It tries to avoid
** updating the button state if the cursor has only moved within the
** textNode of a given node; ie. if the cursor is still inside the same
** node as the last time the button state was calculated, then there is
** no need to update it yet again.
*/
_doUpdateButtonState: function (ed, cm) {
var selectedFormats, listbox, node = ed.selection.getNode(),
collapsed = ed.selection.isCollapsed();
if (!collapsed) {
// !collapsed means a selection; always update button state if
// there is a selection.
this._updateButtonState(ed, cm)
} else if (node !== this._lastButtonUpdateNode) {
// Only update button state if it wasn't already calculated for
// this node already on a previous call.
this._updateButtonState(ed, cm, node, collapsed);

// Remember the node
this._lastButtonUpdateNode = node;
}
},

/* Item9427: Slow cursor movement in IEs on large, >250KiB documents
* Please read perf results and test method on that task
* if you make changes here, to compare before/after. A fast
* PC with a decent browser performs nothing like IE7 on a
* netbook
*
* _updateButtonState() is only called when the cursor has not moved
* for 500ms or more.
*/
_updateButtonState: function (ed, cm, node, collapsed) {
var selectedFormats, listbox;

selectedFormats = ed.formatter.matchAll(
ed.plugins.foswikibuttons.format_names),
listbox = cm.get(ed.id + '_foswikiformat');

return true;

},

_updateButtonState: function (ed, cm, node, collapsed) {
var selectedFormats, listbox;

/* selectedFormats = ed.formatter.matchAll(
ed.plugins.foswikibuttons.format_names),
listbox = cm.get(ed.id + '_foswikiformat');*/

/* if (collapsed) { // Disable the buttons
cm.setDisabled('colour', true);
cm.setDisabled('tt', true);
} else { // A selection means the buttons should be active.
Expand All @@ -280,15 +418,12 @@
cm.setActive('colour', true);
} else {
cm.setActive('colour', false);
}
if (selectedFormats.length > 0) {
}*/
/* if (selectedFormats.length > 0) {
listbox.select(selectedFormats[0]);
} else {
listbox.select('Normal');
}

return true;

}*/
},

_contextMenuVerbatimClasses: function (ed) {
Expand Down

0 comments on commit a79e3f6

Please sign in to comment.