| [ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 /* 2 * Quicktags 3 * 4 * This is the HTML editor in WordPress. It can be attached to any textarea and will 5 * append a toolbar above it. This script is self-contained (does not require external libraries). 6 * 7 * Run quicktags(settings) to initialize it, where settings is an object containing up to 3 properties: 8 * settings = { 9 * id : 'my_id', the HTML ID of the textarea, required 10 * buttons: '' Comma separated list of the names of the default buttons to show. Optional. 11 * Current list of default button names: 'strong,em,link,block,del,ins,img,ul,ol,li,code,more,spell,close'; 12 * } 13 * 14 * The settings can also be a string quicktags_id. 15 * 16 * quicktags_id string The ID of the textarea that will be the editor canvas 17 * buttons string Comma separated list of the default buttons names that will be shown in that instance. 18 */ 19 20 // new edit toolbar used with permission 21 // by Alex King 22 // http://www.alexking.org/ 23 24 var QTags, edButtons = [], edCanvas, 25 26 /** 27 * Back-compat 28 * 29 * Define all former global functions so plugins that hack quicktags.js directly don't cause fatal errors. 30 */ 31 edAddTag = function(){}, 32 edCheckOpenTags = function(){}, 33 edCloseAllTags = function(){}, 34 edInsertImage = function(){}, 35 edInsertLink = function(){}, 36 edInsertTag = function(){}, 37 edLink = function(){}, 38 edQuickLink = function(){}, 39 edRemoveTag = function(){}, 40 edShowButton = function(){}, 41 edShowLinks = function(){}, 42 edSpell = function(){}, 43 edToolbar = function(){}; 44 45 /** 46 * Initialize new instance of the Quicktags editor 47 */ 48 function quicktags(settings) { 49 return new QTags(settings); 50 } 51 52 /** 53 * Inserts content at the caret in the active editor (textarea) 54 * 55 * Added for back compatibility 56 * @see QTags.insertContent() 57 */ 58 function edInsertContent(bah, txt) { 59 return QTags.insertContent(txt); 60 } 61 62 /** 63 * Adds a button to all instances of the editor 64 * 65 * Added for back compatibility, use QTags.addButton() as it gives more flexibility like type of button, button placement, etc. 66 * @see QTags.addButton() 67 */ 68 function edButton(id, display, tagStart, tagEnd, access, open) { 69 return QTags.addButton( id, display, tagStart, tagEnd, access, '', -1 ); 70 } 71 72 (function(){ 73 // private stuff is prefixed with an underscore 74 var _domReady = function(func) { 75 var t, i, DOMContentLoaded; 76 77 if ( typeof jQuery != 'undefined' ) { 78 jQuery(document).ready(func); 79 } else { 80 t = _domReady; 81 t.funcs = []; 82 83 t.ready = function() { 84 if ( ! t.isReady ) { 85 t.isReady = true; 86 for ( i = 0; i < t.funcs.length; i++ ) { 87 t.funcs[i](); 88 } 89 } 90 }; 91 92 if ( t.isReady ) { 93 func(); 94 } else { 95 t.funcs.push(func); 96 } 97 98 if ( ! t.eventAttached ) { 99 if ( document.addEventListener ) { 100 DOMContentLoaded = function(){document.removeEventListener('DOMContentLoaded', DOMContentLoaded, false);t.ready();}; 101 document.addEventListener('DOMContentLoaded', DOMContentLoaded, false); 102 window.addEventListener('load', t.ready, false); 103 } else if ( document.attachEvent ) { 104 DOMContentLoaded = function(){if (document.readyState === 'complete'){ document.detachEvent('onreadystatechange', DOMContentLoaded);t.ready();}}; 105 document.attachEvent('onreadystatechange', DOMContentLoaded); 106 window.attachEvent('onload', t.ready); 107 108 (function(){ 109 try { 110 document.documentElement.doScroll("left"); 111 } catch(e) { 112 setTimeout(arguments.callee, 50); 113 return; 114 } 115 116 t.ready(); 117 })(); 118 } 119 120 t.eventAttached = true; 121 } 122 } 123 }, 124 125 _datetime = (function() { 126 var now = new Date(), zeroise; 127 128 zeroise = function(number) { 129 var str = number.toString(); 130 131 if ( str.length < 2 ) 132 str = "0" + str; 133 134 return str; 135 } 136 137 return now.getUTCFullYear() + '-' + 138 zeroise( now.getUTCMonth() + 1 ) + '-' + 139 zeroise( now.getUTCDate() ) + 'T' + 140 zeroise( now.getUTCHours() ) + ':' + 141 zeroise( now.getUTCMinutes() ) + ':' + 142 zeroise( now.getUTCSeconds() ) + 143 '+00:00'; 144 })(), 145 qt; 146 147 qt = QTags = function(settings) { 148 if ( typeof(settings) == 'string' ) 149 settings = {id: settings}; 150 else if ( typeof(settings) != 'object' ) 151 return false; 152 153 var t = this, 154 id = settings.id, 155 canvas = document.getElementById(id), 156 name = 'qt_' + id, 157 tb, onclick, toolbar_id; 158 159 if ( !id || !canvas ) 160 return false; 161 162 t.name = name; 163 t.id = id; 164 t.canvas = canvas; 165 t.settings = settings; 166 167 if ( id == 'content' && typeof(adminpage) == 'string' && ( adminpage == 'post-new-php' || adminpage == 'post-php' ) ) { 168 // back compat hack :-( 169 edCanvas = canvas; 170 toolbar_id = 'ed_toolbar'; 171 } else { 172 toolbar_id = name + '_toolbar'; 173 } 174 175 tb = document.createElement('div'); 176 tb.id = toolbar_id; 177 tb.className = 'quicktags-toolbar'; 178 179 canvas.parentNode.insertBefore(tb, canvas); 180 t.toolbar = tb; 181 182 // listen for click events 183 onclick = function(e) { 184 e = e || window.event; 185 var target = e.target || e.srcElement, i; 186 187 // as long as it has the class ed_button, execute the callback 188 if ( / ed_button /.test(' ' + target.className + ' ') ) { 189 // we have to reassign canvas here 190 t.canvas = canvas = document.getElementById(id); 191 i = target.id.replace(name + '_', ''); 192 193 if ( t.theButtons[i] ) 194 t.theButtons[i].callback.call(t.theButtons[i], target, canvas, t); 195 } 196 }; 197 198 if ( tb.addEventListener ) { 199 tb.addEventListener('click', onclick, false); 200 } else if ( tb.attachEvent ) { 201 tb.attachEvent('onclick', onclick); 202 } 203 204 t.getButton = function(id) { 205 return t.theButtons[id]; 206 }; 207 208 t.getButtonElement = function(id) { 209 return document.getElementById(name + '_' + id); 210 }; 211 212 qt.instances[id] = t; 213 214 if ( !qt.instances[0] ) { 215 qt.instances[0] = qt.instances[id]; 216 _domReady( function(){ qt._buttonsInit(); } ); 217 } 218 }; 219 220 qt.instances = {}; 221 222 qt.getInstance = function(id) { 223 return qt.instances[id]; 224 }; 225 226 qt._buttonsInit = function() { 227 var t = this, canvas, name, settings, theButtons, html, inst, ed, id, i, use, 228 defaults = ',strong,em,link,block,del,ins,img,ul,ol,li,code,more,spell,close,'; 229 230 for ( inst in t.instances ) { 231 if ( inst == 0 ) 232 continue; 233 234 ed = t.instances[inst]; 235 canvas = ed.canvas; 236 name = ed.name; 237 settings = ed.settings; 238 html = ''; 239 theButtons = {}; 240 use = ''; 241 242 // set buttons 243 if ( settings.buttons ) 244 use = ','+settings.buttons+','; 245 246 for ( i in edButtons ) { 247 if ( !edButtons[i] ) 248 continue; 249 250 id = edButtons[i].id; 251 if ( use && defaults.indexOf(','+id+',') != -1 && use.indexOf(','+id+',') == -1 ) 252 continue; 253 254 if ( !edButtons[i].instance || edButtons[i].instance == inst ) { 255 theButtons[id] = edButtons[i]; 256 257 if ( edButtons[i].html ) 258 html += edButtons[i].html(name + '_'); 259 } 260 } 261 262 if ( use && use.indexOf(',fullscreen,') != -1 ) { 263 theButtons['fullscreen'] = new qt.FullscreenButton(); 264 html += theButtons['fullscreen'].html(name + '_'); 265 } 266 267 268 if ( 'rtl' == document.getElementsByTagName('html')[0].dir ) { 269 theButtons['textdirection'] = new qt.TextDirectionButton(); 270 html += theButtons['textdirection'].html(name + '_'); 271 } 272 273 ed.toolbar.innerHTML = html; 274 ed.theButtons = theButtons; 275 } 276 t.buttonsInitDone = true; 277 }; 278 279 /** 280 * Main API function for adding a button to Quicktags 281 * 282 * Adds qt.Button or qt.TagButton depending on the args. The first three args are always required. 283 * To be able to add button(s) to Quicktags, your script should be enqueued as dependent 284 * on "quicktags" and outputted in the footer. If you are echoing JS directly from PHP, 285 * use add_action( 'admin_print_footer_scripts', 'output_my_js', 100 ) or add_action( 'wp_footer', 'output_my_js', 100 ) 286 * 287 * Minimum required to add a button that calls an external function: 288 * QTags.addButton( 'my_id', 'my button', my_callback ); 289 * function my_callback() { alert('yeah!'); } 290 * 291 * Minimum required to add a button that inserts a tag: 292 * QTags.addButton( 'my_id', 'my button', '<span>', '</span>' ); 293 * QTags.addButton( 'my_id2', 'my button', '<br />' ); 294 * 295 * @param id string required Button HTML ID 296 * @param display string required Button's value="..." 297 * @param arg1 string || function required Either a starting tag to be inserted like "<span>" or a callback that is executed when the button is clicked. 298 * @param arg2 string optional Ending tag like "</span>" 299 * @param access_key string optional Access key for the button. 300 * @param title string optional Button's title="..." 301 * @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. 302 * @param instance string optional Limit the button to a specifric instance of Quicktags, add to all instances if not present. 303 * @return mixed null or the button object that is needed for back-compat. 304 */ 305 qt.addButton = function( id, display, arg1, arg2, access_key, title, priority, instance ) { 306 var btn; 307 308 if ( !id || !display ) 309 return; 310 311 priority = priority || 0; 312 arg2 = arg2 || ''; 313 314 if ( typeof(arg1) === 'function' ) { 315 btn = new qt.Button(id, display, access_key, title, instance); 316 btn.callback = arg1; 317 } else if ( typeof(arg1) === 'string' ) { 318 btn = new qt.TagButton(id, display, arg1, arg2, access_key, title, instance); 319 } else { 320 return; 321 } 322 323 if ( priority == -1 ) // back-compat 324 return btn; 325 326 if ( priority > 0 ) { 327 while ( typeof(edButtons[priority]) != 'undefined' ) { 328 priority++ 329 } 330 331 edButtons[priority] = btn; 332 } else { 333 edButtons[edButtons.length] = btn; 334 } 335 336 if ( this.buttonsInitDone ) 337 this._buttonsInit(); // add the button HTML to all instances toolbars if addButton() was called too late 338 }; 339 340 qt.insertContent = function(content) { 341 var sel, startPos, endPos, scrollTop, text, canvas = document.getElementById(wpActiveEditor); 342 343 if ( !canvas ) 344 return false; 345 346 if ( document.selection ) { //IE 347 canvas.focus(); 348 sel = document.selection.createRange(); 349 sel.text = content; 350 canvas.focus(); 351 } else if ( canvas.selectionStart || canvas.selectionStart == '0' ) { // FF, WebKit, Opera 352 text = canvas.value; 353 startPos = canvas.selectionStart; 354 endPos = canvas.selectionEnd; 355 scrollTop = canvas.scrollTop; 356 357 canvas.value = text.substring(0, startPos) + content + text.substring(endPos, text.length); 358 359 canvas.focus(); 360 canvas.selectionStart = startPos + content.length; 361 canvas.selectionEnd = startPos + content.length; 362 canvas.scrollTop = scrollTop; 363 } else { 364 canvas.value += content; 365 canvas.focus(); 366 } 367 return true; 368 }; 369 370 // a plain, dumb button 371 qt.Button = function(id, display, access, title, instance) { 372 var t = this; 373 t.id = id; 374 t.display = display; 375 t.access = access; 376 t.title = title || ''; 377 t.instance = instance || ''; 378 }; 379 qt.Button.prototype.html = function(idPrefix) { 380 var access = this.access ? ' accesskey="' + this.access + '"' : ''; 381 return '<input type="button" id="' + idPrefix + this.id + '"' + access + ' class="ed_button" title="' + this.title + '" value="' + this.display + '" />'; 382 }; 383 qt.Button.prototype.callback = function(){}; 384 385 // a button that inserts HTML tag 386 qt.TagButton = function(id, display, tagStart, tagEnd, access, title, instance) { 387 var t = this; 388 qt.Button.call(t, id, display, access, title, instance); 389 t.tagStart = tagStart; 390 t.tagEnd = tagEnd; 391 }; 392 qt.TagButton.prototype = new qt.Button(); 393 qt.TagButton.prototype.openTag = function(e, ed) { 394 var t = this; 395 396 if ( ! ed.openTags ) { 397 ed.openTags = []; 398 } 399 if ( t.tagEnd ) { 400 ed.openTags.push(t.id); 401 e.value = '/' + e.value; 402 } 403 }; 404 qt.TagButton.prototype.closeTag = function(e, ed) { 405 var t = this, i = t.isOpen(ed); 406 407 if ( i !== false ) { 408 ed.openTags.splice(i, 1); 409 } 410 411 e.value = t.display; 412 }; 413 // whether a tag is open or not. Returns false if not open, or current open depth of the tag 414 qt.TagButton.prototype.isOpen = function (ed) { 415 var t = this, i = 0, ret = false; 416 if ( ed.openTags ) { 417 while ( ret === false && i < ed.openTags.length ) { 418 ret = ed.openTags[i] == t.id ? i : false; 419 i ++; 420 } 421 } else { 422 ret = false; 423 } 424 return ret; 425 }; 426 qt.TagButton.prototype.callback = function(element, canvas, ed) { 427 var t = this, startPos, endPos, cursorPos, scrollTop, v = canvas.value, l, r, i, sel, endTag = v ? t.tagEnd : ''; 428 429 if ( document.selection ) { // IE 430 canvas.focus(); 431 sel = document.selection.createRange(); 432 if ( sel.text.length > 0 ) { 433 if ( !t.tagEnd ) 434 sel.text = sel.text + t.tagStart; 435 else 436 sel.text = t.tagStart + sel.text + endTag; 437 } else { 438 if ( !t.tagEnd ) { 439 sel.text = t.tagStart; 440 } else if ( t.isOpen(ed) === false ) { 441 sel.text = t.tagStart; 442 t.openTag(element, ed); 443 } else { 444 sel.text = endTag; 445 t.closeTag(element, ed); 446 } 447 } 448 canvas.focus(); 449 } else if ( canvas.selectionStart || canvas.selectionStart == '0' ) { // FF, WebKit, Opera 450 startPos = canvas.selectionStart; 451 endPos = canvas.selectionEnd; 452 cursorPos = endPos; 453 scrollTop = canvas.scrollTop; 454 l = v.substring(0, startPos); // left of the selection 455 r = v.substring(endPos, v.length); // right of the selection 456 i = v.substring(startPos, endPos); // inside the selection 457 if ( startPos != endPos ) { 458 if ( !t.tagEnd ) { 459 canvas.value = l + i + t.tagStart + r; // insert self closing tags after the selection 460 cursorPos += t.tagStart.length; 461 } else { 462 canvas.value = l + t.tagStart + i + endTag + r; 463 cursorPos += t.tagStart.length + endTag.length; 464 } 465 } else { 466 if ( !t.tagEnd ) { 467 canvas.value = l + t.tagStart + r; 468 cursorPos = startPos + t.tagStart.length; 469 } else if ( t.isOpen(ed) === false ) { 470 canvas.value = l + t.tagStart + r; 471 t.openTag(element, ed); 472 cursorPos = startPos + t.tagStart.length; 473 } else { 474 canvas.value = l + endTag + r; 475 cursorPos = startPos + endTag.length; 476 t.closeTag(element, ed); 477 } 478 } 479 480 canvas.focus(); 481 canvas.selectionStart = cursorPos; 482 canvas.selectionEnd = cursorPos; 483 canvas.scrollTop = scrollTop; 484 } else { // other browsers? 485 if ( !endTag ) { 486 canvas.value += t.tagStart; 487 } else if ( t.isOpen(ed) !== false ) { 488 canvas.value += t.tagStart; 489 t.openTag(element, ed); 490 } else { 491 canvas.value += endTag; 492 t.closeTag(element, ed); 493 } 494 canvas.focus(); 495 } 496 }; 497 498 // the spell button 499 qt.SpellButton = function() { 500 qt.Button.call(this, 'spell', quicktagsL10n.lookup, '', quicktagsL10n.dictionaryLookup); 501 }; 502 qt.SpellButton.prototype = new qt.Button(); 503 qt.SpellButton.prototype.callback = function(element, canvas, ed) { 504 var word = '', sel, startPos, endPos; 505 506 if ( document.selection ) { 507 canvas.focus(); 508 sel = document.selection.createRange(); 509 if ( sel.text.length > 0 ) { 510 word = sel.text; 511 } 512 } else if ( canvas.selectionStart || canvas.selectionStart == '0' ) { 513 startPos = canvas.selectionStart; 514 endPos = canvas.selectionEnd; 515 if ( startPos != endPos ) { 516 word = canvas.value.substring(startPos, endPos); 517 } 518 } 519 520 if ( word === '' ) { 521 word = prompt(quicktagsL10n.wordLookup, ''); 522 } 523 524 if ( word !== null && /^\w[\w ]*$/.test(word)) { 525 window.open('http://www.answers.com/' + encodeURIComponent(word)); 526 } 527 }; 528 529 // the close tags button 530 qt.CloseButton = function() { 531 qt.Button.call(this, 'close', quicktagsL10n.closeTags, '', quicktagsL10n.closeAllOpenTags); 532 }; 533 534 qt.CloseButton.prototype = new qt.Button(); 535 536 qt._close = function(e, c, ed) { 537 var button, element, tbo = ed.openTags; 538 539 if ( tbo ) { 540 while ( tbo.length > 0 ) { 541 button = ed.getButton(tbo[tbo.length - 1]); 542 element = document.getElementById(ed.name + '_' + button.id); 543 544 if ( e ) 545 button.callback.call(button, element, c, ed); 546 else 547 button.closeTag(element, ed); 548 } 549 } 550 }; 551 552 qt.CloseButton.prototype.callback = qt._close; 553 554 qt.closeAllTags = function(editor_id) { 555 var ed = this.getInstance(editor_id); 556 qt._close('', ed.canvas, ed); 557 }; 558 559 // the link button 560 qt.LinkButton = function() { 561 qt.TagButton.call(this, 'link', 'link', '', '</a>', 'a'); 562 }; 563 qt.LinkButton.prototype = new qt.TagButton(); 564 qt.LinkButton.prototype.callback = function(e, c, ed, defaultValue) { 565 var URL, t = this; 566 567 if ( typeof(wpLink) != 'undefined' ) { 568 wpLink.open(); 569 return; 570 } 571 572 if ( ! defaultValue ) 573 defaultValue = 'http://'; 574 575 if ( t.isOpen(ed) === false ) { 576 URL = prompt(quicktagsL10n.enterURL, defaultValue); 577 if ( URL ) { 578 t.tagStart = '<a href="' + URL + '">'; 579 qt.TagButton.prototype.callback.call(t, e, c, ed); 580 } 581 } else { 582 qt.TagButton.prototype.callback.call(t, e, c, ed); 583 } 584 }; 585 586 // the img button 587 qt.ImgButton = function() { 588 qt.TagButton.call(this, 'img', 'img', '', '', 'm'); 589 }; 590 qt.ImgButton.prototype = new qt.TagButton(); 591 qt.ImgButton.prototype.callback = function(e, c, ed, defaultValue) { 592 if ( ! defaultValue ) { 593 defaultValue = 'http://'; 594 } 595 var src = prompt(quicktagsL10n.enterImageURL, defaultValue), alt; 596 if ( src ) { 597 alt = prompt(quicktagsL10n.enterImageDescription, ''); 598 this.tagStart = '<img src="' + src + '" alt="' + alt + '" />'; 599 qt.TagButton.prototype.callback.call(this, e, c, ed); 600 } 601 }; 602 603 qt.FullscreenButton = function() { 604 qt.Button.call(this, 'fullscreen', quicktagsL10n.fullscreen, 'f', quicktagsL10n.toggleFullscreen); 605 }; 606 qt.FullscreenButton.prototype = new qt.Button(); 607 qt.FullscreenButton.prototype.callback = function(e, c) { 608 if ( !c.id || typeof(fullscreen) == 'undefined' ) 609 return; 610 611 fullscreen.on(); 612 }; 613 614 qt.TextDirectionButton = function() { 615 qt.Button.call(this, 'textdirection', quicktagsL10n.textdirection, '', quicktagsL10n.toggleTextdirection) 616 }; 617 qt.TextDirectionButton.prototype = new qt.Button(); 618 qt.TextDirectionButton.prototype.callback = function(e, c) { 619 var isRTL = ( 'rtl' == document.getElementsByTagName('html')[0].dir ), 620 currentDirection = c.style.direction; 621 622 if ( ! currentDirection ) 623 currentDirection = ( isRTL ) ? 'rtl' : 'ltr'; 624 625 c.style.direction = ( 'rtl' == currentDirection ) ? 'ltr' : 'rtl'; 626 c.focus(); 627 } 628 629 // ensure backward compatibility 630 edButtons[10] = new qt.TagButton('strong','b','<strong>','</strong>','b'); 631 edButtons[20] = new qt.TagButton('em','i','<em>','</em>','i'), 632 edButtons[30] = new qt.LinkButton(), // special case 633 edButtons[40] = new qt.TagButton('block','b-quote','\n\n<blockquote>','</blockquote>\n\n','q'), 634 edButtons[50] = new qt.TagButton('del','del','<del datetime="' + _datetime + '">','</del>','d'), 635 edButtons[60] = new qt.TagButton('ins','ins','<ins datetime="' + _datetime + '">','</ins>','s'), 636 edButtons[70] = new qt.ImgButton(), // special case 637 edButtons[80] = new qt.TagButton('ul','ul','<ul>\n','</ul>\n\n','u'), 638 edButtons[90] = new qt.TagButton('ol','ol','<ol>\n','</ol>\n\n','o'), 639 edButtons[100] = new qt.TagButton('li','li','\t<li>','</li>\n','l'), 640 edButtons[110] = new qt.TagButton('code','code','<code>','</code>','c'), 641 edButtons[120] = new qt.TagButton('more','more','<!--more-->','','t'), 642 edButtons[130] = new qt.SpellButton(), 643 edButtons[140] = new qt.CloseButton() 644 645 })();
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Fri May 25 03:56:23 2012 | Hosted by follow the white rabbit. |