2012-06-06 06:13:19 +02:00
/ * *
2013-04-13 21:00:13 +02:00
* EGroupware eTemplate2 - JS widget for HTML editing
2012-06-06 06:13:19 +02:00
*
* @ license http : //opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @ package etemplate
* @ subpackage api
* @ link http : //www.egroupware.org
* @ author Nathan Gray
* @ copyright Nathan Gray 2012
* @ version $Id$
* /
/ * e g w : u s e s
jsapi . jsapi ; // Needed for egw_seperateJavaScript
2016-06-06 17:38:20 +02:00
/ v e n d o r / b o w e r - a s s e t / j q u e r y / d i s t / j q u e r y . j s ;
2016-10-07 12:15:03 +02:00
/ v e n d o r / e g r o u p w a r e / c k e d i t o r / c k e d i t o r . j s ;
/ v e n d o r / e g r o u p w a r e / c k e d i t o r / c k e d i t o r . c o n f i g ;
/ v e n d o r / e g r o u p w a r e / c k e d i t o r / c k e d i t o r . a d a p t e r s / j q u e r y ;
2012-06-06 06:13:19 +02:00
et2 _core _baseWidget ;
* /
2013-04-13 21:00:13 +02:00
/ * *
* @ augments et2 _inputWidget
* /
2016-02-29 21:40:43 +01:00
var et2 _htmlarea = ( function ( ) { "use strict" ; return et2 _inputWidget . extend ( [ et2 _IResizeable ] ,
2013-04-13 21:00:13 +02:00
{
2013-06-26 21:34:14 +02:00
modes : [ 'ascii' , 'simple' , 'extended' , 'advanced' ] ,
2013-11-04 11:13:28 +01:00
2012-06-06 06:13:19 +02:00
attributes : {
'mode' : {
'name' : 'Mode' ,
'description' : 'One of {ascii|simple|extended|advanced}' ,
'default' : 'simple' ,
'type' : 'string'
} ,
'height' : {
'name' : 'Height' ,
'default' : et2 _no _init ,
'type' : 'string'
} ,
'width' : {
'name' : 'Width' ,
'default' : et2 _no _init ,
'type' : 'string'
} ,
'expand_toolbar' : {
'name' : 'Expand Toolbar' ,
'default' : true ,
2013-11-04 11:13:28 +01:00
'type' : 'boolean' ,
2013-06-17 23:22:32 +02:00
'description' : 'Have the toolbar expanded (visible)'
2012-06-06 06:13:19 +02:00
} ,
2017-01-31 10:05:13 +01:00
'base_href' : { // seems not to be used anymore
2012-06-06 06:13:19 +02:00
'name' : 'Image base path' ,
'default' : et2 _no _init ,
'type' : 'string' ,
'description' : 'activates the browser for images at the path (relative to the docroot)'
} ,
'config' : {
// internal default configuration
2013-06-17 23:22:32 +02:00
'name' : 'Internal configuration' ,
2012-06-06 06:13:19 +02:00
'type' : 'any' ,
2013-06-17 23:22:32 +02:00
'default' : et2 _no _init ,
2013-10-10 13:57:18 +02:00
'description' : 'Internal configuration - managed by preferences & framework, passed in here' ,
'translate' : 'no_lang'
2012-06-06 06:13:19 +02:00
} ,
2014-02-14 11:14:28 +01:00
value : {
name : "Value" ,
description : "The value of the widget" ,
type : "html" , // "string" would remove html tags by running html_entity_decode
default : et2 _no _init
2014-11-27 14:44:50 +01:00
} ,
2015-08-07 16:18:07 +02:00
imageUpload : {
name : "imageUpload" ,
2017-01-31 10:05:13 +01:00
description : "Url to upload images dragged in or id of link_to widget to it's vfs upload. Can also be just a name for which content array contains a path to upload the picture." ,
2015-08-07 16:18:07 +02:00
type : "string" ,
default : null
2014-02-14 11:14:28 +01:00
}
2012-06-06 06:13:19 +02:00
} ,
legacyOptions : [ 'mode' , 'height' , 'width' , 'expand_toolbar' , 'base_href' ] ,
2013-04-13 21:00:13 +02:00
/ * *
* Constructor
2013-11-04 11:13:28 +01:00
*
2013-04-13 21:00:13 +02:00
* @ param _parent
* @ param _attrs
* @ memberOf et2 _htmlarea
* /
2012-06-06 06:13:19 +02:00
init : function ( _parent , _attrs ) {
2013-04-09 13:50:14 +02:00
// _super.apply is responsible for the actual setting of the params (some magic)
2012-06-06 06:13:19 +02:00
this . _super . apply ( this , arguments ) ;
2014-04-08 00:20:30 +02:00
// CK instance
this . ckeditor = null ;
2012-06-06 06:13:19 +02:00
// Allow no child widgets
this . supportedWidgetClasses = [ ] ;
2016-06-02 16:51:15 +02:00
this . htmlNode = jQuery ( document . createElement ( "textarea" ) )
2013-06-26 21:34:14 +02:00
. css ( 'height' , this . options . height )
. addClass ( 'et2_textbox_ro' ) ;
2012-06-06 06:13:19 +02:00
this . setDOMNode ( this . htmlNode [ 0 ] ) ;
} ,
2013-11-04 11:13:28 +01:00
2013-06-26 21:34:14 +02:00
transformAttributes : function ( _attrs ) {
2012-06-06 06:13:19 +02:00
2013-06-26 21:34:14 +02:00
// Check mode, some apps jammed everything in there
2013-10-10 13:57:18 +02:00
if ( _attrs [ 'mode' ] && jQuery . inArray ( _attrs [ 'mode' ] , this . modes ) < 0 )
2013-06-26 21:34:14 +02:00
{
2013-10-10 13:57:18 +02:00
this . egw ( ) . debug ( "warn" , "'%s' is an invalid mode for htmlarea '%s'. Valid options:" , _attrs [ 'mode' ] , _attrs [ 'id' ] , this . modes ) ;
2013-06-26 21:34:14 +02:00
var list = _attrs [ 'mode' ] . split ( ',' ) ;
for ( var i = 0 ; i < list . length && i < this . legacyOptions . length ; i ++ )
{
_attrs [ this . legacyOptions [ i ] ] = list [ i ] ;
}
}
this . _super . apply ( this , arguments ) ;
} ,
2013-11-04 11:13:28 +01:00
2012-06-06 06:13:19 +02:00
doLoadingFinished : function ( ) {
this . _super . apply ( this , arguments ) ;
2014-04-08 00:20:30 +02:00
if ( this . mode == 'ascii' || this . ckeditor != null ) return ;
2013-11-04 11:13:28 +01:00
2012-06-12 22:50:45 +02:00
var self = this ;
2015-08-07 16:18:07 +02:00
if ( ! this . options . imageUpload )
{
delete self . options . config . imageUploadUrl ;
}
else if ( this . options . imageUpload [ 0 ] !== '/' && this . options . imageUpload . substr ( 0 , 4 ) != 'http' )
{
2016-03-19 17:16:59 +01:00
self . options . config . imageUploadUrl = egw . ajaxUrl ( "EGroupware\\Api\\Etemplate\\Widget\\Vfs::ajax_htmlarea_upload" ) +
2015-08-07 16:18:07 +02:00
'&request_id=' + self . getInstanceManager ( ) . etemplate _exec _id + '&widget_id=' + this . options . imageUpload ;
self . options . config . imageUploadUrl = self . options . config . imageUploadUrl . substr ( egw . webserverUrl . length + 1 ) ;
}
else
{
self . options . config . imageUploadUrl = this . options . imageUpload . substr ( egw . webserverUrl . length + 1 ) ;
}
2013-03-20 21:45:43 +01:00
try
{
2014-04-08 00:20:30 +02:00
this . ckeditor = CKEDITOR . replace ( this . dom _id , jQuery . extend ( { } , this . options . config , this . options ) ) ;
this . ckeditor . setData ( self . value ) ;
2013-04-09 13:50:14 +02:00
delete self . value ;
2013-03-20 21:45:43 +01:00
}
catch ( e )
{
2013-08-23 16:10:37 +02:00
if ( CKEDITOR . instances [ this . dom _id ] )
2013-03-20 21:45:43 +01:00
{
2013-08-23 16:10:37 +02:00
CKEDITOR . instances [ this . dom _id ] . destroy ( ) ;
2013-03-20 21:45:43 +01:00
}
2013-03-25 23:22:53 +01:00
if ( this . htmlNode . ckeditor )
{
2014-04-08 00:20:30 +02:00
this . ckeditor = CKEDITOR . replace ( this . dom _id , this . options . config ) ;
this . ckeditor . setData ( self . value ) ;
2013-04-09 13:50:14 +02:00
delete self . value ;
2013-03-25 23:22:53 +01:00
}
2013-03-20 21:45:43 +01:00
}
2014-11-27 14:44:50 +01:00
2014-06-30 23:28:03 +02:00
if ( this . ckeditor && this . options . config . preference _style )
{
2014-07-02 22:50:39 +02:00
var editor = this . ckeditor ;
2014-06-30 23:28:03 +02:00
this . ckeditor . on ( 'instanceReady' , function ( e ) {
// Add in user font preferences
if ( self . options . config . preference _style && ! e . editor . getData ( ) )
{
2015-03-31 19:01:25 +02:00
e . editor . document . getBody ( ) . setHtml ( self . options . config . preference _style ) ;
2014-06-30 23:28:03 +02:00
delete self . options . config . preference _style ;
}
} ) ;
2014-07-02 22:50:39 +02:00
// Drag & drop of images inline won't work, because of database
// field sizes. For some reason FF ignored just changing the cursor
// when dragging, so we replace dropped images with error icon.
var replaceImgText = function ( html ) {
var ret = html . replace ( /<img[^>]*src="(data:.*;base64,.*?)"[^>]*>/gi , function ( img , src ) {
2014-07-07 22:48:35 +02:00
return '' ;
} ) ;
2014-07-02 22:50:39 +02:00
return ret ;
2014-11-27 14:44:50 +01:00
} ;
2014-07-02 22:50:39 +02:00
2014-07-07 22:48:35 +02:00
var chkImg = function ( e ) {
2014-07-02 22:50:39 +02:00
// don't execute code if the editor is readOnly
if ( editor . readOnly )
return ;
2014-11-27 14:44:50 +01:00
// allow data-URL, returning false to stop regular upload
2015-08-07 16:18:07 +02:00
if ( ! self . options . imageUpload )
2014-11-27 14:44:50 +01:00
{
2015-08-07 16:18:07 +02:00
// Remove the image from the text
setTimeout ( function ( ) {
editor . document . $ . body . innerHTML = replaceImgText ( editor . document . $ . body . innerHTML ) ;
} , 200 ) ;
2014-11-27 14:44:50 +01:00
}
2016-02-29 21:40:43 +01:00
2015-08-07 17:22:20 +02:00
// Supported file types for dropping on CKEditor imageUpload plugin
var supportedTypesByCKEditor = /image\/(jpeg|png|gif)/ ;
2016-02-29 21:40:43 +01:00
2014-07-07 22:48:35 +02:00
// Try to pass the image into the first et2_file that will accept it
2015-08-07 17:22:20 +02:00
if ( e . data . $ . dataTransfer && ! CKEDITOR . fileTools . isTypeSupported ( e . data . $ . dataTransfer . files [ 0 ] , supportedTypesByCKEditor ) )
2014-07-07 22:48:35 +02:00
{
self . getRoot ( ) . iterateOver ( function ( widget ) {
if ( widget . options . drop _target )
{
widget . set _value ( e . data . $ . dataTransfer . files , e . data . $ ) ;
return ;
}
} , e . data . $ , et2 _file ) ;
}
2014-07-02 22:50:39 +02:00
} ;
editor . on ( 'contentDom' , function ( ) {
editor . document . on ( 'drop' , chkImg ) ;
} ) ;
2014-06-30 23:28:03 +02:00
}
2014-11-27 14:44:50 +01:00
2012-06-06 06:13:19 +02:00
} ,
destroy : function ( ) {
2013-03-26 16:54:18 +01:00
try
{
2013-04-09 13:50:14 +02:00
//this.htmlNode.ckeditorGet().destroy(true);
2014-04-08 00:20:30 +02:00
if ( this . ckeditor ) this . ckeditor . destroy ( true ) ;
this . ckeditor = null ;
2013-03-26 16:54:18 +01:00
}
catch ( e )
{
2013-10-10 13:57:18 +02:00
this . egw ( ) . debug ( "warn" , "Removing CKEDITOR: " + e . message , this , e ) ;
// Finish it
delete CKEDITOR . instances [ this . dom _id ] ;
2013-03-26 16:54:18 +01:00
}
2013-10-10 13:57:18 +02:00
this . htmlNode . remove ( ) ;
this . htmlNode = null ;
this . _super . apply ( this , arguments ) ;
2012-06-06 06:13:19 +02:00
} ,
set _value : function ( _value ) {
2013-08-23 17:15:30 +02:00
this . _oldValue = _value ;
2012-06-12 22:50:45 +02:00
try {
2013-04-09 13:50:14 +02:00
//this.htmlNode.ckeditorGet().setData(_value);
2013-08-23 16:10:37 +02:00
var ckeditor = CKEDITOR . instances [ this . dom _id ] ;
if ( ckeditor )
{
ckeditor . setData ( _value ) ;
}
else
{
2013-11-04 11:13:28 +01:00
this . htmlNode . val ( _value ) ;
2013-08-23 16:10:37 +02:00
this . value = _value ;
}
2012-06-12 22:50:45 +02:00
} catch ( e ) {
// CK editor not ready - callback will do it
this . value = _value ;
}
2012-06-06 06:13:19 +02:00
} ,
getValue : function ( ) {
2013-03-20 21:45:43 +01:00
try
{
2013-04-09 13:50:14 +02:00
//return this.htmlNode.ckeditorGet().getData();
2013-08-23 16:10:37 +02:00
var ckeditor = CKEDITOR . instances [ this . dom _id ] ;
return ckeditor ? ckeditor . getData ( ) : this . htmlNode . val ( ) ;
2013-03-20 21:45:43 +01:00
}
catch ( e )
{
// CK Error
this . egw ( ) . debug ( "error" , e ) ;
return null ;
}
2015-02-03 12:11:02 +01:00
} ,
2015-03-31 19:01:25 +02:00
2015-02-03 12:11:02 +01:00
/ * *
* Resize htmlNode tag according to window size
* @ param { type } _height excess height which comes from window resize
* /
resize : function ( _height )
{
2015-04-30 10:07:23 +02:00
if ( _height && this . options . resize _ratio !== '0' )
2015-02-03 12:11:02 +01:00
{
// apply the ratio
_height = ( this . options . resize _ratio != '' ) ? _height * this . options . resize _ratio : _height ;
2015-04-27 11:10:47 +02:00
if ( _height != 0 )
{
if ( this . ckeditor ) // CKEDITOR HTML
{
var h = 0 ;
2015-04-30 10:07:23 +02:00
if ( typeof this . ckeditor . container != 'undefined' && this . ckeditor . container . $ . clientHeight > 0 )
{
2015-08-07 16:18:07 +02:00
h = ( this . ckeditor . container . $ . clientHeight + _height ) > 0 ?
2015-04-27 11:10:47 +02:00
this . ckeditor . container . $ . clientHeight + _height : this . ckeditor . config . height ;
}
2015-04-30 10:07:23 +02:00
else if ( this . ckeditor . ui . space ( 'contents' ) )
2015-04-27 11:10:47 +02:00
{
h = parseInt ( this . ckeditor . ui . space ( 'contents' ) . getStyle ( 'height' ) ) + _height ;
}
2015-04-30 10:07:23 +02:00
else // fallback height size
{
h = this . ckeditor . config . height + _height ;
}
2015-06-11 13:29:44 +02:00
this . ckeditor . resize ( '' , h ) ;
2015-04-27 11:10:47 +02:00
}
else // No CKEDITOR
2015-08-07 16:18:07 +02:00
{
2015-04-27 11:10:47 +02:00
this . htmlNode . height ( this . htmlNode . height ( ) + _height ) ;
}
}
2015-02-03 12:11:02 +01:00
}
2012-06-06 06:13:19 +02:00
}
2016-02-29 21:40:43 +01:00
} ) ; } ) . call ( this ) ;
2012-06-06 06:13:19 +02:00
et2 _register _widget ( et2 _htmlarea , [ "htmlarea" ] ) ;
2017-10-20 18:55:22 +02:00
jQuery . extend ( et2 _htmlarea ,
{
/ * *
* Build VfsSelect widget for CKEditor Browse Server button
* @ param { array } _data
* /
buildVfsSelectForCKEditor : function ( _data )
{
if ( ! _data ) return ;
2017-12-06 16:25:31 +01:00
// Don't rely only on app_name to fetch et2 object as app_name may not
// always represent current app of the window, e.g.: mail admin account.
// Try to fetch et2 from its template name.
var etemplate = jQuery ( 'form' ) . data ( 'etemplate' ) ;
var et2 = { } ;
if ( etemplate && etemplate . name && ! app [ egw ( window ) . app _name ( ) ] )
{
et2 = etemplate2 . getByTemplate ( etemplate . name ) [ 0 ] [ 'widgetContainer' ] ;
}
else
{
et2 = app [ egw ( window ) . app _name ( ) ] . et2 ;
}
2017-10-20 18:55:22 +02:00
var vfsSelect = et2 _createWidget ( 'vfs-select' , {
id : 'upload' ,
mode : 'open' ,
name : '' ,
button _caption : "Link" ,
button _label : "Link" ,
dialog _title : "Link file" ,
method : "ckeditor"
} , et2 ) ;
jQuery ( vfsSelect . getDOMNode ( ) ) . on ( 'change' , function ( ) {
CKEDITOR . tools . callFunction ( _data . funcNum , vfsSelect . get _value ( ) ) ;
} ) ;
// start the file selector dialog
vfsSelect . click ( ) ;
}
} ) ;