// ==UserScript== // @name OZone Forum Helper // @description Adds a simple rich edit interface (Bold, Italic, Blockquote, Link) to any comment textarea on flickr. // @include http://forums.the-ozone.net/* // @include http://forums.theozone.net/* // @grant none // ==/UserScript== // Ozone Forum Helper // version 0.1 BETA! // 2009-04-02 // Copyright (c) 2009, Mark Stang // Released under the GPL license // http://www.gnu.org/copyleft/gpl.html // // Completely and utterly copied from // // Flickr Rich Edit // version 0.4 BETA! // 2007-05-02 // Copyright (c) 2007, Todd Moon (toddmoon.com) & jrhyley ( http://www.flickr.com/people/jrhyley/ ) // Released under the GPL license // http://www.gnu.org/copyleft/gpl.html // // -------------------------------------------------------------------- // // This is a Greasemonkey user script. To install it, you need // Greasemonkey 0.3 or later: http://greasemonkey.mozdev.org/ // Then restart Firefox and revisit this script. // // -------------------------------------------------------------------- // == CONSTANTS == // var CONTROL_BAR_ITEM_COMMAND = { ITALICIZE: 1, EMBOLDEN: 2, QUOTE: 3, LINK: 4, IMAGE: 5 } // == LIFECYCLE == // //Find existing text areas to add rich controls to. textAreas = document.evaluate( "//textarea", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null ); //Add the rich editor to the existing text areas. for ( var i = 0; i < textAreas.snapshotLength; i++) { var textArea = textAreas.snapshotItem(i); var controlBar = new ControlBar( true, true, true, true, true ); controlBar.inject( textArea ); } var pathSegments = getLowercasePathSegments( document.location.pathname ); // == CLASSES == // function ControlBar( showItalic, showBold, showQuote, showLink, showImage ) { this.showItalic = showItalic; this.showBold = showBold; this.showQuote = showQuote; this.showLink = showLink; this.showImage = showImage; this.inject = function( targetTextArea ) { var controlBar = document.createElement("div"); controlBar.setAttribute('style',''); controlBar.style.marginBottom = "4px"; controlBar.style.fontSize = "16px"; if ( showItalic ) { var item = new ControlBarItem( " italic ", CONTROL_BAR_ITEM_COMMAND.ITALICIZE, targetTextArea ); controlBar.appendChild( item.create() ); } if ( showBold ) { var item = new ControlBarItem( " bold ", CONTROL_BAR_ITEM_COMMAND.EMBOLDEN, targetTextArea ); controlBar.appendChild( item.create() ); } if ( showQuote ) { var item = new ControlBarItem( " quote ", CONTROL_BAR_ITEM_COMMAND.QUOTE, targetTextArea ); controlBar.appendChild( item.create() ); } if ( showLink ) { var item = new ControlBarItem( " Add link ", CONTROL_BAR_ITEM_COMMAND.LINK, targetTextArea ); controlBar.appendChild( item.create() ); } if ( showImage ) { var item = new ControlBarItem( " Add image ", CONTROL_BAR_ITEM_COMMAND.IMAGE, targetTextArea ); controlBar.appendChild( item.create() ); } targetTextArea.parentNode.insertBefore( controlBar, targetTextArea ); }; } function ControlBarItem( label, editCommand, targetTextArea ) { this.label = label; this.editCommand = editCommand; this.targetTextArea = targetTextArea; this.create = function() { var link = document.createElement("a"); link.innerHTML = label; link.href = "javascript:;"; link.style.marginRight = "8px;"; link.editCommand = this.editCommand; link.targetTextArea = this.targetTextArea; link.execute = this.execute; link.linkSelection = this.linkSelection; link.imageSelection = this.imageSelection; link.tagSelection = this.tagSelection; addEvent( link, "click", "execute" ); return link; } this.execute = function() { switch( this.editCommand ) { case CONTROL_BAR_ITEM_COMMAND.ITALICIZE: this.tagSelection( "", "" ); break; case CONTROL_BAR_ITEM_COMMAND.EMBOLDEN: this.tagSelection( "", "" ); break; case CONTROL_BAR_ITEM_COMMAND.QUOTE: this.tagSelection( "
", "" ); break; case CONTROL_BAR_ITEM_COMMAND.LINK: this.linkSelection(); break; case CONTROL_BAR_ITEM_COMMAND.IMAGE: this.imageSelection(); break; default: throw "Unknown command encountered"; } } this.linkSelection = function() { var url = prompt( "Enter the URL:", "" ); if ( url != null ) { this.tagSelection( '', '' ); } } this.imageSelection = function() { var url = prompt( "Enter the Image URL:", "" ); if ( url != null ) { this.tagSelection( '', '' ); } } this.tagSelection = function( tagOpen, tagClose ) { if ( this.targetTextArea.selectionStart || this.targetTextArea.selectionStart == 0 ) //relies on this property. { //record scroll top to restore it later. var scrollTop = this.targetTextArea.scrollTop; // work around Mozilla Bug #190382 if ( this.targetTextArea.selectionEnd > this.targetTextArea.value.length ) { this.targetTextArea.selectionEnd = this.targetTextArea.value.length; } //We will restore the selection later, so record the current selection. var selectionStart = this.targetTextArea.selectionStart; var selectionEnd = this.targetTextArea.selectionEnd; this.targetTextArea.value = this.targetTextArea.value.substring( 0, selectionStart ) + //text leading up to the selection start tagOpen + this.targetTextArea.value.substring( selectionStart, selectionEnd ) + //selected text tagClose + this.targetTextArea.value.substring( selectionEnd ); //text after the selection end this.targetTextArea.selectionStart = selectionStart + tagOpen.length; this.targetTextArea.selectionEnd = selectionEnd + tagOpen.length; this.targetTextArea.scrollTop = scrollTop; } } } function DescriptionDivControlBarLoader( descriptionDiv, showBlockQuote ) { this.descriptionDiv = descriptionDiv; this.initialize = function() { if ( typeof( this.descriptionDiv.startEditing ) == 'function' ) { this.descriptionDiv.richEditStartEditing = this.descriptionDiv.startEditing; // richEditStartEditing needs to be a name unique to your script if you want to follow this pattern. this.descriptionDiv.addControlBar = this.addControlBar; this.descriptionDiv.startEditing = function() { this.richEditStartEditing(); this.addControlBar(); }; this.descriptionDiv.onclick = this.descriptionDiv.startEditing; } } this.addControlBar = function() { var nodes = document.evaluate( "./div/form/textarea[@name='content']", this.parentNode, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null ); if ( nodes && nodes.snapshotLength > 0 ) { var textArea = nodes.snapshotItem(0); var controlBar = new ControlBar( true, true, showBlockQuote, true ); controlBar.inject( textArea ); } } } // == FUNCTIONS == // //Finds path segments in the given path. Removes the protocol and domain name if present. Returns an array of the segments. function getLowercasePathSegments( path ) { //replace preceding protocol and domain and then any preceding or trailing slashes then split on the remaining slashes. return path.toLowerCase().replace( /^https?:\/\/[^\/]*/, "" ).replace(/^\/+|\/+$/g,"").split("/"); } //Delegated event wire-up utitlity. Using this allows you to use the "this" keyword in a delegated function. function addEvent( target, eventName, handlerName ) { target.addEventListener(eventName, function(e){target[handlerName](e);}, false); }