2011-02-26 20:21:55 +01:00
/ * *
* eGroupWare egw _action framework - egw action framework
*
* @ link http : //www.egroupware.org
* @ author Andreas Stöckel < as @ stylite . de >
* @ copyright 2011 by Andreas Stöckel
* @ license http : //opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @ package egw _action
* @ version $Id$
* /
2011-08-03 16:04:30 +02:00
/ * e g w : u s e s
jquery . jquery ;
egw _menu ;
2014-10-22 17:12:48 +02:00
/ p h p g w a p i / j s / j q u e r y / j q u e r y - t a p - a n d - h o l d / j q u e r y . t a p a n d h o l d . j s ;
2014-12-16 17:27:33 +01:00
/ p h p g w a p i / j s / z e r o c l i p b o a r d / d i s t / Z e r o C l i p b o a r d . m i n . j s ;
2011-08-03 16:04:30 +02:00
* /
2015-02-16 16:18:25 +01:00
/ * S e t f l a s h L o a d T i m e o u t t o s o m e t h i n g c l o s e t o i m m e d i a t e a s w e n e e d t o i n s p e c t e r r o r e v e n t , r i g h t w h e n
* we open the context menu in order to detect Flash player ' s click - to - play mode * /
ZeroClipboard . config ( { swfPath : egw . webserverUrl + "/phpgwapi/js/zeroclipboard/dist/ZeroClipboard.swf" , flashLoadTimeout : 400 } ) ;
2014-12-16 19:57:41 +01:00
2011-02-26 20:21:55 +01:00
if ( typeof window . _egwActionClasses == "undefined" )
2014-10-24 14:44:15 +02:00
window . _egwActionClasses = { } ;
2011-02-26 20:21:55 +01:00
_egwActionClasses [ "popup" ] = {
"actionConstructor" : egwPopupAction ,
"implementation" : getPopupImplementation
2014-10-24 14:44:15 +02:00
} ;
2011-02-26 20:21:55 +01:00
function egwPopupAction ( _id , _handler , _caption , _icon , _onExecute , _allowOnMultiple )
{
var action = new egwAction ( _id , _handler , _caption , _icon , _onExecute , _allowOnMultiple ) ;
action . type = "popup" ;
2011-03-23 15:05:39 +01:00
action . canHaveChildren = [ "popup" ] ;
2011-02-26 20:21:55 +01:00
action [ "default" ] = false ;
action . order = 0 ;
action . group = 0 ;
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
action . hint = false ;
action . checkbox = false ;
action . radioGroup = 0 ;
action . checked = false ;
2011-06-16 22:43:33 +02:00
action . shortcut = null ;
2011-02-26 20:21:55 +01:00
action . set _default = function ( _value ) {
action [ "default" ] = _value ;
2014-10-24 14:44:15 +02:00
} ;
2011-02-26 20:21:55 +01:00
action . set _order = function ( _value ) {
action . order = _value ;
2014-10-24 14:44:15 +02:00
} ;
2011-02-26 20:21:55 +01:00
action . set _group = function ( _value ) {
action . group = _value ;
2014-10-24 14:44:15 +02:00
} ;
2011-02-26 20:21:55 +01:00
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
action . set _hint = function ( _value ) {
action . hint = _value ;
2014-10-24 14:44:15 +02:00
} ;
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
// If true, the action will be rendered as checkbox
action . set _checkbox = function ( _value ) {
action . checkbox = _value ;
2014-10-24 14:44:15 +02:00
} ;
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
action . set _checked = function ( _value ) {
action . checked = _value ;
2014-10-24 14:44:15 +02:00
} ;
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
// If radioGroup is >0 and the element is a checkbox, radioGroup specifies
// the group of radio buttons this one belongs to
action . set _radioGroup = function ( _value ) {
action . radioGroup = _value ;
2014-10-24 14:44:15 +02:00
} ;
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
2011-06-16 22:43:33 +02:00
action . set _shortcut = function ( _value ) {
2011-06-19 12:48:51 +02:00
if ( _value )
2011-06-16 22:43:33 +02:00
{
2011-06-19 12:48:51 +02:00
var sc = {
"keyCode" : - 1 ,
"shift" : false ,
"ctrl" : false ,
"alt" : false
2014-10-24 14:44:15 +02:00
} ;
2011-06-19 12:48:51 +02:00
2013-10-24 19:01:58 +02:00
if ( typeof _value == "object" && typeof _value . keyCode != "undefined" &&
typeof _value . caption != "undefined" )
2011-06-19 12:48:51 +02:00
{
sc . keyCode = _value . keyCode ;
2011-06-25 16:27:30 +02:00
sc . caption = _value . caption ;
2011-06-19 12:48:51 +02:00
sc . shift = ( typeof _value . shift == "undefined" ) ? false : _value . shift ;
sc . ctrl = ( typeof _value . ctrl == "undefined" ) ? false : _value . ctrl ;
sc . alt = ( typeof _value . alt == "undefined" ) ? false : _value . alt ;
}
2011-06-16 22:43:33 +02:00
2011-06-19 12:48:51 +02:00
this . shortcut = sc ;
}
else
2011-06-16 22:43:33 +02:00
{
2011-06-19 12:48:51 +02:00
this . shortcut = false ;
2011-06-16 22:43:33 +02:00
}
2014-10-24 14:44:15 +02:00
} ;
2011-06-16 22:43:33 +02:00
2011-02-26 20:21:55 +01:00
return action ;
}
var
_popupActionImpl = null ;
function getPopupImplementation ( )
{
if ( ! _popupActionImpl )
{
_popupActionImpl = new egwPopupActionImplementation ( ) ;
}
2014-10-24 14:44:15 +02:00
return _popupActionImpl ;
2011-02-26 20:21:55 +01:00
}
function egwPopupActionImplementation ( )
{
var ai = new egwActionImplementation ( ) ;
ai . type = "popup" ;
2014-11-19 18:59:16 +01:00
ai . auto _paste = true ;
2011-06-03 00:53:23 +02:00
/ * *
* Registers the handler for the default action
2014-10-24 14:44:15 +02:00
*
* @ param { DOMNode } _node
* @ param { function } _callback
* @ param { object } _context
* @ returns { boolean }
2011-06-03 00:53:23 +02:00
* /
2014-10-24 14:44:15 +02:00
ai . _registerDefault = function ( _node , _callback , _context )
{
2011-06-03 00:53:23 +02:00
var defaultHandler = function ( e ) {
2015-03-31 12:01:51 +02:00
// Prevent bubbling bound event on <a> tag, on touch devices
// a tag should be handled by default event
if ( egwIsMobile && e . target . tagName == "A" ) return true ;
2011-06-03 00:53:23 +02:00
if ( typeof document . selection != "undefined" && typeof document . selection . empty != "undefined" )
{
document . selection . empty ( ) ;
}
else if ( typeof window . getSelection != "undefined" )
{
var sel = window . getSelection ( ) ;
sel . removeAllRanges ( ) ;
}
2011-02-26 20:21:55 +01:00
2011-06-03 00:53:23 +02:00
_callback . call ( _context , "default" , ai ) ;
2011-03-16 18:50:25 +01:00
2015-01-12 17:17:29 +01:00
// Stop action from bubbling up to parents
e . stopPropagation ( ) ;
e . cancelBubble = true ;
2015-02-16 16:18:25 +01:00
2015-02-19 12:45:05 +01:00
// remove context menu if we are in mobile theme
// and intended to open the entry
if ( _egw _active _menu && e . which == 1 ) _egw _active _menu . hide ( ) ;
2011-06-03 00:53:23 +02:00
return false ;
2014-10-24 14:44:15 +02:00
} ;
2011-03-14 21:11:08 +01:00
2011-06-03 00:53:23 +02:00
if ( egwIsMobile ( ) ) {
2011-07-03 11:00:36 +02:00
$j ( _node ) . bind ( 'click' , defaultHandler ) ;
2011-06-03 00:53:23 +02:00
} else {
2011-06-03 13:21:36 +02:00
_node . ondblclick = defaultHandler ;
2011-06-03 00:53:23 +02:00
}
2014-10-24 14:44:15 +02:00
} ;
2011-03-14 21:11:08 +01:00
2011-06-16 15:43:46 +02:00
ai . _getDefaultLink = function ( _links ) {
var defaultAction = null ;
for ( var k in _links )
{
if ( _links [ k ] . actionObj [ "default" ] && _links [ k ] . enabled )
{
defaultAction = _links [ k ] . actionObj ;
break ;
}
}
return defaultAction ;
2014-10-24 14:44:15 +02:00
} ;
2011-06-16 15:43:46 +02:00
2011-06-19 12:48:51 +02:00
ai . _searchShortcut = function ( _key , _objs , _links ) {
for ( var i = 0 ; i < _objs . length ; i ++ )
{
var sc = _objs [ i ] . shortcut ;
if ( sc && sc . keyCode == _key . keyCode && sc . shift == _key . shift &&
sc . ctrl == _key . ctrl && sc . alt == _key . alt &&
_objs [ i ] . type == "popup" && ( typeof _links [ _objs [ i ] . id ] == "undefined" ||
_links [ _objs [ i ] . id ] . enabled ) )
{
return _objs [ i ] ;
}
var obj = this . _searchShortcut ( _key , _objs [ i ] . children , _links ) ;
if ( obj ) {
return obj ;
}
}
2014-10-24 14:44:15 +02:00
} ;
2011-06-19 12:48:51 +02:00
ai . _searchShortcutInLinks = function ( _key , _links ) {
var objs = [ ] ;
for ( var k in _links )
{
if ( _links [ k ] . enabled )
{
objs . push ( _links [ k ] . actionObj ) ;
}
}
return ai . _searchShortcut ( _key , objs , _links ) ;
2014-10-24 14:44:15 +02:00
} ;
2011-06-19 12:48:51 +02:00
2011-06-16 15:43:46 +02:00
/ * *
* Handles a key press
2014-10-24 14:44:15 +02:00
*
* @ param { object } _key
* @ param { type } _selected
* @ param { type } _links
* @ param { type } _target
* @ returns { Boolean }
2011-06-16 15:43:46 +02:00
* /
ai . _handleKeyPress = function ( _key , _selected , _links , _target ) {
2011-06-19 12:48:51 +02:00
// Handle the default
2011-06-16 15:43:46 +02:00
if ( _key . keyCode == EGW _KEY _ENTER && ! _key . ctrl && ! _key . shift && ! _key . alt ) {
var defaultAction = this . _getDefaultLink ( _links ) ;
if ( defaultAction )
{
defaultAction . execute ( _selected ) ;
2011-06-19 12:48:51 +02:00
return true ;
2011-06-16 15:43:46 +02:00
}
}
2011-06-19 12:48:51 +02:00
2013-04-09 18:13:10 +02:00
// Menu button
if ( _key . keyCode == EGW _KEY _MENU && ! _key . ctrl )
{
return this . doExecuteImplementation ( { posx : 0 , posy : 0 } , _selected , _links , _target ) ;
}
2014-10-24 14:44:15 +02:00
2013-04-09 18:13:10 +02:00
2011-06-19 12:48:51 +02:00
// Check whether the given shortcut exists
var obj = this . _searchShortcutInLinks ( _key , _links ) ;
if ( obj )
{
obj . execute ( _selected ) ;
return true ;
}
return false ;
2014-10-24 14:44:15 +02:00
} ;
2011-06-16 15:43:46 +02:00
2011-06-03 00:53:23 +02:00
/ * *
* Registers the handler for the context menu
2014-10-24 14:44:15 +02:00
*
* @ param { DOMNode } _node
* @ param { function } _callback
* @ param { object } _context
* @ returns { boolean }
2011-06-03 00:53:23 +02:00
* /
2014-10-24 14:44:15 +02:00
ai . _registerContext = function ( _node , _callback , _context )
{
2011-06-03 00:53:23 +02:00
var contextHandler = function ( e ) {
//Obtain the event object
if ( ! e )
{
e = window . event ;
}
2011-02-26 20:21:55 +01:00
2011-06-03 00:53:23 +02:00
if ( _egw _active _menu )
{
2014-10-24 13:15:33 +02:00
_egw _active _menu . hide ( ) ;
2011-06-03 00:53:23 +02:00
}
2014-11-27 17:57:57 +01:00
else if ( ! e . ctrlKey && e . which == 3 || e . which === 0 ) // tap event indicates by 0
2011-06-03 00:53:23 +02:00
{
2014-10-24 13:15:33 +02:00
var _xy = ai . _getPageXY ( e ) ;
2014-12-17 11:34:27 +01:00
var _implContext = { event : e , posx : _xy . posx , posy : _xy . posy } ;
_callback . call ( _context , _implContext , ai ) ;
2011-06-03 00:53:23 +02:00
}
2011-02-26 20:21:55 +01:00
2014-10-24 13:15:33 +02:00
e . cancelBubble = ! e . ctrlKey || e . which == 1 ;
if ( e . stopPropagation && e . cancelBubble )
2011-06-03 00:53:23 +02:00
{
e . stopPropagation ( ) ;
2011-02-26 20:21:55 +01:00
}
2014-10-24 13:15:33 +02:00
return ! e . cancelBubble ;
} ;
2011-06-03 00:53:23 +02:00
if ( egwIsMobile ( ) ) {
2011-07-03 11:00:36 +02:00
$j ( _node ) . bind ( 'taphold' , contextHandler ) ;
2011-06-03 00:53:23 +02:00
} else {
2013-04-09 18:13:10 +02:00
$j ( _node ) . on ( 'contextmenu' , contextHandler ) ;
2014-10-22 17:35:58 +02:00
}
2014-10-24 13:15:33 +02:00
} ;
2011-06-03 00:53:23 +02:00
ai . doRegisterAction = function ( _aoi , _callback , _context )
{
var node = _aoi . getDOMNode ( ) ;
2011-03-02 22:18:20 +01:00
2011-06-03 00:53:23 +02:00
if ( node )
{
this . _registerDefault ( node , _callback , _context ) ;
this . _registerContext ( node , _callback , _context ) ;
2011-03-02 22:18:20 +01:00
return true ;
2011-02-26 20:21:55 +01:00
}
2011-03-02 22:18:20 +01:00
return false ;
2014-10-24 14:44:15 +02:00
} ;
2011-02-26 20:21:55 +01:00
ai . doUnregisterAction = function ( _aoi )
{
//
2014-10-24 14:44:15 +02:00
} ;
2011-02-26 20:21:55 +01:00
2011-02-26 21:40:27 +01:00
/ * *
* Builds the context menu and shows it at the given position / DOM - Node .
2014-10-24 14:44:15 +02:00
*
* @ param { object } _context
* @ param { type } _selected
* @ param { type } _links
* @ param { type } _target
* @ returns { Boolean }
2011-02-26 21:40:27 +01:00
* /
2011-03-24 18:06:44 +01:00
ai . doExecuteImplementation = function ( _context , _selected , _links , _target )
2011-02-26 20:21:55 +01:00
{
2011-03-24 18:06:44 +01:00
if ( typeof _target == "undefined" )
{
_target = null ;
}
2014-11-10 18:50:58 +01:00
ai . _context = _context ;
2011-06-16 15:43:46 +02:00
if ( typeof _context == "object" && typeof _context . keyEvent == "object" )
{
return ai . _handleKeyPress ( _context . keyEvent , _selected , _links , _target ) ;
}
else if ( _context != "default" )
2011-02-26 21:40:27 +01:00
{
2011-03-14 21:11:08 +01:00
//Check whether the context has the posx and posy parameters
if ( ( typeof _context . posx != "number" || typeof _context . posy != "number" ) &&
typeof _context . id != "undefined" )
{
// Calculate context menu position from the given DOM-Node
var node = _context ;
2011-02-26 21:40:27 +01:00
2011-07-03 11:00:36 +02:00
x = $j ( node ) . offset ( ) . left ;
y = $j ( node ) . offset ( ) . top ;
2011-02-26 21:40:27 +01:00
2014-10-24 14:44:15 +02:00
_context = { "posx" : x , "posy" : y } ;
2011-03-14 21:11:08 +01:00
}
2011-03-24 18:06:44 +01:00
var menu = ai . _buildMenu ( _links , _selected , _target ) ;
2011-03-14 21:11:08 +01:00
menu . showAt ( _context . posx , _context . posy ) ;
2014-12-16 17:27:33 +01:00
// Bindings for copy to system clipboard
menu . instance . dhtmlxmenu . attachEvent ( "onShow" , function ( zoneId , ev ) {
var client = new ZeroClipboard ( $j ( '#' + this . idPrefix + 'egw_os_clipboard' , this . base ) ) ;
2015-02-16 16:18:25 +01:00
// We need to check if the browser settings for Flash player is set to click-to-play
// then prompting user in order to allow or block Flash player
jQuery ( '#global-zeroclipboard-html-bridge' ) . hover ( function ( ) {
var $zeroClip = jQuery ( this ) ;
if ( $zeroClip . hasClass ( 'flash-deactivated' ) )
{
$zeroClip . addClass ( 'flash-click-to-play-dialog' ) ;
egw . message ( "In order to copy to clipboard, you need to allow Flash player by clicking on gray box.\nYou can permanently allow Flash player for EGroupware in your browser settings." , "info" )
}
else
{
$zeroClip . removeClass ( 'flash-click-to-play-dialog' )
}
} ) ;
client . on ( {
error : function ( e ) {
// Detect if the Flash player is on click-to-play mode or is deactivated
if ( e . name == "flash-deactivated" )
{
// Indicate both flash-deactivated and click-to-play mode
jQuery ( '#global-zeroclipboard-html-bridge' ) . addClass ( 'flash-deactivated' ) ;
}
else
{
jQuery ( '#global-zeroclipboard-html-bridge' ) . removeClass ( 'flash-deactivated' ) ;
}
}
} )
2014-12-16 17:27:33 +01:00
client . on ( "copy" , function ( event ) {
2014-12-17 17:20:42 +01:00
event . clipboardData . setData ( 'text/plain' , $j ( _links . egw _os _clipboard . actionObj . data . target ) . text ( ) . trim ( ) ) ;
event . clipboardData . setData ( 'text/html' , $j ( _links . egw _os _clipboard . actionObj . data . target ) . html ( ) ) ;
2014-12-16 17:27:33 +01:00
} ) ;
} ) ;
2011-03-14 21:11:08 +01:00
return true ;
2011-02-26 21:40:27 +01:00
}
2011-03-14 21:11:08 +01:00
else
{
2011-06-16 15:43:46 +02:00
var defaultAction = ai . _getDefaultLink ( _links ) ;
2011-03-14 21:11:08 +01:00
if ( defaultAction )
{
defaultAction . execute ( _selected ) ;
}
}
2011-02-26 21:40:27 +01:00
2011-03-14 21:11:08 +01:00
return false ;
2014-10-24 14:44:15 +02:00
} ;
2011-02-26 20:21:55 +01:00
2011-02-26 21:40:27 +01:00
/ * *
2011-03-23 15:05:39 +01:00
* Groups and sorts the given action tree layer
2014-10-24 14:44:15 +02:00
*
* @ param { type } _layer
* @ param { type } _links
* @ param { type } _parentGroup
2011-02-26 21:40:27 +01:00
* /
2011-03-23 15:05:39 +01:00
ai . _groupLayers = function ( _layer , _links , _parentGroup )
2011-02-26 20:21:55 +01:00
{
2011-03-23 15:05:39 +01:00
// Seperate the multiple groups out of the layer
2011-02-26 20:21:55 +01:00
var link _groups = { } ;
2011-03-23 15:05:39 +01:00
for ( var i = 0 ; i < _layer . children . length ; i ++ )
2011-02-26 20:21:55 +01:00
{
2011-03-23 15:05:39 +01:00
var actionObj = _layer . children [ i ] . action ;
2011-02-26 20:21:55 +01:00
// Check whether the link group of the current element already exists,
// if not, create the group
2011-03-23 15:05:39 +01:00
var grp = actionObj . group ;
2011-02-26 20:21:55 +01:00
if ( typeof link _groups [ grp ] == "undefined" )
{
link _groups [ grp ] = [ ] ;
}
2011-03-23 15:05:39 +01:00
// Search the link data for this action object if none is found,
// visible and enabled = true is assumed
var visible = true ;
var enabled = true ;
if ( typeof _links [ actionObj . id ] != "undefined" )
{
visible = _links [ actionObj . id ] . visible ;
enabled = _links [ actionObj . id ] . enabled ;
}
2011-02-26 20:21:55 +01:00
// Insert the element in order
var inserted = false ;
2011-03-23 15:05:39 +01:00
var groupObj = {
"actionObj" : actionObj ,
"visible" : visible ,
"enabled" : enabled ,
"groups" : [ ]
} ;
for ( var j = 0 ; j < link _groups [ grp ] . length ; j ++ )
2011-02-26 20:21:55 +01:00
{
2011-03-23 15:05:39 +01:00
var elem = link _groups [ grp ] [ j ] . actionObj ;
if ( elem . order > actionObj . order )
2011-02-26 20:21:55 +01:00
{
inserted = true ;
2011-03-23 15:05:39 +01:00
link _groups [ grp ] . splice ( j , 0 , groupObj ) ;
2011-02-26 20:21:55 +01:00
break ;
}
}
// If the object hasn't been inserted, add it to the end of the list
if ( ! inserted )
{
2011-03-23 15:05:39 +01:00
link _groups [ grp ] . push ( groupObj ) ;
}
// If this child itself has children, group those elements too
if ( _layer . children [ i ] . children . length > 0 )
{
this . _groupLayers ( _layer . children [ i ] , _links , groupObj ) ;
2011-02-26 20:21:55 +01:00
}
}
2011-03-23 15:05:39 +01:00
// Transform the link_groups object into an sorted array
2011-02-26 20:21:55 +01:00
var groups = [ ] ;
2011-03-23 15:05:39 +01:00
2011-04-23 11:07:31 +02:00
for ( var k in link _groups )
2011-03-23 15:05:39 +01:00
{
2011-02-26 20:21:55 +01:00
groups . push ( { "grp" : k , "links" : link _groups [ k ] } ) ;
2011-03-23 15:05:39 +01:00
}
2011-02-26 20:21:55 +01:00
groups . sort ( function ( a , b ) {
2011-03-23 15:05:39 +01:00
var ia = parseInt ( a . grp ) ;
var ib = parseInt ( b . grp ) ;
return ( ia > ib ) ? 1 : ( ( ia < ib ) ? - 1 : 0 ) ;
2011-02-26 20:21:55 +01:00
} ) ;
2011-03-23 15:05:39 +01:00
// Append the groups to the groups2 array
var groups2 = [ ] ;
2011-03-25 14:12:24 +01:00
for ( var i = 0 ; i < groups . length ; i ++ )
2011-03-23 15:05:39 +01:00
{
2011-03-25 14:12:24 +01:00
groups2 . push ( groups [ i ] . links ) ;
2011-03-23 15:05:39 +01:00
}
_parentGroup . groups = groups2 ;
2014-10-24 14:44:15 +02:00
} ;
2011-03-23 15:05:39 +01:00
/ * *
* Build the menu layers
2014-10-24 14:44:15 +02:00
*
* @ param { type } _menu
* @ param { type } _groups
* @ param { type } _selected
* @ param { type } _enabled
* @ param { type } _target
2011-03-23 15:05:39 +01:00
* /
2011-03-24 18:06:44 +01:00
ai . _buildMenuLayer = function ( _menu , _groups , _selected , _enabled , _target )
2011-03-23 15:05:39 +01:00
{
2011-03-23 21:08:33 +01:00
var firstGroup = true ;
2011-03-23 15:05:39 +01:00
for ( var i = 0 ; i < _groups . length ; i ++ )
2011-02-26 20:21:55 +01:00
{
2011-03-23 21:08:33 +01:00
var firstElem = true ;
2011-02-26 20:21:55 +01:00
// Go through the elements of each group
2011-03-23 15:05:39 +01:00
for ( var j = 0 ; j < _groups [ i ] . length ; j ++ )
2011-02-26 20:21:55 +01:00
{
2011-03-23 15:05:39 +01:00
var link = _groups [ i ] [ j ] ;
2011-02-26 20:21:55 +01:00
if ( link . visible )
{
2011-03-23 21:08:33 +01:00
// Add an seperator after each group
if ( ! firstGroup && firstElem )
{
_menu . addItem ( "" , "-" ) ;
}
firstElem = false ;
2011-03-23 15:05:39 +01:00
var item = _menu . addItem ( link . actionObj . id , link . actionObj . caption ,
2011-02-26 20:21:55 +01:00
link . actionObj . iconUrl ) ;
2011-03-14 21:11:08 +01:00
item [ "default" ] = link . actionObj [ "default" ] ;
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
// As this code is also used when a drag-drop popup menu is built,
// we have to perform this check
if ( link . actionObj . type == "popup" )
{
item . set _hint ( link . actionObj . hint ) ;
item . set _checkbox ( link . actionObj . checkbox ) ;
item . set _checked ( link . actionObj . checked ) ;
item . set _groupIndex ( link . actionObj . radioGroup ) ;
2011-06-19 12:48:51 +02:00
if ( link . actionObj . shortcut )
{
var sc = link . actionObj . shortcut ;
2011-06-25 16:27:30 +02:00
item . set _shortcutCaption ( sc . caption ) ;
2011-06-19 12:48:51 +02:00
}
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
}
item . set _data ( link . actionObj ) ;
2011-03-23 15:05:39 +01:00
if ( link . enabled && _enabled )
2011-02-26 20:21:55 +01:00
{
item . set _onClick ( function ( elem ) {
2014-11-10 18:50:58 +01:00
// Pass the context
elem . data . menu _context = ai . _context ;
Fixed problems with executeActionImplementation when called from a container object, fixed problem with popup menu which did not open in some cases, added the ability to use the 'enabled' property of an action as an callback function (actionObject is passed as parameter), introduced egwFnct-class which consistently handles 'javaScript:fnct' strings, added 'allowOnMultiple':'only' setting, added 'hint', 'checkbox', 'checked', 'radioGroup' properties to popup actions, added 'setDefaultExecute' function to egwAction objects, which applies an handler to all objects which don't have a handler yet
2011-04-17 17:38:46 +02:00
// Copy the "checked" state
2011-04-17 19:55:37 +02:00
if ( typeof elem . data . checked != "undefined" )
{
elem . data . checked = elem . checked ;
}
elem . data . execute ( _selected , _target ) ;
if ( typeof elem . data . checkbox != "undefined" && elem . data . checkbox )
{
return elem . data . checked ;
}
2011-02-26 20:21:55 +01:00
} ) ;
}
else
{
item . set _enabled ( false ) ;
}
2011-03-23 15:05:39 +01:00
// Append the parent groups
if ( link . groups )
{
2011-03-24 18:06:44 +01:00
this . _buildMenuLayer ( item , link . groups , _selected , link . enabled , _target ) ;
2011-03-23 15:05:39 +01:00
}
2011-02-26 20:21:55 +01:00
}
}
2011-03-23 21:08:33 +01:00
firstGroup = firstGroup && firstElem ;
2011-02-26 20:21:55 +01:00
}
2014-10-24 14:44:15 +02:00
} ;
2011-03-23 15:05:39 +01:00
/ * *
* Builds the context menu from the given action links
2014-10-24 14:44:15 +02:00
*
* @ param { type } _links
* @ param { type } _selected
* @ param { type } _target
* @ returns { egwMenu | egwActionImplementation . _buildMenu . menu }
2011-03-23 15:05:39 +01:00
* /
2011-03-24 18:06:44 +01:00
ai . _buildMenu = function ( _links , _selected , _target )
2011-03-23 15:05:39 +01:00
{
// Build a tree containing all actions
var tree = { "root" : [ ] } ;
2014-10-30 23:18:37 +01:00
// Automatically add in Drag & Drop actions
2014-11-19 18:59:16 +01:00
if ( this . auto _paste )
{
this . _addCopyPaste ( _links , _selected ) ;
}
2014-10-30 23:18:37 +01:00
2011-04-23 11:07:31 +02:00
for ( var k in _links )
2011-03-23 15:05:39 +01:00
{
_links [ k ] . actionObj . appendToTree ( tree ) ;
}
2014-10-24 14:44:15 +02:00
// We need the dummy object container in order to pass the array by
2011-03-23 15:36:25 +01:00
// reference
2011-03-23 15:05:39 +01:00
var groups = {
"groups" : [ ]
} ;
if ( tree . root . length > 0 )
{
// Sort every action object layer by the given sort position and grouping
this . _groupLayers ( tree . root [ 0 ] , _links , groups ) ;
}
var menu = new egwMenu ( ) ;
// Build the menu layers
2011-03-24 18:06:44 +01:00
this . _buildMenuLayer ( menu , groups . groups , _selected , true , _target ) ;
2011-02-26 20:21:55 +01:00
return menu ;
2014-10-24 14:44:15 +02:00
} ;
2011-02-26 20:21:55 +01:00
2011-02-26 21:40:27 +01:00
ai . _getPageXY = function getPageXY ( event )
{
// document.body.scrollTop does not work in IE
var scrollTop = document . body . scrollTop ? document . body . scrollTop :
document . documentElement . scrollTop ;
var scrollLeft = document . body . scrollLeft ? document . body . scrollLeft :
document . documentElement . scrollLeft ;
return { 'posx' : ( event . clientX + scrollLeft ) , 'posy' : ( event . clientY + scrollTop ) } ;
2014-10-24 14:44:15 +02:00
} ;
2011-02-26 21:40:27 +01:00
2014-10-30 23:18:37 +01:00
/ * *
* Automagically add in context menu items for copy and paste from
* drag and drop actions , based on current clipboard and the accepted types
*
* @ param { object [ ] } _links Actions for inclusion in the menu
* @ param { egwActionObject [ ] } _selected Currently selected entries
* /
ai . _addCopyPaste = function ( _links , _selected )
{
// Get a list of drag & drop actions
var drag = _selected [ 0 ] . getSelectedLinks ( 'drag' ) . links ;
var drop = _selected [ 0 ] . getSelectedLinks ( 'drop' ) . links ;
// No drags & no drops means early exit
if ( ( ! drag || jQuery . isEmptyObject ( drag ) ) && ( ! drop || jQuery . isEmptyObject ( drop ) ) )
{
return ;
}
// Find existing actions so we don't get copies
var mgr = _selected [ 0 ] . manager ;
var copy _action = mgr . getActionById ( 'egw_copy' ) ;
2014-11-04 22:34:18 +01:00
var add _action = mgr . getActionById ( 'egw_copy_add' ) ;
2014-12-16 17:27:33 +01:00
var clipboard _action = mgr . getActionById ( 'egw_os_clipboard' ) ;
2014-10-30 23:18:37 +01:00
var paste _action = mgr . getActionById ( 'egw_paste' ) ;
2014-11-06 22:33:23 +01:00
// Fake UI so we can simulate the position of the drop
2014-12-17 11:34:27 +01:00
if ( this . _context . event )
2014-11-06 22:33:23 +01:00
{
var ui = {
position : { top : 0 , left : 0 } ,
offset : { top : 0 , left : 0 }
} ;
2014-12-17 11:34:27 +01:00
var event = this . _context . event . originalEvent ;
ui . position = { top : event . pageY , left : event . pageX } ;
ui . offset = { top : event . offsetY , left : event . offsetX } ;
2014-11-06 22:33:23 +01:00
}
2014-10-30 23:18:37 +01:00
// Create default copy menu action
if ( drag && ! jQuery . isEmptyObject ( drag ) )
{
// Don't re-add if it's there
if ( copy _action == null )
{
// Create a drag action that allows linking
copy _action = mgr . addAction ( 'popup' , 'egw_copy' , egw . lang ( 'Copy to clipboard' ) , egw . image ( 'copy' ) , function ( action , selected ) {
// Copied, now add to clipboard
var clipboard = {
type : [ ] ,
selected : [ ]
2014-10-31 09:31:23 +01:00
} ;
2014-10-30 23:18:37 +01:00
// When pasting we need to know the type of drag
for ( var k in drag )
{
if ( drag [ k ] . enabled && drag [ k ] . actionObj . dragType . length > 0 )
{
clipboard . type = clipboard . type . concat ( drag [ k ] . actionObj . dragType ) ;
}
}
2014-11-04 22:34:18 +01:00
clipboard . type = jQuery . unique ( clipboard . type ) ;
2014-10-30 23:18:37 +01:00
// egwAction is a circular structure and can't be stringified so just take what we want
// Hopefully that's enough for the action handlers
for ( var k in selected )
{
2014-10-31 09:31:23 +01:00
if ( selected [ k ] . id ) clipboard . selected . push ( { id : selected [ k ] . id , data : selected [ k ] . data } ) ;
2014-10-30 23:18:37 +01:00
}
// Save it in session
egw . setSessionItem ( 'phpgwapi' , 'egw_clipboard' , JSON . stringify ( clipboard ) ) ;
} , true ) ;
2014-11-18 00:57:15 +01:00
copy _action . group = 2.5 ;
2014-10-30 23:18:37 +01:00
}
2014-11-04 22:34:18 +01:00
if ( add _action == null )
{
// Create an action to add selected to clipboard
add _action = mgr . addAction ( 'popup' , 'egw_copy_add' , egw . lang ( 'Add to clipboard' ) , egw . image ( 'copy' ) , function ( action , selected ) {
// Copied, now add to clipboard
var clipboard = JSON . parse ( egw . getSessionItem ( 'phpgwapi' , 'egw_clipboard' ) ) || {
type : [ ] ,
selected : [ ]
} ;
// When pasting we need to know the type of drag
for ( var k in drag )
{
if ( drag [ k ] . enabled && drag [ k ] . actionObj . dragType . length > 0 )
{
clipboard . type = clipboard . type . concat ( drag [ k ] . actionObj . dragType ) ;
}
}
clipboard . type = jQuery . unique ( clipboard . type ) ;
// egwAction is a circular structure and can't be stringified so just take what we want
// Hopefully that's enough for the action handlers
for ( var k in selected )
{
if ( selected [ k ] . id ) clipboard . selected . push ( { id : selected [ k ] . id , data : selected [ k ] . data } ) ;
}
// Save it in session
egw . setSessionItem ( 'phpgwapi' , 'egw_clipboard' , JSON . stringify ( clipboard ) ) ;
} , true ) ;
2014-11-18 00:57:15 +01:00
add _action . group = 2.5 ;
2014-11-04 22:34:18 +01:00
}
2014-12-16 17:27:33 +01:00
if ( clipboard _action == null )
{
// Create an action to add selected to clipboard
clipboard _action = mgr . addAction ( 'popup' , 'egw_os_clipboard' , egw . lang ( 'Copy to OS clipboard' ) , egw . image ( 'copy' ) , function ( action ) {
2015-02-16 16:18:25 +01:00
2014-12-16 17:27:33 +01:00
} , true ) ;
clipboard _action . group = 2.5 ;
}
2014-12-17 11:34:27 +01:00
var os _clipboard _caption = this . _context . event . originalEvent . target . innerHTML . trim ( ) ;
2014-12-16 17:27:33 +01:00
clipboard _action . set _caption ( egw . lang ( 'Copy "%1"' , os _clipboard _caption . length > 20 ? os _clipboard _caption . substring ( 0 , 20 ) + '...' : os _clipboard _caption ) ) ;
2014-12-17 11:34:27 +01:00
clipboard _action . data . target = this . _context . event . originalEvent . target ;
2014-10-30 23:18:37 +01:00
if ( typeof _links [ copy _action . id ] == 'undefined' )
{
_links [ copy _action . id ] = {
"actionObj" : copy _action ,
"enabled" : true ,
"visible" : true ,
"cnt" : 0
} ;
}
2014-11-04 22:34:18 +01:00
if ( typeof _links [ add _action . id ] == 'undefined' )
{
_links [ add _action . id ] = {
"actionObj" : add _action ,
"enabled" : true ,
"visible" : true ,
"cnt" : 0
} ;
}
2014-12-16 17:27:33 +01:00
if ( typeof _links [ clipboard _action . id ] == 'undefined' )
{
_links [ clipboard _action . id ] = {
"actionObj" : clipboard _action ,
"enabled" : os _clipboard _caption . length > 0 ,
"visible" : os _clipboard _caption . length > 0 ,
"cnt" : 0
} ;
}
2014-10-30 23:18:37 +01:00
}
// Create default paste menu item
if ( drop && ! jQuery . isEmptyObject ( drop ) )
{
// Create paste action
// This injects the clipboard data and calls the original handler
var paste _exec = function ( action , selected ) {
// Add in clipboard as a sender
var clipboard = JSON . parse ( egw . getSessionItem ( 'phpgwapi' , 'egw_clipboard' ) ) ;
2014-11-06 22:33:23 +01:00
// Fake drop position
drop [ action . id ] . actionObj . ui = ui ;
2014-11-19 21:13:46 +01:00
// Set a flag so apps can tell the difference, if they need to
drop [ action . id ] . actionObj . paste = true ;
2014-10-30 23:18:37 +01:00
drop [ action . id ] . actionObj . execute ( clipboard . selected , selected [ 0 ] ) ;
2014-11-19 21:13:46 +01:00
drop [ action . id ] . actionObj . paste = false ;
2014-10-30 23:18:37 +01:00
} ;
2014-10-31 09:31:23 +01:00
var clipboard = JSON . parse ( egw . getSessionItem ( 'phpgwapi' , 'egw_clipboard' ) ) || {
type : [ ] ,
selected : [ ]
} ;
2014-10-30 23:18:37 +01:00
// Don't re-add if action already exists
if ( paste _action == null )
{
paste _action = mgr . addAction ( 'popup' , 'egw_paste' , egw . lang ( 'Paste' ) , egw . image ( 'editpaste' ) , paste _exec , true ) ;
2014-11-18 00:57:15 +01:00
paste _action . group = 2.5 ;
paste _action . order = 9 ;
2014-10-30 23:18:37 +01:00
paste _action . canHaveChildren . push ( 'drop' ) ;
}
// Set hint to something resembling current clipboard
var hint = egw . lang ( 'Clipboard' ) + ":\n[" + clipboard . type . join ( ',' ) + "]\n" ;
paste _action . set _hint ( hint ) ;
// Add titles of entries
for ( var i = 0 ; i < clipboard . selected . length ; i ++ )
{
var id = clipboard . selected [ i ] . id . split ( '::' ) ;
egw . link _title ( id [ 0 ] , id [ 1 ] , function ( title ) { if ( title ) this . hint += title + "\n" ; } , paste _action ) ;
}
// Add into links so it's included in menu
if ( typeof _links [ paste _action . id ] == 'undefined' )
{
_links [ paste _action . id ] = {
"actionObj" : paste _action ,
"enabled" : false ,
"visible" : clipboard != null ,
"cnt" : 0
} ;
}
while ( paste _action . children . length > 0 )
{
paste _action . children [ 0 ] . remove ( ) ;
}
// If nothing [valid] in the clipboard, don't bother with children
if ( clipboard == null || typeof clipboard . type != 'object' )
{
return ;
}
// Add in actual actions as children
for ( var k in drop )
{
// Add some choices - need to be a copy, or they interfere with
// the original
var drop _clone = jQuery . extend ( { } , drop [ k ] . actionObj ) ;
drop _clone . parent = paste _action ;
drop _clone . onExecute = new egwFnct ( this , null , [ ] ) ;
drop _clone . set _onExecute ( paste _exec ) ;
paste _action . children . push ( drop _clone ) ;
paste _action . allowOnMultiple = paste _action . allowOnMultiple && drop _clone . allowOnMultiple ;
_links [ k ] = jQuery . extend ( { } , drop [ k ] ) ;
_links [ k ] . actionObj = drop _clone ;
// Drop is allowed if clipboard types intersect drop types
_links [ k ] . enabled = false ;
_links [ k ] . visible = false ;
for ( var i = 0 ; i < drop _clone . acceptedTypes . length ; i ++ )
{
if ( clipboard . type . indexOf ( drop _clone . acceptedTypes [ i ] ) != - 1 )
{
_links [ paste _action . id ] . enabled = true ;
_links [ k ] . enabled = true ;
_links [ k ] . visible = true ;
break ;
}
}
}
}
} ;
2011-02-26 20:21:55 +01:00
return ai ;
}