{"id":2580,"date":"2012-08-20T14:07:14","date_gmt":"2012-08-20T20:07:14","guid":{"rendered":"http:\/\/bililite.com\/blog\/?p=2580"},"modified":"2012-09-05T20:46:50","modified_gmt":"2012-09-06T02:46:50","slug":"custom-buttons-in-the-wordpress-html-editor","status":"publish","type":"post","link":"https:\/\/bililite.com\/blog\/2012\/08\/20\/custom-buttons-in-the-wordpress-html-editor\/","title":{"rendered":"Custom Buttons in the WordPress HTML Editor"},"content":{"rendered":"<p>I use the <a href=\"http:\/\/thesisthemetools.com\/how-and-when-to-use-the-wordpress-html-editor\/\">WordPress HTML editor<\/a> exclusively, with the <a href=\"http:\/\/wordpress.org\/extend\/plugins\/text-control\/\">Text Control plugin<\/a> with no formatting, so the posts contain exactly the markup I want. The editor comes with \"Quick Tag\" buttons, that let you insert HTML tags with one click, and allows you to add custom buttons. So, for all my code samples, I want to have a <code class=\"language-html\" >&lt;pre&gt;<\/code> button, I just create a javascript file (say, called <a href=\"\/blog\/wp-content\/plugins\/jquery\/quicktags.custom.js\"><code>quicktags.custom.js<\/code><\/a>):<\/p>\r\n<pre><code class=\"language-javascript\" >QTags.addButton('pre', 'pre', '&lt;pre&gt;', '&lt;\/pre&gt;\\n');<\/code><\/pre>\r\n<p>And include it with the following PHP either in a plugin or my theme <code>functions.php<\/code>:<\/p>\r\n<pre><code class=\"language-php\" >if(is_admin()){\r\n\t\/\/ it's an admin page. Add the custom editor buttons\r\n\twp_register_script('customeditor', plugins_url('quicktags.custom.js', __FILE__), array('quicktags')); \/\/ this script depends on 'quicktags'\r\n\twp_enqueue_script('customeditor');\r\n}<\/code><\/pre>\r\n<p>And now I have a button that inserts <code class=\"language-html\" >&lt;pre&gt;&lt;\/pre&gt;<\/code> pairs. But there's much more you can do.<\/p>\r\n<!--more-->\r\n<p>The documentation, as far as I can tell, is only in the unminified source code, <code>wp-includes\/js\/quicktags.dev.js<\/code>. The only \"official\" API is the <code class=\"language-javascript\" >QTags.addButton<\/code> function, with the following parameters:\r\n<pre><code>\t * @param id string required Button HTML ID\r\n\t * @param display string required Button's value=\"...\"\r\n\t * @param arg1 string || function required Either a starting tag to be inserted like \"&lt;span&gt;\" or a callback that is executed when the button is clicked.\r\n\t * @param arg2 string optional Ending tag like \"&lt;\/span&gt;\"\r\n\t * @param access_key string optional Access key for the button.\r\n\t * @param title string optional Button's title=\"...\"\r\n\t * @param priority int optional Number representing the desired position of the button in the toolbar. 1 - 9 = first, 11 - 19 = second, 21 - 29 = third, etc.\r\n\t * @param instance string optional Limit the button to a specifric instance of Quicktags, add to all instances if not present.\r\n<\/code><\/pre>\r\n<p>So <code class=\"language-javascript\" >QTags.addButton('pre', 'pre', '&lt;pre&gt;', '&lt;\/pre&gt;\\n');<\/code> creates a button, <code class=\"language-html\" >&lt;input type=\"button\" id=\"qt_content_pre\" class=\"ed_button\" title=\"\" value=\"pre\"&gt;<\/code> (the id is a predefined prefix followed by what was passed as the <code>id<\/code> parameter), that inserts <code class=\"language-html\" >&lt;pre&gt;<\/code> before the selection and <code class=\"language-html\" >&lt;\/pre&gt;<\/code> after the selection (plus a newline).<\/p>\r\n<p>But the power is in using a function rather than a string as the <code>arg1<\/code>. They provide a function <code class=\"language-javascript\" >QTags.insertContent(string)<\/code> that replaces the current selection with <code>string<\/code>, but you also need a simple selection-getting function:<\/p>\r\n<pre><code class=\"language-javascript\" >function getSelectedText(canvas){ \/\/ \"canvas\" is what they call the textarea of the editor\r\n\tcanvas.focus();\r\n\tif (document.selection) { \/\/ IE\r\n\t\treturn document.selection.createRange().text;\r\n\t}else{ \/\/ standards\r\n\t\treturn canvas.value.substring(canvas.selectionStart, canvas.selectionEnd);\r\n\t}\r\n}<\/code><\/pre>\r\n<p>And now I can add buttons to do the inconvient HTML encoding\/decoding:<\/p>\r\n<pre><code class=\"language-javascript\" >QTags.addButton('toHTML', 'HTML Entities', function(el, canvas){\r\n\tQTags.insertContent(getSelectedText(canvas).replace(\/&amp;\/g,'&amp;amp;').replace(\/&lt;\/g,'&amp;lt;').replace(\/&gt;\/g,'&amp;gt;'));\r\n}, 'Encode HTML Entities');\r\nQTags.addButton('fromHTML', 'Decode HTML', function(el, canvas){\r\n\tQTags.insertContent(getSelectedText(canvas).replace(\/&amp;amp;\/g,'&amp;').replace(\/&amp;lt;\/g,'&lt;').replace(\/&amp;gt;\/g,'&gt;'));\r\n}, 'Decode HTML Entities');<\/code><\/pre>\r\n<p>The callback function is passed two paramers, <code>el<\/code>, the element that was clicked, and <code>canvas<\/code>, the <code>textarea<\/code> of the editor.<\/p>\r\n<p>But I want even more. My code examples use the <a href=\"http:\/\/www.w3.org\/TR\/html5\/the-code-element.html#the-code-element\"><code>language-javascript<\/code> etc. standard<\/a>, which involves a lot of typing. I could create separate buttons for <code class=\"language-javascript\" >'&lt;code class=\"language-javascript\"&gt;'<\/code>,  <code class=\"language-javascript\" >'&lt;code class=\"language-html\"&gt;'<\/code>,  <code class=\"language-javascript\" >'&lt;code class=\"language-php\"&gt;'<\/code>,  ad nauseum, but that gets very cluttered. I'd rather have a pull-down menu of languages that will change the <code>code<\/code> button itself.<\/p>\r\n<p>To do this, I need to hack a bit: the buttons are all represented by a global array, <code>edButtons<\/code> of objects that represent the data that goes into creating each button. We need the index of the <code>code<\/code> button (which is 110, by inspecting the source), so our pull-down menu will be index 111. Each object in the array has a member <code>html<\/code>, which is a function that is called with the prefix to affix to the id:<\/p>\r\n<pre><code class=\"language-javascript\" >var languages = ['html','javascript','lisp','pdf','php','vb'];\r\nedButtons[111] = { \/\/ insert right after the code button\r\n\thtml: function(idPrefix){\r\n\t\treturn '&lt;select id=\"'+idPrefix+'code_language\" class=\"language-select\"&gt;'+\r\n\t\t'&lt;option&gt;&lt;\/option&gt;'+ \/\/ include a blank option\r\n\t\t'&lt;option&gt;'+languages.join('&lt;\/option&gt;&lt;option&gt;')+'&lt;\/option&gt;'+\r\n\t\t'&lt;\/select&gt;';\r\n\t}\r\n}<\/code><\/pre>\r\n<p>The other members of the objects in the <code>edButtons<\/code> don't concern us now, except for the <code>tagStart<\/code> member, which is the\r\nstring that is inserted as the start tag. This is what we will modify in the code button itself:<\/p>\r\n<pre><code class=\"language-javascript\" >$('body').on('change', 'select.language-select', function(){\r\n\tvar lang = $(this).val();\r\n\tedButtons[110].tagStart = lang ? '&lt;code class=\"language-'+lang+'\" &gt;' : '&lt;code&gt;';\r\n});<\/code><\/pre>\r\n<p>In reality, we'd like to make this more general without the <code>110<\/code> index hard-wired, but that is straightforward. And now we have a customizable <code>code<\/code> button along with our HTML encoding goodies, and editing code snippets is much easier.<\/p>\r\n<p>You can see a <a href=\"\/blog\/blogfiles\/customeditor.html\">sample page<\/a> with my custom Prism syntax highlighting. It avoids hard-coding the index of the button by searching <code>edButtons<\/code> for the appropriate button.<\/p>\r\n\r\n","protected":false},"excerpt":{"rendered":"I use the WordPress HTML editor exclusively, with the Text Control plugin with no formatting, so the posts contain exactly the markup I want. The editor comes with \"Quick Tag\" buttons, that let you insert HTML tags with one click, and allows you to add custom buttons. So, for all my code samples, I want [&hellip;]","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10,9,17],"tags":[],"_links":{"self":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/2580"}],"collection":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/comments?post=2580"}],"version-history":[{"count":28,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/2580\/revisions"}],"predecessor-version":[{"id":2616,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/2580\/revisions\/2616"}],"wp:attachment":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/media?parent=2580"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/categories?post=2580"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/tags?post=2580"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}