From 6b0932fb960948cba79d96d10b952c70b5b8dd04 Mon Sep 17 00:00:00 2001 From: Ricky Barrette Date: Sun, 27 Jan 2013 01:25:42 -0500 Subject: [PATCH] Forgot to commit ckeditor bbcode plugin and conf --- .../ckeditor/bbcode/dev/bbcode.html | 172 ++++ .../javascripts/ckeditor/bbcode/plugin.js | 777 ++++++++++++++++++ .../ckeditor/bbcode/samples/bbcode.html | 111 +++ app/assets/javascripts/ckeditor/config.js | 13 + app/views/topics/show.html.erb | 7 +- 5 files changed, 1077 insertions(+), 3 deletions(-) create mode 100644 app/assets/javascripts/ckeditor/bbcode/dev/bbcode.html create mode 100644 app/assets/javascripts/ckeditor/bbcode/plugin.js create mode 100644 app/assets/javascripts/ckeditor/bbcode/samples/bbcode.html create mode 100644 app/assets/javascripts/ckeditor/config.js diff --git a/app/assets/javascripts/ckeditor/bbcode/dev/bbcode.html b/app/assets/javascripts/ckeditor/bbcode/dev/bbcode.html new file mode 100644 index 0000000..f60e39f --- /dev/null +++ b/app/assets/javascripts/ckeditor/bbcode/dev/bbcode.html @@ -0,0 +1,172 @@ + + + + + + BBCode plugin playground – CKEditor Sample + + + + + +

+ CKEditor Sample — BBCode plugin playground +

+
+
+

+ This sample shows how to configure CKEditor to output BBCode format instead of HTML. + Please note that the editor configuration was modified to reflect what is needed in a BBCode editing environment. + Smiley images, for example, were stripped to the emoticons that are commonly used in some BBCode dialects. +

+

+ Please note that currently there is no standard for the BBCode markup language, so its implementation + for different platforms (message boards, blogs etc.) can vary. This means that before using CKEditor to + output BBCode you may need to adjust the implementation to your own environment. +

+

+ A snippet of the configuration code can be seen below; check the source of this page for + a full definition: +

+
+CKEDITOR.inline( 'editor1', {
+	extraPlugins : 'bbcode',
+	(below configurations details omitted:)
+	toolbar : ...,
+	fontSize_sizes : ...,
+	smiley_images : ...,
+	smiley_descriptions : ...
+});
+
+ +
+ +
+
+

BBCode Output:

+
+		
+
+
+ + + + diff --git a/app/assets/javascripts/ckeditor/bbcode/plugin.js b/app/assets/javascripts/ckeditor/bbcode/plugin.js new file mode 100644 index 0000000..1890d6e --- /dev/null +++ b/app/assets/javascripts/ckeditor/bbcode/plugin.js @@ -0,0 +1,777 @@ +/** + * @license Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.html or http://ckeditor.com/license + */ + +(function() { + CKEDITOR.on( 'dialogDefinition', function( ev ) { + var tab, + name = ev.data.name, + definition = ev.data.definition; + + if ( name == 'link' ) { + definition.removeContents( 'target' ); + definition.removeContents( 'upload' ); + definition.removeContents( 'advanced' ); + tab = definition.getContents( 'info' ); + tab.remove( 'emailSubject' ); + tab.remove( 'emailBody' ); + } else if ( name == 'image' ) { + definition.removeContents( 'advanced' ); + tab = definition.getContents( 'Link' ); + tab.remove( 'cmbTarget' ); + tab = definition.getContents( 'info' ); + tab.remove( 'txtAlt' ); + tab.remove( 'basic' ); + } + }); + + var bbcodeMap = { b: 'strong', u: 'u', i: 'em', color: 'span', size: 'span', quote: 'blockquote', code: 'code', url: 'a', email: 'span', img: 'span', '*': 'li', list: 'ol' }, + convertMap = { strong: 'b', b: 'b', u: 'u', em: 'i', i: 'i', code: 'code', li: '*' }, + tagnameMap = { strong: 'b', em: 'i', u: 'u', li: '*', ul: 'list', ol: 'list', code: 'code', a: 'link', img: 'img', blockquote: 'quote' }, + stylesMap = { color: 'color', size: 'font-size' }, + attributesMap = { url: 'href', email: 'mailhref', quote: 'cite', list: 'listType' }; + + // List of block-like tags. + var dtd = CKEDITOR.dtd, + blockLikeTags = CKEDITOR.tools.extend( { table:1 }, dtd.$block, dtd.$listItem, dtd.$tableContent, dtd.$list ); + + var semicolonFixRegex = /\s*(?:;\s*|$)/; + + function serializeStyleText( stylesObject ) { + var styleText = ''; + for ( var style in stylesObject ) { + var styleVal = stylesObject[ style ], + text = ( style + ':' + styleVal ).replace( semicolonFixRegex, ';' ); + + styleText += text; + } + return styleText; + } + + // Maintain the map of smiley-to-description. + var smileyMap = { smiley: ':)', sad: ':(', wink: ';)', laugh: ':D', cheeky: ':P', blush: ':*)', surprise: ':-o', indecision: ':|', angry: '>:(', angel: 'o:)', cool: '8-)', devil: '>:-)', crying: ';(', kiss: ':-*' }, + smileyReverseMap = {}, + smileyRegExp = []; + + // Build regexp for the list of smiley text. + for ( var i in smileyMap ) { + smileyReverseMap[ smileyMap[ i ] ] = i; + smileyRegExp.push( smileyMap[ i ].replace( /\(|\)|\:|\/|\*|\-|\|/g, function( match ) { + return '\\' + match; + })); + } + + smileyRegExp = new RegExp( smileyRegExp.join( '|' ), 'g' ); + + var decodeHtml = (function() { + var regex = [], + entities = { + nbsp: '\u00A0', // IE | FF + shy: '\u00AD', // IE + gt: '\u003E', // IE | FF | -- | Opera + lt: '\u003C' // IE | FF | Safari | Opera + }; + + for ( var entity in entities ) + regex.push( entity ); + + regex = new RegExp( '&(' + regex.join( '|' ) + ');', 'g' ); + + return function( html ) { + return html.replace( regex, function( match, entity ) { + return entities[ entity ]; + }); + }; + })(); + + CKEDITOR.BBCodeParser = function() { + this._ = { + bbcPartsRegex: /(?:\[([^\/\]=]*?)(?:=([^\]]*?))?\])|(?:\[\/([a-z]{1,16})\])/ig + }; + }; + + CKEDITOR.BBCodeParser.prototype = { + parse: function( bbcode ) { + var parts, part, + lastIndex = 0; + + while ( ( parts = this._.bbcPartsRegex.exec( bbcode ) ) ) { + var tagIndex = parts.index; + if ( tagIndex > lastIndex ) { + var text = bbcode.substring( lastIndex, tagIndex ); + this.onText( text, 1 ); + } + + lastIndex = this._.bbcPartsRegex.lastIndex; + + // "parts" is an array with the following items: + // 0 : The entire match for opening/closing tags and line-break; + // 1 : line-break; + // 2 : open of tag excludes option; + // 3 : tag option; + // 4 : close of tag; + + part = ( parts[ 1 ] || parts[ 3 ] || '' ).toLowerCase(); + // Unrecognized tags should be delivered as a simple text (#7860). + if ( part && !bbcodeMap[ part ] ) { + this.onText( parts[ 0 ] ); + continue; + } + + // Opening tag + if ( parts[ 1 ] ) { + var tagName = bbcodeMap[ part ], + attribs = {}, + styles = {}, + optionPart = parts[ 2 ]; + + if ( optionPart ) { + if ( part == 'list' ) { + if ( !isNaN( optionPart ) ) + optionPart = 'decimal'; + else if ( /^[a-z]+$/.test( optionPart ) ) + optionPart = 'lower-alpha'; + else if ( /^[A-Z]+$/.test( optionPart ) ) + optionPart = 'upper-alpha'; + } + + if ( stylesMap[ part ] ) { + // Font size represents percentage. + if ( part == 'size' ) + optionPart += '%'; + + styles[ stylesMap[ part ] ] = optionPart; + attribs.style = serializeStyleText( styles ); + } else if ( attributesMap[ part ] ) + attribs[ attributesMap[ part ] ] = optionPart; + } + + // Two special handling - image and email, protect them + // as "span" with an attribute marker. + if ( part == 'email' || part == 'img' ) + attribs[ 'bbcode' ] = part; + + this.onTagOpen( tagName, attribs, CKEDITOR.dtd.$empty[ tagName ] ); + } + // Closing tag + else if ( parts[ 3 ] ) + this.onTagClose( bbcodeMap[ part ] ); + } + + if ( bbcode.length > lastIndex ) + this.onText( bbcode.substring( lastIndex, bbcode.length ), 1 ); + } + }; + + /** + * Creates a {@link CKEDITOR.htmlParser.fragment} from an HTML string. + * + * var fragment = CKEDITOR.htmlParser.fragment.fromHtml( 'Sample Text' ); + * alert( fragment.children[ 0 ].name ); // 'b' + * alert( fragment.children[ 1 ].value ); // ' Text' + * + * @static + * @member CKEDITOR.htmlParser.fragment + * @param {String} source The HTML to be parsed, filling the fragment. + * @returns {CKEDITOR.htmlParser.fragment} The fragment created. + */ + CKEDITOR.htmlParser.fragment.fromBBCode = function( source ) { + var parser = new CKEDITOR.BBCodeParser(), + fragment = new CKEDITOR.htmlParser.fragment(), + pendingInline = [], + pendingBrs = 0, + currentNode = fragment, + returnPoint; + + function checkPending( newTagName ) { + if ( pendingInline.length > 0 ) { + for ( var i = 0; i < pendingInline.length; i++ ) { + var pendingElement = pendingInline[ i ], + pendingName = pendingElement.name, + pendingDtd = CKEDITOR.dtd[ pendingName ], + currentDtd = currentNode.name && CKEDITOR.dtd[ currentNode.name ]; + + if ( ( !currentDtd || currentDtd[ pendingName ] ) && ( !newTagName || !pendingDtd || pendingDtd[ newTagName ] || !CKEDITOR.dtd[ newTagName ] ) ) { + // Get a clone for the pending element. + pendingElement = pendingElement.clone(); + + // Add it to the current node and make it the current, + // so the new element will be added inside of it. + pendingElement.parent = currentNode; + currentNode = pendingElement; + + // Remove the pending element (back the index by one + // to properly process the next entry). + pendingInline.splice( i, 1 ); + i--; + } + } + } + } + + function checkPendingBrs( tagName, closing ) { + var len = currentNode.children.length, + previous = len > 0 && currentNode.children[ len - 1 ], + lineBreakParent = !previous && writer.getRule( tagnameMap[ currentNode.name ], 'breakAfterOpen' ), + lineBreakPrevious = previous && previous.type == CKEDITOR.NODE_ELEMENT && writer.getRule( tagnameMap[ previous.name ], 'breakAfterClose' ), + lineBreakCurrent = tagName && writer.getRule( tagnameMap[ tagName ], closing ? 'breakBeforeClose' : 'breakBeforeOpen' ); + + if ( pendingBrs && ( lineBreakParent || lineBreakPrevious || lineBreakCurrent ) ) + pendingBrs--; + + // 1. Either we're at the end of block, where it requires us to compensate the br filler + // removing logic (from htmldataprocessor). + // 2. Or we're at the end of pseudo block, where it requires us to compensate + // the bogus br effect. + if ( pendingBrs && tagName in blockLikeTags ) + pendingBrs++; + + while ( pendingBrs && pendingBrs-- ) + currentNode.children.push( previous = new CKEDITOR.htmlParser.element( 'br' ) ); + } + + function addElement( node, target ) { + checkPendingBrs( node.name, 1 ); + + target = target || currentNode || fragment; + + var len = target.children.length, + previous = len > 0 && target.children[ len - 1 ] || null; + + node.previous = previous; + node.parent = target; + + target.children.push( node ); + + if ( node.returnPoint ) { + currentNode = node.returnPoint; + delete node.returnPoint; + } + } + + parser.onTagOpen = function( tagName, attributes, selfClosing ) { + var element = new CKEDITOR.htmlParser.element( tagName, attributes ); + + // This is a tag to be removed if empty, so do not add it immediately. + if ( CKEDITOR.dtd.$removeEmpty[ tagName ] ) { + pendingInline.push( element ); + return; + } + + var currentName = currentNode.name; + + var currentDtd = currentName && ( CKEDITOR.dtd[ currentName ] || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) ); + + // If the element cannot be child of the current element. + if ( currentDtd && !currentDtd[ tagName ] ) { + var reApply = false, + addPoint; // New position to start adding nodes. + + // If the element name is the same as the current element name, + // then just close the current one and append the new one to the + // parent. This situation usually happens with

,

  • ,
    and + //
    , specially in IE. Do not enter in this if block in this case. + if ( tagName == currentName ) + addElement( currentNode, currentNode.parent ); + else if ( tagName in CKEDITOR.dtd.$listItem ) { + parser.onTagOpen( 'ul', {} ); + addPoint = currentNode; + reApply = true; + } else { + addElement( currentNode, currentNode.parent ); + + // The current element is an inline element, which + // cannot hold the new one. Put it in the pending list, + // and try adding the new one after it. + pendingInline.unshift( currentNode ); + reApply = true; + } + + if ( addPoint ) + currentNode = addPoint; + // Try adding it to the return point, or the parent element. + else + currentNode = currentNode.returnPoint || currentNode.parent; + + if ( reApply ) { + parser.onTagOpen.apply( this, arguments ); + return; + } + } + + checkPending( tagName ); + checkPendingBrs( tagName ); + + element.parent = currentNode; + element.returnPoint = returnPoint; + returnPoint = 0; + + if ( element.isEmpty ) + addElement( element ); + else + currentNode = element; + }; + + parser.onTagClose = function( tagName ) { + // Check if there is any pending tag to be closed. + for ( var i = pendingInline.length - 1; i >= 0; i-- ) { + // If found, just remove it from the list. + if ( tagName == pendingInline[ i ].name ) { + pendingInline.splice( i, 1 ); + return; + } + } + + var pendingAdd = [], + newPendingInline = [], + candidate = currentNode; + + while ( candidate.type && candidate.name != tagName ) { + // If this is an inline element, add it to the pending list, if we're + // really closing one of the parents element later, they will continue + // after it. + if ( !candidate._.isBlockLike ) + newPendingInline.unshift( candidate ); + + // This node should be added to it's parent at this point. But, + // it should happen only if the closing tag is really closing + // one of the nodes. So, for now, we just cache it. + pendingAdd.push( candidate ); + + candidate = candidate.parent; + } + + if ( candidate.type ) { + // Add all elements that have been found in the above loop. + for ( i = 0; i < pendingAdd.length; i++ ) { + var node = pendingAdd[ i ]; + addElement( node, node.parent ); + } + + currentNode = candidate; + + + addElement( candidate, candidate.parent ); + + // The parent should start receiving new nodes now, except if + // addElement changed the currentNode. + if ( candidate == currentNode ) + currentNode = currentNode.parent; + + pendingInline = pendingInline.concat( newPendingInline ); + } + }; + + parser.onText = function( text ) { + var currentDtd = CKEDITOR.dtd[ currentNode.name ]; + if ( !currentDtd || currentDtd[ '#' ] ) { + checkPendingBrs(); + checkPending(); + + text.replace( /(\r\n|[\r\n])|[^\r\n]*/g, function( piece, lineBreak ) { + if ( lineBreak !== undefined && lineBreak.length ) + pendingBrs++; + else if ( piece.length ) { + var lastIndex = 0; + + // Create smiley from text emotion. + piece.replace( smileyRegExp, function( match, index ) { + addElement( new CKEDITOR.htmlParser.text( piece.substring( lastIndex, index ) ), currentNode ); + addElement( new CKEDITOR.htmlParser.element( 'smiley', { desc: smileyReverseMap[ match ] } ), currentNode ); + lastIndex = index + match.length; + }); + + if ( lastIndex != piece.length ) + addElement( new CKEDITOR.htmlParser.text( piece.substring( lastIndex, piece.length ) ), currentNode ); + } + }); + } + }; + + // Parse it. + parser.parse( CKEDITOR.tools.htmlEncode( source ) ); + + // Close all hanging nodes. + while ( currentNode.type != CKEDITOR.NODE_DOCUMENT_FRAGMENT ) { + var parent = currentNode.parent, + node = currentNode; + + addElement( node, parent ); + currentNode = parent; + } + + return fragment; + }; + + var BBCodeWriter = CKEDITOR.tools.createClass({ + $: function() { + this._ = { + output: [], + rules: [] + }; + + // List and list item. + this.setRules( 'list', { breakBeforeOpen:1,breakAfterOpen:1,breakBeforeClose:1,breakAfterClose:1 }); + + this.setRules( '*', { + breakBeforeOpen: 1, + breakAfterOpen: 0, + breakBeforeClose: 1, + breakAfterClose: 0 + }); + + this.setRules( 'quote', { + breakBeforeOpen: 1, + breakAfterOpen: 0, + breakBeforeClose: 0, + breakAfterClose: 1 + }); + }, + + proto: { + // + // Sets formatting rules for a given tag. The possible rules are: + //
      + //
    • breakBeforeOpen: break line before the opener tag for this element.
    • + //
    • breakAfterOpen: break line after the opener tag for this element.
    • + //
    • breakBeforeClose: break line before the closer tag for this element.
    • + //
    • breakAfterClose: break line after the closer tag for this element.
    • + //
    + // + // All rules default to "false". Each call to the function overrides + // already present rules, leaving the undefined untouched. + // + // @param {String} tagName The tag name to which set the rules. + // @param {Object} rules An object containing the element rules. + // @example + // // Break line before and after "img" tags. + // writer.setRules( 'list', + // { + // breakBeforeOpen : true + // breakAfterOpen : true + // }); + setRules: function( tagName, rules ) { + var currentRules = this._.rules[ tagName ]; + + if ( currentRules ) + CKEDITOR.tools.extend( currentRules, rules, true ); + else + this._.rules[ tagName ] = rules; + }, + + getRule: function( tagName, ruleName ) { + return this._.rules[ tagName ] && this._.rules[ tagName ][ ruleName ]; + }, + + openTag : function( tag ) { + if ( tag in bbcodeMap ) { + if ( this.getRule( tag, 'breakBeforeOpen' ) ) + this.lineBreak( 1 ); + + this.write( '[', tag ); + } + }, + + openTagClose : function( tag ) { + if ( tag == 'br' ) + this._.output.push( '\n' ); + else if ( tag in bbcodeMap ) { + this.write( ']' ); + if ( this.getRule( tag, 'breakAfterOpen' ) ) + this.lineBreak( 1 ); + } + }, + + attribute : function( name, val ) { + if ( name == 'option' ) { + // Force simply ampersand in attributes. + if ( typeof val == 'string' ) + val = val.replace( /&/g, '&' ); + + this.write( '=', val ); + } + }, + + closeTag: function( tag ) { + if ( tag in bbcodeMap ) { + if ( this.getRule( tag, 'breakBeforeClose' ) ) + this.lineBreak( 1 ); + + tag != '*' && this.write( '[/', tag, ']' ); + + if ( this.getRule( tag, 'breakAfterClose' ) ) + this.lineBreak( 1 ); + } + }, + + text: function( text ) { + this.write( text ); + }, + + comment: function() {}, + + // Output line-break for formatting. + lineBreak: function() { + // Avoid line break when: + // 1) Previous tag already put one. + // 2) We're at output start. + if ( !this._.hasLineBreak && this._.output.length ) { + this.write( '\n' ); + this._.hasLineBreak = 1; + } + }, + + write: function() { + this._.hasLineBreak = 0; + var data = Array.prototype.join.call( arguments, '' ); + this._.output.push( data ); + }, + + reset: function() { + this._.output = []; + this._.hasLineBreak = 0; + }, + + getHtml: function( reset ) { + var bbcode = this._.output.join( '' ); + + if ( reset ) + this.reset(); + + return decodeHtml( bbcode ); + } + } + }); + + var writer = new BBCodeWriter(); + + CKEDITOR.plugins.add( 'bbcode', { + requires: 'entities', + + beforeInit: function( editor ) { + // Adapt some critical editor configuration for better support + // of BBCode environment. + var config = editor.config; + CKEDITOR.tools.extend( config, { + enterMode: CKEDITOR.ENTER_BR, + basicEntities: false, + entities: false, + fillEmptyBlocks: false + }, true ); + }, + + init: function( editor ) { + var config = editor.config; + + function BBCodeToHtml( code ) { + var fragment = CKEDITOR.htmlParser.fragment.fromBBCode( code ), + writer = new CKEDITOR.htmlParser.basicWriter(); + + fragment.writeHtml( writer, bbcodeFilter ); + return writer.getHtml( true ); + } + + var bbcodeFilter = new CKEDITOR.htmlParser.filter(); + bbcodeFilter.addRules({ + elements: { + blockquote: function( element ) { + var quoted = new CKEDITOR.htmlParser.element( 'div' ); + quoted.children = element.children; + element.children = [ quoted ]; + var citeText = element.attributes.cite; + if ( citeText ) { + var cite = new CKEDITOR.htmlParser.element( 'cite' ); + cite.add( new CKEDITOR.htmlParser.text( citeText.replace( /^"|"$/g, '' ) ) ); + delete element.attributes.cite; + element.children.unshift( cite ); + } + }, + span: function( element ) { + var bbcode; + if ( ( bbcode = element.attributes.bbcode ) ) { + if ( bbcode == 'img' ) { + element.name = 'img'; + element.attributes.src = element.children[ 0 ].value; + element.children = []; + } else if ( bbcode == 'email' ) { + element.name = 'a'; + element.attributes.href = 'mailto:' + element.children[ 0 ].value; + } + + delete element.attributes.bbcode; + } + }, + ol: function( element ) { + if ( element.attributes.listType ) { + if ( element.attributes.listType != 'decimal' ) + element.attributes.style = 'list-style-type:' + element.attributes.listType; + } else + element.name = 'ul'; + + delete element.attributes.listType; + }, + a: function( element ) { + if ( !element.attributes.href ) + element.attributes.href = element.children[ 0 ].value; + }, + smiley: function( element ) { + element.name = 'img'; + + var description = element.attributes.desc, + image = config.smiley_images[ CKEDITOR.tools.indexOf( config.smiley_descriptions, description ) ], + src = CKEDITOR.tools.htmlEncode( config.smiley_path + image ); + + element.attributes = { + src: src, + 'data-cke-saved-src': src, + title: description, + alt: description + }; + } + } + }); + + editor.dataProcessor.htmlFilter.addRules({ + elements: { + $: function( element ) { + var attributes = element.attributes, + style = CKEDITOR.tools.parseCssText( attributes.style, 1 ), + value; + + var tagName = element.name; + if ( tagName in convertMap ) + tagName = convertMap[ tagName ]; + else if ( tagName == 'span' ) { + if ( ( value = style.color ) ) { + tagName = 'color'; + value = CKEDITOR.tools.convertRgbToHex( value ); + } else if ( ( value = style[ 'font-size' ] ) ) { + var percentValue = value.match( /(\d+)%$/ ); + if ( percentValue ) { + value = percentValue[ 1 ]; + tagName = 'size'; + } + } + } else if ( tagName == 'ol' || tagName == 'ul' ) { + if ( ( value = style[ 'list-style-type' ] ) ) { + switch ( value ) { + case 'lower-alpha': + value = 'a'; + break; + case 'upper-alpha': + value = 'A'; + break; + } + } else if ( tagName == 'ol' ) + value = 1; + + tagName = 'list'; + } else if ( tagName == 'blockquote' ) { + try { + var cite = element.children[ 0 ], + quoted = element.children[ 1 ], + citeText = cite.name == 'cite' && cite.children[ 0 ].value; + + if ( citeText ) { + value = '"' + citeText + '"'; + element.children = quoted.children; + } + + } catch ( er ) {} + + tagName = 'quote'; + } else if ( tagName == 'a' ) { + if ( ( value = attributes.href ) ) { + if ( value.indexOf( 'mailto:' ) !== -1 ) { + tagName = 'email'; + // [email] should have a single text child with email address. + element.children = [ new CKEDITOR.htmlParser.text( value.replace( 'mailto:', '' ) ) ]; + value = ''; + } else { + var singleton = element.children.length == 1 && element.children[ 0 ]; + if ( singleton && singleton.type == CKEDITOR.NODE_TEXT && singleton.value == value ) + value = ''; + + tagName = 'url'; + } + } + } else if ( tagName == 'img' ) { + element.isEmpty = 0; + + // Translate smiley (image) to text emotion. + var src = attributes[ 'data-cke-saved-src' ] || attributes.src, + alt = attributes.alt; + + if ( src && src.indexOf( editor.config.smiley_path ) != -1 && alt ) + return new CKEDITOR.htmlParser.text( smileyMap[ alt ] ); + else + element.children = [ new CKEDITOR.htmlParser.text( src ) ]; + } + + element.name = tagName; + value && ( element.attributes.option = value ); + + return null; + }, + + // Remove any bogus br from the end of a pseudo block, + // e.g.
    some text

    paragraph

    + br: function( element ) { + var next = element.next; + if ( next && next.name in blockLikeTags ) + return false; + } + } + }, 1 ); + + editor.dataProcessor.writer = writer; + + function onSetData( evt ) { + var bbcode = evt.data.dataValue; + evt.data.dataValue = BBCodeToHtml( bbcode ); + } + + // Skip the first "setData" call from inline creator, to allow content of + // HTML to be loaded from the page element. + if ( editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE ) + editor.once( 'contentDom', function() { + editor.on( 'setData', onSetData ); + }); + else + editor.on( 'setData', onSetData ); + + }, + + afterInit: function( editor ) { + var filters; + if ( editor._.elementsPath ) { + // Eliminate irrelevant elements from displaying, e.g body and p. + if ( ( filters = editor._.elementsPath.filters ) ) { + filters.push( function( element ) { + var htmlName = element.getName(), + name = tagnameMap[ htmlName ] || false; + + // Specialized anchor presents as email. + if ( name == 'link' && element.getAttribute( 'href' ).indexOf( 'mailto:' ) === 0 ) + name = 'email'; + // Styled span could be either size or color. + else if ( htmlName == 'span' ) { + if ( element.getStyle( 'font-size' ) ) + name = 'size'; + else if ( element.getStyle( 'color' ) ) + name = 'color'; + } else if ( name == 'img' ) { + var src = element.data( 'cke-saved-src' ) || element.getAttribute( 'src' ); + if ( src && src.indexOf( editor.config.smiley_path ) === 0 ) + name = 'smiley'; + } + + return name; + }); + } + } + } + }); + +})(); diff --git a/app/assets/javascripts/ckeditor/bbcode/samples/bbcode.html b/app/assets/javascripts/ckeditor/bbcode/samples/bbcode.html new file mode 100644 index 0000000..cf4ed99 --- /dev/null +++ b/app/assets/javascripts/ckeditor/bbcode/samples/bbcode.html @@ -0,0 +1,111 @@ + + + + + BBCode Plugin — CKEditor Sample + + + + + + + + + + +

    + CKEditor Samples » BBCode Plugin +

    +
    +

    + This sample shows how to configure CKEditor to output BBCode format instead of HTML. + Please note that the editor configuration was modified to reflect what is needed in a BBCode editing environment. + Smiley images, for example, were stripped to the emoticons that are commonly used in some BBCode dialects. +

    +

    + Please note that currently there is no standard for the BBCode markup language, so its implementation + for different platforms (message boards, blogs etc.) can vary. This means that before using CKEditor to + output BBCode you may need to adjust the implementation to your own environment. +

    +

    + A snippet of the configuration code can be seen below; check the source of this page for + a full definition: +

    +
    +CKEDITOR.replace( 'editor1', {
    +	extraPlugins: 'bbcode',
    +	toolbar: [
    +		[ 'Source', '-', 'Save', 'NewPage', '-', 'Undo', 'Redo' ],
    +		[ 'Find', 'Replace', '-', 'SelectAll', 'RemoveFormat' ],
    +		[ 'Link', 'Unlink', 'Image' ],
    +		'/',
    +		[ 'FontSize', 'Bold', 'Italic', 'Underline' ],
    +		[ 'NumberedList', 'BulletedList', '-', 'Blockquote' ],
    +		[ 'TextColor', '-', 'Smiley', 'SpecialChar', '-', 'Maximize' ]
    +	],
    +	... some other configurations omitted here
    +});	
    +
    +
    +

    + + + +

    +

    + +

    +
    + + + diff --git a/app/assets/javascripts/ckeditor/config.js b/app/assets/javascripts/ckeditor/config.js new file mode 100644 index 0000000..2bfe2c7 --- /dev/null +++ b/app/assets/javascripts/ckeditor/config.js @@ -0,0 +1,13 @@ +CKEDITOR.editorConfig = function( config ) { + config.extraPlugins = 'bbcode'; + + config.toolbar = [ + ['Source', '-', 'Save','NewPage','-','Undo','Redo'], + ['Find','Replace','-','SelectAll','RemoveFormat'], + ['Link', 'Unlink', 'Image'], + '/', + ['FontSize', 'Bold', 'Italic','Underline'], + ['NumberedList','BulletedList','-','Blockquote'], + ['TextColor', '-', 'Smiley','SpecialChar', '-', 'Maximize'] + ]; +} diff --git a/app/views/topics/show.html.erb b/app/views/topics/show.html.erb index 7bc7df7..79311cc 100755 --- a/app/views/topics/show.html.erb +++ b/app/views/topics/show.html.erb @@ -17,8 +17,7 @@ <% i += 1 %> <% if !post.user.blank? %> - - <%= link_to ("#"+ i.to_s ), post %> + <%= post.user.username %> <%= image_tag post.user.gravatar_url %> @@ -28,7 +27,9 @@ - + <%= link_to "Reply", new_topic_post_path(@topic) if can? :create, @topic.posts.new %> <%= link_to "Quote", new_topic_post_path(@topic, :quote => post) if can? :create, @topic.posts.new %>