2013-05-22 22:13:12 +02:00
/ * *
2013-05-29 21:25:12 +02:00
* EGroupware - Home - Javascript UI
2013-05-22 22:13:12 +02:00
*
* @ link http : //www.egroupware.org
2013-05-29 21:25:12 +02:00
* @ package home
* @ author Nathan Gray
* @ copyright ( c ) 2013 Nathan Gray
2013-05-22 22:13:12 +02:00
* @ license http : //opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @ version $Id$
* /
/ * e g w : u s e s
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 ;
2021-11-10 11:55:28 +01:00
/ a p i / j s / j q u e r y / j q u e r y - u i / j q u e r y - u i . j s ;
2016-06-06 17:38:20 +02:00
/ v e n d o r / n p m - a s s e t / g r i d s t e r / d i s t / j q u e r y . g r i d s t e r . j s ;
2013-05-22 22:13:12 +02:00
* /
/ * *
* JS for home application
2013-11-04 21:54:23 +01:00
*
2013-05-22 22:13:12 +02:00
* Home is a collection of little bits of content ( portlets ) from the other applications .
*
2013-11-04 21:54:23 +01:00
*
2013-05-22 22:13:12 +02:00
* Uses Gridster for the grid layout
2013-05-29 21:25:12 +02:00
* @ see http : //gridster.net
2013-05-22 22:13:12 +02:00
* @ augments AppJS
* /
2016-02-29 17:40:33 +01:00
app . classes . home = ( function ( ) { "use strict" ; return AppJS . extend (
2013-05-22 22:13:12 +02:00
{
/ * *
* AppJS requires overwriting this with the actual application name
* /
appname : "home" ,
2013-05-29 21:25:12 +02:00
/ * *
* Grid resolution . Must match et2 _portlet GRID
* /
2014-11-25 01:45:14 +01:00
GRID : 50 ,
2013-05-29 21:25:12 +02:00
/ * *
* Default size for new portlets
* /
DEFAULT : {
2014-11-25 01:45:14 +01:00
WIDTH : 4 ,
2013-05-29 21:25:12 +02:00
HEIGHT : 1
} ,
2015-01-13 18:36:18 +01:00
// List of portlets
portlets : { } ,
2013-05-22 22:13:12 +02:00
/ * *
* Constructor
2013-11-04 21:54:23 +01:00
*
2013-05-23 00:44:27 +02:00
* @ memberOf app . home
* /
init : function ( )
{
// call parent
this . _super . apply ( this , arguments ) ;
} ,
/ * *
* Destructor
* @ memberOf app . home
* /
destroy : function ( )
{
delete this . et2 ;
2013-05-22 22:13:12 +02:00
delete this . portlet _container ;
2015-01-13 18:36:18 +01:00
this . portlets = { } ;
2013-05-22 22:13:12 +02:00
// call parent
2013-05-23 00:44:27 +02:00
this . _super . apply ( this , arguments ) ;
2014-11-06 22:40:03 +01:00
// Make sure all other sub-etemplates in portlets are done
var others = etemplate2 . getByApplication ( this . appname ) ;
for ( var i = 0 ; i < others . length ; i ++ )
{
others [ i ] . clear ( ) ;
}
2013-05-23 00:44:27 +02:00
} ,
2013-05-22 22:13:12 +02:00
2013-05-23 00:44:27 +02:00
/ * *
* This function is called when the etemplate2 object is loaded
* and ready . If you must store a reference to the et2 object ,
* make sure to clean it up in destroy ( ) .
*
2014-11-06 22:40:03 +01:00
* @ param { etemplate2 } et2 Newly ready object
* @ param { string } Template name
2013-05-23 00:44:27 +02:00
* /
2014-11-06 22:40:03 +01:00
et2 _ready : function ( et2 , name )
2013-05-23 00:44:27 +02:00
{
2014-11-06 22:40:03 +01:00
// Top level
if ( name == 'home.index' )
{
// call parent
this . _super . apply ( this , arguments ) ;
2016-02-29 17:40:33 +01:00
2014-11-06 22:40:03 +01:00
this . et2 . set _id ( 'home.index' ) ;
this . et2 . set _actions ( this . et2 . getArrayMgr ( 'modifications' ) . getEntry ( 'home.index' ) [ 'actions' ] ) ;
2013-11-04 21:54:23 +01:00
2014-11-06 22:40:03 +01:00
this . portlet _container = this . et2 . getWidgetById ( "portlets" ) ;
2013-05-22 22:13:12 +02:00
2014-11-06 22:40:03 +01:00
// Set up sorting of portlets
this . _do _ordering ( ) ;
2014-11-12 00:07:35 +01:00
// Accept drops of favorites, which aren't part of action system
2016-06-02 16:51:15 +02:00
jQuery ( this . et2 . getDOMNode ( ) . parentNode ) . droppable ( {
2014-11-12 00:07:35 +01:00
hoverClass : 'drop-hover' ,
accept : function ( draggable ) {
// Check for direct support for that application
if ( draggable [ 0 ] . dataset && draggable [ 0 ] . dataset . appname )
{
return egw _getActionManager ( 'home' , false , 1 ) . getActionById ( 'drop_' + draggable [ 0 ] . dataset . appname + '_favorite_portlet' ) != null ;
}
return false ;
} ,
drop : function ( event , ui ) {
// Favorite dropped on home - fake an action and divert to normal handler
var action = {
data : {
class : 'add_home_favorite_portlet'
}
}
// Check for direct support for that application
if ( ui . helper . context . dataset && ui . helper . context . dataset . appname )
{
var action = egw _getActionManager ( 'home' , false , 1 ) . getActionById ( 'drop_' + ui . helper . context . dataset . appname + '_favorite_portlet' ) || { }
}
action . ui = ui ;
app . home . add _from _drop ( action , [ { data : ui . helper . context . dataset } ] )
}
} )
2015-01-13 18:36:18 +01:00
// Bind to unload to remove it from our list
. on ( 'clear' , '.et2_container[id]' , jQuery . proxy ( function ( e ) {
if ( e . target && e . target . id && this . portlets [ e . target . id ] )
{
this . portlets [ e . target . id ] . destroy ( ) ;
delete this . portlets [ e . target . id ] ;
}
} , this ) ) ;
2014-11-06 22:40:03 +01:00
}
else if ( et2 . uniqueId )
2013-05-22 22:13:12 +02:00
{
2020-07-30 21:00:53 +02:00
let portlet _container = this . portlet _container || window . app . home ? . portlet _container ;
2014-11-06 22:40:03 +01:00
// Handle bad timing - a sub-template was finished first
2020-07-30 21:00:53 +02:00
if ( ! portlet _container )
2014-11-06 22:40:03 +01:00
{
2015-02-10 19:50:43 +01:00
window . setTimeout ( jQuery . proxy ( function ( ) { this . et2 _ready ( et2 , name ) ; } , this ) , 200 ) ;
2014-11-06 22:40:03 +01:00
return ;
}
2020-07-30 21:00:53 +02:00
var portlet = portlet _container . getWidgetById ( et2 . uniqueId ) ;
2014-11-06 22:40:03 +01:00
// Check for existing etemplate, this one loaded over it
// NOTE: Moving them around like this can cause problems with event handlers
var existing = etemplate2 . getById ( et2 . uniqueId ) ;
2016-02-23 22:29:13 +01:00
if ( portlet && existing )
2014-11-06 22:40:03 +01:00
{
for ( var i = 0 ; i < portlet . _children . length ; i ++ )
{
2016-02-23 22:29:13 +01:00
if ( typeof portlet . _children [ i ] . _init == 'undefined' )
{
portlet . removeChild ( portlet . _children [ i ] )
}
2014-11-06 22:40:03 +01:00
}
}
// It's in the right place for original load, but move it into portlet
2016-06-02 16:51:15 +02:00
var misplaced = jQuery ( etemplate2 . getById ( 'home-index' ) . DOMContainer ) . siblings ( '#' + et2 . DOMContainer . id ) ;
2014-11-06 22:40:03 +01:00
if ( portlet )
{
2016-06-02 16:51:15 +02:00
portlet . content = jQuery ( et2 . DOMContainer ) . appendTo ( portlet . content ) ;
2014-11-06 22:40:03 +01:00
portlet . addChild ( et2 . widgetContainer ) ;
2014-11-12 00:07:35 +01:00
et2 . resize ( ) ;
2014-11-06 22:40:03 +01:00
}
if ( portlet && misplaced . length )
{
et2 . DOMContainer . id = et2 . uniqueId ;
}
2015-01-13 18:36:18 +01:00
// Instanciate custom code for this portlet
this . _get _portlet _code ( portlet ) ;
2013-05-22 22:13:12 +02:00
}
2014-12-15 17:39:52 +01:00
// Special handling to deal with legacy (non-et2) calendar links
if ( name == 'home.legacy' )
{
2016-06-02 16:51:15 +02:00
jQuery ( '.calendar_calDayColHeader a, .calendar_plannerDayScale a, .calendar_plannerWeekScale a, .calendar_plannerMonthScale a, .calendar_calGridHeader a' , et2 . DOMContainer )
2014-12-15 17:39:52 +01:00
. on ( 'click' , function ( e ) {
egw . link _handler ( this . href , 'calendar' ) ;
return false ;
} ) ;
}
2014-11-06 22:40:03 +01:00
} ,
2013-05-22 22:13:12 +02:00
2015-01-13 18:36:18 +01:00
/ * *
* Observer method receives update notifications from all applications
*
* Home passes the notification off to specific code for each portlet , which
* decide if they should be updated or not .
*
* @ param { string } _msg message ( already translated ) to show , eg . 'Entry deleted'
* @ param { string } _app application name
* @ param { ( string | number ) } _id id of entry to refresh or null
* @ param { string } _type either 'update' , 'edit' , 'delete' , 'add' or null
* - update : request just modified data from given rows . Sorting is not considered ,
* so if the sort field is changed , the row will not be moved .
* - edit : rows changed , but sorting may be affected . Requires full reload .
* - delete : just delete the given rows clientside ( no server interaction neccessary )
* - add : requires full reload for proper sorting
* @ param { string } _msg _type 'error' , 'warning' or 'success' ( default )
* @ param { string } _targetapp which app ' s window should be refreshed , default current
* @ return { false | * } false to stop regular refresh , thought all observers are run
* /
observer : function ( _msg , _app , _id , _type , _msg _type , _targetapp )
{
for ( var id in this . portlets )
{
// App is home, refresh all portlets
if ( _app == 'home' )
{
this . refresh ( id ) ;
continue ;
}
// Ask the portlets if they're interested
try
{
var code = this . portlets [ id ] ;
if ( code )
{
code . observer ( _msg , _app , _id , _type , _msg _type , _targetapp ) ;
}
}
catch ( e )
{
this . egw . debug ( "error" , "Error trying to update portlet " + id , e ) ;
}
}
return false ;
} ,
2013-05-22 22:13:12 +02:00
/ * *
2013-05-23 00:44:27 +02:00
* Add a new portlet from the context menu
2013-05-22 22:13:12 +02:00
* /
2013-06-05 00:34:21 +02:00
add : function ( action , source ) {
2014-11-19 00:46:58 +01:00
// Basic portlet attributes
var attrs = {
id : this . _create _id ( ) ,
class : action . data . class ,
width : this . DEFAULT . WIDTH ,
height : this . DEFAULT . HEIGHT
} ;
2014-11-12 00:07:35 +01:00
// Try to put it about where the menu was opened
2014-11-10 19:44:11 +01:00
if ( action . menu _context )
{
2016-06-02 16:51:15 +02:00
var $portlet _container = jQuery ( this . portlet _container . getDOMNode ( ) ) ;
2014-11-17 23:18:24 +01:00
attrs . row = Math . max ( 1 , Math . round ( ( action . menu _context . posy - $portlet _container . offset ( ) . top ) / this . GRID ) + 1 ) ;
2014-11-10 19:44:11 +01:00
attrs . col = Math . max ( 1 , Math . round ( ( action . menu _context . posx - $portlet _container . offset ( ) . left ) / this . GRID ) + 1 ) ;
}
2013-06-05 00:34:21 +02:00
2015-01-13 22:58:53 +01:00
// Don't pass default width & height so class can set it
delete attrs . width ;
delete attrs . height ;
var portlet = et2 _createWidget ( 'portlet' , jQuery . extend ( { } , attrs ) , this . portlet _container ) ;
2013-05-22 22:13:12 +02:00
portlet . loadingFinished ( ) ;
2014-11-25 22:50:42 +01:00
// Immediately add content ID so etemplate loads into the right place
portlet . content . append ( '<div id="' + attrs . id + '" class="et2_container"/>' ) ;
2015-01-13 22:58:53 +01:00
2013-05-22 22:13:12 +02:00
// Get actual attributes & settings, since they're not available client side yet
2014-11-19 00:46:58 +01:00
portlet . _process _edit ( et2 _dialog . OK _BUTTON , attrs ) ;
2013-05-22 22:13:12 +02:00
// Set up sorting/grid of new portlet
2016-06-02 16:51:15 +02:00
var $portlet _container = jQuery ( this . portlet _container . getDOMNode ( ) ) ;
2013-05-22 22:13:12 +02:00
$portlet _container . data ( "gridster" ) . add _widget (
2013-06-05 00:34:21 +02:00
portlet . getDOMNode ( ) ,
this . DEFAULT . WIDTH , this . DEFAULT . HEIGHT ,
attrs . col , attrs . row
2013-05-22 22:13:12 +02:00
) ;
2015-01-13 18:36:18 +01:00
// Instanciate custom code for this portlet
this . _get _portlet _code ( portlet ) ;
2013-05-22 22:13:12 +02:00
} ,
2013-05-23 00:44:27 +02:00
/ * *
* User dropped something on home . Add a new portlet
* /
2014-11-12 00:07:35 +01:00
add _from _drop : function ( action , source ) {
2013-05-29 21:25:12 +02:00
// Actions got confused drop vs popup
if ( source [ 0 ] . id == 'portlets' )
{
return this . add ( action ) ;
}
2016-06-02 16:51:15 +02:00
var $portlet _container = jQuery ( this . portlet _container . getDOMNode ( ) ) ;
2013-05-29 21:25:12 +02:00
// Basic portlet attributes
2014-11-19 00:46:58 +01:00
var attrs = {
id : this . _create _id ( ) ,
class : action . data . class || action . id . substr ( 5 ) ,
width : this . DEFAULT . WIDTH ,
height : this . DEFAULT . HEIGHT
} ;
2013-05-29 21:25:12 +02:00
// Try to find where the drop was
if ( action != null && action . ui && action . ui . position )
{
2014-11-17 23:18:24 +01:00
attrs . row = Math . max ( 1 , Math . round ( ( action . ui . position . top - $portlet _container . offset ( ) . top ) / this . GRID ) ) ;
attrs . col = Math . max ( 1 , Math . round ( ( action . ui . position . left - $portlet _container . offset ( ) . left ) / this . GRID ) ) ;
2013-05-29 21:25:12 +02:00
}
2015-01-13 22:58:53 +01:00
var portlet = et2 _createWidget ( 'portlet' , jQuery . extend ( { } , attrs ) , this . portlet _container ) ;
2014-11-25 23:42:44 +01:00
portlet . loadingFinished ( ) ;
2014-11-25 22:50:42 +01:00
// Immediately add content ID so etemplate loads into the right place
portlet . content . append ( '<div id="' + attrs . id + '" class="et2_container"/>' ) ;
2013-05-23 00:44:27 +02:00
// Get actual attributes & settings, since they're not available client side yet
var drop _data = [ ] ;
for ( var i = 0 ; i < source . length ; i ++ )
{
2014-11-12 00:07:35 +01:00
if ( source [ i ] . id )
{
drop _data . push ( source [ i ] . id ) ;
}
else
{
drop _data . push ( source [ i ] . data ) ;
}
2013-05-23 00:44:27 +02:00
}
2015-01-13 22:58:53 +01:00
// Don't pass default width & height so class can set it
delete attrs . width ;
delete attrs . height ;
2014-11-19 00:46:58 +01:00
portlet . _process _edit ( et2 _dialog . OK _BUTTON , jQuery . extend ( { dropped _data : drop _data } , attrs ) ) ;
2013-05-23 00:44:27 +02:00
// Set up sorting/grid of new portlet
$portlet _container . data ( "gridster" ) . add _widget (
2013-05-29 21:25:12 +02:00
portlet . getDOMNode ( ) ,
this . DEFAULT . WIDTH , this . DEFAULT . HEIGHT ,
attrs . col , attrs . row
2013-05-23 00:44:27 +02:00
) ;
2015-01-13 18:36:18 +01:00
// Instanciate custom code for this portlet
this . _get _portlet _code ( portlet ) ;
2013-05-23 00:44:27 +02:00
} ,
2014-12-19 23:26:29 +01:00
/ * *
* Set the current selection as default for other users
2016-02-29 17:40:33 +01:00
*
2014-12-19 23:26:29 +01:00
* Only works ( and available ) for admins , this shows a dialog to select
* the group , and then sets the default for that group .
2016-02-29 17:40:33 +01:00
*
2014-12-19 23:26:29 +01:00
* @ param { egwAction } action
* @ param { egwActionObject [ ] } selected
* /
set _default : function ( action , selected ) {
// Gather just IDs, server will handle the details
var portlet _ids = [ ] ;
var group = action . data . portlet _group || false ;
if ( selected [ 0 ] . id == 'home.index' )
{
// Set all
this . portlet _container . iterateOver ( function ( portlet ) {
portlet _ids . push ( portlet . id ) ;
} , this , et2 _portlet ) ;
}
else
{
for ( var i = 0 ; i < selected . length ; i ++ )
{
portlet _ids . push ( selected [ i ] . id ) ;
// Read the associated group so we can properly remove it
var portlet = egw . preference ( selected [ i ] . id , 'home' ) ;
if ( ! group && portlet && portlet . group )
{
group = portlet . group ;
}
}
}
if ( action . id . indexOf ( "remove_default" ) == 0 )
{
// Disable action for feedback
action . set _enabled ( false ) ;
2016-02-29 17:40:33 +01:00
2014-12-19 23:26:29 +01:00
// Pass them to server
egw . json ( 'home_ui::ajax_set_default' , [ 'delete' , portlet _ids , group ] ) . sendRequest ( true ) ;
return ;
}
var dialog = et2 _createWidget ( "dialog" , {
// If you use a template, the second parameter will be the value of the template, as if it were submitted.
callback : function ( button _id , value ) {
if ( button _id != et2 _dialog . OK _BUTTON ) return ;
// Pass them to server
egw . json ( 'home_ui::ajax_set_default' , [ 'add' , portlet _ids , value . group || false ] ) . sendRequest ( true ) ;
} ,
buttons : et2 _dialog . BUTTONS _OK _CANCEL ,
title : action . caption ,
template : "home.set_default" ,
value : { content : { } , sel _options : { group : { default : egw . lang ( 'All' ) , forced : egw . lang ( 'Forced' ) } } }
} ) ;
} ,
2014-11-12 22:33:42 +01:00
/ * *
* Allow a refresh from anywhere by triggering an update with no changes
2016-02-29 17:40:33 +01:00
*
2014-11-12 22:33:42 +01:00
* @ param { string } id
* /
2015-01-13 18:36:18 +01:00
refresh : function ( id ) {
var p = this . portlet _container . getWidgetById ( id ) ;
2014-11-12 22:33:42 +01:00
if ( p )
{
p . _process _edit ( et2 _dialog . OK _BUTTON , '~reload~' ) ;
}
} ,
2015-01-13 18:36:18 +01:00
/ * *
* Determine the best fitting code to use for the given portlet , instanciate
* it and add it to the list .
*
* @ param { et2 _portlet } portlet
* @ returns { home _portlet }
* /
_get _portlet _code : function ( portlet ) {
var classname = portlet . class ;
// Freshly added portlets can have 'add_' prefix
if ( classname . indexOf ( 'add_' ) == 0 )
{
classname = classname . replace ( 'add_' , '' ) ;
}
// Prefer a specific match
var _class = app . classes . home [ classname ] ||
// If it has a nextmatch, use favorite base class
( portlet . getWidgetById ( 'nm' ) ? app . classes . home . home _favorite _portlet : false ) ||
// Fall back to base class
app . classes . home . home _portlet ;
this . portlets [ portlet . id ] = new _class ( portlet ) ;
return this . portlets [ portlet . id ] ;
} ,
2013-05-22 22:13:12 +02:00
/ * *
* For link _portlet - opens the configured record when the user
* double - clicks or chooses view from the context menu
* /
open _link : function ( action ) {
// Get widget
var widget = null ;
while ( action . parent != null )
{
if ( action . data && action . data . widget )
{
widget = action . data . widget ;
break ;
}
action = action . parent ;
}
if ( widget == null )
{
egw ( ) . log ( "warning" , "Could not find widget" ) ;
return ;
}
2014-11-25 01:45:14 +01:00
egw ( ) . open ( widget . options . settings . entry , "" , 'view' , null , widget . options . settings . entry . app ) ;
2013-05-22 22:13:12 +02:00
} ,
/ * *
* Set up the drag / drop / re - order of portlets
* /
_do _ordering : function ( ) {
2016-06-02 16:51:15 +02:00
var $portlet _container = jQuery ( this . portlet _container . getDOMNode ( ) ) ;
2013-05-22 22:13:12 +02:00
$portlet _container
. addClass ( "home ui-helper-clearfix" )
. disableSelection ( )
/* Gridster */
. gridster ( {
widget _selector : 'div.et2_portlet' ,
2013-05-29 21:25:12 +02:00
// Dimensions + margins = grid spacing
widget _base _dimensions : [ this . GRID - 5 , this . GRID - 5 ] ,
2013-05-22 22:13:12 +02:00
widget _margins : [ 5 , 5 ] ,
extra _rows : 1 ,
extra _cols : 1 ,
min _cols : 3 ,
min _rows : 3 ,
/ * *
* Set which parameters we want when calling serialize ( ) .
* @ param $w jQuery jQuery - wrapped element
* @ param grid Object Grid settings
* @ return Object - will be returned by gridster . serialize ( )
* /
serialize _params : function ( $w , grid ) {
2013-11-04 21:54:23 +01:00
return {
2014-11-12 22:33:42 +01:00
id : $w . attr ( 'id' ) . replace ( app . home . portlet _container . getInstanceManager ( ) . uniqueId + '_' , '' ) ,
2013-11-04 21:54:23 +01:00
row : grid . row ,
col : grid . col ,
2014-11-06 22:40:03 +01:00
width : grid . size _x ,
height : grid . size _y
2013-05-22 22:13:12 +02:00
} ;
} ,
/ * *
* Gridster ' s internal drag settings
* /
draggable : {
handle : '.ui-widget-header' ,
stop : function ( event , ui ) {
// Update widget(s)
var changed = this . serialize _changed ( ) ;
2014-11-06 22:40:03 +01:00
// Reset changed, or they keep accumulating
2016-06-02 16:51:15 +02:00
this . $changed = jQuery ( [ ] ) ;
2016-02-29 17:40:33 +01:00
2013-05-22 22:13:12 +02:00
for ( var key in changed )
{
if ( ! changed [ key ] . id ) continue ;
2014-11-06 22:40:03 +01:00
// Changed ID is the ID
var widget = window . app . home . portlet _container . getWidgetById ( changed [ key ] . id ) ;
2013-05-22 22:13:12 +02:00
if ( ! widget || widget == window . app . home . portlet _container ) continue ;
2014-12-29 18:35:50 +01:00
egw ( ) . jsonq ( "home.home_ui.ajax_set_properties" , [ changed [ key ] . id , { } , {
2013-05-22 22:13:12 +02:00
row : changed [ key ] . row ,
col : changed [ key ] . col
2014-12-19 23:26:29 +01:00
} , widget . settings ? widget . settings . group : false ] ,
2013-05-22 22:13:12 +02:00
null ,
widget , true , widget
2013-05-29 21:25:12 +02:00
) ;
2013-05-22 22:13:12 +02:00
}
}
}
} ) ;
2015-01-12 19:12:45 +01:00
// Rescue selectboxes from Firefox
$portlet _container . on ( 'mousedown touchstart' , 'select' , function ( e ) {
e . stopPropagation ( ) ;
} ) ;
2014-11-06 22:40:03 +01:00
// Bind window resize to re-layout gridster
2016-06-02 16:51:15 +02:00
jQuery ( window ) . one ( "resize." + this . et2 . _inst . uniqueId , function ( ) {
2014-11-06 22:40:03 +01:00
// Note this doesn't change the positions, just makes them invalid
$portlet _container . data ( 'gridster' ) . recalculate _faux _grid ( ) ;
} ) ;
2013-05-22 22:13:12 +02:00
// Bind resize to update gridster - this may happen _before_ the widget gets a
// chance to update itself, so we can't use the widget
$portlet _container
. on ( "resizestop" , function ( event , ui ) {
$portlet _container . data ( "gridster" ) . resize _widget (
ui . element ,
2013-06-10 17:05:21 +02:00
Math . round ( ui . size . width / app . home . GRID ) ,
Math . round ( ui . size . height / app . home . GRID )
2013-05-22 22:13:12 +02:00
) ;
} ) ;
} ,
/ * *
* Create an ID that should be unique , at least amoung a single user ' s portlets
* /
_create _id : function ( ) {
var id = '' ;
do
{
id = Math . floor ( ( 1 + Math . random ( ) ) * 0x10000 )
. toString ( 16 )
. substring ( 1 ) ;
}
2014-12-19 23:26:29 +01:00
while ( this . portlet _container . getWidgetById ( 'portlet_' + id ) ) ;
return 'portlet_' + id ;
2013-05-29 21:25:12 +02:00
} ,
/ * *
* Functions for the list portlet
* /
2014-11-19 00:46:58 +01:00
/ * *
* For list _portlet - opens a dialog to add a new entry to the list
*
* @ param { egwAction } action Drop or add action
* @ param { egwActionObject [ ] } Selected entries
* @ param { egwActionObject } target _action Drop target
* /
add _link : function ( action , source , target _action ) {
// Actions got confused drop vs popup
if ( source [ 0 ] . id == 'portlets' )
{
return this . add _link ( action ) ;
}
2013-05-29 21:25:12 +02:00
2014-11-19 00:46:58 +01:00
// Get widget
var widget = null ;
while ( action . parent != null )
{
if ( action . data && action . data . widget )
2013-05-29 21:25:12 +02:00
{
2014-11-19 00:46:58 +01:00
widget = action . data . widget ;
break ;
2013-05-29 21:25:12 +02:00
}
2014-11-19 00:46:58 +01:00
action = action . parent ;
}
if ( target _action == null )
{
// use template base url from initial template, to continue using webdav, if that was loaded via webdav
var splitted = 'home.edit' . split ( '.' ) ;
var path = app . home . portlet _container . getRoot ( ) . _inst . template _base _url + splitted . shift ( ) + "/templates/default/" +
splitted . join ( '.' ) + ".xet" ;
var dialog = et2 _createWidget ( "dialog" , {
callback : function ( button _id , value ) {
if ( button _id == et2 _dialog . CANCEL _BUTTON ) return ;
var new _list = widget . options . settings . list || [ ] ;
for ( var i = 0 ; i < new _list . length ; i ++ )
2014-11-06 22:40:03 +01:00
{
2014-11-19 00:46:58 +01:00
if ( new _list [ i ] . app == value . add . app && new _list [ i ] . id == value . add . id )
2014-11-06 22:40:03 +01:00
{
// Duplicate - skip it
2014-11-19 00:46:58 +01:00
return ;
2014-11-06 22:40:03 +01:00
}
}
2014-11-19 00:46:58 +01:00
value . add . link _id = value . add . app + ':' + value . add . id ;
// Update server side
new _list . push ( value . add ) ;
widget . _process _edit ( button _id , { list : new _list } ) ;
// Update client side
2014-12-19 23:26:29 +01:00
var list = widget . getWidgetById ( 'list' ) ;
if ( list )
{
list . set _value ( new _list ) ;
}
2014-11-19 00:46:58 +01:00
} ,
buttons : et2 _dialog . BUTTONS _OK _CANCEL ,
title : app . home . egw . lang ( 'add' ) ,
template : path ,
value : { content : [ { label : app . home . egw . lang ( 'add' ) , type : 'link-entry' , name : 'add' , size : '' } ] }
} ) ;
}
else
{
// Drag'n'dropped something on the list - just send action IDs
var new _list = widget . options . settings . list || [ ] ;
var changed = false ;
for ( var i = 0 ; i < new _list . length ; i ++ )
{
// Avoid duplicates
for ( var j = 0 ; j < source . length ; j ++ )
2014-11-17 23:18:24 +01:00
{
2014-11-19 00:46:58 +01:00
if ( ! source [ j ] . id || new _list [ i ] . app + "::" + new _list [ i ] . id == source [ j ] . id )
2014-11-17 23:18:24 +01:00
{
2014-11-19 00:46:58 +01:00
// Duplicate - skip it
source . splice ( j , 1 ) ;
2014-11-17 23:18:24 +01:00
}
}
2013-05-29 21:25:12 +02:00
}
2014-11-19 00:46:58 +01:00
for ( var i = 0 ; i < source . length ; i ++ )
{
var explode = source [ i ] . id . split ( '::' ) ;
new _list . push ( { app : explode [ 0 ] , id : explode [ 1 ] , link _id : explode . join ( ':' ) } ) ;
changed = true ;
}
if ( changed )
{
widget . _process _edit ( et2 _dialog . OK _BUTTON , {
list : new _list || { }
} ) ;
}
// Filemanager support - links need app = 'file' and type set
for ( var i = 0 ; i < new _list . length ; i ++ )
{
if ( new _list [ i ] [ 'app' ] == 'filemanager' )
{
new _list [ i ] [ 'app' ] = 'file' ;
new _list [ i ] [ 'path' ] = new _list [ i ] [ 'title' ] = new _list [ i ] [ 'icon' ] = new _list [ i ] [ 'id' ] ;
}
}
widget . getWidgetById ( 'list' ) . set _value ( new _list ) ;
2013-05-29 21:25:12 +02:00
}
2014-11-12 22:33:42 +01:00
} ,
2014-11-19 00:46:58 +01:00
/ * *
* Remove a link from the list
* /
link _change : function ( list , link _id , row ) {
// Quick response client side
row . slideUp ( row . remove ) ;
// Actual removal
var portlet = list . _parent . _parent ;
portlet . options . settings . list . splice ( row . index ( ) , 1 ) ;
portlet . _process _edit ( et2 _dialog . OK _BUTTON , { list : portlet . options . settings . list || { } } ) ;
} ,
2014-11-12 22:33:42 +01:00
/ * *
* Functions for the note portlet
* /
/ * *
* Set up for editing a note
* CKEditor has CSP issues , so we need a popup
*
* @ param { egwAction } action
* @ param { egwActionObject [ ] } Selected
* /
note _edit : function ( action , selected ) {
2014-11-12 22:54:58 +01:00
if ( ! selected && typeof action == 'string' )
{
var id = action ;
}
else
{
var id = selected [ 0 ] . id ;
}
2014-11-12 22:33:42 +01:00
// Aim to match the size
2016-06-02 16:51:15 +02:00
var portlet _dom = jQuery ( '[id$=' + id + '][data-sizex]' , this . portlet _container . getDOMNode ( ) ) ;
2014-11-12 22:33:42 +01:00
var width = portlet _dom . attr ( 'data-sizex' ) * this . GRID ;
var height = portlet _dom . attr ( 'data-sizey' ) * this . GRID ;
2016-02-29 17:40:33 +01:00
2014-11-12 22:33:42 +01:00
// CKEditor is impossible to use below a certain size
// Add 35px for the toolbar, 35px for the buttons
var window _width = Math . max ( 580 , width + 20 ) ;
var window _height = Math . max ( 350 , height + 70 ) ;
2016-02-29 17:40:33 +01:00
2014-11-17 19:47:47 +01:00
// Open popup, but add 70 to the height for the toolbar
2014-11-12 22:33:42 +01:00
egw . open _link ( egw . link ( '/index.php' , {
menuaction : 'home.home_note_portlet.edit' ,
2014-11-12 22:54:58 +01:00
id : id ,
2014-11-12 22:33:42 +01:00
height : window _height - 70
2014-11-12 22:54:58 +01:00
} ) , 'home_' + id , window _width + 'x' + window _height , 'home' ) ;
2014-11-19 00:46:58 +01:00
} ,
/ * *
* Favorites / nextmatch
* /
/ * *
* Toggle the nextmatch header shown / hidden
*
* @ param { Event } event
* @ param { et2 _button } widget
* /
nextmatch _toggle _header : function ( event , widget ) {
2014-11-25 01:45:14 +01:00
widget . set _class ( widget . class == 'opened' ? 'closed' : 'opened' ) ;
2014-11-19 00:46:58 +01:00
// We operate on the DOM here, nm should be unaware of our fiddling
var nm = widget . getParent ( ) . getWidgetById ( 'nm' ) ;
if ( ! nm ) return ;
// Hide header
nm . div . toggleClass ( 'header_hidden' ) ;
2014-11-25 01:45:14 +01:00
nm . set _hide _header ( nm . div . hasClass ( 'header_hidden' ) ) ;
2014-11-25 22:50:42 +01:00
nm . resize ( ) ;
2013-05-22 22:13:12 +02:00
}
2016-04-13 19:08:11 +02:00
} ) } ) . call ( this ) ;
2015-01-13 18:36:18 +01:00
/// Base class code
/ * *
* Base class for portlet specific javascript
*
* Should this maybe extend et2 _portlet ? It would complicate instantiation .
2016-02-29 17:40:33 +01:00
*
2015-01-13 18:36:18 +01:00
* @ type @ exp ; Class @ call ; extend
* /
app . classes . home . home _portlet = Class . extend ( {
portlet : null ,
init : function ( portlet ) {
this . portlet = portlet ;
} ,
destroy : function ( ) {
this . portlet = null ;
} ,
/ * *
* Handle framework refresh messages to determine if the portlet needs to
* refresh too .
*
* App is responsible for only reacting to "messages" it is interested in !
*
* /
observer : function ( _msg , _app , _id , _type , _msg _type , _targetapp )
{
// Not interested
}
} ) ;
app . classes . home . home _link _portlet = app . classes . home . home _portlet . extend ( {
2015-03-05 00:13:36 +01:00
init : function ( portlet ) {
// call parent
this . _super . apply ( this , arguments ) ;
// Check for tooltip
if ( this . portlet )
{
2016-06-02 16:51:15 +02:00
var content = jQuery ( '.tooltip' , this . portlet . content ) ;
2015-03-05 00:13:36 +01:00
if ( content . length && content . children ( ) . length )
{
//Check if the tooltip is already initialized
this . portlet . content . tooltip ( {
items : this . portlet . content ,
content : content . html ( ) ,
tooltipClass : 'portlet_' + this . portlet . id ,
show : { effect : 'slideDown' , delay : 500 } ,
hide : { effect : 'slideUp' , delay : 500 } ,
position : { my : "left top" , at : "left bottom" , collision : "flipfit" } ,
open : jQuery . proxy ( function ( event , ui ) {
// Calendar specific formatting
if ( ui . tooltip . has ( '.calendar_calEventTooltip' ) . length )
{
ui . tooltip . removeClass ( "ui-tooltip" ) ;
ui . tooltip . addClass ( "calendar_uitooltip" ) ;
}
} , this ) ,
close : function ( event , ui ) {
ui . tooltip . hover (
function ( ) {
2016-06-02 16:51:15 +02:00
jQuery ( this ) . stop ( true ) . fadeTo ( 100 , 1 ) ;
2015-03-05 00:13:36 +01:00
} ,
function ( ) {
2016-06-02 16:51:15 +02:00
jQuery ( this ) . slideUp ( "400" , function ( ) { jQuery ( this ) . remove ( ) ; } ) ;
2015-03-05 00:13:36 +01:00
}
) ;
}
} ) ;
}
}
} ,
2015-01-13 18:36:18 +01:00
observer : function ( _msg , _app , _id , _type )
{
2015-08-05 23:15:38 +02:00
if ( this . portlet && this . portlet . settings )
2015-01-13 18:36:18 +01:00
{
var value = this . portlet . settings . entry || { } ;
if ( value . app && value . app == _app && value . id && value . id == _id )
{
// We don't just get the updated title, in case there's a custom
// template with more fields
app . home . refresh ( this . portlet . id ) ;
}
}
}
} ) ;
app . classes . home . home _list _portlet = app . classes . home . home _portlet . extend ( {
observer : function ( _msg , _app , _id , _type )
{
if ( this . portlet && this . portlet . getWidgetById ( 'list' ) )
{
var list = this . portlet . getWidgetById ( 'list' ) . options . value ;
for ( var i = 0 ; i < list . length ; i ++ )
{
if ( list [ i ] . app == _app && list [ i ] . id == _id )
{
app . home . refresh ( this . portlet . id ) ;
return ;
}
}
}
}
} ) ;
2015-04-14 01:02:45 +02:00
app . classes . home . home _weather _portlet = app . classes . home . home _portlet . extend ( {
init : function ( portlet ) {
// call parent
this . _super . apply ( this , arguments ) ;
// Use location API
if ( ! this . portlet . options . settings && 'geolocation' in navigator )
{
navigator . geolocation . getCurrentPosition ( function ( position ) {
if ( portlet && portlet . options && portlet . options . settings &&
portlet . options . settings . position && portlet . options . settings . position == position . coords . latitude + ',' + position . coords . longitude )
{
return ;
}
portlet . _process _edit ( et2 _dialog . OK _BUTTON , { position : position . coords . latitude + ',' + position . coords . longitude } ) ;
} ) ;
}
}
} ) ;
2015-01-13 18:36:18 +01:00
app . classes . home . home _favorite _portlet = app . classes . home . home _portlet . extend ( {
2015-05-04 17:26:18 +02:00
init : function ( portlet ) {
// call parent
this . _super . apply ( this , arguments ) ;
// Somehow favorite got lost, or is not set
2015-05-04 22:11:52 +02:00
if ( portlet . options && portlet . options . settings && typeof portlet . options . settings !== 'undefined' &&
! portlet . options . settings . favorite
)
2015-05-04 17:26:18 +02:00
{
portlet . edit _settings ( ) ;
}
} ,
2015-01-13 18:36:18 +01:00
observer : function ( _msg , _app , _id , _type , _msg _type , _targetapp )
{
if ( this . portlet . class . indexOf ( _app ) == 0 || this . portlet . class == 'home_favorite_portlet' )
{
this . portlet . getWidgetById ( 'nm' ) . refresh ( _id , _type ) ;
}
}
2016-04-13 19:08:11 +02:00
} ) ;
2015-01-13 18:36:18 +01:00
/ * *
* An example illustrating extending the base code for a application specific code .
* See also the calendar app , which needs custom handlers
2016-02-29 17:40:33 +01:00
*
2015-01-13 18:36:18 +01:00
* @ type @ exp ; app @ pro ; classes @ pro ; home @ pro ; home _favorite _portlet @ call ; extend
* Note we put it in home , but this code should go in addressbook / js / addressbook _favorite _portlet . js
*
app . classes . home . addressbook _favorite _portlet = app . classes . home . home _favorite _portlet . extend ( {
observer : function ( _msg , _app , _id , _type , _msg _type , _targetapp )
{
// Just checking...
debugger ;
}
} ) ;
* /