Post by Virgil Sovereign on Jan 21, 2013 23:52:08 GMT -8
Developer Tips for Modifying and Working with the WYSIWYG Editor
I spent several hours tearing my hair out working through Proboards' totally undocumented, minified WYSIWYG editor to add in some additional features. I felt it pertinent to post some of my findings to help others forgo the pain and suffering.
The editor itself is a widget sitting on top of jQuery UI. As best I can tell, it approximates TinyMCE, with some special modifications to get it to work with the Proboards backend.
The actual DOM object representing an editor is buried in jQuery's 'private' (i.e. lacking an access API) widget data. Safely obtaining a reference to it is all but impossible (correct me if I'm wrong, Proboards), and hence the only exposed methods are those than can be accessed through the
method, where <textarea selector> selects the textarea first used to instantiate the editor.
Combined with the fact that Proboards doesn't expose constructor parameters to the editor at any time before it's created, this means that you cannot change the form ID, default editor, or smileys of any editor that Proboards constructs using $[form.message.input]. If you want to modify any of these options elegantly (i.e. within the WYSIWYG framework without hack workarounds), you will need to create your own editor programatically. You can PM me on this board or post here if you need advice on how to do this safely.
Dealing with Contents
The contents of the editor are accessible at any time using the call
They will be returned in one of four formats: two variants of HTML and two variants of BBCode. The HTML variants will be returned when the editor is currently in visual editing mode, and the BBCode variants will be returned when the editor is currently in BBCode editing mode.
HTML variant 1 is what I call 'clueless HTML mode', where images recognized as smileys by the editor are missing a 'text' tag that the BBCode side of the editor needs to recognize them as smileys. Hence, the editor is 'clueless' about which images are smileys and which ones are not. If you attempt to convert clueless HTML into BBCode, you will get [img] tags instead of smiley mnemonics.
The other HTML variant is 'clued-in HTML', where smileys in the editor contain a text tag identifying them as smileys, allowing the BBCode side to replace them with their corresponding mnemonics (as passed into the .wysiwyg() constructor). If you use the default 'add smiley' interface in visual mode, the smiles added in will be clued-in HTML. Likewise if you use the command
to insert a smiley, where <mnemonic> is the smiley mnemonic (as registered in the constructor), and URL is the smiley URL.
The two BBCode variants are 'clueless BBCode' (where smileys are represented as either [img src="..."] or [img][/img]) BBCode markup instead of mnemonics, and 'mnemonic BBCode', which includes the smiley mnemonics (e.g. ).
The Mess
To convert between HTML and BBCode, Proboards uses a synchronous AJAX call on the backend.
As expected, it will convert clueless HTML into BBCode [img] tags, and clued-in HTML to registered smiley mnemonics. However, when going from mnemonic BBCode back to the visual editor, the Proboards' converter ignores all non-standard smiley mnemonics and instead leaves them as mnemonics. Hence you will not get the smiley images back in the visual editor for extended smileys.
To remedy this, you must hook onto the editor switcher (more about this below) and convert your mnemonic BBCode into clueless BBCode, where smileys are represented by [img] tags. However, when you do this, what comes out at the other end of the converter is clueless HTML, because the smiley images now lack the tags that identify them as smileys. Moreover, the Proboards converter does not respect the 'text' tag as part of a BBCode declaration, hence there is no easy way to fix this. Clueless HTML isn't bad ipso facto, but if you try to convert clueless HTML back into BBCode, it will end up as clueless BBCode, rather than the nice smiley mnemonics.
My preference is to hook into the visual-to-bbcode switch and manually replace clueless HTML with clued-in HTML, whereafter the smileys appear properly.
As if this wasn't complicated enough, when submitting the form to Proboards, the server apparently doesn't support clued-in HTML for non-standard smileys. Before submitting, if you're in visual mode, you need to hook the submit and convert the HTML being sent to Proboards into clueless HTML, or HTML markup will appear where your smileys are supposed to.
And the coup de grace: if you want to enhance the Quick Reply to include a visual editing mode, the backend that receives the form only supports BBCode. Hence, you need to insert a hook to convert clueless HTML into BBCode before the submit.
I notice some existing smiley plugins that have attempted to work around these restrictions by manually forcing rapid tab flips in the editor, which I consider to be an eyesore. They also completely circumvent the ability to insert smileys at the cursor location, or replace existing text, etc.
Code
Pseudocode I found worked for initializing an editor:
And have the hooks inserted into the stock HTML code used for tab switching:
Additional Caveats
In general, you should not attempt to replace anything broader than $[form.message.input] in the form portion of your posting templates, given you need (at the very least) the crsf_token input tag Proboards provides.
I can provide a list of commands supported by the execCommand() function of the WYSIWYG editor if so desired.
Unfortunately, you cannot move the editor or any element containing the editor once the .wysiwyg() constructor has been called. In several browsers, this will cause the iframe element used for the editor to lose all of its scripts and functionality, and it will become totally inert. If you need to move the editor or place it in a special location, you either need to do so using the forum templates or before you invoke the .wysiwyg() constructor.
Special care is needed to make sure editors respect the default editor selected by each member, and to properly initialize the editor content (if any) in the case that a post is being edited, quoted, or if a member has non-empty default UBBC. All of this means that you can't get around dumping $[form.message.input] somewhere into your document.
Unfortunately, I know of know way to get a full WYSIWYG QuickReply to respect a member's default editor settings. What I've defaulted to doing is storing their last used QuickReply editor mode in localStorage or a cookie.
Conclusions
All questions, comments, errata, additional info, etc. welcome.
I spent several hours tearing my hair out working through Proboards' totally undocumented, minified WYSIWYG editor to add in some additional features. I felt it pertinent to post some of my findings to help others forgo the pain and suffering.
The editor itself is a widget sitting on top of jQuery UI. As best I can tell, it approximates TinyMCE, with some special modifications to get it to work with the Proboards backend.
The actual DOM object representing an editor is buried in jQuery's 'private' (i.e. lacking an access API) widget data. Safely obtaining a reference to it is all but impossible (correct me if I'm wrong, Proboards), and hence the only exposed methods are those than can be accessed through the
$('<textarea selector>').wysiwyg( );
method, where <textarea selector> selects the textarea first used to instantiate the editor.
Combined with the fact that Proboards doesn't expose constructor parameters to the editor at any time before it's created, this means that you cannot change the form ID, default editor, or smileys of any editor that Proboards constructs using $[form.message.input]. If you want to modify any of these options elegantly (i.e. within the WYSIWYG framework without hack workarounds), you will need to create your own editor programatically. You can PM me on this board or post here if you need advice on how to do this safely.
Dealing with Contents
The contents of the editor are accessible at any time using the call
$('<editor>').wysiwyg( 'getContents' );
They will be returned in one of four formats: two variants of HTML and two variants of BBCode. The HTML variants will be returned when the editor is currently in visual editing mode, and the BBCode variants will be returned when the editor is currently in BBCode editing mode.
HTML variant 1 is what I call 'clueless HTML mode', where images recognized as smileys by the editor are missing a 'text' tag that the BBCode side of the editor needs to recognize them as smileys. Hence, the editor is 'clueless' about which images are smileys and which ones are not. If you attempt to convert clueless HTML into BBCode, you will get [img] tags instead of smiley mnemonics.
The other HTML variant is 'clued-in HTML', where smileys in the editor contain a text tag identifying them as smileys, allowing the BBCode side to replace them with their corresponding mnemonics (as passed into the .wysiwyg() constructor). If you use the default 'add smiley' interface in visual mode, the smiles added in will be clued-in HTML. Likewise if you use the command
$('<editor>').wysiwyg( 'execCommand', 'insertSmiley', false, { symbol: <mnemonic>, url: <url> } );
to insert a smiley, where <mnemonic> is the smiley mnemonic (as registered in the constructor), and URL is the smiley URL.
The two BBCode variants are 'clueless BBCode' (where smileys are represented as either [img src="..."] or [img][/img]) BBCode markup instead of mnemonics, and 'mnemonic BBCode', which includes the smiley mnemonics (e.g. ).
The Mess
To convert between HTML and BBCode, Proboards uses a synchronous AJAX call on the backend.
As expected, it will convert clueless HTML into BBCode [img] tags, and clued-in HTML to registered smiley mnemonics. However, when going from mnemonic BBCode back to the visual editor, the Proboards' converter ignores all non-standard smiley mnemonics and instead leaves them as mnemonics. Hence you will not get the smiley images back in the visual editor for extended smileys.
To remedy this, you must hook onto the editor switcher (more about this below) and convert your mnemonic BBCode into clueless BBCode, where smileys are represented by [img] tags. However, when you do this, what comes out at the other end of the converter is clueless HTML, because the smiley images now lack the tags that identify them as smileys. Moreover, the Proboards converter does not respect the 'text' tag as part of a BBCode declaration, hence there is no easy way to fix this. Clueless HTML isn't bad ipso facto, but if you try to convert clueless HTML back into BBCode, it will end up as clueless BBCode, rather than the nice smiley mnemonics.
My preference is to hook into the visual-to-bbcode switch and manually replace clueless HTML with clued-in HTML, whereafter the smileys appear properly.
As if this wasn't complicated enough, when submitting the form to Proboards, the server apparently doesn't support clued-in HTML for non-standard smileys. Before submitting, if you're in visual mode, you need to hook the submit and convert the HTML being sent to Proboards into clueless HTML, or HTML markup will appear where your smileys are supposed to.
And the coup de grace: if you want to enhance the Quick Reply to include a visual editing mode, the backend that receives the form only supports BBCode. Hence, you need to insert a hook to convert clueless HTML into BBCode before the submit.
I notice some existing smiley plugins that have attempted to work around these restrictions by manually forcing rapid tab flips in the editor, which I consider to be an eyesore. They also completely circumvent the ability to insert smileys at the cursor location, or replace existing text, etc.
Code
Pseudocode I found worked for initializing an editor:
<namespace>.mnem2cluelessbb = function( s ) {
converts from mnemonic BBCode to clueless BBCode
e.g. "I -heart- Huckabee." to "I [img src="http://whatever.com/heart.gif"] Huckabee."
};
<namespace>.cluelessbb2mnem = function( s ) {
converts from clueless BBCode to mnemonic BBCode
e.g. "I love [img src="http://xyz.com/cat.gif"] and [img alt=" " src="http://xyz.com/dog.png"]." to
"I love -cat- and -dog-."
};
<namespace>.cluelesshtml2cluedinhtml = function( s ) {
converts from clueless html to clued-in HTML for smileys
e.g. "This is a <img src="xyz.com/cat.gif" style="max-width:100%;">." to
"This is a <img src="xyz.com/cat.gif" alt="cat" text="cat">."
};
<namespace>.cluedinhtml2cluelesshtml = function( s ) {
converts from clueless html to clued-in HTML
return s.replace( /<img src="([^"]+)" alt="[^"]+" text="[^"]+">/gi,
function( s_, s1_ ) { return '<img src="' + s1_ + '" style="max-width:100%;">'; } );
};
<namespace>.hookbb2html = function() {
var $M = $(<editor>);
$M.wysiwyg( 'setContent', <namespace>.mnem2cluelessbb( $M.wysiwyg( 'getContent' ) ) );
};
<namespace>.hookhtml2bb = function() {
var $M = $(<editor>);
$M.wysiwyg( 'setContent', <namespace>.cluelesshtml2cluedinhtml( $M.wysiwyg( 'getContent' ) ) );
};
<namespace>.prepareForPost = function( sCode, isUBBC ) {
if( isUBBC )
return <namespace>.mnem2cluelessbb( sCode );
if( <<in quick reply>> ) {
proboards.ajax( {
url: "/filter/html2bbcode",
data: { html: sCode, attachments: JSON.stringify( {} ) },
async: false,
dataType: "json",
method: "post",
success: function( oData ) {
sCode = oData.bbcode;
}
} );
return <namespace>.mnem2cluelessbb( sCode );
}
return <namespace>.cluedinhtml2cluelesshtml( sCode );
};
<namespace>.switch = function( iMode ) {
var $T = s = ['visual','bbcode'],
$('#real-menu-item-' + s[iMode]),
fx = [<namespace>.hookbb2html,<namespace>.hookhtml2bb];
if( $T.hasClass( 'ui-active' ) )
return;
fx[iMode]();
$(<editor>).wysiwyg( 'switchEditor', s[iMode] );
$('.wysiwyg-tabs li').removeClass( 'ui-active' );
$T.addClass( 'ui-active' );
};
<namespace>.prepUI = function() {
var $M = $(<editor>),
$F = $M.closest( 'form' );
...
$M.wysiwyg( {
varName : 'message',
formId : <form ID>,
defaultEditor : <default editor>,
defaultFormat : 'bbcode',
smiles : <custom smileys>
} );
$F.form( {
submit: function() { proboards.autosave.clear(); },
beforeSubmit: function() {
proboards.data( 'submitting', 1 );
if( !$('input[name="is_mobile"]').val() ) {
$M.val( <namespace>.prepareForPost( $M.wysiwyg( 'getContent' ), <is BBCode mode active> ) );
}
...
},
validations: <validations>,
errors: {},
positionError: null
} );
...
};
And have the hooks inserted into the stock HTML code used for tab switching:
<ul class="wysiwyg-tabs">
<li id="real-menu-item-visual">
<a onclick="<namespace>.switch(0); return false;" href="javascript:void(0);">Visual</a>
</li>
<li id="real-menu-item-bbcode">
<a onclick="<namespace>.switch(1); return false;" href="javascript:void(0);">BBCode</a>
</li>
</ul>
Additional Caveats
In general, you should not attempt to replace anything broader than $[form.message.input] in the form portion of your posting templates, given you need (at the very least) the crsf_token input tag Proboards provides.
I can provide a list of commands supported by the execCommand() function of the WYSIWYG editor if so desired.
Unfortunately, you cannot move the editor or any element containing the editor once the .wysiwyg() constructor has been called. In several browsers, this will cause the iframe element used for the editor to lose all of its scripts and functionality, and it will become totally inert. If you need to move the editor or place it in a special location, you either need to do so using the forum templates or before you invoke the .wysiwyg() constructor.
Special care is needed to make sure editors respect the default editor selected by each member, and to properly initialize the editor content (if any) in the case that a post is being edited, quoted, or if a member has non-empty default UBBC. All of this means that you can't get around dumping $[form.message.input] somewhere into your document.
Unfortunately, I know of know way to get a full WYSIWYG QuickReply to respect a member's default editor settings. What I've defaulted to doing is storing their last used QuickReply editor mode in localStorage or a cookie.
Conclusions
All questions, comments, errata, additional info, etc. welcome.