1253 lines
31 KiB
JavaScript
1253 lines
31 KiB
JavaScript
/**
|
|
* Wrapper for CKEditor based goodness
|
|
* Written by Matt Mecham for his sins.
|
|
* (c) 2011 IPS, Inc.
|
|
*/
|
|
|
|
ipsTextArea = {
|
|
/**
|
|
* Insert text at the cursor position
|
|
* Some code from
|
|
* @link http://bytes.com/topic/javascript/answers/149268-moving-cursor-end-textboxes-text
|
|
* @param editorId
|
|
* @param text
|
|
*/
|
|
insertAtCursor: function( editorId, text )
|
|
{
|
|
var te = $('cke_' + editorId).down('textarea');
|
|
|
|
var scrollPos = te.scrollTop;
|
|
|
|
if ( CKEDITOR.env.ie )
|
|
{
|
|
te.focus();
|
|
sel = document.selection.createRange();
|
|
sel.text = text;
|
|
sel.select();
|
|
}
|
|
else if ( te.selectionStart || te.selectionStart == '0' )
|
|
{
|
|
var startPos = te.selectionStart;
|
|
var endPos = te.selectionEnd;
|
|
|
|
te.value = te.value.substring(0, startPos) + text + te.value.substring(endPos, te.value.length);
|
|
|
|
if ( startPos == endPos )
|
|
{
|
|
this.setSelectionRange( te, startPos + text.length, endPos + text.length );
|
|
}
|
|
else
|
|
{
|
|
this.setCaretToPos( te, startPos + text.length );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
te.value += text;
|
|
}
|
|
|
|
te.scrollTop = scrollPos;
|
|
},
|
|
|
|
/**
|
|
* Set selection range
|
|
* Some code from
|
|
* @link http://bytes.com/topic/javascript/answers/149268-moving-cursor-end-textboxes-text
|
|
*/
|
|
setSelectionRange: function(input, selectionStart, selectionEnd)
|
|
{
|
|
if ( input.setSelectionRange )
|
|
{
|
|
input.focus();
|
|
input.setSelectionRange(selectionStart, selectionEnd);
|
|
}
|
|
else if ( input.createTextRange )
|
|
{
|
|
var range = input.createTextRange();
|
|
range.collapse(true);
|
|
range.moveEnd('character', selectionEnd);
|
|
range.moveStart('character', selectionStart);
|
|
range.select();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Set caret position
|
|
* Some code from
|
|
* @link http://bytes.com/topic/javascript/answers/149268-moving-cursor-end-textboxes-text
|
|
*/
|
|
setCaretToPos: function( input, pos )
|
|
{
|
|
this.setSelectionRange( input, pos, pos );
|
|
}
|
|
}
|
|
|
|
IPSCKTools = {
|
|
/**
|
|
* Get the selected HTML from the editor
|
|
* @param object
|
|
*/
|
|
getSelectionHtml: function( editor )
|
|
{
|
|
var selection = editor.getSelection();
|
|
|
|
if ( CKEDITOR.env.ie )
|
|
{
|
|
try {
|
|
if ( ! Prototype.Browser.IE8 )
|
|
{
|
|
selection.unlock(true);
|
|
}
|
|
} catch(e){}
|
|
|
|
var text = selection.getNative().createRange().htmlText;
|
|
|
|
if ( text.toLowerCase().strip() == '<p> </p>' )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return text;
|
|
}
|
|
else if ( CKEDITOR.env.opera )
|
|
{
|
|
var selection = selection.getNative();
|
|
|
|
var range = selection ? selection.getRangeAt(0) : selection.createRange();
|
|
var div = document.createElement('div');
|
|
div.appendChild( range.cloneContents() );
|
|
|
|
return div.innerHTML.replace( /<p><\/p>/g, '<p><br /></p>' );
|
|
}
|
|
else
|
|
{
|
|
var range = selection.getNative().getRangeAt( selection.rangeCount -1 ).cloneRange();
|
|
var div = document.createElement('div');
|
|
div.appendChild( range.cloneContents() );
|
|
|
|
return div.innerHTML;
|
|
}
|
|
},
|
|
/**
|
|
* Clean HTML for inserting between tags
|
|
* @param object
|
|
*/
|
|
cleanHtmlForTagWrap: function( html, convert )
|
|
{
|
|
var text = ( typeof( html ) != 'undefined' ) ? html.replace( /<br( \/)?>$/, '' ) : '';
|
|
|
|
/* The text may have become double encoded */
|
|
if ( convert )
|
|
{
|
|
text = text.replace( /</g , '<' );
|
|
text = text.replace( />/g , '>' );
|
|
text = text.replace( /&/g , '&' );
|
|
text = text.replace( /'/g , "'" );
|
|
text = text.replace( /"/g, '"' );
|
|
}
|
|
|
|
return text;
|
|
},
|
|
|
|
/**
|
|
* Remove HTML formatting
|
|
* @param string
|
|
*/
|
|
stripHtmlTags: function( html )
|
|
{
|
|
return html.stripTags();
|
|
}
|
|
};
|
|
|
|
/*
|
|
* This is just a wrapper class around the objects to give it a global interface
|
|
*/
|
|
IPBoard.prototype.textEditor = {
|
|
|
|
mainStore: $H(),
|
|
lastSetUp: null,
|
|
htmlCheckbox: $H(),
|
|
_tmpContent: '',
|
|
IPS_TEXTEDITOR_POLLING: 10000, /* 10 seconds */
|
|
IPS_NEW_POST_POLLING: (2 * 60000), /* 2 mins */
|
|
IPS_SAVED_MSG: "<a href='javascript:void()' class='_as_explain desc'>" + ipb.lang['ck_auto_saved'] + "</a>",
|
|
IPS_AUTOSAVETEMPLATE: "<a href='javascript:void()' class='_as_launch desc'>" + ipb.lang['ck_view_saved'] + "</a>",
|
|
IPS_AUTOSAVEVIEW: "<div><h3>" + ipb.lang['ck_saved'] + "</h3><div class='row2' style='padding:4px'><div class='as_content'>#{content}</div><div class='as_buttons'><input type='button' class='input_submit _as_restore' value='" + ipb.lang['ck_restore'] + "' /></div>",
|
|
IPS_AUTOSAVEEXPLAIN: "<div><h3>" + ipb.lang['ck_saved_title'] + "</h3><div class='row2' style='padding:8px'>" + ipb.lang['ck_saved_desc'] + "</div>",
|
|
ajaxUrl: '',
|
|
|
|
initialize: function( editorId, options )
|
|
{
|
|
if ( inACP )
|
|
{
|
|
ipb.textEditor.ajaxUrl = ipb.vars['front_url'];
|
|
ipb.vars['secure_hash'] = ipb.vars['md5_hash'];
|
|
}
|
|
else
|
|
{
|
|
ipb.textEditor.ajaxUrl = ipb.vars['base_url'];
|
|
}
|
|
|
|
/* Insert into main store */
|
|
if ( ! ipb.textEditor.mainStore.get( editorId ) )
|
|
{
|
|
newEditorObject = new ipb.textEditorObjects( editorId, options );
|
|
|
|
ipb.textEditor.mainStore.set( editorId, newEditorObject );
|
|
}
|
|
|
|
/* Set up */
|
|
ipb.textEditor.lastSetUp = editorId;
|
|
},
|
|
|
|
/**
|
|
* Bind a HTML checkbox to the toggle stuffs
|
|
*/
|
|
bindHtmlCheckbox: function( elem, editorId )
|
|
{
|
|
editorId = ( editorId ) ? editorId : ipb.textEditor.getEditor().editorId;
|
|
|
|
if ( $( elem ) )
|
|
{
|
|
ipb.textEditor.htmlCheckbox[ editorId ] = $( elem );
|
|
|
|
$( elem ).writeAttribute( 'data-editorId', editorId );
|
|
$( elem ).observe('change', ipb.textEditor.htmlModeToggled.bindAsEventListener( this, $( elem ) ) );
|
|
}
|
|
},
|
|
|
|
/**
|
|
* HTML mode has been toggled so we force STD mode
|
|
*/
|
|
htmlModeToggled: function( e, elem )
|
|
{
|
|
textObj = ipb.textEditor.getEditor( $( elem ).readAttribute( 'data-editorId' ) );
|
|
isRte = textObj.isRte();
|
|
button = $( 'cke_' + textObj.editorId ).down('.cke_button_ipsswitch');
|
|
|
|
if ( elem.checked )
|
|
{
|
|
$('editor_html_message_' + textObj.editorId).show();
|
|
|
|
if ( isRte )
|
|
{
|
|
button.hide();
|
|
|
|
ipb.textEditor.switchEditor(true, textObj.editorId);
|
|
}
|
|
else
|
|
{
|
|
button.hide();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$('editor_html_message_' + textObj.editorId).hide();
|
|
|
|
if ( ! button.visible() )
|
|
{
|
|
button.show();
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Switches an editor from rte to std or vice-versa
|
|
*/
|
|
switchEditor: function( noSaveChange, editorId )
|
|
{
|
|
editorId = ( editorId ) ? editorId : ipb.textEditor.getEditor().editorId;
|
|
textObj = ipb.textEditor.getEditor( editorId );
|
|
isRte = textObj.isRte();
|
|
ourcontent = textObj.getText();
|
|
saveCookie = ( noSaveChange ) ? false : true;
|
|
htmlOps = null;
|
|
noSmilies = 0;
|
|
|
|
if ( $( 'noSmilies_' + editorId ) )
|
|
{
|
|
noSmilies = parseInt( $F( 'noSmilies_' + editorId ) );
|
|
}
|
|
|
|
if ( $( ipb.textEditor.htmlCheckbox[ editorId ] ) )
|
|
{
|
|
htmlOps = ( ipb.textEditor.htmlCheckbox[ editorId ].checked ) ? 1 : 0;
|
|
}
|
|
|
|
var _url = ipb.textEditor.ajaxUrl + '&app=core&module=ajax§ion=editor&do=switch&secure_key=' + ipb.vars['secure_hash'];
|
|
|
|
if ( htmlOps !== null )
|
|
{
|
|
_url += '&htmlStatus=' + htmlOps;
|
|
}
|
|
|
|
Debug.write( _url );
|
|
Debug.write( 'Fetched editor content: ' + ourcontent );
|
|
|
|
new Ajax.Request( _url,
|
|
{
|
|
method: 'post',
|
|
parameters: { 'content' : ourcontent.encodeParam(),
|
|
'isRte' : isRte,
|
|
'noSmilies' : noSmilies },
|
|
onSuccess: function(t)
|
|
{
|
|
/* No Permission */
|
|
if ( t.responseText != 'nopermission' )
|
|
{
|
|
Debug.write( 'Converted to: ' + t.responseText );
|
|
|
|
/* Switch the text editor around */
|
|
if ( textObj.isRte() )
|
|
{
|
|
/* Reset flag */
|
|
textObj.setIsRte( false, noSaveChange );
|
|
|
|
textObj.CKEditor.setMode( 'source' );
|
|
|
|
/* Update */
|
|
$( textObj.editorId ).value = ipb.textEditor.stdPre( t.responseText );
|
|
|
|
try
|
|
{
|
|
Debug.write( $F( textObj.editorId ) );
|
|
textObj.CKEditor.setData( $F( textObj.editorId ) );
|
|
}
|
|
catch( err )
|
|
{
|
|
Debug.write( "CKEditor error: " + err );
|
|
}
|
|
|
|
if ( saveCookie )
|
|
{
|
|
ipb.Cookie.set( 'rteStatus', 'std' );
|
|
}
|
|
|
|
/* Update text-direction, since CKE forces LTR in 'source' mode */
|
|
if( isRTL )
|
|
{
|
|
$$('.cke_contents > textarea').each( function(elem) {
|
|
$(elem).setStyle( { textAlign: 'right' } );
|
|
});
|
|
}
|
|
|
|
if ( $('cke_' + textObj.editorId + '_stray') )
|
|
{
|
|
$('cke_' + textObj.editorId + '_stray').remove();
|
|
|
|
if ( $('ips_x_smile_show_all') )
|
|
{
|
|
$('ips_x_smile_show_all').remove();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Reset flag */
|
|
textObj.setIsRte( true, noSaveChange );
|
|
|
|
try
|
|
{
|
|
textObj.CKEditor.setData( ipb.textEditor.ckPre( t.responseText ) );
|
|
}
|
|
catch( err )
|
|
{
|
|
Debug.write( "CKEditor error: " + err );
|
|
}
|
|
|
|
/* Init editor */
|
|
textObj.CKEditor.setMode( 'wysiwyg' );
|
|
|
|
if ( saveCookie )
|
|
{
|
|
ipb.Cookie.set( 'rteStatus', 'rte' );
|
|
}
|
|
}
|
|
}
|
|
}.bind(this),
|
|
onException: function (t)
|
|
{
|
|
Debug.dir( t );
|
|
}
|
|
} );
|
|
|
|
},
|
|
|
|
/*
|
|
* Change all editor modes besides the current one
|
|
* (current one is changed inside switchEditor)
|
|
*/
|
|
toggleEditors: function( mode )
|
|
{
|
|
return false;
|
|
|
|
/* We no longer use this as it doesn't ping ajax to convert! */
|
|
ipb.textEditor.mainStore.each( function( instance ){
|
|
if( instance.key != ipb.textEditor.lastSetUp )
|
|
{
|
|
_editor = ipb.textEditor.getEditor( instance.key );
|
|
|
|
if ( ! _editor.options.isHtml )
|
|
{
|
|
if( mode == 'std' )
|
|
{
|
|
instance.value.setIsRte( false );
|
|
instance.value.CKEditor.setMode( 'source' );
|
|
|
|
if ( $('cke_' + _editor.editorId + '_stray') )
|
|
{
|
|
$('cke_' + _editor.editorId + '_stray').remove();
|
|
|
|
if ( $('ips_x_smile_show_all') )
|
|
{
|
|
$('ips_x_smile_show_all').remove();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
instance.value.setIsRte( true );
|
|
instance.value.CKEditor.setMode( 'wysiwyg' );
|
|
}
|
|
}
|
|
}
|
|
});
|
|
},
|
|
|
|
/*
|
|
* Fetches an editor by ID
|
|
*/
|
|
getEditor: function( editorId )
|
|
{
|
|
editorId = ( ! editorId ) ? ipb.textEditor.getCurrentEditorId() : editorId;
|
|
|
|
return ipb.textEditor.mainStore.get( editorId );
|
|
},
|
|
|
|
/**
|
|
* Fetches the current focused editor or the last one set up
|
|
* @returns string editor id
|
|
*/
|
|
getCurrentEditorId: function()
|
|
{
|
|
if ( typeof(CKEDITOR) == 'undefined' || Object.isUndefined( CKEDITOR ) )
|
|
{
|
|
return ipb.textEditor.lastSetUp;
|
|
}
|
|
else
|
|
{
|
|
if ( CKEDITOR.currentInstance && ipb.textEditor.mainStore.get( CKEDITOR.currentInstance.name ).CKEditor )
|
|
{
|
|
return CKEDITOR.currentInstance.name;
|
|
}
|
|
else
|
|
{
|
|
return ipb.textEditor.lastSetUp;
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Make safe for CKEditor
|
|
*/
|
|
ckPre: function( text )
|
|
{
|
|
text = text.replace( '<script', '<script' );
|
|
text = text.replace( '</script', '</script' );
|
|
|
|
// If the first content is text and not an HTML element, CKEditor throws an error
|
|
// Error: body.getFirst().hasAttribute is not a function
|
|
// Similar to: http://dev.ckeditor.com/ticket/6152
|
|
// We'll just wrap in a span tab to be safe if it doesn't look like HTML is there
|
|
if( text.charAt(0) != '<' )
|
|
{
|
|
// Disabled as we're not using BR mode
|
|
//text = '<p>' + text + '</p>';
|
|
|
|
}
|
|
|
|
return text;
|
|
},
|
|
|
|
/**
|
|
* Make safe for std editor
|
|
*/
|
|
stdPre: function( text )
|
|
{
|
|
/* < / > is made safe by CKEditor */
|
|
text = text.replace( /</g, '<' );
|
|
text = text.replace( />/g, '>' );
|
|
|
|
/* Properly encoded HTML &#39; isn't parsed in the text area */
|
|
text = text.replace( /&/g, '&' );
|
|
|
|
if ( ! $(ipb.textEditor.htmlCheckbox) || ! $(ipb.textEditor.htmlCheckbox).checked )
|
|
{
|
|
//text = text.replace( /<br([^>]+?)?>(\n)?/g, '\n' );
|
|
}
|
|
|
|
return text;
|
|
},
|
|
|
|
/**
|
|
* 0 pad times
|
|
* @param n
|
|
* @returns
|
|
*/
|
|
pad: function(n)
|
|
{
|
|
return ("0" + n).slice(-2);
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Each CKEditor object is referenced via this class
|
|
*/
|
|
IPBoard.prototype.textEditorObjects = Class.create( {
|
|
|
|
editorId: {},
|
|
popups: [],
|
|
cookie: 'rte',
|
|
timers: {},
|
|
options: {},
|
|
CKEditor: null,
|
|
EditorObj: null,
|
|
|
|
/*------------------------------*/
|
|
/* Constructor */
|
|
initialize: function( editorId, options )
|
|
{
|
|
this.editorId = editorId;
|
|
this.cookie = ipb.Cookie.get('rteStatus');
|
|
|
|
this.options = Object.extend( { type: 'full',
|
|
ips_AutoSaveKey: false,
|
|
height: 0,
|
|
minimize: 0,
|
|
minimizeNowOpen: 0,
|
|
isRte: 1,
|
|
isHtml: 0,
|
|
bypassCKEditor: 0,
|
|
isTypingCallBack: false,
|
|
delayInit: false,
|
|
noSmilies: false,
|
|
ips_AutoSaveData: {},
|
|
ips_AutoSaveTemplate: new Template( ipb.textEditor.IPS_AUTOSAVETEMPLATE ) }, arguments[1] );
|
|
|
|
/* Force */
|
|
if ( this.options.type == 'ipsacp' )
|
|
{
|
|
this.setIsRte( true );
|
|
}
|
|
else if ( this.options.isRte == 0 )
|
|
{
|
|
this.setIsRte( false );
|
|
}
|
|
else if ( this.options.isRte == 1 )
|
|
{
|
|
this.setIsRte( true );
|
|
}
|
|
else
|
|
{
|
|
/* Do we have an override? */
|
|
this.setIsRte( this.cookie == 'rte' ? 1 : ( this.cookie == 'std' ? 0 : 1 ) );
|
|
}
|
|
|
|
/* Force STD if iDevice */
|
|
if ( ipb.vars['is_touch'] !== false )
|
|
{
|
|
this.setIsRte( 0 );
|
|
this.options.bypassCKEditor = 1;
|
|
}
|
|
|
|
/* Create the CKEditor */
|
|
if ( ! this.options.delayInit )
|
|
{
|
|
this.initEditor();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Set RTe status of current editor
|
|
*/
|
|
setIsRte: function( value, noSaveChange )
|
|
{
|
|
value = ( value ) ? 1 : 0;
|
|
saveCookie = ( noSaveChange ) ? false : true;
|
|
|
|
this.options.isRte = value;
|
|
|
|
if ( $( 'isRte_' + this.editorId ) )
|
|
{
|
|
$( 'isRte_' + this.editorId ).value = value;
|
|
}
|
|
|
|
/* Set cookie */
|
|
if ( saveCookie )
|
|
{
|
|
ipb.Cookie.set( 'rteStatus', ( value ) ? 'rte' : 'std', 1 );
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Returns whether current editor is RTE (ckeditor)
|
|
*/
|
|
isRte: function()
|
|
{
|
|
return this.options.isRte ? 1 : 0;
|
|
},
|
|
|
|
/**
|
|
* Fetch editor contents
|
|
*/
|
|
getText: function()
|
|
{
|
|
var val = '';
|
|
|
|
if ( ! this.options.bypassCKEditor && this.CKEditor )
|
|
{
|
|
val = this.CKEditor.getData();
|
|
}
|
|
else
|
|
{
|
|
/* If CKEditor isn't beingn used (iOS, etc) */
|
|
if( $( this.editorId ) )
|
|
{
|
|
val = $( this.editorId ).value;
|
|
}
|
|
}
|
|
|
|
return val;
|
|
},
|
|
|
|
/**
|
|
* Create the CKEditor
|
|
*/
|
|
initEditor: function(initContent)
|
|
{
|
|
/* Bypassing the CKEditor completely? Why so mean? */
|
|
if ( this.options.bypassCKEditor )
|
|
{
|
|
if ( this.options.minimize )
|
|
{
|
|
this.options.minimizeNowOpen = 1;
|
|
}
|
|
|
|
if ( typeof( initContent ) == 'string' )
|
|
{
|
|
$( this.editorId ).value = initContent;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* Start the process of initiating */
|
|
if ( $( this.editorId ).value && this.isRte() )
|
|
{
|
|
$( this.editorId ).value = ipb.textEditor.ckPre( $( this.editorId ).value );
|
|
}
|
|
else
|
|
{
|
|
$( this.editorId ).value = ipb.textEditor.stdPre( $( this.editorId ).value );
|
|
}
|
|
|
|
/* RTE init */
|
|
try
|
|
{
|
|
var config = {
|
|
toolbar: ( this.options.type == 'ipsacp' ) ? 'ipsacp' : ( this.options.type == 'mini' ? 'ipsmini' : 'ipsfull' ),
|
|
height: ( Object.isNumber( this.options.height ) && this.options.height > 0 ) ? this.options.height : ( this.options.type == 'mini' ? 150 : 300 ),
|
|
ips_AutoSaveKey: this.options.ips_AutoSaveKey
|
|
};
|
|
|
|
/* STD editor? */
|
|
if ( ! this.isRte() )
|
|
{
|
|
config.startupMode = 'source';
|
|
}
|
|
|
|
/* Minimized - force ipsmini */
|
|
if ( this.options.minimize )
|
|
{
|
|
config.toolbarStartupExpanded = false;
|
|
}
|
|
|
|
CKEDITOR.replace( this.editorId, config );
|
|
}
|
|
catch( err )
|
|
{
|
|
Debug.write( 'CKEditor error: ' + err );
|
|
}
|
|
|
|
this.CKEditor = CKEDITOR.instances[ this.editorId ];
|
|
|
|
/* Bug in ckeditor init which treats initContent as event object inside an .on() */
|
|
ipb.textEditor._tmpContent = ( Object.isString(initContent) || Object.isNumber(initContent) ) ? initContent : '';
|
|
|
|
/* Got any saved data to show? */
|
|
CKEDITOR.on( 'instanceReady', function( ev )
|
|
{
|
|
if ( ev.editor.name == this.editorId )
|
|
{
|
|
/* This is dumb and only way to access editor object */
|
|
this.EditorObj = ev;
|
|
|
|
/* Quickly make some changes if minimized */
|
|
if ( this.options.minimize )
|
|
{
|
|
try
|
|
{
|
|
$('cke_top_' + this.editorId ).down('.cke_toolbox_collapser').hide();
|
|
$('cke_bottom_' + this.editorId ).hide();
|
|
|
|
$('cke_' + this.editorId ).down('.cke_wrapper').addClassName('minimized');
|
|
|
|
if ( ! this.isRte() )
|
|
{
|
|
$('cke_' + this.editorId ).down('.cke_wrapper').addClassName('std');
|
|
}
|
|
else
|
|
{
|
|
/* IE bug when clicking the text editor to expand and it also registers a toolbar click*/
|
|
if ( Prototype.Browser.IE9 )
|
|
{
|
|
$('cke_top_' + this.editorId ).down('.cke_toolbox').setStyle( { 'position': 'absolute', 'left': '-2000px' } );
|
|
}
|
|
}
|
|
}catch(e){}
|
|
|
|
ev.editor.on('focus', function()
|
|
{
|
|
try
|
|
{
|
|
if ( ! this.options.minimizeNowOpen )
|
|
{
|
|
this.showEditor().bind(this);
|
|
}
|
|
}catch(e){}
|
|
}.bind(this) );
|
|
}
|
|
|
|
/* Fix for some tags */
|
|
new Array( 'p', 'ul', 'li', 'blockquote', 'div' ).each( function ( tag )
|
|
{
|
|
ev.editor.dataProcessor.writer.setRules( tag, {
|
|
indent : false,
|
|
breakBeforeOpen : true,
|
|
breakAfterOpen : false,
|
|
breakBeforeClose : false,
|
|
breakAfterClose : true
|
|
} );
|
|
} );
|
|
|
|
/* Insert */
|
|
if ( ipb.textEditor._tmpContent.length )
|
|
{
|
|
if ( ! this.isRte() )
|
|
{
|
|
ev.editor.setData( ipb.textEditor.stdPre( ipb.textEditor._tmpContent ) );
|
|
}
|
|
else
|
|
{
|
|
ev.editor.setData( ipb.textEditor.ckPre( ipb.textEditor._tmpContent ) );
|
|
}
|
|
}
|
|
|
|
/* Clear tmp content */
|
|
ipb.textEditor._tmpContent = '';
|
|
|
|
this.displayAutoSaveData();
|
|
|
|
if ( this.options.isTypingCallBack !== false )
|
|
{
|
|
this.timers['dirty'] = setInterval( this.checkForInput.bind(this), ipb.textEditor.IPS_TEXTEDITOR_POLLING );
|
|
}
|
|
|
|
/* Make sure our menus close */
|
|
if ( $('cke_contents_' + this.editorId ).down('iframe') )
|
|
{
|
|
try
|
|
{
|
|
$('cke_contents_' + this.editorId ).down('iframe').contentWindow.document.onclick = parent.ipb.menus.docCloseAll;
|
|
} catch( e ) { }
|
|
}
|
|
|
|
/* Some ACP styles conflict */
|
|
$$('.cke_top').each( function(elem) { elem.setStyle('background: transparent !important; padding: 0px !important'); } );
|
|
$$('.cke_bottom').each( function(elem) { elem.setStyle('background: transparent !important; padding: 0px !important'); } );
|
|
$$('.cke_contents').each( function(elem) { elem.setStyle('padding: 0px !important'); } );
|
|
|
|
/* CKEditor tends to add a bunch of inline styles to cke_top_x which messes up custom styles */
|
|
$('cke_top_' + this.editorId ).writeAttribute( 'style', '' );
|
|
|
|
/* Update text-direction, since CKE forces LTR in 'source' mode */
|
|
if( isRTL && !this.isRte() )
|
|
{
|
|
$$('.cke_contents > textarea').each( function(elem) {
|
|
$(elem).setStyle( { textAlign: 'right' } );
|
|
});
|
|
}
|
|
|
|
/* Is HTML? */
|
|
if ( this.options.isHtml == 1 )
|
|
{
|
|
if ( Object.isUndefined( ipb.textEditor.htmlCheckbox[ this.editorId ] ) || ipb.textEditor.htmlCheckbox[ this.editorId ] == null )
|
|
{
|
|
$('cke_' + this.editorId ).insert( new Element( 'input', { type: 'checkbox', id: 'cbx_' + this.editorId, checked: true } ).hide() );
|
|
|
|
ipb.textEditor.bindHtmlCheckbox( $('cbx_' + this.editorId ), this.editorId );
|
|
}
|
|
|
|
$( ipb.textEditor.htmlCheckbox[ this.editorId ] ).checked = true;
|
|
}
|
|
|
|
/* HTML checkbox checked? */
|
|
if ( $( ipb.textEditor.htmlCheckbox[ this.editorId ] ) )
|
|
{
|
|
/* Check status of HTML checkbox on load */
|
|
ipb.textEditor.htmlModeToggled( this, ipb.textEditor.htmlCheckbox[ this.editorId ] );
|
|
}
|
|
|
|
if ( this.options.noSmilies )
|
|
{
|
|
$('cke_top_' + this.editorId ).down('.cke_button_ipsemoticon').up('.cke_button').hide();
|
|
}
|
|
|
|
}
|
|
}.bind(this) );
|
|
},
|
|
|
|
/**
|
|
* Init CKEditor
|
|
*/
|
|
showEditor: function( content )
|
|
{
|
|
if ( this.options.minimize )
|
|
{
|
|
this.options.minimizeNowOpen = 1;
|
|
|
|
$('cke_top_' + this.editorId ).down('.cke_toolbox_collapser').show();
|
|
$('cke_bottom_' + this.editorId ).show();
|
|
$('cke_' + this.editorId ).down('.cke_wrapper').removeClassName('minimized');
|
|
|
|
this.EditorObj.editor.execCommand('toolbarCollapse');
|
|
|
|
/* IE bug when clicking the text editor to expand and it also registers a toolbar click*/
|
|
if ( Prototype.Browser.IE9 )
|
|
{
|
|
setTimeout( function() { $('cke_top_' + this.editorId ).down('.cke_toolbox').setStyle( { 'position': 'relative', 'left': '0px' } ) }.bind(this), 300 );
|
|
}
|
|
|
|
/* Shift screen if need be */
|
|
try
|
|
{
|
|
var dims = document.viewport.getDimensions();
|
|
var editorDims = $('cke_' + this.editorId).getDimensions();
|
|
var cOffset = $('cke_' + this.editorId).cumulativeScrollOffset();
|
|
var pOffset = $('cke_' + this.editorId).positionedOffset();
|
|
|
|
var bottomOfEditor = pOffset.top + editorDims.height;
|
|
var bottomOfScreen = cOffset.top + dims.height;
|
|
|
|
if ( bottomOfEditor > bottomOfScreen )
|
|
{
|
|
var diff = bottomOfEditor - bottomOfScreen;
|
|
|
|
/* Scroll but with 100 extra pixels for comfort */
|
|
window.scrollTo( 0, cOffset.top + diff + 100 );
|
|
}
|
|
}
|
|
catch(e){ }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Check for dirty status and throw it to a callback then cancel timer
|
|
*/
|
|
checkForInput: function()
|
|
{
|
|
if ( this.options.minimize == 1 && this.options.minimizeNowOpen == 0 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var content = this.getText();
|
|
|
|
if ( content && content.length && Object.isFunction( this.options.isTypingCallBack ) )
|
|
{
|
|
/* We have content, so throw to call back */
|
|
this.options.isTypingCallBack();
|
|
|
|
/* And cancel timer */
|
|
clearInterval( this.timers['dirty'] );
|
|
this.timers['dirty'] = '';
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Close previously minimized editor
|
|
*/
|
|
minimizeOpenedEditor: function()
|
|
{
|
|
if ( this.options.minimize == 1 && this.options.minimizeNowOpen == 1 )
|
|
{
|
|
if ( ! this.options.bypassCKEditor && this.CKEditor )
|
|
{
|
|
if ( $('cke_' + CKEDITOR.plugins.ipsemoticon.editor.name + '_stray') )
|
|
{
|
|
$('cke_' + CKEDITOR.plugins.ipsemoticon.editor.name + '_stray').remove();
|
|
|
|
if ( $('ips_x_smile_show_all') )
|
|
{
|
|
$('ips_x_smile_show_all').remove();
|
|
}
|
|
}
|
|
|
|
/* Re-shrink editor */
|
|
this.EditorObj.editor.execCommand('toolbarCollapse');
|
|
$('cke_top_' + this.editorId ).down('.cke_toolbox_collapser').hide();
|
|
$('cke_bottom_' + this.editorId ).hide();
|
|
$('cke_' + this.editorId ).down('.cke_wrapper').addClassName('minimized');
|
|
|
|
if ( this.isRte() )
|
|
{
|
|
this.EditorObj.editor.setData('<p></p>');
|
|
this.EditorObj.editor.focusManager.forceBlur();
|
|
}
|
|
else
|
|
{
|
|
this.EditorObj.editor.setData('');
|
|
|
|
try
|
|
{
|
|
$('cke_' + this.editorId).down('textarea').blur();
|
|
}
|
|
catch(error){ Debug.write( error ); }
|
|
|
|
}
|
|
|
|
this.options.minimizeNowOpen = 0;
|
|
}
|
|
|
|
if ( this.options.bypassCKEditor )
|
|
{
|
|
$( this.editorId ).value = '';
|
|
}
|
|
|
|
try
|
|
{
|
|
if ( ! Object.isUndefined( $H( this.timers ) ) )
|
|
{
|
|
$H( this.timers ).each( function (timer)
|
|
{
|
|
var name = timer.key;
|
|
|
|
if ( name.match( /^interval_/ ) )
|
|
{
|
|
clearInterval( this.timers[ name ] );
|
|
Debug.write( 'Cleared Interval ' + name );
|
|
}
|
|
else
|
|
{
|
|
clearTimeout( this.timers[ name ] );
|
|
Debug.write( 'Cleared Timeout ' + name );
|
|
}
|
|
|
|
this.timers[ name ] = '';
|
|
}.bind(this) );
|
|
}
|
|
}
|
|
catch(e) { Debug.error(e); }
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Make sure editor is in view
|
|
*
|
|
*/
|
|
scrollTo: function()
|
|
{
|
|
var dims = document.viewport.getDimensions();
|
|
var where = $( this.editorId ).positionedOffset();
|
|
var offsets = document.viewport.getScrollOffsets();
|
|
|
|
/* Is editor off the page? */
|
|
if ( offsets.top + dims.height < where.top )
|
|
{
|
|
window.scroll( 0, ( parseInt( where.top ) - 200 ) );
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Remove editor completely
|
|
*/
|
|
remove: function()
|
|
{
|
|
this.CKEditor.destroy( true );
|
|
|
|
this.CKEditor = null;
|
|
|
|
/* Remove object */
|
|
ipb.textEditor.mainStore.unset( this.editorId );
|
|
|
|
/* Remove timers */
|
|
if ( ! Object.isUndefined( $H( this.timers ) ) )
|
|
{
|
|
$H( this.timers ).each( function (timer)
|
|
{
|
|
var name = timer.key;
|
|
|
|
if ( name.match( /^interval_/ ) )
|
|
{
|
|
clearInterval( this.timers[ name ] );
|
|
Debug.write( 'Cleared Interval ' + name );
|
|
}
|
|
else
|
|
{
|
|
clearTimeout( this.timers[ name ] );
|
|
Debug.write( 'Cleared Timeout ' + name );
|
|
}
|
|
|
|
this.timers[ name ] = '';
|
|
}.bind(this) );
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Inserts content into the text editor
|
|
*/
|
|
insert: function( content, scrollOnInit, clearFirst )
|
|
{
|
|
/* Minimized... */
|
|
if ( this.options.minimize == 1 && this.options.minimizeNowOpen != 1 )
|
|
{
|
|
/* Scroll to editor when it loads? */
|
|
if ( scrollOnInit )
|
|
{
|
|
$( this.editorId ).scrollTo();
|
|
}
|
|
|
|
this.showEditor();
|
|
}
|
|
else
|
|
{
|
|
/* Scroll always? */
|
|
if ( scrollOnInit === 'always' )
|
|
{
|
|
if ( this.options.bypassCKEditor != 1 )
|
|
{
|
|
$( 'cke_' + this.editorId ).scrollTo();
|
|
}
|
|
else
|
|
{
|
|
$( this.editorId ).scrollTo();
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( this.options.bypassCKEditor != 1 )
|
|
{
|
|
if ( this.isRte() )
|
|
{
|
|
// Using insertHtml() because if you have content in the editor and insert more (i.e. click reply on two posts in a topic),
|
|
// subsequent inserts will show the HTML instead of formatting it properly
|
|
this.CKEditor.insertHtml( content );
|
|
}
|
|
else
|
|
{
|
|
if ( this.CKEditor.getData() )
|
|
{
|
|
if ( clearFirst )
|
|
{
|
|
this.CKEditor.setData( ipb.textEditor.stdPre( content ) );
|
|
}
|
|
else
|
|
{
|
|
ipsTextArea.insertAtCursor( this.editorId, ipb.textEditor.stdPre( content ) );
|
|
//this.CKEditor.setData( this.CKEditor.getData() + ipb.textEditor.stdPre( content ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.CKEditor.setData( ipb.textEditor.stdPre( content ) );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$( this.editorId ).value += content;
|
|
}
|
|
},
|
|
|
|
|
|
/**
|
|
* Show any display data we might have
|
|
*/
|
|
displayAutoSaveData: function()
|
|
{
|
|
if ( inACP )
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Keep looping until editor is ready */
|
|
try
|
|
{
|
|
if ( Object.isUndefined( this.CKEditor ) || ! this.CKEditor.name || ! $('cke_' + this.editorId ) )
|
|
{
|
|
setTimeout( this.displayAutoSaveData.bind(this), 1000 );
|
|
return;
|
|
}
|
|
} catch(e) { }
|
|
|
|
Debug.write( 'Ready to show saved data: ' + 'cke_' + this.editorId );
|
|
|
|
var sd = this.options.ips_AutoSaveData;
|
|
|
|
if ( sd.key )
|
|
{
|
|
html = this.options.ips_AutoSaveTemplate.evaluate( sd );
|
|
|
|
if ( $('cke_' + this.editorId ).down('.cke_bottom').select('.cke_path').length < 1 )
|
|
{
|
|
$('cke_' + this.editorId ).down('.cke_resizer').insert( { before: new Element('div').addClassName('cke_path').update(html) } );
|
|
|
|
ipb.delegate.register('._as_launch', this.launchViewContent.bind(this) );
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Show the saved content in a natty little windah
|
|
*/
|
|
launchViewContent: function(e)
|
|
{
|
|
Event.stop(e);
|
|
|
|
if ( ! Object.isUndefined( this.popups['view'] ) )
|
|
{
|
|
this.popups['view'].show();
|
|
}
|
|
else
|
|
{
|
|
/* easy one this... */
|
|
this.popups['view'] = new ipb.Popup( 'view', { type: 'modal',
|
|
initial: new Template( ipb.textEditor.IPS_AUTOSAVEVIEW ).evaluate( { content: this.options.ips_AutoSaveData.parsed } ),
|
|
stem: false,
|
|
warning: false,
|
|
hideAtStart: false,
|
|
w: '600px' } );
|
|
|
|
ipb.delegate.register('._as_restore', this.restoreAutoSaveData.bind(this) );
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Show the about saved content in a natty little windah
|
|
*/
|
|
launchExplain: function(e)
|
|
{
|
|
Event.stop(e);
|
|
|
|
var s = '__last_update_stamp_' + this.editorId;
|
|
|
|
if ( ! Object.isUndefined( this.popups['explain'] ) )
|
|
{
|
|
this.popups['explain'].kill();
|
|
}
|
|
|
|
/* easy one this... */
|
|
this.popups['explain'] = new ipb.Popup( 'explain', { type: 'balloon',
|
|
initial: ipb.textEditor.IPS_AUTOSAVEEXPLAIN,
|
|
stem: true,
|
|
warning: false,
|
|
hideAtStart: false,
|
|
attach: { target: $$('.' + s ).first() },
|
|
w: '300px' } );
|
|
|
|
},
|
|
|
|
/**
|
|
* Restore auto saved content
|
|
*/
|
|
restoreAutoSaveData: function(e)
|
|
{
|
|
Event.stop(e);
|
|
|
|
this.popups['view'].hide();
|
|
|
|
if ( this.isRte() )
|
|
{
|
|
setTimeout( function() { this.CKEditor.setData( ipb.textEditor.ckPre( this.options.ips_AutoSaveData.restore_rte.replace( /&/g, '&' ) ) ); }.bind(this), 500 );
|
|
}
|
|
else
|
|
{
|
|
setTimeout( function() { this.CKEditor.setData( this.options.ips_AutoSaveData.restore_std ); }.bind(this), 500 );
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Save contents of the editor
|
|
* @param object Current editor object (passed via plugin)
|
|
* @param object Current command object (passed via plugin)
|
|
*/
|
|
save: function( editor, command )
|
|
{
|
|
if ( inACP )
|
|
{
|
|
return;
|
|
}
|
|
|
|
var _url = ipb.textEditor.ajaxUrl + '&app=core&module=ajax§ion=editor&do=autoSave&secure_key=' + ipb.vars['secure_hash'] + '&autoSaveKey=' + this.options.ips_AutoSaveKey;
|
|
Debug.write( _url );
|
|
|
|
/* Fetch data */
|
|
var content = this.CKEditor.getData();
|
|
|
|
Debug.write( 'Fetched editor content: ' + content );
|
|
|
|
new Ajax.Request( _url,
|
|
{
|
|
method: 'post',
|
|
evalJSON: 'force',
|
|
hideLoader: true,
|
|
parameters: { 'content' : content.encodeParam() },
|
|
onSuccess: function(t)
|
|
{
|
|
/* No Permission */
|
|
if ( t.responseJSON && ( t.responseJSON['status'] == 'ok' || t.responseJSON['status'] == 'nothingToSave' ) )
|
|
{
|
|
/* Reset 'dirty' */
|
|
editor.resetDirty();
|
|
|
|
/* No longer busy */
|
|
command.setState( CKEDITOR.TRISTATE_OFF );
|
|
|
|
if ( t.responseJSON['status'] == 'ok' )
|
|
{
|
|
this.updateSaveMessage();
|
|
}
|
|
}
|
|
}.bind(this)
|
|
} );
|
|
|
|
},
|
|
|
|
/**
|
|
* Display the time the post was last auto saved
|
|
*/
|
|
updateSaveMessage: function()
|
|
{
|
|
var s = '__last_update_stamp_' + this.editorId;
|
|
var d = new Date();
|
|
var c = new Template( ipb.textEditor.IPS_SAVED_MSG ).evaluate( { time: d.toLocaleTimeString() } );
|
|
|
|
/* remove old */
|
|
$$('.' + s ).invoke('remove');
|
|
|
|
/* Add new */
|
|
$('cke_' + this.editorId ).down('.cke_resizer').insert( { before: new Element('div').addClassName('cke_path ' + s).update(c) } );
|
|
|
|
ipb.delegate.register('._as_explain', this.launchExplain.bind(this) );
|
|
}
|
|
} ); |