@@ -7,168 +7,129 @@
* @link http://www.kunena.org
**/

//
// Element helper functions for the Kunena bbcode editor
//
// The code is based on nawte - a generic helper class for mootools to implement
// simple markup languge editors. The clas has been adopted and expanded to meed the needs
// of the Kunena bbcode engine. With a wide variety of tags supported, css sprite based
// toolbar, mouseover helpline, selective sub toolbars for various options and live preview
// before changes get applied.
//
/*
* Fix some Mootools bugs
*
* provides: [Element.Forms]
*/

Element.implement({
tidy: function() {
this.set('value', this.get('value').tidy());
},

getTextInRange: function(start, end) {
return this.get('value').substring(start, end);
},

getSelectedText: function() {
if (typeof this.selectionStart != "undefined") {
return this.get('value').substring(this.selectionStart, this.selectionEnd);
}
if(typeof document.selection != "undefined") {
// Something for IE
this.focus();
return document.selection.createRange().text;
}
return '';
if (this.setSelectionRange) return this.getTextInRange(this.getSelectionStart(), this.getSelectionEnd());
this.focus();
return document.selection.createRange().text;
},

wrapSelectedText: function(newtext, wrapperLeft, wrapperRight, isLast) {
isLast = (isLast == null) ? true : isLast;

var scroll_top = this.scrollTop;
getSelectedRange: function() {
if (this.selectionStart != null){
return {
start: this.selectionStart,
end: this.selectionEnd
};
}

if(typeof this.selectionStart != "undefined") {
var originalStart = this.selectionStart;
var originalEnd = this.selectionEnd;
this.value = this.get('value').substring(0, originalStart) + wrapperLeft + newtext + wrapperRight + this.get('value').substring(originalEnd);
if(isLast == false) {
this.setSelectionRange(originalStart + wrapperLeft.length, originalStart + wrapperLeft.length + newtext.length);
} else {
var position = originalStart + newtext.length + wrapperLeft.length + wrapperRight.length;
this.setSelectionRange(position, position);
this.scrollTop = scroll_top;
}
this.focus();

} else if(typeof document.selection != "undefined") {
// Something for IE
this.focus();
var range = document.selection.createRange();
range.text = wrapperLeft + newtext + wrapperRight;
if(isLast) {
range.select();
this.scrollTop = scroll_top;
}
var pos = {
start: 0,
end: 0
};
var range = this.getDocument().selection.createRange();
if (!range || range.parentElement() != this) return pos;
var duplicate = range.duplicate();

if (this.type == 'text') {
pos.start = 0 - duplicate.moveStart('character', -100000);
pos.end = pos.start + range.text.length;
} else {
var value = this.get('value');
var offset = value.length;
duplicate.moveToElementText(this);
duplicate.setEndPoint('StartToEnd', range);
if (duplicate.text.length) offset -= value.match(/[\n\r]*$/)[0].length;
pos.end = offset - duplicate.text.length;
duplicate.setEndPoint('StartToStart', range);
pos.start = offset - duplicate.text.length;
}
return pos;
},

replaceSelectedText: function(newtext, isLast) {
isLast = (isLast == null) ? true : isLast;
this.wrapSelectedText(newtext, '', '', isLast);
}
});

// A few variable we use in some of the functions
var _currentElement="";
var _previewActive=false;

//
// function kToggleOrSwap (elementId)
//
// Helper function for bbeditor optional/detailed toolbar
//
// Toogles the visibility of the element passed by ID. If another element in the
// optional toolbar is already visible, it hides the prior one before displaying
// the new option.
//
function kToggleOrSwap(id)
{
e = document.id(id);
if (e) {
if (e.getStyle('display') == "none"){
if (_currentElement != "") {_currentElement.setStyle('display', 'none');}
e.setStyle('display', 'block');
_currentElement=e;
}
else
{
e.setStyle('display', 'none');
_currentElement = "";
}
}
}
getSelectionStart: function() {
return this.getSelectedRange().start;
},

//
// function kToggleOrSwapPreview (className)
//
// Helper function for bbeditor optional/detailed toolbar
//
// Toogles the visibility of the preview element passed by ID. If another preview
// is already visible and active, it hides the prior one before displaying
// the new preview.
// That way we can not only turn preview on and off, but also switch between horizontal
// (preview to the right) and vertical (preview at the bottom) modes
//
getSelectionEnd: function() {
return this.getSelectedRange().end;
},

function kToggleOrSwapPreview(kunenaclass)
{
e = document.id("kbbcode-preview");
f = document.id("kbbcode-message");
if (e) {
if (e.getStyle('display') == "none" || e.getProperty('class') != kunenaclass){
e.setStyle('display', 'block');
setCaretPosition: function(pos) {
if (pos == 'end') pos = this.get('value').length;
this.selectRange(pos, pos);
return this;
},

if (kunenaclass=="kbbcode-preview-right"){
f.setStyle('width', '47%');
} else {
f.setStyle('width', '95%');
}
getCaretPosition: function() {
return this.getSelectedRange().start;
},

_previewActive=true;
kPreviewHelper();
selectRange: function(start, end) {
this.focus();
if (this.setSelectionRange) {
this.setSelectionRange(start, end);
} else {
var value = this.get('value');
var diff = value.substr(start, end - start).replace(/\r/g, '').length;
start = value.substr(0, start).replace(/\r/g, '').length;
var range = this.createTextRange();
range.collapse(true);
range.moveEnd('character', start + diff);
range.moveStart('character', start);
range.select();
}
else
{
e.setStyle('display', 'none');
f.setStyle('width', '95%');
_previewActive=false;
}
e.setProperty('class', kunenaclass);
var height = f.getStyle('height');
e.setStyle('height', f.getStyle('height'));
}
}
return this;
},

//
// kGenColorPalette(width, height)
//
// Helper function to generate the color palette for the bbcode color picker
//
function kGenerateColorPalette(width, height)
{
var r = 0, g = 0, b = 0;
var numberList = new Array(6);
var color = '';
numberList[0] = '00';
numberList[1] = '44';
numberList[2] = '88';
numberList[3] = 'BB';
numberList[4] = 'FF';
insertAtCursor: function(value, select) {
var pos = this.getSelectedRange();
var text = this.get('value');
this.set('value', text.substring(0, pos.start) + value + text.substring(pos.end, text.length));
if (select !== false) this.selectRange(pos.start, pos.start + value.length);
else this.setCaretPosition(pos.start + value.length);
return this;
},

document.writeln('<table id="kbbcode-colortable" class="kbbcode-colortable" cellspacing="1" cellpadding="0" border="0" style="width: 100%;">');

for (r = 0; r < 5; r++)
{
document.writeln('<tr>');
for (g = 0; g < 5; g++) {
for (b = 0; b < 5; b++) {
color = String(numberList[r]) + String(numberList[g]) + String(numberList[b]);
document.writeln('<td style="background-color:#' + color + '; width: ' + width + '; height: ' + height + ';">&nbsp;</td>');
}
insertAroundCursor: function(options, select) {
// Mootools 1.3+
//options = Object.append({
options = $extend({
before: '',
defaultMiddle: '',
after: ''
}, options);

var value = this.getSelectedText() || options.defaultMiddle;
var pos = this.getSelectedRange();
var text = this.get('value');

if (pos.start == pos.end){
this.set('value', text.substring(0, pos.start) + options.before + value + options.after + text.substring(pos.end, text.length));
this.selectRange(pos.start + options.before.length, pos.end + options.before.length + value.length);
} else {
var current = text.substring(pos.start, pos.end);
this.set('value', text.substring(0, pos.start) + options.before + current + options.after + text.substring(pos.end, text.length));
var selStart = pos.start + options.before.length;
if (select !== false) this.selectRange(selStart, selStart + current.length);
else this.setCaretPosition(selStart + text.length);
}
document.writeln('</tr>');
return this;
}
document.writeln('</table>');
}
});

/*
Class: kbbcode (derived from NAWTE)
@@ -272,11 +233,11 @@ var kbbcode = new Class({

if(this.options.dispatchChangeEvent) {
this.el.addEvents({
'focus': function() {
'focus': function(event) {
this.timer = this.watchChange.periodical(this.options.changeEventDelay, this);
}.bind(this),

'blur': function() {
'blur': function(event) {
this.timer = $clear(this.timer);
}.bind(this)
});
@@ -350,38 +311,38 @@ var kbbcode = new Class({
Wrap the selection with wrapper text
Arguments:
wrapper - a string, this string will wrap the textarea's current selection
isLast - see information at the top
wrapperLeft - a string, this string will wrap the textarea's current selection
wrapperRight - a string, this string will wrap the textarea's current selection
select - selects the text after it's been inserted
Example:
>this.wrapSelection("-+", "+-");
>//selection will become: -+selection+-
*/
wrapSelection: function(wrapperLeft, wrapperRight, isLast) {
isLast = (isLast == null) ? true : isLast;
this.el.wrapSelectedText(this.el.getSelectedText(), wrapperLeft, wrapperRight, isLast);
wrapSelection: function(wrapperLeft, wrapperRight, select) {
select = (select === null) ? true : select;
this.el.insertAroundCursor({before: wrapperLeft, after: wrapperRight, defaultMiddle: ""});
if (!select) this.el.selectRange(this.el.getSelectionEnd(), this.el.getSelectionEnd());
},

/*
Function: insert
Insert text before or after the current selection. (This is a TextTransform method...)
Arguments:
insertText - a string, the text to insert before the selection
newText - a string, the text to insert before the selection
where - a string, either "before" or "after" depending on where you want to insert
isLast - see information at the top
select - selects the text after it's been inserted
Example:
>this.insert("Hello ", 'before');
>//selection will become: Hello selection
*/
insert: function(insertText, where, isLast) {
isLast = (isLast == null) ? true : isLast;
var wrapperLeft = '';
var wrapperRight = '';
where = (where == "") ? 'after' : where;
var newText = (where == "before") ? wrapperLeft = insertText : wrapperRight = insertText;
this.el.wrapSelectedText(this.el.getSelectedText(), wrapperLeft, wrapperRight, isLast);
insert: function(newText, where, select) {
select = (select === null) ? true : select;
var pos = (where == "before") ? this.el.getSelectionStart() : this.el.getSelectionEnd();
this.el.selectRange(pos, pos);
this.el.insertAtCursor(newText, select);
},

/*
@@ -390,15 +351,14 @@ var kbbcode = new Class({
Arguments:
newText - a string, the text that will replace the selection
isLast - see information at the top
Example:
>this.replaceSelection("Hello World");
>//selection will become: Hello World
*/
replaceSelection: function(newText, isLast) {
isLast = (isLast == null) ? true : isLast;
this.el.replaceSelectedText(newText, isLast);
replaceSelection: function(newText, select) {
select = (select === null) ? true : select;
this.el.insertAtCursor(newText, select);
},

/*
@@ -407,7 +367,6 @@ var kbbcode = new Class({
Arguments:
callback - a function, will transform each lines of the selection, should accept the "line" parameter, MUST return the new transformed line
isLast - see information at the top
Example:
(start code)
@@ -419,8 +378,7 @@ var kbbcode = new Class({
(end)
*/

processEachLine: function(callback, isLast) {
isLast = (isLast == null) ? true : isLast;
processEachLine: function(callback) {
var lines = this.el.getSelectedText().split("\n");
var newlines = [];
lines.each(function(line) {
@@ -430,7 +388,7 @@ var kbbcode = new Class({
newlines.push("");
}.bind(this));

this.el.replaceSelectedText(newlines.join("\n"), isLast);
this.el.insertAtCursor(newlines.join("\n")+"\n", true);
},

/*
@@ -510,6 +468,107 @@ var kbbcode = new Class({

});

//A few variable we use in some of the functions
var _currentElement="";
var _previewActive=false;

//
// function kToggleOrSwap (elementId)
//
// Helper function for bbeditor optional/detailed toolbar
//
// Toogles the visibility of the element passed by ID. If another element in the
// optional toolbar is already visible, it hides the prior one before displaying
// the new option.
//
function kToggleOrSwap(id)
{
e = document.id(id);
if (e) {
if (e.getStyle('display') == "none"){
if (_currentElement != "") {_currentElement.setStyle('display', 'none');}
e.setStyle('display', 'block');
_currentElement=e;
} else {
e.setStyle('display', 'none');
_currentElement = "";
}
}
}

//
// function kToggleOrSwapPreview (className)
//
// Helper function for bbeditor optional/detailed toolbar
//
// Toogles the visibility of the preview element passed by ID. If another preview
// is already visible and active, it hides the prior one before displaying
// the new preview.
// That way we can not only turn preview on and off, but also switch between horizontal
// (preview to the right) and vertical (preview at the bottom) modes
//

function kToggleOrSwapPreview(kunenaclass)
{
e = document.id("kbbcode-preview");
f = document.id("kbbcode-message");
if (e) {
if (e.getStyle('display') == "none" || e.getProperty('class') != kunenaclass){
e.setStyle('display', 'block');

if (kunenaclass=="kbbcode-preview-right"){
f.setStyle('width', '47%');
} else {
f.setStyle('width', '95%');
}

_previewActive=true;
kPreviewHelper();
}
else
{
e.setStyle('display', 'none');
f.setStyle('width', '95%');
_previewActive=false;
}
e.setProperty('class', kunenaclass);
var height = f.getStyle('height');
e.setStyle('height', f.getStyle('height'));
}
}

//
// kGenColorPalette(width, height)
//
// Helper function to generate the color palette for the bbcode color picker
//
function kGenerateColorPalette(width, height)
{
var r = 0, g = 0, b = 0;
var numberList = new Array(6);
var color = '';
numberList[0] = '00';
numberList[1] = '44';
numberList[2] = '88';
numberList[3] = 'BB';
numberList[4] = 'FF';

document.writeln('<table id="kbbcode-colortable" class="kbbcode-colortable" cellspacing="1" cellpadding="0" border="0" style="width: 100%;">');

for (r = 0; r < 5; r++)
{
document.writeln('<tr>');
for (g = 0; g < 5; g++) {
for (b = 0; b < 5; b++) {
color = String(numberList[r]) + String(numberList[g]) + String(numberList[b]);
document.writeln('<td style="background-color:#' + color + '; width: ' + width + '; height: ' + height + ';">&nbsp;</td>');
}
}
document.writeln('</tr>');
}
document.writeln('</table>');
}

function kInsertCode() {
var kcodetype = document.id('kcodetype').get('value');
if (kcodetype != '') kcodetype = ' type='+kcodetype;
@@ -525,9 +584,9 @@ function kInsertCode() {
function kInsertImageLink() {
var size = document.id("kbbcode-image_size").get("value");
if (size == "") {
kbbcode.replaceSelection('[img]'+ document.id("kbbcode-image_url").get("value") +'[/img]');
kbbcode.replaceSelection('[img]'+ document.id("kbbcode-image_url").get("value") +'[/img]', false);
} else {
kbbcode.replaceSelection('[img size='+size+']'+ document.id("kbbcode-image_url").get("value") +'[/img]');
kbbcode.replaceSelection('[img size='+size+']'+ document.id("kbbcode-image_url").get("value") +'[/img]', false);
}
kToggleOrSwap("kbbcode-image-options");
}
@@ -586,7 +645,7 @@ function newAttachment() {
});
__file.getElement('input.kfile-input-textbox').set('value', __filename);

__file.getElement('.kattachment-insert').removeProperty('style').addEvent('click', function() {kbbcode.insert('\n[attachment:'+ __id +']'+ __filename +'[/attachment]\n', 'after', true); return false; } );
__file.getElement('.kattachment-insert').removeProperty('style').addEvent('click', function() {kbbcode.insert('\n[attachment:'+ __id +']'+ __filename +'[/attachment]\n', 'after', false); return false; } );
__file.getElement('.kattachment-remove').removeProperty('style').addEvent('click', function() {__file.dispose(); return false; } );
newAttachment();
});
@@ -596,7 +655,7 @@ function bindAttachments() {
var __kattachment = $$('.kattachment-old');
if (!__kattachment) return;
__kattachment.each(function(el) {
el.getElement('.kattachment-insert').removeProperty('style').addEvent('click', function() {kbbcode.replaceSelection('\n[attachment='+ el.getElement('input').get('value') +']'+ el.getElement('.kfilename').get('text') +'[/attachment]\n', 'after', true); return false; } );
el.getElement('.kattachment-insert').removeProperty('style').addEvent('click', function() {kbbcode.insert('\n[attachment='+ el.getElement('input').get('value') +']'+ el.getElement('.kfilename').get('text') +'[/attachment]\n', 'after', false); return false; } );
});
}

@@ -643,13 +702,13 @@ function kInsertVideo1() {
provider = '';
}
var videoid = document.id('kvideoid').get('value');
kbbcode.replaceSelection( '[video'+(videosize ? ' size='+videosize:'')+' width='+videowidth+' height='+videoheigth+' type='+provider+']'+videoid+'[/video]', false);
kbbcode.insert( '[video'+(videosize ? ' size='+videosize:'')+' width='+videowidth+' height='+videoheigth+' type='+provider+']'+videoid+'[/video]', 'after', false);
kToggleOrSwap("kbbcode-video-options");
}

function kInsertVideo2() {
var videourl = document.id("kvideourl").get("value");
kbbcode.replaceSelection('[video]'+ videourl +'[/video]', false);
kbbcode.insert('[video]'+ videourl +'[/video]', 'after', false);
kToggleOrSwap("kbbcode-video-options");
}

@@ -666,15 +725,15 @@ function kEditorInitialize() {
if (color) {
color.addEvent("click", function(){
var bg = this.getStyle( "background-color" );
kbbcode.wrapSelection('[color='+ bg +']', '[/color]', false);
kToggleOrSwap("kbbcode-colorpalette");
kbbcode.wrapSelection('[color='+ bg +']', '[/color]', true);
kToggleOrSwap("kbbcode-color-options");
});
}
var size = $$("div#kbbcode-size-options span");
if (size) {
size.addEvent("click", function(){
var tag = this.get( "title" );
kbbcode.wrapSelection(tag , '[/size]', false);
kbbcode.wrapSelection(tag , '[/size]', true);
kToggleOrSwap("kbbcode-size-options");
});
}
@@ -29,7 +29,7 @@
</li>
</ul>
</div>

<div class="postbuttons kbox-full">
<div style="display: none;" id="kbbcode-size-options">
<span class="kmsgtext-xs hasTip" title="[size=1]" title="<?php echo JText::_('COM_KUNENA_EDITOR_HELPLINE_FONTSIZE_XS') ?>">
@@ -51,29 +51,29 @@
<?php echo JText::_('COM_KUNENA_EDITOR_SIZE_SAMPLETEXT') ?>
</span>
</div>

<div style="display: none;" id="kbbcode-color-options">
<script type="text/javascript">kGenerateColorPalette('4%', '15px');</script>
</div>

<div style="display: none;" id="kbbcode-link-options">
<?php echo JText::_('COM_KUNENA_EDITOR_LINK_URL') ?>
<input class="hasTip" id="kbbcode-link_url" name="url" type="text" size="40" value="http://" title="<?php echo JText::_('COM_KUNENA_EDITOR_HELPLINE_LINKURL') ?>" />
<?php echo JText::_('COM_KUNENA_EDITOR_LINK_TEXT') ?>
<input class="hasTip" name="text2" id="kbbcode-link_text" type="text" size="30" maxlength="150" title="<?php echo JText::_('COM_KUNENA_EDITOR_HELPLINE_LINKTEXT') ?>" />
<input class="hasTip" type="button" name="insterLink" value="<?php echo JText::_('COM_KUNENA_EDITOR_INSERT') ?>"
onclick="kbbcode.replaceSelection('[url=' + document.id('kbbcode-link_url').get('value') + ']' + document.id('kbbcode-link_text').get('value') + '[/url]'); kToggleOrSwap('kbbcode-link-options');"
onclick="kbbcode.replaceSelection('[url=' + document.id('kbbcode-link_url').get('value') + ']' + document.id('kbbcode-link_text').get('value') + '[/url]', false); kToggleOrSwap('kbbcode-link-options');"
title="<?php echo JText::_('COM_KUNENA_EDITOR_HELPLINE_LINKAPPLY') ?>" />
</div>

<div style="display: none;" id="kbbcode-image-options">
<?php echo JText::_('COM_KUNENA_EDITOR_IMAGELINK_SIZE') ?>
<input class="hasTip" id="kbbcode-image_size" name="size" type="text" size="10" maxlength="10" title="<?php echo JText::_('COM_KUNENA_EDITOR_HELPLINE_IMAGELINKSIZE') ?>" />
<?php echo JText::_('COM_KUNENA_EDITOR_IMAGELINK_URL') ?>
<input class="hasTip" name="url2" id="kbbcode-image_url" type="text" size="40" value="http://" title="<?php echo JText::_('COM_KUNENA_EDITOR_HELPLINE_IMAGELINKURL') ?>" />
<input class="hasTip" type="button" name="Link" value="<?php echo JText::_('COM_KUNENA_EDITOR_INSERT') ?>" onclick="kInsertImageLink()" title="<?php echo JText::_('COM_KUNENA_EDITOR_HELPLINE_IMAGELINKAPPLY') ?>" />
</div>

<?php if (!$this->message->parent && isset($this->poll)) : ?>
<div style="display: none;" id="kbbcode-poll-options">
<?php JHTML::_('behavior.calendar'); ?>
@@ -86,7 +86,7 @@
<img class="hasTip" id="kbutton-poll-rem"
src="<?php echo $this->ktemplate->getImagePath('icons/poll_rem_options.png') ?>"
title="<?php echo JText::_('COM_KUNENA_EDITOR_HELPLINE_REMPOLLOPTION'); ?>" alt="<?php echo JText::_('COM_KUNENA_POLL_REMOVE_POLL_OPTION'); ?>" />

<label class="kpoll-term-lbl" for="kpoll-time-to-live"><?php echo JText::_('COM_KUNENA_POLL_TIME_TO_LIVE'); ?></label>
<?php echo JHTML::_('calendar', isset($this->poll->polltimetolive) ? $this->escape($this->poll->polltimetolive) : '0000-00-00', 'poll_time_to_live', 'kpoll-time-to-live', '%Y-%m-%d',array('onmouseover'=>'javascript:document.id(\'helpbox\').set(\'value\', \''.JText::_('COM_KUNENA_EDITOR_HELPLINE_POLLLIFESPAN',true).'\')')); ?>
<?php
@@ -102,7 +102,7 @@
<input type="hidden" name="number_total_options" id="numbertotal" value="<?php echo ! empty ( $this->polloptionstotal ) ? $this->escape($this->polloptionstotal) : '' ?>" />
</div>
<?php endif; ?>

<?php
if ($this->config->highlightcode) :
if (version_compare(JVERSION, '1.6','>')) {
@@ -126,7 +126,7 @@
</div>
<?php endif ?>
<?php endif ?>

<?php if ($this->config->showvideotag) : ?>
<div style="display: none;" id="kbbcode-video-options">
<?php echo JText::_('COM_KUNENA_EDITOR_VIDEO_SIZE') ?>
@@ -154,19 +154,19 @@
</div>
<?php endif ?>
</div>

<?php if (!$this->config->disemoticons) : ?>
<div class="postbuttons clear ">
<div id="ksmiliebar"><?php
$emoticons = KunenaHtmlParser::getEmoticons(0, 1);
foreach ( $emoticons as $emo_code=>$emo_url ) {
echo '<img class="btnImage hasTip" src="' . $emo_url . '" border="0" alt="' . $emo_code . ' " title="' . $emo_code . ' " onclick="kbbcode.insert(\' '. $emo_code .' \', \'after\', true);" style="cursor:pointer"/> ';
echo '<img class="btnImage hasTip" src="' . $emo_url . '" border="0" alt="' . $emo_code . ' " title="' . $emo_code . ' " onclick="kbbcode.insert(\' '. $emo_code .' \', \'after\', false);" style="cursor:pointer"/> ';
}
?>
</div>
</div>
<?php endif ?>

<div class="kposthint kbox-full">
<input type="text" value="<?php echo JText::_('COM_KUNENA_EDITOR_HELPLINE_HINT') ?>" maxlength="100" disabled="disabled" class="kbox-width inputbox" size="45" id="helpbox" name="helpbox" />
</div>