2011-09-08 01:32:24 +02:00
/ * *
2013-04-13 21:00:13 +02:00
* EGroupware eTemplate2 - JS Link object
2011-09-08 01:32:24 +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 2011 Nathan Gray
* @ version $Id$
* /
"use strict" ;
/ * e g w : u s e s
jquery . jquery ;
jquery . jquery - ui ;
et2 _core _inputWidget ;
et2 _core _valueWidget ;
2012-06-19 20:59:53 +02:00
// Include menu system for list context menu
2013-04-13 21:00:13 +02:00
egw _action . egw _menu _dhtmlx ;
2011-09-08 01:32:24 +02:00
* /
/ * *
* UI widgets for Egroupware linking system
2014-02-11 13:17:08 +01:00
*
2013-04-13 21:00:13 +02:00
* @ augments et2 _inputWidget
2014-02-11 13:17:08 +01:00
* /
2013-04-13 21:00:13 +02:00
var et2 _link _to = et2 _inputWidget . extend (
{
2011-09-08 01:32:24 +02:00
attributes : {
2013-02-07 17:01:41 +01:00
"only_app" : {
2011-09-08 01:32:24 +02:00
"name" : "Application" ,
"type" : "string" ,
2011-09-14 22:36:39 +02:00
"default" : "" ,
2013-02-07 17:01:41 +01:00
"description" : "Limit to just this one application - hides app selection"
} ,
"application_list" : {
"name" : "Application list" ,
"type" : "any" ,
"default" : "" ,
2011-09-08 01:32:24 +02:00
"description" : "Limit to the listed application or applications (comma seperated)"
} ,
"blur" : {
"name" : "Placeholder" ,
"type" : "string" ,
"default" : "" ,
2014-05-07 14:23:02 +02:00
"description" : "This text get displayed if an input-field is empty and does not have the input-focus (blur). It can be used to show a default value or a kind of help-text." ,
translate : true
2011-09-08 01:32:24 +02:00
} ,
2011-09-14 02:06:04 +02:00
"no_files" : {
"name" : "No files" ,
"type" : "boolean" ,
"default" : false ,
"description" : "Suppress attach-files"
} ,
"search_label" : {
"name" : "Search label" ,
"type" : "string" ,
"default" : "" ,
"description" : "Label to use for search"
} ,
"link_label" : {
"name" : "Link label" ,
"type" : "string" ,
2014-02-11 13:17:08 +01:00
"default" : "Link" ,
2011-09-14 02:06:04 +02:00
"description" : "Label for the link button"
2012-07-24 01:54:16 +02:00
} ,
"value" : {
// Could be string or int if application is provided, or an Object
"type" : "any"
2011-09-14 02:06:04 +02:00
}
2011-09-08 01:32:24 +02:00
} ,
2013-04-13 21:00:13 +02:00
/ * *
* Constructor
2014-02-11 13:17:08 +01:00
*
2013-04-13 21:00:13 +02:00
* @ memberOf et2 _link _to
* /
2011-09-08 01:32:24 +02:00
init : function ( ) {
this . _super . apply ( this , arguments ) ;
this . div = null ;
2011-09-13 01:43:39 +02:00
2011-09-09 02:05:46 +02:00
this . link _button = null ;
this . status _span = null ;
2014-04-15 13:34:50 +02:00
2011-09-13 01:43:39 +02:00
this . link _entry = null ;
this . file _upload = null ;
2011-09-08 01:32:24 +02:00
this . createInputWidget ( ) ;
} ,
destroy : function ( ) {
2011-09-09 02:05:46 +02:00
this . link _button = null ;
this . status _span = null ;
2014-04-15 13:34:50 +02:00
2011-09-13 01:43:39 +02:00
this . link _entry . destroy ( ) ;
this . link _entry = null ;
2011-09-09 02:05:46 +02:00
this . file _upload . destroy ( ) ;
this . file _upload = null ;
2011-09-13 01:43:39 +02:00
this . div = null ;
2012-03-01 11:13:12 +01:00
this . _super . apply ( this , arguments ) ;
2011-09-09 02:05:46 +02:00
} ,
2011-09-08 01:32:24 +02:00
2011-09-09 02:05:46 +02:00
/ * *
2011-09-13 01:43:39 +02:00
* Override to provide proper node for sub widgets to go in
2014-04-15 13:34:50 +02:00
*
* @ param { Object } _sender
2011-09-09 02:05:46 +02:00
* /
getDOMNode : function ( _sender ) {
if ( _sender == this ) {
return this . div [ 0 ] ;
2011-09-13 01:43:39 +02:00
} else if ( _sender . _type == 'link-entry' ) {
return this . link _div [ 0 ] ;
} else if ( _sender . _type == 'file' ) {
2011-09-09 02:05:46 +02:00
return this . file _div [ 0 ] ;
2013-10-01 17:40:14 +02:00
} else if ( _sender . _type == 'vfs-select' ) {
return this . filemanager _button [ 0 ] ;
2011-09-09 02:05:46 +02:00
}
2011-09-08 01:32:24 +02:00
} ,
createInputWidget : function ( ) {
2011-09-09 02:05:46 +02:00
this . div = $j ( document . createElement ( "div" ) ) . addClass ( "et2_link_to" ) ;
2011-09-08 01:32:24 +02:00
2011-09-13 01:43:39 +02:00
// One common link button
this . link _button = $j ( document . createElement ( "button" ) )
2012-03-02 11:44:56 +01:00
. text ( this . egw ( ) . lang ( this . options . link _label ) )
2011-09-13 01:43:39 +02:00
. appendTo ( this . div ) . hide ( )
. click ( this , this . createLink ) ;
2014-02-11 13:17:08 +01:00
2011-09-13 01:43:39 +02:00
// Span for indicating status
this . status _span = $j ( document . createElement ( "span" ) )
. appendTo ( this . div ) . addClass ( "status" ) . hide ( ) ;
// Need a div for link-to widget
2013-03-20 19:26:18 +01:00
this . link _div = $j ( document . createElement ( "div" ) )
. css ( "margin-bottom" , "1ex" )
// Leave room for link button
. css ( "width" , "89%" )
. appendTo ( this . div ) ;
2011-09-13 01:43:39 +02:00
2012-06-27 01:01:04 +02:00
// Filemanager link popup
2013-10-01 17:40:14 +02:00
this . filemanager _button = $j ( document . createElement ( "div" ) ) . appendTo ( this . div ) ;
2014-02-11 13:17:08 +01:00
2011-09-13 01:43:39 +02:00
// Need a div for file upload widget
this . file _div = $j ( document . createElement ( "div" ) ) . appendTo ( this . div ) ;
this . setDOMNode ( this . div [ 0 ] ) ;
} ,
doLoadingFinished : function ( ) {
this . _super . apply ( this , arguments ) ;
var self = this ;
2013-10-17 00:39:41 +02:00
if ( this . link _entry && this . vfs _select && this . file _upload )
{
// Already done
return false ;
}
2014-02-11 13:17:08 +01:00
2011-09-13 01:43:39 +02:00
// Link-to
var link _entry _attrs = {
id : this . id + '_link_entry' ,
2013-02-07 17:01:41 +01:00
only _app : this . options . only _app ,
2013-03-20 19:26:18 +01:00
application _list : this . options . application _list ,
2012-03-02 11:44:56 +01:00
blur : this . options . search _label ? this . options . search _label : this . egw ( ) . lang ( 'Search...' ) ,
2014-04-15 13:34:50 +02:00
query : function ( ) { self . link _button . hide ( ) ; return true ; } ,
select : function ( ) { self . link _button . show ( ) ; return true ; }
2013-04-13 21:00:13 +02:00
} ;
2011-09-13 01:43:39 +02:00
this . link _entry = et2 _createWidget ( "link-entry" , link _entry _attrs , this ) ;
2013-10-01 17:40:14 +02:00
// Filemanager select
var select _attrs = {
method : 'etemplate_widget_link::link_existing' ,
method _id : function ( ) { return self . options . value . to _app + ':' + self . options . value . to _id ; } ,
button _label : egw . lang ( 'Link' )
2014-04-15 13:34:50 +02:00
} ;
2013-10-01 17:40:14 +02:00
this . vfs _select = et2 _createWidget ( "vfs-select" , select _attrs , this ) ;
$j ( this . vfs _select . getDOMNode ( ) ) . change ( function ( ) {
2014-01-14 13:11:13 +01:00
var values = true ;
// If entry not yet saved, store for linking on server
if ( ! self . options . value . to _id || typeof self . options . value . to _id == 'object' )
{
values = self . options . value . to _id || { } ;
var files = self . vfs _select . getValue ( ) ;
for ( var i = 0 ; i < files . length ; i ++ )
{
values [ 'link:' + files [ i ] ] = {
app : 'link' ,
id : files [ i ] ,
type : 'unknown' ,
icon : 'link' ,
remark : '' ,
title : files [ i ]
} ;
}
}
self . _link _result ( values ) ;
2013-10-01 17:40:14 +02:00
} ) ;
2014-02-11 13:17:08 +01:00
2011-09-13 01:43:39 +02:00
// File upload
var file _attrs = {
multiple : true ,
id : this . id + '_file' ,
2014-02-11 13:17:08 +01:00
2013-08-13 22:46:23 +02:00
// Make the whole template a drop target
2013-08-27 19:26:02 +02:00
drop _target : this . getInstanceManager ( ) . DOMContainer . getAttribute ( "id" ) ,
2014-02-11 13:17:08 +01:00
2013-08-13 22:46:23 +02:00
// Change to this tab when they drop
onStart : function ( event , file _count ) {
// Find the tab widget, if there is one
var tabs = self ;
do {
tabs = tabs . _parent ;
} while ( tabs != self . getRoot ( ) && tabs . _type != 'tabbox' ) ;
if ( tabs != self . getRoot ( ) )
{
// Find the tab index
for ( var i = 0 ; i < tabs . tabData . length ; i ++ )
{
// Find the tab
if ( tabs . tabData [ i ] . contentDiv . has ( self . div ) . length )
{
tabs . setActiveTab ( i ) ;
break ;
}
}
}
return true ;
} ,
2011-09-13 01:43:39 +02:00
onFinish : function ( event , file _count ) {
2012-04-24 18:33:56 +02:00
event . data = self ;
2011-09-13 01:43:39 +02:00
self . filesUploaded ( event ) ;
2012-04-24 18:33:56 +02:00
// Auto-link uploaded files
2014-01-14 11:16:36 +01:00
self . createLink ( event ) ;
2011-09-13 01:43:39 +02:00
}
} ;
this . file _upload = et2 _createWidget ( "file" , file _attrs , this ) ;
return true ;
} ,
getValue : function ( ) {
return this . options . value ;
} ,
filesUploaded : function ( event ) {
var self = this ;
this . link _button . show ( ) ;
} ,
/ * *
* Create a link using the current internal values
2014-04-15 13:34:50 +02:00
*
* @ param { Object } event
2011-09-13 01:43:39 +02:00
* /
createLink : function ( event ) {
// Disable link button
event . data . link _button . attr ( "disabled" , true ) ;
var values = event . data . options . value ;
var self = event . data ;
var links = [ ] ;
// Links to other entries
event . data = self . link _entry ;
self . link _entry . createLink ( event , links ) ;
2014-04-15 13:34:50 +02:00
2011-09-13 01:43:39 +02:00
// Files
2013-06-05 01:05:31 +02:00
if ( ! self . options . no _files )
{
for ( var file in self . file _upload . options . value ) {
2014-02-11 13:17:08 +01:00
2013-06-05 01:05:31 +02:00
links . push ( {
app : 'file' ,
id : file ,
name : self . file _upload . options . value [ file ] . name ,
type : self . file _upload . options . value [ file ] . type ,
remark : jQuery ( "li[file='" + self . file _upload . options . value [ file ] . name + "'] > input" , self . file _upload . progress )
. filter ( function ( ) { return jQuery ( this ) . attr ( "placeholder" ) != jQuery ( this ) . val ( ) ; } ) . val ( )
} ) ;
}
2011-09-13 01:43:39 +02:00
}
2013-11-12 21:25:04 +01:00
if ( links . length == 0 )
{
return ;
}
2014-02-11 13:17:08 +01:00
2013-11-14 00:47:01 +01:00
var request = egw . json ( self . egw ( ) . getAppName ( ) + ".etemplate_widget_link.ajax_link.etemplate" ,
2011-09-13 01:43:39 +02:00
[ values . to _app , values . to _id , links ] ,
2013-09-10 19:57:41 +02:00
self . _link _result ,
self ,
true ,
self
2011-09-13 01:43:39 +02:00
) ;
2013-09-10 19:57:41 +02:00
request . sendRequest ( ) ;
2011-09-13 01:43:39 +02:00
} ,
/ * *
* Sent some links , server has a result
2014-04-15 13:34:50 +02:00
*
* @ param { Object } success
2011-09-13 01:43:39 +02:00
* /
_link _result : function ( success ) {
if ( success ) {
this . link _button . hide ( ) . attr ( "disabled" , false ) ;
2013-08-27 19:26:02 +02:00
this . status _span . removeClass ( "error" ) . addClass ( "success" ) ;
2011-09-13 01:43:39 +02:00
this . status _span . fadeIn ( ) . delay ( 1000 ) . fadeOut ( ) ;
delete this . options . value . app ;
delete this . options . value . id ;
for ( var file in this . file _upload . options . value ) {
delete this . file _upload . options . value [ file ] ;
}
this . file _upload . progress . empty ( ) ;
2011-09-14 02:06:04 +02:00
2013-10-10 19:00:30 +02:00
// Server says it's OK, but didn't store - we'll send this again on submit
2013-10-11 10:41:24 +02:00
// This happens if you link to something before it's saved to the DB
2014-01-14 11:16:36 +01:00
if ( typeof success == "object" )
2013-10-10 19:00:30 +02:00
{
2013-10-11 10:41:24 +02:00
// Save as appropriate in value
if ( typeof this . options . value != "object" )
{
this . options . value = { } ;
}
2014-01-14 11:16:36 +01:00
this . options . value . to _id = success ;
for ( var link in success )
2013-10-11 10:41:24 +02:00
{
2013-10-10 19:00:30 +02:00
// Icon should be in registry
2014-01-14 11:16:36 +01:00
if ( typeof success [ link ] . icon == 'undefined' )
{
success [ link ] . icon = egw . link _get _registry ( success [ link ] . app , 'icon' ) ;
// No icon, try by mime type - different place for un-saved entries
if ( success [ link ] . icon == false && success [ link ] . id . type )
{
// Triggers icon by mime type, not thumbnail or app
success [ link ] . type = success [ link ] . id . type ;
success [ link ] . icon = true ;
}
}
// Special handling for file - if not existing, we can't ask for title
if ( success [ link ] . app == 'file' && typeof success [ link ] . title == 'undefined' )
{
success [ link ] . title = success [ link ] . id . name || '' ;
}
}
2013-10-10 19:00:30 +02:00
}
2011-09-14 02:06:04 +02:00
// Look for a link-list with the same ID, refresh it
var self = this ;
2013-10-10 19:00:30 +02:00
var list _widget = null ;
2011-09-14 02:06:04 +02:00
this . getRoot ( ) . iterateOver (
function ( widget ) {
if ( widget . id == self . id ) {
2013-10-10 19:00:30 +02:00
list _widget = widget ;
if ( success === true )
{
widget . _get _links ( ) ;
}
2011-09-14 02:06:04 +02:00
}
} ,
this , et2 _link _list
) ;
2014-02-11 13:17:08 +01:00
2014-01-14 11:16:36 +01:00
// If there's an array of data (entry is not yet saved), updating the list will
// not work, so add them in explicitly.
if ( list _widget && success )
2013-10-10 19:00:30 +02:00
{
2014-01-14 11:16:36 +01:00
// Clear list
list _widget . set _value ( null ) ;
// Add temp links in
for ( var link _id in success )
{
var link = success [ link _id ] ;
if ( typeof link . title == 'undefined' )
{
// Callback to server for title
egw . link _title ( link . app , link . id , function ( title ) {
link . title = title ;
list _widget . _add _link ( link ) ;
} ) ;
}
else
{
// Add direct
list _widget . _add _link ( link ) ;
}
}
2013-10-10 19:00:30 +02:00
}
2011-09-13 01:43:39 +02:00
}
2013-08-27 19:26:02 +02:00
else
{
this . status _span . removeClass ( "success" ) . addClass ( "error" )
. fadeIn ( ) ;
}
this . div . trigger ( 'link.et2_link_to' , success ) ;
2013-06-05 01:05:31 +02:00
} ,
set _no _files : function ( no _files )
{
if ( no _files )
{
this . file _div . hide ( ) ;
this . filemanager _button . hide ( ) ;
}
else
{
this . file _div . show ( ) ;
this . filemanager _button . show ( ) ;
}
this . options . no _files = no _files ;
2011-09-13 01:43:39 +02:00
}
} ) ;
et2 _register _widget ( et2 _link _to , [ "link-to" ] ) ;
2013-04-13 21:00:13 +02:00
/ * *
* @ augments et2 _selectbox
* /
var et2 _link _apps = et2 _selectbox . extend (
{
2011-09-14 22:36:39 +02:00
attributes : {
2013-02-07 17:01:41 +01:00
"only_app" : {
2011-09-14 22:36:39 +02:00
"name" : "Application" ,
"type" : "string" ,
"default" : "" ,
2013-02-07 17:01:41 +01:00
"description" : "Limit to just this one application - hides app selection"
} ,
"application_list" : {
"name" : "Application list" ,
"type" : "any" ,
"default" : "" ,
2011-09-14 22:36:39 +02:00
"description" : "Limit to the listed application or applications (comma seperated)"
}
} ,
2013-04-13 21:00:13 +02:00
/ * *
* Constructor
2014-02-11 13:17:08 +01:00
*
2013-04-13 21:00:13 +02:00
* @ memberOf et2 _link _apps
* /
2011-09-14 22:36:39 +02:00
init : function ( ) {
this . _super . apply ( this , arguments ) ;
2013-11-04 20:00:43 +01:00
if ( this . options . select _options != null )
{
// Preset to last application
if ( ! this . options . value )
{
2014-02-11 13:54:56 +01:00
this . set _value ( egw . preference ( 'link_app' , this . egw ( ) . getAppName ( ) ) ) ;
2013-11-04 20:00:43 +01:00
}
// Register to update preference
this . input . on ( "click" , jQuery . proxy ( function ( ) {
2014-02-11 13:54:56 +01:00
egw . set _preference ( this . options . value . to _app || this . egw ( ) . getAppName ( ) , 'link_app' , this . getValue ( ) ) ;
2013-11-04 20:00:43 +01:00
} ) , this ) ;
}
} ,
2014-02-11 13:17:08 +01:00
2013-11-04 20:00:43 +01:00
/ * *
* We get some minor speedups by overriding parent searching and directly setting select options
2014-04-15 13:34:50 +02:00
*
* @ param { Array } _attrs an array of attributes
2013-11-04 20:00:43 +01:00
* /
transformAttributes : function ( _attrs ) {
2011-09-14 22:36:39 +02:00
var select _options = { } ;
// Limit to one app
2013-11-04 20:00:43 +01:00
if ( _attrs . only _app ) {
2013-02-07 17:01:41 +01:00
select _options [ _attrs . only _app ] = this . egw ( ) . lang ( _attrs . only _app ) ;
2011-09-14 22:36:39 +02:00
} else {
2013-11-04 20:00:43 +01:00
select _options = _attrs . application _list ? _attrs . application _list : egw . link _app _list ( 'query' ) ;
2011-09-14 22:36:39 +02:00
}
2013-11-04 20:00:43 +01:00
_attrs . select _options = select _options ;
this . _super . apply ( this , arguments ) ;
2011-09-14 22:36:39 +02:00
}
} ) ;
et2 _register _widget ( et2 _link _apps , [ "link-apps" ] ) ;
2013-04-13 21:00:13 +02:00
/ * *
* @ augments et2 _inputWidget
* /
var et2 _link _entry = et2 _inputWidget . extend (
{
2011-09-13 01:43:39 +02:00
attributes : {
2012-06-20 01:30:07 +02:00
"value" : {
"type" : "any" ,
"default" : { }
} ,
2013-02-06 09:06:11 +01:00
"only_app" : {
2011-09-13 01:43:39 +02:00
"name" : "Application" ,
"type" : "string" ,
2011-09-14 22:36:39 +02:00
"default" : "" ,
2013-02-06 09:06:11 +01:00
"description" : "Limit to just this one application - hides app selection"
} ,
"application_list" : {
"name" : "Application list" ,
"type" : "any" ,
"default" : "" ,
"description" : "Limit to the listed applications (comma seperated)"
2011-09-13 01:43:39 +02:00
} ,
"blur" : {
"name" : "Placeholder" ,
"type" : "string" ,
2012-04-09 20:56:04 +02:00
"default" : et2 _no _init ,
2014-05-07 14:23:02 +02:00
"description" : "This text get displayed if an input-field is empty and does not have the input-focus (blur). It can be used to show a default value or a kind of help-text." ,
translate : true
2011-09-13 01:43:39 +02:00
} ,
"query" : {
"name" : "Query callback" ,
"type" : "any" ,
"default" : false ,
"description" : "Callback before query to server. Must return true, or false to abort query."
} ,
"select" : {
"name" : "Select callback" ,
"type" : "any" ,
"default" : false ,
"description" : "Callback when user selects an option. Must return true, or false to abort normal action."
2014-04-15 13:34:50 +02:00
}
2011-09-13 01:43:39 +02:00
} ,
2013-02-06 09:06:11 +01:00
legacyOptions : [ "only_app" , "application_list" ] ,
2014-03-26 18:55:50 +01:00
search _timeout : 500 , //ms after change to send query
2011-09-13 01:43:39 +02:00
minimum _characters : 2 , // Don't send query unless there's at least this many chars
2013-04-13 21:00:13 +02:00
/ * *
* Constructor
2014-02-11 13:17:08 +01:00
*
2013-04-13 21:00:13 +02:00
* @ memberOf et2 _link _entry
* /
2011-09-13 01:43:39 +02:00
init : function ( ) {
this . _super . apply ( this , arguments ) ;
this . search = null ;
this . app _select = null ;
2013-05-29 17:26:22 +02:00
this . _oldValue = {
id : null ,
app : this . options . value && this . options . value . app ? this . options . value . app : this . options . only _app
} ;
2011-09-13 01:43:39 +02:00
2013-03-20 19:26:18 +01:00
if ( typeof this . options . value == 'undefined' || this . options . value == null )
{
this . options . value = { } ;
}
2011-09-13 01:43:39 +02:00
this . cache = { } ;
2014-03-26 18:55:50 +01:00
this . request = null ;
2011-09-13 01:43:39 +02:00
this . createInputWidget ( ) ;
} ,
destroy : function ( ) {
this . _super . apply ( this , arguments ) ;
this . div = null ;
2013-07-20 15:51:12 +02:00
if ( this . search . data ( "ui-autocomplete" ) )
{
this . search . autocomplete ( "destroy" ) ;
}
2011-09-13 01:43:39 +02:00
this . search = null ;
this . app _select = null ;
2014-03-26 18:55:50 +01:00
this . request = null ;
2011-09-13 01:43:39 +02:00
} ,
createInputWidget : function ( ) {
var self = this ;
this . div = $j ( document . createElement ( "div" ) ) . addClass ( "et2_link_entry" ) ;
2011-09-08 01:32:24 +02:00
// Application selection
this . app _select = $j ( document . createElement ( "select" ) ) . appendTo ( this . div )
2013-11-12 21:25:04 +01:00
. val ( this . options . value . app || '' )
2011-09-08 01:32:24 +02:00
. change ( function ( e ) {
2013-10-21 18:32:07 +02:00
// Clear cache when app changes
self . cache = { } ;
2014-02-11 13:17:08 +01:00
2013-10-21 18:32:07 +02:00
// Update preference with new value
2014-02-11 18:30:56 +01:00
egw . set _preference ( self . options . value . to _app || self . egw ( ) . getAppName ( ) , 'link_app' , self . app _select . val ( ) ) ;
2014-02-11 13:17:08 +01:00
2012-06-11 18:35:46 +02:00
if ( typeof self . options . value != 'object' ) self . options . value = { } ;
2011-09-09 02:05:46 +02:00
self . options . value . app = self . app _select . val ( ) ;
2012-07-11 21:01:06 +02:00
} ) ;
2011-10-18 21:15:32 +02:00
var opt _count = 0 ;
2011-09-08 01:32:24 +02:00
for ( var key in this . options . select _options ) {
2011-10-18 21:15:32 +02:00
opt _count ++ ;
2011-09-08 01:32:24 +02:00
var option = $j ( document . createElement ( "option" ) )
. attr ( "value" , key )
. text ( this . options . select _options [ key ] ) ;
option . appendTo ( this . app _select ) ;
}
2014-02-11 13:17:08 +01:00
if ( this . options . only _app )
2011-10-18 21:15:32 +02:00
{
2013-05-29 17:26:22 +02:00
this . app _select . val ( this . options . only _app ) ;
2011-10-18 21:15:32 +02:00
this . app _select . hide ( ) ;
2012-04-24 23:27:48 +02:00
this . div . addClass ( "no_app" ) ;
2011-10-18 21:15:32 +02:00
}
2011-09-08 01:32:24 +02:00
// Search input
2014-02-11 13:17:08 +01:00
this . search = $j ( document . createElement ( "input" ) )
2012-04-11 17:43:33 +02:00
// .attr("type", "search") // Fake it for all browsers below
2013-02-06 09:06:11 +01:00
. focus ( function ( ) { if ( ! self . options . only _app ) {
2012-04-06 00:30:06 +02:00
// Adjust width, leave room for app select & link button
2012-04-24 23:27:48 +02:00
self . div . removeClass ( "no_app" ) ; self . app _select . show ( ) ;
2012-04-06 00:30:06 +02:00
} } )
2011-09-09 02:05:46 +02:00
. appendTo ( this . div ) ;
2011-09-08 01:32:24 +02:00
2012-03-02 11:44:56 +01:00
this . set _blur ( this . options . blur ? this . options . blur : this . egw ( ) . lang ( "search" ) , this . search ) ;
2014-02-11 13:17:08 +01:00
2011-09-09 02:05:46 +02:00
// Autocomplete
2011-09-08 01:32:24 +02:00
this . search . autocomplete ( {
2014-03-26 18:55:50 +01:00
source : function ( request , response ) {
return self . query ( request , response ) ;
} ,
2014-01-20 14:59:58 +01:00
select : function ( event , item ) {
event . data = self ;
// Correct changed value from server
item . item . value = item . item . value . trim ( ) ;
self . select ( event , item ) ;
return false ;
} ,
2014-02-11 13:17:08 +01:00
focus : function ( event , item ) {
event . stopPropagation ( ) ;
2011-09-08 01:32:24 +02:00
self . search . val ( item . item . label ) ;
return false ;
} ,
minLength : self . minimum _characters ,
2014-03-26 18:55:50 +01:00
delay : self . search _timeout ,
2013-07-20 19:20:55 +02:00
disabled : self . options . disabled ,
appendTo : self . div
2011-09-08 01:32:24 +02:00
} ) ;
2014-02-11 13:17:08 +01:00
2012-06-06 20:47:04 +02:00
// Custom display (colors)
2013-07-19 11:31:00 +02:00
this . search . data ( "uiAutocomplete" ) . _renderItem = function ( ul , item ) {
2012-06-06 20:47:04 +02:00
var li = jQuery ( document . createElement ( 'li' ) )
. data ( "item.autocomplete" , item ) ;
var extra = { } ;
// Extra stuff
if ( typeof item . label == 'object' ) {
extra = item . label ;
item . label = extra . label ? extra . label : extra ;
if ( extra [ 'style.backgroundColor' ] || extra . color )
{
li . css ( 'backgroundColor' , extra . color ? extra . color : extra [ 'style.backgroundColor' ] ) ;
}
// Careful with this, some browsers may have trouble loading all at once, which can slow display
if ( extra . icon )
{
var img = self . egw ( ) . image ( extra . icon ) ;
if ( img )
{
jQuery ( document . createElement ( "img" ) )
. attr ( "src" , img )
. css ( "float" , "right" )
. appendTo ( li ) ;
}
}
}
// Normal stuff
li . append ( jQuery ( "<a></a>" ) . text ( item . label ) )
. appendTo ( ul ) ;
return li ;
} ;
2011-09-09 02:05:46 +02:00
2012-04-06 00:57:38 +02:00
// Bind to enter key to start search early
this . search . keydown ( function ( e ) {
var keycode = ( e . keyCode ? e . keyCode : e . which ) ;
2014-02-24 21:00:34 +01:00
if ( keycode == '13' && ! self . processing )
2012-04-06 00:57:38 +02:00
{
self . search . autocomplete ( "option" , "minLength" , 0 ) ;
self . search . autocomplete ( "search" ) ;
self . search . autocomplete ( "option" , "minLength" , self . minimum _characters ) ;
e . stopPropagation ( ) ;
}
} ) ;
2012-04-11 17:43:33 +02:00
// Clear / last button
2012-04-25 00:00:18 +02:00
this . clear = $j ( document . createElement ( "span" ) )
2012-05-03 00:28:23 +02:00
. addClass ( "ui-icon ui-icon-close" )
2012-04-11 17:43:33 +02:00
. click ( function ( e ) {
// No way to tell if the results is open, so if they click the button while open, it clears
if ( self . last _search && self . last _search != self . search . val ( ) )
{
// Repeat last search (should be cached)
self . search . val ( self . last _search ) ;
self . last _search = "" ;
self . search . autocomplete ( "search" ) ;
}
else
{
// Clear
self . search . autocomplete ( "close" ) ;
2013-11-28 18:31:22 +01:00
self . set _value ( { app : self . options . only _app || self . options . value . app || null , id : null } ) ;
self . search . val ( "" ) ;
2012-06-19 00:41:54 +02:00
self . search . trigger ( "change" ) ;
2012-04-11 17:43:33 +02:00
}
self . search . focus ( ) ;
} )
2012-06-20 01:30:07 +02:00
. appendTo ( this . div )
. hide ( ) ;
2012-04-11 17:43:33 +02:00
2011-09-08 01:32:24 +02:00
this . setDOMNode ( this . div [ 0 ] ) ;
} ,
2011-09-13 01:43:39 +02:00
getDOMNode : function ( ) {
2013-02-08 11:38:09 +01:00
return this . div ? this . div [ 0 ] : null ;
2011-09-09 02:05:46 +02:00
} ,
2011-09-08 01:32:24 +02:00
transformAttributes : function ( _attrs ) {
this . _super . apply ( this , arguments ) ;
2011-09-13 01:43:39 +02:00
2011-10-18 21:15:32 +02:00
_attrs [ "select_options" ] = { } ;
2013-02-06 09:06:11 +01:00
if ( _attrs [ "application_list" ] )
2011-10-18 21:15:32 +02:00
{
2013-02-06 09:06:11 +01:00
var apps = ( typeof _attrs [ "application_list" ] == "string" ) ? et2 _csvSplit ( _attrs [ "application_list" ] , null , "," ) : _attrs [ "application_list" ] ;
2011-10-18 21:15:32 +02:00
for ( var i = 0 ; i < apps . length ; i ++ )
{
2012-03-02 11:44:56 +01:00
_attrs [ "select_options" ] [ apps [ i ] ] = this . egw ( ) . lang ( apps [ i ] ) ;
2011-10-18 21:15:32 +02:00
}
}
else
{
2012-03-02 11:44:56 +01:00
_attrs [ "select_options" ] = this . egw ( ) . link _app _list ( 'query' ) ;
2011-10-18 21:15:32 +02:00
}
2011-09-08 01:32:24 +02:00
// Check whether the options entry was found, if not read it from the
// content array.
if ( _attrs [ "select_options" ] == null )
{
_attrs [ "select_options" ] = this . getArrayMgr ( 'content' )
2013-04-13 21:00:13 +02:00
. getEntry ( "options-" + this . id ) ;
2011-09-08 01:32:24 +02:00
}
// Default to an empty object
if ( _attrs [ "select_options" ] == null )
{
_attrs [ "select_options" ] = { } ;
}
} ,
2013-10-24 18:04:44 +02:00
doLoadingFinished : function ( ) {
if ( typeof this . options . value == 'object' && ! this . options . value . app )
{
2014-02-11 13:54:56 +01:00
this . options . value . app = egw . preference ( 'link_app' , this . options . value . to _app || this . egw ( ) . getAppName ( ) ) ;
2013-10-24 18:04:44 +02:00
this . app _select . val ( this . options . value . app ) ;
}
return this . _super . apply ( this , arguments ) ;
} ,
2014-02-11 13:17:08 +01:00
2011-09-08 01:32:24 +02:00
getValue : function ( ) {
2013-02-06 09:06:11 +01:00
var value = this . options . only _app ? this . options . value . id : this . options . value ;
if ( ! this . options . only _app )
{
value . search = this . search . val ( ) ;
}
return value ;
2011-09-08 01:32:24 +02:00
} ,
2011-09-14 22:36:39 +02:00
set _value : function ( _value ) {
2013-12-12 00:11:33 +01:00
if ( typeof _value == 'string' || typeof _value == 'number' )
2011-09-14 22:36:39 +02:00
{
2013-12-12 00:11:33 +01:00
if ( typeof _value == 'string' && _value . indexOf ( "," ) > 0 ) _value = _value . replace ( "," , ":" ) ;
if ( typeof _value == 'string' && _value . indexOf ( ":" ) >= 0 )
2011-09-14 22:36:39 +02:00
{
2014-03-13 19:56:51 +01:00
var split = _value . split ( ":" ) ;
2011-09-14 22:36:39 +02:00
_value = {
2014-03-13 19:56:51 +01:00
app : split . shift ( ) ,
2014-03-31 20:20:38 +02:00
id : split . length == 1 ? split [ 0 ] : split
2011-09-14 22:36:39 +02:00
} ;
}
2013-02-06 09:06:11 +01:00
else if ( _value && this . options . only _app )
2011-09-14 22:36:39 +02:00
{
_value = {
2013-02-06 09:06:11 +01:00
app : this . options . only _app ,
2011-09-14 22:36:39 +02:00
id : _value
} ;
}
}
2012-06-26 01:05:18 +02:00
this . _oldValue = this . options . value ;
2012-06-20 01:30:07 +02:00
if ( ! _value || _value . length == 0 || _value == null || jQuery . isEmptyObject ( _value ) )
2012-06-19 00:41:54 +02:00
{
this . search . val ( "" ) ;
this . clear . hide ( ) ;
2012-06-20 01:30:07 +02:00
this . options . value = _value = { 'id' : null } ;
2012-06-19 00:41:54 +02:00
}
2013-02-06 09:06:11 +01:00
if ( ! _value . app ) _value . app = this . options . only _app ;
2012-06-26 01:05:18 +02:00
2012-06-20 01:30:07 +02:00
if ( _value . id ) {
2013-10-24 18:04:44 +02:00
// Remove specific display and revert to CSS file
// show() would use inline, should be inline-block
this . clear . css ( 'display' , '' ) ;
2012-06-20 01:30:07 +02:00
} else {
this . clear . hide ( ) ;
return ;
}
if ( typeof _value != 'object' || ( ! _value . app && ! _value . id ) )
2011-09-14 22:36:39 +02:00
{
console . warn ( "Bad value for link widget. Need an object with keys 'app', 'id', and optionally 'title'" , _value ) ;
return ;
}
if ( ! _value . title ) {
2012-03-02 11:44:56 +01:00
var title = this . egw ( ) . link _title ( _value . app , _value . id ) ;
2011-09-14 22:36:39 +02:00
if ( title != null ) {
_value . title = title ;
}
else
{
// Title will be fetched from server and then set
2012-04-05 22:02:29 +02:00
var title = this . egw ( ) . link _title ( _value . app , _value . id , function ( title ) {
2012-04-25 00:00:18 +02:00
this . search . removeClass ( "loading" ) . val ( title + "" ) ;
2013-10-24 18:04:44 +02:00
// Remove specific display and revert to CSS file
// show() would use inline, should be inline-block
this . clear . css ( 'display' , '' ) ;
2012-04-25 00:00:18 +02:00
} , this ) ;
2012-04-05 22:02:29 +02:00
this . search . addClass ( "loading" ) ;
2011-09-14 22:36:39 +02:00
}
}
2012-04-16 23:52:31 +02:00
if ( _value . title )
2012-04-05 22:02:29 +02:00
{
this . search . val ( _value . title + "" ) ;
}
2012-06-20 01:30:07 +02:00
this . options . value = _value ;
2014-02-11 13:17:08 +01:00
2013-07-20 19:20:55 +02:00
jQuery ( "option[value='" + _value . app + "']" , this . app _select ) . prop ( "selected" , true ) ;
2011-09-14 22:36:39 +02:00
this . app _select . hide ( ) ;
2012-04-24 23:27:48 +02:00
this . div . addClass ( "no_app" ) ;
2011-09-14 22:36:39 +02:00
} ,
2011-09-09 02:05:46 +02:00
set _blur : function ( _value , input ) {
2011-09-13 01:43:39 +02:00
2011-09-09 02:05:46 +02:00
if ( typeof input == 'undefined' ) input = this . search ;
2011-09-08 01:32:24 +02:00
if ( _value ) {
2011-09-09 02:05:46 +02:00
input . attr ( "placeholder" , _value ) ; // HTML5
if ( ! input [ 0 ] . placeholder ) {
2011-09-08 01:32:24 +02:00
// Not HTML5
2011-09-13 01:43:39 +02:00
if ( input . val ( ) == "" ) input . val ( _value ) ;
2011-09-09 02:05:46 +02:00
input . focus ( input , function ( e ) {
var placeholder = _value ;
if ( e . data . val ( ) == placeholder ) e . data . val ( "" ) ;
} ) . blur ( input , function ( e ) {
var placeholder = _value ;
if ( e . data . val ( ) == "" ) e . data . val ( placeholder ) ;
2011-09-08 01:32:24 +02:00
} ) ;
2011-09-09 02:05:46 +02:00
if ( input . val ( ) == "" ) input . val ( _value ) ;
2011-09-08 01:32:24 +02:00
}
} else {
this . search . removeAttr ( "placeholder" ) ;
}
} ,
2014-02-11 13:17:08 +01:00
2011-09-08 01:32:24 +02:00
/ * *
* Ask server for entries matching selected app / type and filtered by search string
2014-04-15 13:34:50 +02:00
*
* @ param { Object } request
* @ param { Object } response
2011-09-08 01:32:24 +02:00
* /
query : function ( request , response ) {
2014-03-26 18:55:50 +01:00
// If there is a pending request, abort it
if ( this . request )
{
this . request . abort ( ) ;
this . request = null ;
}
2012-04-11 17:43:33 +02:00
// Remember last search
this . last _search = this . search . val ( ) ;
2011-09-13 01:43:39 +02:00
// Allow hook / tie in
if ( this . options . query && typeof this . options . query == 'function' )
{
if ( ! this . options . query ( request , response ) ) return false ;
}
2012-05-30 00:25:40 +02:00
2013-09-30 18:54:25 +02:00
if ( ( typeof request . no _cache == 'undefined' && ! request . no _cache ) && request . term in this . cache ) {
2012-05-30 00:25:40 +02:00
return response ( this . cache [ request . term ] ) ;
}
2014-03-26 18:55:50 +01:00
// Remember callback
this . response = response ;
2012-05-30 00:25:40 +02:00
2011-09-08 01:32:24 +02:00
this . search . addClass ( "loading" ) ;
2013-10-24 18:04:44 +02:00
// Remove specific display and revert to CSS file
// show() would use inline, should be inline-block
this . clear . css ( 'display' , '' ) ;
2014-03-26 18:55:50 +01:00
this . request = egw . json ( this . egw ( ) . getAppName ( ) + ".etemplate_widget_link.ajax_link_search.etemplate" ,
2012-05-30 00:25:40 +02:00
[ this . app _select . val ( ) , '' , request . term , request . options ] ,
2013-09-10 19:57:41 +02:00
this . _results ,
this , true , this
2014-03-26 18:55:50 +01:00
) . sendRequest ( ) ;
2011-09-08 01:32:24 +02:00
} ,
/ * *
* User selected a value
2014-04-15 13:34:50 +02:00
*
* @ param { Object } event
* @ param { Object } selected
*
2011-09-08 01:32:24 +02:00
* /
select : function ( event , selected ) {
2014-02-13 01:36:36 +01:00
if ( selected . item . value !== null && typeof selected . item . value == "string" )
2014-01-31 17:30:59 +01:00
{
// Correct changed value from server
selected . item . value = selected . item . value . trim ( ) ;
}
2011-09-13 01:43:39 +02:00
if ( this . options . select && typeof this . options . select == 'function' )
{
if ( ! this . options . select ( event , selected ) ) return false ;
}
2012-06-20 01:30:07 +02:00
if ( typeof event . data . options . value != 'object' || event . data . options . value == null )
2012-03-23 00:20:56 +01:00
{
2012-06-20 01:30:07 +02:00
event . data . options . value = { } ;
2012-03-23 00:20:56 +01:00
}
2012-06-20 01:30:07 +02:00
event . data . options . value . id = selected . item . value ;
2012-04-11 17:43:33 +02:00
2014-02-24 21:00:34 +01:00
// Set a processing flag to filter some events
event . data . processing = true ;
2013-10-24 18:04:44 +02:00
// Remove specific display and revert to CSS file
// show() would use inline, should be inline-block
this . clear . css ( 'display' , '' ) ;
2011-09-08 01:32:24 +02:00
event . data . search . val ( selected . item . label ) ;
2011-09-13 01:43:39 +02:00
2012-06-19 00:41:54 +02:00
// Fire change event
this . search . change ( ) ;
2014-02-24 21:00:34 +01:00
// Turn off processing flag when done
window . setTimeout ( jQuery . proxy ( function ( ) { delete this . processing ; } , event . data ) ) ;
2011-09-08 01:32:24 +02:00
} ,
/ * *
* Server found some results
2014-04-15 13:34:50 +02:00
*
* @ param { Array } data
2011-09-08 01:32:24 +02:00
* /
_results : function ( data ) {
2014-03-26 18:55:50 +01:00
if ( this . request )
{
this . request = null ;
}
2011-09-08 01:32:24 +02:00
this . search . removeClass ( "loading" ) ;
var result = [ ] ;
for ( var id in data ) {
result . push ( { "value" : id , "label" : data [ id ] } ) ;
}
this . cache [ this . search . val ( ) ] = result ;
this . response ( result ) ;
} ,
/ * *
* Create a link using the current internal values
2014-04-15 13:34:50 +02:00
*
* @ param { Object } event
* @ param { Object } _links
2011-09-08 01:32:24 +02:00
* /
2011-09-13 01:43:39 +02:00
createLink : function ( event , _links ) {
2011-09-09 02:05:46 +02:00
var values = event . data . options . value ;
var self = event . data ;
2011-09-30 18:19:09 +02:00
var links = [ ] ;
2011-09-09 02:05:46 +02:00
2011-09-13 01:43:39 +02:00
if ( typeof _links == 'undefined' )
{
links = [ ] ;
2011-09-14 02:06:04 +02:00
}
else
{
2011-09-13 01:43:39 +02:00
links = _links ;
}
2011-09-09 02:05:46 +02:00
// Links to other entries
if ( values . id ) {
links . push ( {
app : values . app ,
id : values . id ,
} ) ;
self . search . val ( "" ) ;
}
2014-02-11 13:17:08 +01:00
2011-09-13 01:43:39 +02:00
// If a link array was passed in, don't make the ajax call
if ( typeof _links == 'undefined' )
{
2013-11-14 00:47:01 +01:00
var request = egw . json ( self . egw ( ) . getAppName ( ) + ".etemplate_widget_link.ajax_link.etemplate" ,
2011-09-13 01:43:39 +02:00
[ values . to _app , values . to _id , links ] ,
2013-09-10 19:57:41 +02:00
self . _link _result ,
this ,
true
2011-09-13 01:43:39 +02:00
) ;
2013-09-10 19:57:41 +02:00
request . sendRequest ( ) ;
2011-09-09 02:05:46 +02:00
}
} ,
/ * *
* Sent some links , server has a result
2014-04-15 13:34:50 +02:00
*
* @ param { Object } success
*
2011-09-09 02:05:46 +02:00
* /
_link _result : function ( success ) {
if ( success ) {
this . link _button . hide ( ) . attr ( "disabled" , false ) ;
this . status _span . fadeIn ( ) . delay ( 1000 ) . fadeOut ( ) ;
delete this . options . value . app ;
delete this . options . value . id ;
}
2011-09-08 01:32:24 +02:00
}
} ) ;
2011-09-13 01:43:39 +02:00
et2 _register _widget ( et2 _link _entry , [ "link-entry" ] ) ;
2011-09-08 01:32:24 +02:00
2011-09-13 01:43:39 +02:00
/ * *
* UI widget for a single ( read - only ) link
2014-02-11 13:17:08 +01:00
*
2013-04-13 21:00:13 +02:00
* @ augments et2 _valueWidget
2014-02-11 13:17:08 +01:00
* /
2014-01-31 17:30:59 +01:00
var et2 _link = et2 _valueWidget . extend ( [ et2 _IDetachedDOM ] ,
2013-04-13 21:00:13 +02:00
{
2011-09-13 01:43:39 +02:00
attributes : {
2013-06-12 01:13:04 +02:00
"only_app" : {
2011-09-13 01:43:39 +02:00
"name" : "Application" ,
"type" : "string" ,
"default" : "" ,
"description" : "Use the given application, so you can pass just the ID for value"
} ,
"value" : {
description : "Array with keys app, id, and optionally title" ,
type : "any"
2012-07-25 22:54:47 +02:00
} ,
2013-06-26 22:50:10 +02:00
"needed" : {
2012-07-25 22:54:47 +02:00
"ignore" : true
2011-09-13 01:43:39 +02:00
}
} ,
2013-06-12 01:13:04 +02:00
legacyOptions : [ "only_app" ] ,
2014-02-11 13:17:08 +01:00
2013-04-13 21:00:13 +02:00
/ * *
* Constructor
2014-02-11 13:17:08 +01:00
*
2013-04-13 21:00:13 +02:00
* @ memberOf et2 _link
* /
2011-09-13 01:43:39 +02:00
init : function ( ) {
this . _super . apply ( this , arguments ) ;
2013-10-08 10:19:42 +02:00
this . label _span = $j ( document . createElement ( "label" ) )
. addClass ( "et2_label" ) ;
2011-09-22 21:02:33 +02:00
this . link = $j ( document . createElement ( "span" ) )
2013-10-08 10:19:42 +02:00
. addClass ( "et2_link" )
. appendTo ( this . label _span ) ;
2011-09-13 01:43:39 +02:00
2013-10-08 10:19:42 +02:00
if ( this . options [ 'class' ] ) this . label _span . addClass ( this . options [ 'class' ] ) ;
this . setDOMNode ( this . label _span [ 0 ] ) ;
2011-09-13 01:43:39 +02:00
} ,
destroy : function ( ) {
2012-05-30 01:05:26 +02:00
if ( this . link ) this . link . unbind ( ) ;
2011-09-22 21:02:33 +02:00
this . link = null ;
2013-03-26 16:47:31 +01:00
this . _super . apply ( this , arguments ) ;
2011-09-13 01:43:39 +02:00
} ,
2014-02-11 13:17:08 +01:00
set _label : function ( label ) {
2013-10-08 10:19:42 +02:00
// Remove current label
this . label _span . contents ( )
. filter ( function ( ) { return this . nodeType == 3 ; } ) . remove ( ) ;
2014-02-11 13:17:08 +01:00
2013-10-08 10:19:42 +02:00
var parts = et2 _csvSplit ( label , 2 , "%s" ) ;
this . label _span . prepend ( parts [ 0 ] ) ;
this . label _span . append ( parts [ 1 ] ) ;
this . label = label ;
} ,
2011-09-13 01:43:39 +02:00
set _value : function ( _value ) {
2013-06-12 01:13:04 +02:00
if ( typeof _value != 'object' && _value && ! this . options . only _app )
2011-10-18 22:09:48 +02:00
{
2012-05-24 19:49:28 +02:00
if ( _value . indexOf ( ':' ) >= 0 )
{
var app = _value . split ( ':' , 1 ) ;
var id = _value . substr ( app [ 0 ] . length + 1 ) ;
_value = { 'app' : app [ 0 ] , 'id' : id } ;
}
else
{
console . warn ( "Bad value for link widget. Need an object with keys 'app', 'id', and optionally 'title'" , _value ) ;
return ;
}
2011-09-13 01:43:39 +02:00
}
2011-10-18 22:09:48 +02:00
// Application set, just passed ID
2012-03-23 00:20:56 +01:00
else if ( typeof _value != "object" )
2011-09-13 01:43:39 +02:00
{
2011-10-18 22:09:48 +02:00
_value = {
2013-06-12 01:13:04 +02:00
app : this . options . only _app ,
2011-10-18 22:09:48 +02:00
id : _value
} ;
}
if ( ! _value || jQuery . isEmptyObject ( _value ) ) {
this . link . text ( "" ) . unbind ( ) ;
2011-09-13 01:43:39 +02:00
return ;
}
2014-04-03 01:31:01 +02:00
var self = this ;
this . link . unbind ( ) ;
if ( _value . id && _value . app )
{
this . link . click ( function ( ) {
self . egw ( ) . open ( _value , "" , "view" , null , _value . app , _value . app ) ;
} ) ;
}
2011-09-14 02:06:04 +02:00
if ( ! _value . title ) {
2011-10-18 22:09:48 +02:00
var self = this ;
2012-03-14 20:00:38 +01:00
var node = this . link [ 0 ] ;
2013-03-13 20:32:25 +01:00
if ( _value . app && _value . id )
{
var title = this . egw ( ) . link _title ( _value . app , _value . id , function ( title ) { self . set _title ( node , title ) ; } , this ) ;
if ( title != null ) {
_value . title = title ;
}
else
{
// Title will be fetched from server and then set
return ;
}
2011-09-14 02:06:04 +02:00
}
else
{
2013-03-13 20:32:25 +01:00
_value . title = "" ;
2011-09-14 02:06:04 +02:00
}
}
2011-10-18 22:09:48 +02:00
this . set _title ( this . link , _value . title ) ;
2011-09-13 01:43:39 +02:00
} ,
2011-10-18 22:09:48 +02:00
/ * *
* Sets the text to be displayed .
* Used as a callback , so node is provided to make sure we get the right one
2014-04-15 13:34:50 +02:00
*
* @ param { Object } node
* @ param { String } _value description
2011-10-18 22:09:48 +02:00
* /
set _title : function ( node , _value ) {
2012-03-12 23:32:13 +01:00
if ( _value === false || _value === null ) _value = "" ;
2011-10-18 22:09:48 +02:00
jQuery ( node ) . text ( _value + "" ) ;
} ,
2011-09-13 01:43:39 +02:00
/ * *
* Creates a list of attributes which can be set when working in the
* "detached" mode . The result is stored in the _attrs array which is provided
* by the calling code .
2014-04-15 13:34:50 +02:00
*
* @ param { Array } _attrs an array of attributes
2011-09-13 01:43:39 +02:00
* /
getDetachedAttributes : function ( _attrs ) {
2013-08-14 19:35:50 +02:00
_attrs . push ( "label" , "value" ) ;
2011-09-13 01:43:39 +02:00
} ,
/ * *
* Returns an array of DOM nodes . The ( relatively ) same DOM - Nodes have to be
* passed to the "setDetachedAttributes" function in the same order .
* /
getDetachedNodes : function ( ) {
2013-10-08 10:19:42 +02:00
return [ this . node , this . link [ 0 ] ] ;
2011-09-13 01:43:39 +02:00
} ,
/ * *
* Sets the given associative attribute - > value array and applies the
* attributes to the given DOM - Node .
*
* @ param _nodes is an array of nodes which have to be in the same order as
* the nodes returned by "getDetachedNodes"
* @ param _values is an associative array which contains a subset of attributes
* returned by the "getDetachedAttributes" function and sets them to the
* given values .
* /
setDetachedAttributes : function ( _nodes , _values ) {
2013-08-14 19:35:50 +02:00
this . node = _nodes [ 0 ] ;
2013-10-08 10:19:42 +02:00
this . label _span = jQuery ( _nodes [ 0 ] ) ;
this . link = jQuery ( _nodes [ 1 ] ) ;
2012-03-14 20:00:38 +01:00
if ( typeof _values [ "id" ] !== "undefined" ) this . set _id ( _values [ 'id' ] ) ;
2013-08-14 19:35:50 +02:00
if ( typeof _values [ "label" ] !== "undefined" ) this . set _label ( _values [ 'label' ] ) ;
2013-10-08 10:26:59 +02:00
if ( typeof _values [ "value" ] !== "undefined" ) this . set _value ( _values [ "value" ] ) ;
2011-09-13 01:43:39 +02:00
}
} ) ;
2011-10-18 22:09:48 +02:00
et2 _register _widget ( et2 _link , [ "link" , "link-entry_ro" ] ) ;
2011-09-13 01:43:39 +02:00
/ * *
* UI widget for one or more links , comma separated
2014-02-11 13:17:08 +01:00
*
2013-04-13 21:00:13 +02:00
* @ augments et2 _valueWidget
2014-02-11 13:17:08 +01:00
* /
var et2 _link _string = et2 _valueWidget . extend ( [ et2 _IDetachedDOM ] ,
2013-04-13 21:00:13 +02:00
{
2011-09-13 01:43:39 +02:00
attributes : {
"application" : {
"name" : "Application" ,
"type" : "string" ,
"default" : "" ,
"description" : "Use the given application, so you can pass just the ID for value"
} ,
"value" : {
2011-09-14 02:06:04 +02:00
"description" : "Either an array of link information (see egw_link::link()) or array with keys to_app and to_id" ,
"type" : "any"
} ,
"only_app" : {
"name" : "Application filter" ,
"type" : "string" ,
"default" : "" ,
"description" : "Appname, eg. 'projectmananager' to list only linked projects"
} ,
"link_type" : {
"name" : "Type filter" ,
"type" : "string" ,
"default" : "" ,
"description" : "Sub-type key to list only entries of that type"
2011-09-13 01:43:39 +02:00
}
} ,
2014-02-11 13:17:08 +01:00
2013-04-13 21:00:13 +02:00
/ * *
* Constructor
2014-02-11 13:17:08 +01:00
*
2013-04-13 21:00:13 +02:00
* @ memberOf et2 _link _string
* /
2011-09-13 01:43:39 +02:00
init : function ( ) {
this . _super . apply ( this , arguments ) ;
2011-09-08 01:32:24 +02:00
2011-09-14 02:06:04 +02:00
this . list = $j ( document . createElement ( "ul" ) )
2011-09-13 01:43:39 +02:00
. addClass ( "et2_link_string" ) ;
2011-09-08 01:32:24 +02:00
2012-05-01 01:22:48 +02:00
if ( this . options [ 'class' ] ) this . list . addClass ( this . options [ 'class' ] ) ;
2011-09-14 02:06:04 +02:00
this . setDOMNode ( this . list [ 0 ] ) ;
2011-09-13 01:43:39 +02:00
} ,
destroy : function ( ) {
this . _super . apply ( this , arguments ) ;
2012-03-01 11:13:12 +01:00
if ( this . node != null ) {
this . node . children ( ) . unbind ( ) ;
}
2011-09-13 01:43:39 +02:00
} ,
set _value : function ( _value ) {
// Get data
2014-02-11 13:17:08 +01:00
if ( ! _value || _value == null )
2012-05-30 00:25:40 +02:00
{
this . list . empty ( ) ;
return ;
}
2012-05-30 01:15:58 +02:00
if ( typeof _value == "string" && _value . indexOf ( ',' ) > 0 )
{
_value = _value . split ( ',' ) ;
}
2014-02-11 13:17:08 +01:00
if ( ! _value . to _app && typeof _value == "object" && this . options . application )
2012-04-17 01:13:48 +02:00
{
_value . to _app = this . options . application ;
}
2011-09-14 02:06:04 +02:00
if ( typeof _value == 'object' && _value . to _app && _value . to _id )
{
this . value = _value ;
this . _get _links ( ) ;
return ;
2011-09-13 01:43:39 +02:00
}
2012-05-30 00:25:40 +02:00
this . list . empty ( ) ;
2012-04-17 01:13:48 +02:00
if ( typeof _value == 'object' && _value . length > 0 ) {
2011-09-14 02:06:04 +02:00
// Have full info
// Don't store new value, just update display
2011-09-13 01:43:39 +02:00
// Make new links
2011-09-14 02:06:04 +02:00
for ( var i = 0 ; i < _value . length ; i ++ )
{
if ( ! this . options . only _app || this . options . only _app && _value [ i ] . app == this . options . only _app )
{
2012-04-17 20:33:38 +02:00
this . _add _link ( _value [ i ] . id ? _value [ i ] : { id : _value [ i ] , app : _value . to _app } ) ;
2011-09-14 02:06:04 +02:00
}
2011-09-13 01:43:39 +02:00
}
}
2012-04-17 01:13:48 +02:00
else if ( this . options . application )
{
this . _add _link ( { id : _value , app : this . options . application } ) ;
}
2011-09-13 01:43:39 +02:00
} ,
2011-09-14 02:06:04 +02:00
_get _links : function ( ) {
var _value = this . value ;
// Just IDs - get from server
if ( this . options . only _app )
{
_value . only _app = this . options . only _app ;
}
2013-11-14 00:47:01 +01:00
this . egw ( ) . jsonq ( this . egw ( ) . getAppName ( ) + '.etemplate_widget_link.ajax_link_list' , [ _value ] , this . set _value , this ) ;
2011-09-14 02:06:04 +02:00
return ;
} ,
2011-09-13 01:43:39 +02:00
_add _link : function ( _link _data ) {
2012-03-02 13:41:29 +01:00
var self = this ;
2011-09-14 02:06:04 +02:00
var link = $j ( document . createElement ( "li" ) )
. appendTo ( this . list )
2012-06-20 01:30:07 +02:00
. addClass ( "et2_link loading" )
2013-10-21 17:39:09 +02:00
. click ( function ( ) { self . egw ( ) . open ( _link _data , "" , "view" , null , _link _data . app , _link _data . app ) ; } ) ;
2012-03-21 17:02:50 +01:00
2011-09-22 23:40:21 +02:00
if ( _link _data . title ) link . text ( _link _data . title ) ;
2011-09-14 02:06:04 +02:00
// Now that link is created, get title from server & update
if ( ! _link _data . title ) {
2012-06-20 01:30:07 +02:00
this . egw ( ) . link _title ( _link _data . app , _link _data . id , function ( title ) {
2014-02-26 18:38:43 +01:00
if ( title )
this . removeClass ( "loading" ) . text ( title ) ;
else
this . remove ( ) ; // no rights or not found
2012-06-20 01:30:07 +02:00
} , link ) ;
2011-09-14 02:06:04 +02:00
}
2011-09-13 01:43:39 +02:00
} ,
/ * *
* Creates a list of attributes which can be set when working in the
* "detached" mode . The result is stored in the _attrs array which is provided
* by the calling code .
2014-04-15 13:34:50 +02:00
*
* @ param { Array } _attrs an array of attributes
2011-09-13 01:43:39 +02:00
* /
getDetachedAttributes : function ( _attrs ) {
_attrs . push ( "value" ) ;
} ,
/ * *
* Returns an array of DOM nodes . The ( relatively ) same DOM - Nodes have to be
* passed to the "setDetachedAttributes" function in the same order .
* /
getDetachedNodes : function ( ) {
2011-09-14 02:06:04 +02:00
return [ this . list [ 0 ] ] ;
2011-09-13 01:43:39 +02:00
} ,
/ * *
* Sets the given associative attribute - > value array and applies the
* attributes to the given DOM - Node .
*
* @ param _nodes is an array of nodes which have to be in the same order as
* the nodes returned by "getDetachedNodes"
* @ param _values is an associative array which contains a subset of attributes
* returned by the "getDetachedAttributes" function and sets them to the
* given values .
* /
setDetachedAttributes : function ( _nodes , _values ) {
2011-09-14 02:06:04 +02:00
this . list = $j ( _nodes [ 0 ] ) ;
2011-09-13 01:43:39 +02:00
this . set _value ( _values [ "value" ] ) ;
}
} ) ;
et2 _register _widget ( et2 _link _string , [ "link-string" ] ) ;
2011-09-14 02:06:04 +02:00
/ * *
* UI widget for one or more links in a list ( table )
2014-02-11 13:17:08 +01:00
*
2013-04-13 21:00:13 +02:00
* @ augments et2 _link _string
2014-02-11 13:17:08 +01:00
* /
2013-04-13 21:00:13 +02:00
var et2 _link _list = et2 _link _string . extend (
{
2011-09-14 02:06:04 +02:00
attributes : {
"show_deleted" : {
"name" : "Show deleted" ,
"type" : "boolean" ,
"default" : false ,
"description" : "Show links that are marked as deleted, being held for purge"
}
} ,
2014-02-11 13:17:08 +01:00
2013-04-13 21:00:13 +02:00
/ * *
* Constructor
2014-02-11 13:17:08 +01:00
*
2013-04-13 21:00:13 +02:00
* @ memberOf et2 _link _list
* /
2011-09-14 02:06:04 +02:00
init : function ( ) {
this . _super . apply ( this , arguments ) ;
this . list = $j ( document . createElement ( "table" ) )
. addClass ( "et2_link_list" ) ;
2012-03-22 17:24:48 +01:00
if ( this . options [ 'class' ] ) this . node . addClass ( this . options [ 'class' ] ) ;
2011-09-14 02:06:04 +02:00
this . setDOMNode ( this . list [ 0 ] ) ;
2012-06-19 20:59:53 +02:00
// Set up context menu
var self = this ;
this . context = new egwMenu ( ) ;
this . context . addItem ( "comment" , this . egw ( ) . lang ( "Comment" ) , "" , function ( ) {
var link _id = self . context . data . link _id ;
2014-02-11 13:17:08 +01:00
2013-09-10 19:57:41 +02:00
et2 _dialog . show _prompt (
function ( button , comment ) {
if ( button != et2 _dialog . OK _BUTTON ) return ;
var remark = jQuery ( '#link_' + link _id , self . list ) . children ( '.remark' ) ;
remark . addClass ( "loading" ) ;
2013-11-14 00:47:01 +01:00
var request = egw . json ( self . egw ( ) . getAppName ( ) + ".etemplate_widget_link.ajax_link_comment.etemplate" ,
2013-09-10 19:57:41 +02:00
[ link _id , comment ] ,
function ( ) {
if ( remark )
{
// Append "" to make sure it's a string, not undefined
remark . removeClass ( "loading" ) . text ( comment + "" ) ;
// Update internal data
self . context . data . remark = comment + "" ;
}
} ,
this , true
) . sendRequest ( ) ;
} ,
'' , self . egw ( ) . lang ( "Comment" ) , self . context . data . remark || ''
) ;
2014-02-11 13:17:08 +01:00
2012-06-19 20:59:53 +02:00
} ) ;
2012-07-09 21:05:06 +02:00
this . context . addItem ( "file_info" , this . egw ( ) . lang ( "File information" ) , this . egw ( ) . image ( "edit" ) , function ( menu _item ) {
var link _data = self . context . data ;
if ( link _data . app == 'file' )
{
var url = self . egw ( ) . mime _open ( link _data ) ;
if ( typeof url == 'string' && url . indexOf ( 'webdav.php' ) )
{
// URL is url to file in webdav, so get rid of that part
url = url . replace ( '/webdav.php' , '' ) ;
}
else if ( typeof url == 'object' && url . path )
{
url = url . path ;
}
self . egw ( ) . open ( url , "filemanager" , "edit" ) ;
}
} ) ;
this . context . addItem ( "-" , "-" ) ;
2012-06-19 20:59:53 +02:00
this . context . addItem ( "delete" , this . egw ( ) . lang ( "Delete link" ) , this . egw ( ) . image ( "delete" ) , function ( menu _item ) {
var link _id = self . context . data . link _id ;
self . _delete _link ( link _id ) ;
} ) ;
2011-09-14 02:06:04 +02:00
} ,
2014-02-11 13:17:08 +01:00
2013-09-10 19:57:41 +02:00
destroy : function ( ) {
2014-02-11 13:17:08 +01:00
2013-09-10 19:57:41 +02:00
this . _super . apply ( this , arguments ) ;
if ( this . context )
{
this . context . clear ( ) ;
delete this . context ;
}
} ,
2011-09-14 02:06:04 +02:00
_add _link : function ( _link _data ) {
var row = $j ( document . createElement ( "tr" ) )
2012-06-19 20:59:53 +02:00
. attr ( "id" , "link_" + _link _data . link _id )
2013-04-13 21:00:13 +02:00
. appendTo ( this . list ) ;
2011-09-14 02:06:04 +02:00
// Icon
var icon = $j ( document . createElement ( "td" ) )
. appendTo ( row )
. addClass ( "icon" ) ;
if ( _link _data . icon )
{
var icon _widget = et2 _createWidget ( "image" ) ;
2012-04-04 21:15:07 +02:00
var src = '' ;
if ( _link _data . type )
{
// VFS - file
src = this . egw ( ) . mime _icon ( _link _data . type , _link _data . icon ) ;
}
else
{
src = this . egw ( ) . image ( _link _data . icon ) ;
}
if ( src ) icon _widget . set _src ( src ) ;
2011-09-14 02:06:04 +02:00
icon . append ( icon _widget . getDOMNode ( ) ) ;
}
2014-02-11 13:17:08 +01:00
2012-04-04 21:21:13 +02:00
var columns = [ 'title' , 'remark' ] ;
2014-02-11 13:17:08 +01:00
2012-03-02 13:41:29 +01:00
var self = this ;
2011-09-14 02:06:04 +02:00
for ( var i = 0 ; i < columns . length ; i ++ ) {
$j ( document . createElement ( "td" ) )
. appendTo ( row )
. addClass ( columns [ i ] )
2013-10-21 17:39:09 +02:00
. click ( function ( ) { self . egw ( ) . open ( _link _data , "" , "view" , null , _link _data . app , _link _data . app ) ; } )
2013-10-10 16:43:54 +02:00
. text ( _link _data [ columns [ i ] ] ? _link _data [ columns [ i ] ] + "" : "" ) ;
2011-09-14 02:06:04 +02:00
}
// Date
/ *
var date _row = $j ( document . createElement ( "td" ) )
. appendTo ( row ) ;
if ( _link _data . lastmod )
{
var date _widget = et2 _createWidget ( "date-since" ) ;
date _widget . set _value ( _link _data . lastmod ) ;
date _row . append ( date _widget . getDOMNode ( ) ) ;
}
* /
// Delete
var delete _button = $j ( document . createElement ( "td" ) )
2013-04-13 21:00:13 +02:00
. appendTo ( row ) ;
2013-03-20 19:26:18 +01:00
$j ( "<div />" )
. appendTo ( delete _button )
// We don't use ui-icon because it assigns a bg image
2011-09-14 02:06:04 +02:00
. addClass ( "delete icon" )
2014-03-04 01:26:42 +01:00
. bind ( 'click' , function ( ) { self . _delete _link (
self . value && typeof self . value . to _id != 'object' && _link _data . link _id ? _link _data . link _id : _link _data ,
row
) ; } ) ;
2012-06-19 20:59:53 +02:00
// Context menu
row . bind ( "contextmenu" , function ( e ) {
2012-07-09 21:05:06 +02:00
// File info only available for files
self . context . getItem ( "file_info" ) . set _enabled ( _link _data . app == 'file' ) ;
2012-06-19 20:59:53 +02:00
self . context . data = _link _data ;
self . context . showAt ( e . pageX , e . pageY , true ) ;
e . preventDefault ( ) ;
} ) ;
} ,
2013-05-28 01:21:13 +02:00
_delete _link : function ( link _id , row ) {
2013-03-20 19:26:18 +01:00
var delete _button = jQuery ( '.delete' , row ) ;
delete _button . removeClass ( "delete" ) . addClass ( "loading" ) ;
2013-10-11 10:41:24 +02:00
if ( typeof link _id != "object" )
2013-05-28 01:21:13 +02:00
{
2013-11-14 00:47:01 +01:00
egw . json ( this . egw ( ) . getAppName ( ) + ".etemplate_widget_link.ajax_delete.etemplate" , [ link _id ] ,
2013-09-10 19:57:41 +02:00
function ( data ) { if ( data ) { row . slideUp ( row . remove ) ; } }
) . sendRequest ( ) ;
2013-05-28 01:21:13 +02:00
}
2013-10-11 10:41:24 +02:00
else if ( row )
{
// No link ID means a link on an unsaved entry.
// Just remove the row, but need to adjust the link_to value also
row . slideUp ( row . remove ) ;
2014-03-04 01:26:42 +01:00
// Look for a link-to with the same ID, refresh it
if ( link _id . link _id )
{
var self = this ;
var _widget = link _id . widget || null ;
this . getRoot ( ) . iterateOver (
function ( widget ) {
if ( widget . id == self . id ) {
_widget = widget ;
}
} ,
this , et2 _link _to
) ;
var value = _widget != null ? _widget . getValue ( ) : false ;
if ( _widget && value && value . to _id )
2013-10-11 10:41:24 +02:00
{
2014-03-04 01:26:42 +01:00
delete value . to _id [ link _id . link _id ] ;
_widget . set _value ( value ) ;
}
2013-10-11 10:41:24 +02:00
}
}
2011-09-14 02:06:04 +02:00
}
} ) ;
et2 _register _widget ( et2 _link _list , [ "link-list" ] ) ;
2011-09-14 22:36:39 +02:00
/ * *
* UI widget for one or more links in a list ( table )
2014-02-11 13:17:08 +01:00
*
2013-04-13 21:00:13 +02:00
* @ augments et2 _inputWidget
2014-02-11 13:17:08 +01:00
* /
2013-04-13 21:00:13 +02:00
var et2 _link _add = et2 _inputWidget . extend (
{
2011-09-14 22:36:39 +02:00
attributes : {
2013-10-10 16:43:54 +02:00
"value" : {
"description" : "Either an array of link information (see egw_link::link()) or array with keys to_app and to_id" ,
"type" : "any"
} ,
2011-09-14 22:36:39 +02:00
"application" : {
"name" : "Application" ,
"type" : "string" ,
"default" : "" ,
"description" : "Limit to the listed application or applications (comma seperated)"
}
} ,
2013-04-13 21:00:13 +02:00
/ * *
* Constructor
2014-02-11 13:17:08 +01:00
*
2013-04-13 21:00:13 +02:00
* @ memberOf et2 _link _add
* /
2011-09-14 22:36:39 +02:00
init : function ( ) {
this . _super . apply ( this , arguments ) ;
2012-03-02 11:44:56 +01:00
this . div = jQuery ( document . createElement ( "div" ) ) . text ( this . egw ( ) . lang ( "Add new" ) ) ;
2011-09-14 22:36:39 +02:00
this . setDOMNode ( this . div [ 0 ] ) ;
} ,
doLoadingFinished : function ( ) {
this . _super . apply ( this , arguments ) ;
2013-10-17 00:39:41 +02:00
if ( this . app _select && this . button )
{
// Already done
return false ;
}
2013-11-04 20:00:43 +01:00
this . app _select = et2 _createWidget ( "link-apps" , jQuery . extend ( { } , this . options , {
'id' : this . options . id + 'app' ,
value : this . options . application ? this . options . application : this . options . value && this . options . value . add _app ? this . options . value . add _app : null
} ) , this ) ;
2011-09-14 22:36:39 +02:00
this . div . append ( this . app _select . getDOMNode ( ) ) ;
2012-03-02 11:44:56 +01:00
this . button = et2 _createWidget ( "button" , { label : this . egw ( ) . lang ( "add" ) } , this ) ;
this . button . set _label ( this . egw ( ) . lang ( "add" ) ) ;
2011-09-14 22:36:39 +02:00
var self = this ;
this . button . click = function ( ) {
2012-03-02 13:41:29 +01:00
self . egw ( ) . open ( self . options . value . to _app + ":" + self . options . value . to _id , self . app _select . get _value ( ) , 'add' ) ;
2011-09-14 22:36:39 +02:00
} ;
this . div . append ( this . button . getDOMNode ( ) ) ;
2014-02-11 13:17:08 +01:00
2013-11-04 20:00:43 +01:00
return true ;
2012-06-26 01:05:18 +02:00
} ,
/ * *
* Should be handled client side .
* Return null to avoid overwriting other link values , in case designer used the same ID for multiple widgets
* /
getValue : function ( ) {
return null ;
2011-09-14 22:36:39 +02:00
}
} ) ;
et2 _register _widget ( et2 _link _add , [ "link-add" ] ) ;