2020-01-24 12:14:08 +01:00
"use strict" ;
2011-08-25 15:35:53 +02:00
/ * *
2013-04-13 21:00:13 +02:00
* EGroupware eTemplate2 - JS Nextmatch object
2011-08-25 15:35:53 +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 Andreas Stöckel
* @ copyright Stylite 2011
* @ version $Id$
2020-01-31 21:07:27 +01:00
*
2011-08-25 15:35:53 +02:00
/ * e g w : u s e s
2011-09-09 16:32:55 +02:00
2020-01-24 12:14:08 +01:00
// Include the action system
egw _action . egw _action ;
egw _action . egw _action _popup ;
egw _action . egw _action _dragdrop ;
egw _action . egw _menu _dhtmlx ;
// Include some core classes
et2 _core _widget ;
et2 _core _interfaces ;
et2 _core _DOMWidget ;
// Include all widgets the nextmatch extension will create
et2 _widget _template ;
et2 _widget _grid ;
et2 _widget _selectbox ;
et2 _widget _selectAccount ;
et2 _widget _taglist ;
et2 _extension _customfields ;
// Include all nextmatch subclasses
et2 _extension _nextmatch _rowProvider ;
2020-01-31 21:07:27 +01:00
et2 _extension _nextmatch _controller ;
2020-02-12 22:49:22 +01:00
et2 _widget _dynheight ;
2020-01-24 12:14:08 +01:00
// Include the grid classes
et2 _dataview ;
2011-09-09 16:32:55 +02:00
2011-08-25 15:35:53 +02:00
* /
2020-01-31 21:07:27 +01:00
var _ _extends = ( this && this . _ _extends ) || ( function ( ) {
var extendStatics = function ( d , b ) {
extendStatics = Object . setPrototypeOf ||
( { _ _proto _ _ : [ ] } instanceof Array && function ( d , b ) { d . _ _proto _ _ = b ; } ) ||
function ( d , b ) { for ( var p in b ) if ( b . hasOwnProperty ( p ) ) d [ p ] = b [ p ] ; } ;
return extendStatics ( d , b ) ;
} ;
return function ( d , b ) {
extendStatics ( d , b ) ;
function _ _ ( ) { this . constructor = d ; }
d . prototype = b === null ? Object . create ( b ) : ( _ _ . prototype = b . prototype , new _ _ ( ) ) ;
} ;
} ) ( ) ;
Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
2020-01-24 12:14:08 +01:00
require ( "./et2_core_common" ) ;
require ( "./et2_core_interfaces" ) ;
2020-02-05 00:07:50 +01:00
var et2 _core _inheritance _1 = require ( "./et2_core_inheritance" ) ;
2020-01-24 12:14:08 +01:00
var et2 _core _widget _1 = require ( "./et2_core_widget" ) ;
var et2 _core _DOMWidget _1 = require ( "./et2_core_DOMWidget" ) ;
var et2 _core _baseWidget _1 = require ( "./et2_core_baseWidget" ) ;
var et2 _core _inputWidget _1 = require ( "./et2_core_inputWidget" ) ;
var et2 _widget _selectbox _1 = require ( "./et2_widget_selectbox" ) ;
2020-01-31 21:07:27 +01:00
var et2 _extension _nextmatch _rowProvider _1 = require ( "./et2_extension_nextmatch_rowProvider" ) ;
var et2 _extension _nextmatch _controller _1 = require ( "./et2_extension_nextmatch_controller" ) ;
var et2 _dataview _1 = require ( "./et2_dataview" ) ;
var et2 _dataview _model _columns _1 = require ( "./et2_dataview_model_columns" ) ;
2020-02-12 19:16:34 +01:00
var et2 _extension _customfields _1 = require ( "./et2_extension_customfields" ) ;
2020-01-29 22:29:06 +01:00
var et2 _INextmatchHeader = "et2_INextmatchHeader" ;
function implements _et2 _INextmatchHeader ( obj ) {
return implements _methods ( obj , [ "setNextmatch" ] ) ;
}
var et2 _INextmatchSortable = "et2_INextmatchSortable" ;
function implements _et2 _INextmatchSortable ( obj ) {
return implements _methods ( obj , [ "setSortmode" ] ) ;
}
2020-03-18 17:36:14 +01:00
;
2011-08-25 15:35:53 +02:00
/ * *
* Class which implements the "nextmatch" XET - Tag
2014-01-27 17:26:00 +01:00
*
2016-02-12 18:19:27 +01:00
* NM header is build like this in DOM
*
* + - nextmatch _header -- -- - + -- -- -- -- -- -- + -- -- -- -- -- + -- -- -- -- + -- -- -- -- - + -- -- -- -- -- -- -- + -- -- -- -- -- - + -- -- -- - +
* + header _left | search . . | header _row | category | filter | filter2 | header _right | favorites | count |
* + -- -- -- -- -- -- - + -- -- -- -- -- + -- -- -- -- -- -- + -- -- -- -- -- + -- -- -- -- + -- -- -- -- - + -- -- -- -- -- -- -- + -- -- -- -- -- - + -- -- -- - +
*
* everything left incl . standard filters is floated left :
* + - nextmatch _header -- -- - + -- -- -- -- -- -- + -- -- -- -- -- + -- -- -- -- + -- -- -- -- - +
* + header _left | search . . | header _row | category | filter | filter2 |
* + -- -- -- -- -- -- - + -- -- -- -- -- + -- -- -- -- -- -- + -- -- -- -- -- + -- -- -- -- + -- -- -- -- - +
* everything from header _right on is floated right :
* + -- -- -- -- -- -- -- + -- -- -- -- -- - + -- -- -- - +
* | header _right | favorites | count |
* + -- -- -- -- -- -- -- + -- -- -- -- -- - + -- -- -- - +
2013-04-13 21:00:13 +02:00
* @ augments et2 _DOMWidget
2014-01-27 17:26:00 +01:00
* /
2020-01-31 21:07:27 +01:00
var et2 _nextmatch = /** @class */ ( function ( _super ) {
_ _extends ( et2 _nextmatch , _super ) ;
2020-01-24 12:14:08 +01:00
/ * *
* Constructor
*
* @ memberOf et2 _nextmatch
* /
function et2 _nextmatch ( _parent , _attrs , _child ) {
2020-03-23 17:05:46 +01:00
var _this = _super . call ( this , _parent , _attrs , et2 _core _inheritance _1 . ClassWithAttributes . extendAttributes ( et2 _nextmatch . _attributes , _child || { } ) ) || this ;
// When printing, we change the layout around. Keep some values so it can be restored after
_this . print = {
old _height : 0 ,
row _selector : '' ,
orientation _style : null
} ;
_this . activeFilters = { col _filter : { } } ;
_this . columns = [ ] ;
// keeps sorted columns
_this . sortedColumnsList = [ ] ;
// Directly set current col_filters from settings
jQuery . extend ( _this . activeFilters . col _filter , _this . options . settings . col _filter ) ;
/ *
Process selected custom fields here , so that the settings are correctly
set before the row template is parsed
* /
2020-01-24 12:14:08 +01:00
var prefs = _this . _getPreferences ( ) ;
var cfs = { } ;
for ( var i = 0 ; i < prefs . visible . length ; i ++ ) {
2020-02-12 19:32:29 +01:00
if ( prefs . visible [ i ] . indexOf ( et2 _nextmatch _customfields . PREFIX ) == 0 ) {
2020-01-24 12:14:08 +01:00
cfs [ prefs . visible [ i ] . substr ( 1 ) ] = ! prefs . negated ;
}
}
var global _data = _this . getArrayMgr ( "modifications" ) . getRoot ( ) . getEntry ( '~custom_fields~' ) ;
if ( typeof global _data == 'object' && global _data != null ) {
global _data . fields = cfs ;
}
_this . div = jQuery ( document . createElement ( "div" ) )
. addClass ( "et2_nextmatch" ) ;
_this . header = et2 _createWidget ( "nextmatch_header_bar" , { } , _this ) ;
_this . innerDiv = jQuery ( document . createElement ( "div" ) )
. appendTo ( _this . div ) ;
// Create the dynheight component which dynamically scales the inner
// container.
_this . dynheight = _this . _getDynheight ( ) ;
// Create the outer grid container
2020-01-31 21:07:27 +01:00
_this . dataview = new et2 _dataview _1 . et2 _dataview ( _this . innerDiv , _this . egw ( ) ) ;
2020-01-24 12:14:08 +01:00
// Blank placeholder
_this . blank = jQuery ( document . createElement ( "div" ) )
. appendTo ( _this . dataview . table ) ;
// We cannot create the grid controller now, as this depends on the grid
// instance, which can first be created once we have the columns
_this . controller = null ;
_this . rowProvider = null ;
return _this ;
}
/ * *
* Destroys all
* /
et2 _nextmatch . prototype . destroy = function ( ) {
2020-02-11 19:32:50 +01:00
// Stop auto-refresh
2020-01-24 12:14:08 +01:00
if ( this . _autorefresh _timer ) {
window . clearInterval ( this . _autorefresh _timer ) ;
this . _autorefresh _timer = null ;
}
// Unbind handler used for toggling autorefresh
jQuery ( this . getInstanceManager ( ) . DOMContainer . parentNode ) . off ( 'show.et2_nextmatch' ) ;
jQuery ( this . getInstanceManager ( ) . DOMContainer . parentNode ) . off ( 'hide.et2_nextmatch' ) ;
// Free the grid components
2020-01-31 21:07:27 +01:00
this . dataview . destroy ( ) ;
2020-01-24 12:14:08 +01:00
if ( this . rowProvider ) {
2020-01-31 21:07:27 +01:00
this . rowProvider . destroy ( ) ;
2020-01-24 12:14:08 +01:00
}
if ( this . controller ) {
2020-01-31 21:07:27 +01:00
this . controller . destroy ( ) ;
2020-01-24 12:14:08 +01:00
}
2020-01-31 21:07:27 +01:00
this . dynheight . destroy ( ) ;
_super . prototype . destroy . call ( this ) ;
2020-01-24 12:14:08 +01:00
} ;
2020-02-28 14:45:58 +01:00
et2 _nextmatch . prototype . getController = function ( ) {
return this . controller ;
} ;
2020-01-24 12:14:08 +01:00
/ * *
* Loads the nextmatch settings
*
* @ param { object } _attrs
* /
et2 _nextmatch . prototype . transformAttributes = function ( _attrs ) {
2020-01-31 21:07:27 +01:00
_super . prototype . transformAttributes . call ( this , _attrs ) ;
2020-01-24 12:14:08 +01:00
if ( this . id ) {
var entry = this . getArrayMgr ( "content" ) . data ;
_attrs [ "settings" ] = { } ;
if ( entry ) {
_attrs [ "settings" ] = entry ;
// Make sure there's an action var parameter
if ( _attrs [ "settings" ] [ "actions" ] && ! _attrs . settings [ "action_var" ] ) {
_attrs . settings . action _var = "action" ;
}
// Merge settings mess into attributes
for ( var attr in this . attributes ) {
if ( _attrs . settings [ attr ] ) {
_attrs [ attr ] = _attrs . settings [ attr ] ;
delete _attrs . settings [ attr ] ;
}
}
}
}
} ;
et2 _nextmatch . prototype . doLoadingFinished = function ( ) {
2020-01-31 21:07:27 +01:00
_super . prototype . doLoadingFinished . call ( this ) ;
2020-01-24 12:14:08 +01:00
if ( ! this . dynheight ) {
this . dynheight = this . _getDynheight ( ) ;
}
// Register handler for dropped files, if possible
if ( this . options . settings . row _id ) {
// Appname should be first part of the template name
var split = this . options . template . split ( '.' ) ;
var appname = split [ 0 ] ;
// Check link registry
if ( this . egw ( ) . link _get _registry ( appname ) ) {
2020-02-11 19:32:50 +01:00
var self _1 = this ;
2020-01-24 12:14:08 +01:00
// Register a handler
// @ts-ignore
jQuery ( this . div )
. on ( 'dragenter' , '.egwGridView_grid tr' , function ( e ) {
// Figure out _which_ row
2020-02-11 19:32:50 +01:00
var row = self _1 . controller . getRowByNode ( this ) ;
2020-01-24 12:14:08 +01:00
if ( ! row || ! row . uid ) {
return false ;
}
e . stopPropagation ( ) ;
e . preventDefault ( ) ;
// Indicate acceptance
if ( row . controller && row . controller . _selectionMgr ) {
row . controller . _selectionMgr . setFocused ( row . uid , true ) ;
}
return false ;
} )
2020-02-11 19:32:50 +01:00
. on ( 'dragexit' , '.egwGridView_grid tr' , function ( ) {
self _1 . controller . _selectionMgr . setFocused ( ) ;
2020-01-24 12:14:08 +01:00
} )
. on ( 'dragover' , '.egwGridView_grid tr' , false ) . attr ( "dropzone" , "copy" )
. on ( 'drop' , '.egwGridView_grid tr' , function ( e ) {
2020-02-11 19:32:50 +01:00
self _1 . handle _drop ( e , this ) ;
2020-01-24 12:14:08 +01:00
return false ;
} ) ;
}
}
// stop invalidation in no visible tabs
2020-02-11 19:32:50 +01:00
jQuery ( this . getInstanceManager ( ) . DOMContainer . parentNode ) . on ( 'hide.et2_nextmatch' , jQuery . proxy ( function ( ) {
2020-01-24 12:14:08 +01:00
if ( this . controller && this . controller . _grid ) {
this . controller . _grid . doInvalidate = false ;
}
} , this ) ) ;
2020-02-11 19:32:50 +01:00
jQuery ( this . getInstanceManager ( ) . DOMContainer . parentNode ) . on ( 'show.et2_nextmatch' , jQuery . proxy ( function ( ) {
2020-01-24 12:14:08 +01:00
if ( this . controller && this . controller . _grid ) {
this . controller . _grid . doInvalidate = true ;
}
} , this ) ) ;
return true ;
} ;
/ * *
* Implements the et2 _IResizeable interface - lets the dynheight manager
* update the width and height and then update the dataview container .
* /
et2 _nextmatch . prototype . resize = function ( ) {
if ( this . dynheight ) {
this . dynheight . update ( function ( _w , _h ) {
this . dataview . resize ( _w , _h ) ;
} , this ) ;
}
} ;
/ * *
* Sorts the nextmatch widget by the given ID .
*
* @ param { string } _id is the id of the data entry which should be sorted .
* @ param { boolean } _asc if true , the elements are sorted ascending , otherwise
* descending . If not set , the sort direction will be determined
* automatically .
* @ param { boolean } _update true / undefined : call applyFilters , false : only set sort
* /
et2 _nextmatch . prototype . sortBy = function ( _id , _asc , _update ) {
if ( typeof _update == "undefined" ) {
_update = true ;
}
// Create the "sort" entry in the active filters if it did not exist
// yet.
if ( typeof this . activeFilters [ "sort" ] == "undefined" ) {
this . activeFilters [ "sort" ] = {
"id" : null ,
"asc" : true
} ;
}
// Determine the sort direction automatically if it is not set
if ( typeof _asc == "undefined" ) {
_asc = true ;
if ( this . activeFilters [ "sort" ] . id == _id ) {
_asc = ! this . activeFilters [ "sort" ] . asc ;
}
}
// Set the sortmode display
this . iterateOver ( function ( _widget ) {
_widget . setSortmode ( ( _widget . id == _id ) ? ( _asc ? "asc" : "desc" ) : "none" ) ;
} , this , et2 _INextmatchSortable ) ;
if ( _update ) {
this . applyFilters ( { sort : { id : _id , asc : _asc } } ) ;
}
else {
// Update the entry in the activeFilters object
this . activeFilters [ "sort" ] = {
"id" : _id ,
"asc" : _asc
} ;
}
} ;
/ * *
* Removes the sort entry from the active filters object and thus returns to
* the natural sort order .
* /
et2 _nextmatch . prototype . resetSort = function ( ) {
// Check whether the nextmatch widget is currently sorted
if ( typeof this . activeFilters [ "sort" ] != "undefined" ) {
2020-02-11 19:32:50 +01:00
// Reset the sort mode
2020-01-24 12:14:08 +01:00
this . iterateOver ( function ( _widget ) {
_widget . setSortmode ( "none" ) ;
} , this , et2 _INextmatchSortable ) ;
// Delete the "sort" filter entry
this . applyFilters ( { sort : undefined } ) ;
}
} ;
/ * *
* Apply current or modified filters on NM widget ( updating rows accordingly )
*
* @ param _set filter ( s ) to set eg . { filter : '' } to reset filter in NM header
* /
et2 _nextmatch . prototype . applyFilters = function ( _set ) {
var changed = false ;
var keep _selection = false ;
// Avoid loops cause by change events
if ( this . update _in _progress )
return ;
this . update _in _progress = true ;
// Cleared explicitly
if ( typeof _set != 'undefined' && jQuery . isEmptyObject ( _set ) ) {
changed = true ;
this . activeFilters = { col _filter : { } } ;
}
if ( typeof this . activeFilters == "undefined" ) {
this . activeFilters = { col _filter : { } } ;
}
if ( typeof this . activeFilters . col _filter == "undefined" ) {
this . activeFilters . col _filter = { } ;
}
if ( typeof _set == 'object' ) {
for ( var s in _set ) {
if ( s == 'col_filter' ) {
// allow apps setState() to reset all col_filter by using undefined or null for it
// they can not pass {} for _set / state.state, if they need to set something
if ( _set . col _filter === undefined || _set . col _filter === null ) {
this . activeFilters . col _filter = { } ;
changed = true ;
}
else {
for ( var c in _set . col _filter ) {
if ( this . activeFilters . col _filter [ c ] !== _set . col _filter [ c ] ) {
if ( _set . col _filter [ c ] ) {
this . activeFilters . col _filter [ c ] = _set . col _filter [ c ] ;
}
else {
delete this . activeFilters . col _filter [ c ] ;
}
changed = true ;
}
}
}
}
else if ( s === 'selected' ) {
changed = true ;
keep _selection = true ;
this . controller . _selectionMgr . resetSelection ( ) ;
this . controller . _objectManager . clear ( ) ;
for ( var i in _set . selected ) {
this . controller . _selectionMgr . setSelected ( _set . selected [ i ] . indexOf ( '::' ) > 0 ? _set . selected [ i ] : this . controller . dataStorePrefix + '::' + _set . selected [ i ] , true ) ;
}
delete _set . selected ;
}
else if ( this . activeFilters [ s ] !== _set [ s ] ) {
this . activeFilters [ s ] = _set [ s ] ;
changed = true ;
}
}
}
this . egw ( ) . debug ( "info" , "Changing nextmatch filters to " , this . activeFilters ) ;
// Keep the selection after applying filters, but only if unchanged
if ( ! changed || keep _selection ) {
this . controller . keepSelection ( ) ;
}
else {
// Do not keep selection
2014-05-29 18:21:41 +02:00
this . controller . _selectionMgr . resetSelection ( ) ;
2020-01-24 12:14:08 +01:00
this . controller . _objectManager . clear ( ) ;
this . controller . keepSelection ( ) ;
}
// Update the filters in the grid controller
this . controller . setFilters ( this . activeFilters ) ;
// Update the header
this . header . setFilters ( this . activeFilters ) ;
// Update any column filters
this . iterateOver ( function ( column ) {
// Skip favorites - it implements et2_INextmatchHeader, but we don't want it in the filter
if ( typeof column . id != "undefined" && column . id . indexOf ( 'favorite' ) == 0 )
return ;
if ( typeof column . set _value != "undefined" && column . id ) {
column . set _value ( typeof this [ column . id ] == "undefined" || this [ column . id ] == null ? "" : this [ column . id ] ) ;
}
if ( column . id && typeof column . get _value == "function" ) {
this [ column . id ] = column . get _value ( ) ;
}
} , this . activeFilters . col _filter , et2 _INextmatchHeader ) ;
// Trigger an update
this . controller . update ( true ) ;
if ( changed ) {
// Highlight matching favorite in sidebox
if ( this . getInstanceManager ( ) . app ) {
var appname = this . getInstanceManager ( ) . app ;
if ( app [ appname ] && app [ appname ] . highlight _favorite ) {
app [ appname ] . highlight _favorite ( ) ;
}
}
}
this . update _in _progress = false ;
} ;
/ * *
* Refresh given rows for specified change
*
* Change type parameters allows for quicker refresh then complete server side reload :
* - update : request just modified data from given rows . Sorting is not considered ,
* so if the sort field is changed , the row will not be moved .
2020-07-29 13:58:02 +02:00
* - update - in - place : update row , but do NOT move it , or refresh if uid does not exist
2020-07-27 22:07:15 +02:00
* - edit : rows changed , but sorting may be affected . May require full reload .
2020-01-24 12:14:08 +01:00
* - delete : just delete the given rows clientside ( no server interaction neccessary )
2020-07-27 22:07:15 +02:00
* - add : put the new row in at the top , unless app says otherwise
*
* Nextmatch checks the application callback nm _refresh _index , which has a default implementation
* in egw _app . nm _refresh _index ( ) .
2020-01-24 12:14:08 +01:00
*
* @ param { string [ ] | string } _row _ids rows to refresh
* @ param { ? string } _type "update" , "edit" , "delete" or "add"
*
* @ see jsapi . egw _refresh ( )
2020-07-27 22:07:15 +02:00
* @ see egw _app . nm _refresh _index ( )
2020-01-24 12:14:08 +01:00
* @ fires refresh from the widget itself
* /
et2 _nextmatch . prototype . refresh = function ( _row _ids , _type ) {
// Framework trying to refresh, but nextmatch not fully initialized
if ( this . controller === null || ! this . div ) {
return ;
}
if ( ! this . div . is ( ':visible' ) ) // run refresh, once we become visible again
{
jQuery ( this . getInstanceManager ( ) . DOMContainer . parentNode ) . one ( 'show.et2_nextmatch' ,
// Important to use anonymous function instead of just 'this.refresh' because
// of the parameters passed
2020-07-21 23:32:13 +02:00
function ( ) { this . nm . refresh ( this . ids , this . type ) ; }
. bind ( { nm : this , ids : _row _ids , type : _type } ) ) ;
2020-01-24 12:14:08 +01:00
return ;
}
if ( typeof _type == 'undefined' )
_type = 'edit' ;
if ( typeof _row _ids == 'string' || typeof _row _ids == 'number' )
_row _ids = [ _row _ids ] ;
if ( typeof _row _ids == "undefined" || _row _ids === null ) {
this . applyFilters ( ) ;
// Trigger an event so app code can act on it
jQuery ( this ) . triggerHandler ( "refresh" , [ this ] ) ;
return ;
}
if ( _type == "delete" ) {
// Record current & next index
var uid = _row _ids [ 0 ] . toString ( ) . indexOf ( this . controller . dataStorePrefix ) == 0 ? _row _ids [ 0 ] : this . controller . dataStorePrefix + "::" + _row _ids [ 0 ] ;
var entry = this . controller . _selectionMgr . _getRegisteredRowsEntry ( uid ) ;
var next = ( entry . ao ? entry . ao . getNext ( _row _ids . length ) : null ) ;
if ( next == null || ! next . id || next . id == uid ) {
// No next, select previous
next = ( entry . ao ? entry . ao . getPrevious ( 1 ) : null ) ;
}
// Stop automatic updating
this . dataview . grid . doInvalidate = false ;
for ( var i = 0 ; i < _row _ids . length ; i ++ ) {
uid = _row _ids [ i ] . toString ( ) . indexOf ( this . controller . dataStorePrefix ) == 0 ? _row _ids [ i ] : this . controller . dataStorePrefix + "::" + _row _ids [ i ] ;
// Delete from internal references
this . controller . deleteRow ( uid ) ;
}
// Select & focus next row
if ( next && next . id ) {
this . controller . _selectionMgr . setSelected ( next . id , true ) ;
this . controller . _selectionMgr . setFocused ( next . id , true ) ;
}
// Update the count
var total = this . dataview . grid . _total - _row _ids . length ;
// This will remove the last row!
// That's OK, because grid adds one in this.controller.deleteRow()
this . dataview . grid . setTotalCount ( total ) ;
// Re-enable automatic updating
this . dataview . grid . doInvalidate = true ;
this . dataview . grid . invalidate ( ) ;
}
id _loop : for ( var i = 0 ; i < _row _ids . length ; i ++ ) {
var uid = _row _ids [ i ] . toString ( ) . indexOf ( this . controller . dataStorePrefix ) == 0 ? _row _ids [ i ] : this . controller . dataStorePrefix + "::" + _row _ids [ i ] ;
switch ( _type ) {
2020-07-29 13:58:02 +02:00
case "update-in-place" :
this . egw ( ) . dataRefreshUID ( uid ) ;
break ;
2020-07-27 22:07:15 +02:00
case "edit" :
2020-01-24 12:14:08 +01:00
case "update" :
2020-07-22 19:59:42 +02:00
if ( ! this . refresh _update ( uid ) ) {
// Could not update just the row, full refresh has been requested
2020-01-24 12:14:08 +01:00
break id _loop ;
}
break ;
case "delete" :
// Handled above, more code to execute after loop
break ;
case "add" :
2020-07-22 16:58:21 +02:00
if ( this . refresh _add ( uid ) )
break ;
// fall-through / full refresh, if refresh_add returns false
2020-01-24 12:14:08 +01:00
default :
// Trigger refresh
this . applyFilters ( ) ;
break id _loop ;
}
}
// Trigger an event so app code can act on it
jQuery ( this ) . triggerHandler ( "refresh" , [ this , _row _ids , _type ] ) ;
} ;
2020-07-22 19:59:42 +02:00
/ * *
* An entry has been updated . Request new data , and ask app about where the row
* goes now .
*
* @ param uid
* /
et2 _nextmatch . prototype . refresh _update = function ( uid ) {
if ( ! this . egw ( ) . dataRefreshUID ( uid ) ) {
// Could not update just that row
this . applyFilters ( ) ;
return false ;
}
// Row data update has been sent, let's move it where app wants it
var entry = this . controller . _selectionMgr . _getRegisteredRowsEntry ( uid ) ;
// Need to delete first as there's a good chance indexes will change in an unknown way
// and we can't always find it by UID after due to duplication
this . controller . _grid . deleteRow ( entry . idx ) ;
// Pretend it's a new row, let app tell us where it goes and we'll mark it as new
if ( ! this . refresh _add ( uid , "update" ) ) {
// App did not want the row, or doesn't know where it goes but we've already removed it...
// Put it back before anyone notices. New data coming from server anyway.
var callback _1 = function ( data ) {
2020-07-22 21:27:40 +02:00
data . class += " new_entry" ;
2020-07-22 19:59:42 +02:00
this . egw ( ) . dataUnregisterUID ( uid , callback _1 , this ) ;
} ;
this . egw ( ) . dataRegisterUID ( uid , callback _1 , this , this . getInstanceManager ( ) . etemplate _exec _id , this . id ) ;
this . controller . _insertDataRow ( entry , true ) ;
}
return true ;
} ;
2020-07-20 21:43:26 +02:00
/ * *
* An entry has been added . Put it in the list .
*
* @ param uid
2020-07-22 16:58:21 +02:00
* @ return boolean false : not added , true : added
2020-07-20 21:43:26 +02:00
* /
2020-07-22 19:59:42 +02:00
et2 _nextmatch . prototype . refresh _add = function ( uid , type ) {
if ( type === void 0 ) { type = "add" ; }
2020-07-21 23:32:13 +02:00
var index = 0 ;
var appname = this . _get _appname ( ) ;
2020-07-22 19:59:42 +02:00
if ( appname && this . egw ( ) . window . app [ appname ] && typeof this . egw ( ) . window . app [ appname ] . nm _refresh _index == "function" ) {
2020-07-21 23:32:13 +02:00
var sort = Object . values ( this . controller . _indexMap ) . map ( function ( e ) { return ( { index : e . idx , uid : e . uid } ) ; } ) ;
2020-07-22 19:59:42 +02:00
index = this . egw ( ) . window . app [ appname ] . nm _refresh _index ( this , uid , sort , type ) ;
2020-07-21 23:32:13 +02:00
}
// App cancelled the add
if ( index === false ) {
2020-07-22 16:58:21 +02:00
return false ;
2020-07-20 21:43:26 +02:00
}
2020-07-21 23:32:13 +02:00
// Insert at the top of the list, or where app said
var entry = this . controller . _selectionMgr . _getRegisteredRowsEntry ( uid ) ;
entry . idx = typeof index == "number" ? index : 0 ;
this . controller . _insertDataRow ( entry , true ) ;
2020-07-20 21:43:26 +02:00
// Set "new entry" class - but it has to stay so register and re-add it after the data is there
entry . row . tr . addClass ( "new_entry" ) ;
var callback = function ( data ) {
2020-07-22 21:27:40 +02:00
data . class += " new_entry" ;
2020-07-20 21:43:26 +02:00
this . egw ( ) . dataUnregisterUID ( uid , callback , this ) ;
} ;
this . egw ( ) . dataRegisterUID ( uid , callback , this , this . getInstanceManager ( ) . etemplate _exec _id , this . id ) ;
2020-07-22 16:58:21 +02:00
return true ;
2020-07-20 21:43:26 +02:00
} ;
2020-07-21 23:32:13 +02:00
et2 _nextmatch . prototype . _get _appname = function ( ) {
var app = '' ;
var list = [ ] ;
list = et2 _csvSplit ( this . options . settings . columnselection _pref , 2 , "." ) ;
if ( this . options . settings . columnselection _pref . indexOf ( 'nextmatch' ) == 0 ) {
app = list [ 0 ] . substring ( 'nextmatch' . length + 1 ) ;
}
else {
app = list [ 0 ] ;
}
return app ;
} ;
2020-01-24 12:14:08 +01:00
/ * *
* Gets the selection
*
* @ return Object { ids : [ UIDs ] , inverted : boolean }
* /
et2 _nextmatch . prototype . getSelection = function ( ) {
var selected = this . controller && this . controller . _selectionMgr ? this . controller . _selectionMgr . getSelected ( ) : null ;
if ( typeof selected == "object" && selected != null ) {
return selected ;
}
return { ids : [ ] , all : false } ;
} ;
/ * *
* Event handler for when the selection changes
*
* If the onselect attribute was set to a string with javascript code , it will
* be executed "legacy style" . You can get the selected values with getSelection ( ) .
* If the onselect attribute is in app . appname . function style , it will be called
* with the nextmatch and an array of selected row IDs .
*
* The array can be empty , if user cleared the selection .
*
* @ param action ActionObject From action system . Ignored .
* @ param senders ActionObjectImplemetation From action system . Ignored .
* /
et2 _nextmatch . prototype . onselect = function ( action , senders ) {
// Execute the JS code connected to the event handler
if ( typeof this . options . onselect == 'function' ) {
return this . options . onselect . call ( this , this . getSelection ( ) . ids , this ) ;
}
} ;
2020-01-29 22:29:06 +01:00
/ * *
* Nextmatch needs a namespace
* /
et2 _nextmatch . prototype . _createNamespace = function ( ) {
return true ;
} ;
2020-01-24 12:14:08 +01:00
/ * *
* Create the dynamic height so nm fills all available space
*
* @ returns { undefined }
* /
et2 _nextmatch . prototype . _getDynheight = function ( ) {
// Find the parent container, either a tab or the main container
var tab = this . get _tab _info ( ) ;
if ( ! tab ) {
return new et2 _dynheight ( this . getInstanceManager ( ) . DOMContainer , this . innerDiv , 100 ) ;
}
else if ( tab && tab . contentDiv ) {
return new et2 _dynheight ( tab . contentDiv , this . innerDiv , 100 ) ;
}
return false ;
} ;
/ * *
* Generates the column caption for the given column widget
*
* @ param { et2 _widget } _widget
* /
et2 _nextmatch . prototype . _genColumnCaption = function ( _widget ) {
var result = null ;
if ( typeof _widget . _genColumnCaption == "function" )
return _widget . _genColumnCaption ( ) ;
var self = this ;
_widget . iterateOver ( function ( _widget ) {
var label = self . egw ( ) . lang ( _widget . options . label || _widget . options . empty _label || '' ) ;
if ( ! label )
return ; // skip empty, undefined or null labels
if ( ! result ) {
result = label ;
}
else {
result += ", " + label ;
}
} , this , et2 _INextmatchHeader ) ;
return result ;
} ;
/ * *
* Generates the column name ( internal ) for the given column widget
* Used in preferences to refer to the columns by name instead of position
*
* See _getColumnCaption ( ) for human fiendly captions
*
* @ param { et2 _widget } _widget
* /
et2 _nextmatch . prototype . _getColumnName = function ( _widget ) {
if ( typeof _widget . _getColumnName == 'function' )
return _widget . _getColumnName ( ) ;
var name = _widget . id ;
var child _names = [ ] ;
var children = _widget . getChildren ( ) ;
for ( var i = 0 ; i < children . length ; i ++ ) {
if ( children [ i ] . id )
child _names . push ( children [ i ] . id ) ;
}
var colName = name + ( name != "" && child _names . length > 0 ? "_" : "" ) + child _names . join ( "_" ) ;
if ( colName == "" ) {
this . egw ( ) . debug ( "info" , "Unable to generate nm column name for " , _widget ) ;
}
return colName ;
} ;
/ * *
* Retrieve the user ' s preferences for this nextmatch merged with defaults
* Column display , column size , etc .
* /
et2 _nextmatch . prototype . _getPreferences = function ( ) {
// Read preference or default for column visibility
var negated = false ;
var columnPreference = "" ;
if ( this . options . settings . default _cols ) {
negated = this . options . settings . default _cols [ 0 ] == "!" ;
columnPreference = negated ? this . options . settings . default _cols . substring ( 1 ) : this . options . settings . default _cols ;
}
if ( this . options . settings . selectcols && this . options . settings . selectcols . length ) {
columnPreference = this . options . settings . selectcols ;
negated = false ;
}
if ( ! this . options . settings . columnselection _pref ) {
// Set preference name so changes are saved
this . options . settings . columnselection _pref = this . options . template ;
}
var app = '' ;
var list = [ ] ;
if ( this . options . settings . columnselection _pref ) {
var pref = { } ;
list = et2 _csvSplit ( this . options . settings . columnselection _pref , 2 , "." ) ;
if ( this . options . settings . columnselection _pref . indexOf ( 'nextmatch' ) == 0 ) {
app = list [ 0 ] . substring ( 'nextmatch' . length + 1 ) ;
pref = egw . preference ( this . options . settings . columnselection _pref , app ) ;
}
else {
app = list [ 0 ] ;
// 'nextmatch-' prefix is there in preference name, but not in setting, so add it in
pref = egw . preference ( "nextmatch-" + this . options . settings . columnselection _pref , app ) ;
}
if ( pref ) {
negated = ( pref [ 0 ] == "!" ) ;
columnPreference = negated ? pref . substring ( 1 ) : pref ;
}
}
var columnDisplay = [ ] ;
// If no column preference or default set, use all columns
if ( typeof columnPreference == "string" && columnPreference . length == 0 ) {
columnDisplay = [ ] ;
negated = true ;
}
columnDisplay = typeof columnPreference === "string"
? et2 _csvSplit ( columnPreference , null , "," ) : columnPreference ;
// Adjusted column sizes
var size = { } ;
if ( this . options . settings . columnselection _pref && app ) {
var size _pref = this . options . settings . columnselection _pref + "-size" ;
// If columnselection pref is missing prefix, add it in
if ( size _pref . indexOf ( 'nextmatch' ) == - 1 ) {
size _pref = 'nextmatch-' + size _pref ;
}
size = this . egw ( ) . preference ( size _pref , app ) ;
}
if ( ! size )
size = { } ;
// Column order
var order = { } ;
for ( var i = 0 ; i < columnDisplay . length ; i ++ ) {
order [ columnDisplay [ i ] ] = i ;
}
return {
visible : columnDisplay ,
visible _negated : negated ,
negated : negated ,
size : size ,
order : order
} ;
} ;
/ * *
* Apply stored user preferences to discovered columns
*
* @ param { array } _row
* @ param { array } _colData
* /
et2 _nextmatch . prototype . _applyUserPreferences = function ( _row , _colData ) {
var prefs = this . _getPreferences ( ) ;
var columnDisplay = prefs . visible ;
var size = prefs . size ;
var negated = prefs . visible _negated ;
var order = prefs . order ;
var colName = '' ;
// Add in display preferences
if ( columnDisplay && columnDisplay . length > 0 ) {
RowLoop : for ( var i = 0 ; i < _row . length ; i ++ ) {
colName = '' ;
if ( _row [ i ] . disabled === true ) {
_colData [ i ] . visible = false ;
continue ;
}
// Customfields needs special processing
if ( _row [ i ] . widget . instanceOf ( et2 _nextmatch _customfields ) ) {
// Find cf field
for ( var j = 0 ; j < columnDisplay . length ; j ++ ) {
if ( columnDisplay [ j ] . indexOf ( _row [ i ] . widget . id ) == 0 ) {
_row [ i ] . widget . options . fields = { } ;
for ( var k = j ; k < columnDisplay . length ; k ++ ) {
if ( columnDisplay [ k ] . indexOf ( _row [ i ] . widget . prefix ) == 0 ) {
_row [ i ] . widget . options . fields [ columnDisplay [ k ] . substr ( 1 ) ] = true ;
}
}
// Resets field visibility too
_row [ i ] . widget . _getColumnName ( ) ;
_colData [ i ] . visible = ! ( negated || jQuery . isEmptyObject ( _row [ i ] . widget . options . fields ) ) ;
break ;
}
}
// Disable if there are no custom fields
if ( jQuery . isEmptyObject ( _row [ i ] . widget . customfields ) ) {
_colData [ i ] . visible = false ;
continue ;
}
colName = _row [ i ] . widget . id ;
}
else {
colName = this . _getColumnName ( _row [ i ] . widget ) ;
}
2020-04-01 19:46:05 +02:00
if ( ! negated ) {
_colData [ i ] . order = typeof order [ colName ] === 'undefined' ? i : order [ colName ] ;
}
if ( ! colName )
continue ;
2020-03-23 17:05:46 +01:00
_colData [ i ] . visible = negated ;
var stop _1 = false ;
for ( var j = 0 ; j < columnDisplay . length && ! stop _1 ; j ++ ) {
if ( columnDisplay [ j ] == colName ) {
_colData [ i ] . visible = ! negated ;
stop _1 = true ;
}
}
2020-01-24 12:14:08 +01:00
if ( size [ colName ] ) {
// Make sure percentages stay percentages, and forget any preference otherwise
if ( _colData [ i ] . width . charAt ( _colData [ i ] . width . length - 1 ) == "%" ) {
_colData [ i ] . width = typeof size [ colName ] == 'string' && size [ colName ] . charAt ( size [ colName ] . length - 1 ) == "%" ? size [ colName ] : _colData [ i ] . width ;
}
else {
_colData [ i ] . width = parseInt ( size [ colName ] ) + 'px' ;
}
}
}
}
_colData . sort ( function ( a , b ) {
return a . order - b . order ;
} ) ;
_row . sort ( function ( a , b ) {
if ( typeof a . colData !== 'undefined' && typeof b . colData !== 'undefined' ) {
return a . colData . order - b . colData . order ;
}
else if ( typeof a . order !== 'undefined' && typeof b . order !== 'undefined' ) {
return a . order - b . order ;
}
} ) ;
} ;
/ * *
* Take current column display settings and store them in this . egw ( ) . preferences
* for next time
* /
et2 _nextmatch . prototype . _updateUserPreferences = function ( ) {
var colMgr = this . dataview . getColumnMgr ( ) ;
var app = "" ;
if ( ! this . options . settings . columnselection _pref ) {
this . options . settings . columnselection _pref = this . options . template ;
}
var visibility = colMgr . getColumnVisibilitySet ( ) ;
var colDisplay = [ ] ;
var colSize = { } ;
var custom _fields = [ ] ;
// visibility is indexed by internal ID, widget is referenced by position, preference needs name
for ( var i = 0 ; i < colMgr . columns . length ; i ++ ) {
// @ts-ignore
var widget = this . columns [ i ] . widget ;
var colName = this . _getColumnName ( widget ) ;
if ( colName ) {
// Server side wants each cf listed as a seperate column
if ( widget . instanceOf ( et2 _nextmatch _customfields ) ) {
// Just the ID for server side, not the whole nm name - some apps use it to skip custom fields
colName = widget . id ;
2020-02-11 19:32:50 +01:00
for ( var name _1 in widget . options . fields ) {
if ( widget . options . fields [ name _1 ] )
2020-02-12 19:32:29 +01:00
custom _fields . push ( et2 _nextmatch _customfields . PREFIX + name _1 ) ;
2020-01-24 12:14:08 +01:00
}
}
if ( visibility [ colMgr . columns [ i ] . id ] . visible )
colDisplay . push ( colName ) ;
// When saving sizes, only save columns with explicit values, preserving relative vs fixed
// Others will be left to flex if width changes or more columns are added
if ( colMgr . columns [ i ] . relativeWidth ) {
colSize [ colName ] = ( colMgr . columns [ i ] . relativeWidth * 100 ) + "%" ;
}
else if ( colMgr . columns [ i ] . fixedWidth ) {
colSize [ colName ] = colMgr . columns [ i ] . fixedWidth ;
}
}
else if ( colMgr . columns [ i ] . fixedWidth || colMgr . columns [ i ] . relativeWidth ) {
this . egw ( ) . debug ( "info" , "Could not save column width - no name" , colMgr . columns [ i ] . id ) ;
}
}
var list = et2 _csvSplit ( this . options . settings . columnselection _pref , 2 , "." ) ;
var pref = this . options . settings . columnselection _pref ;
if ( pref . indexOf ( 'nextmatch' ) == 0 ) {
app = list [ 0 ] . substring ( 'nextmatch' . length + 1 ) ;
}
else {
app = list [ 0 ] ;
// 'nextmatch-' prefix is there in preference name, but not in setting, so add it in
pref = "nextmatch-" + this . options . settings . columnselection _pref ;
}
// Server side wants each cf listed as a seperate column
jQuery . merge ( colDisplay , custom _fields ) ;
// Update query value, so data source can use visible columns to exclude expensive sub-queries
var oldCols = this . activeFilters . selectcols ? this . activeFilters . selectcols : [ ] ;
this . activeFilters . selectcols = this . sortedColumnsList ? this . sortedColumnsList : colDisplay ;
// We don't need to re-query if they've removed a column
var changed = [ ] ;
ColLoop : for ( var i = 0 ; i < colDisplay . length ; i ++ ) {
for ( var j = 0 ; j < oldCols . length ; j ++ ) {
if ( colDisplay [ i ] == oldCols [ j ] )
continue ColLoop ;
}
changed . push ( colDisplay [ i ] ) ;
}
// If a custom field column was added, throw away cache to deal with
// efficient apps that didn't send all custom fields in the first request
var cf _added = jQuery ( changed ) . filter ( jQuery ( custom _fields ) ) . length > 0 ;
2020-04-09 20:39:16 +02:00
// Save visible columns and sizes if selectcols is not emtpy (an empty selectcols actually deletes the prefrence)
if ( ! jQuery . isEmptyObject ( this . activeFilters . selectcols ) ) {
// 'nextmatch-' prefix is there in preference name, but not in setting, so add it in
this . egw ( ) . set _preference ( app , pref , this . activeFilters . selectcols . join ( "," ) ,
// Use callback after the preference gets set to trigger refresh, in case app
// isn't looking at selectcols and just uses preference
cf _added ? jQuery . proxy ( function ( ) { if ( this . controller )
this . controller . update ( true ) ; } , this ) : null ) ;
// Save adjusted column sizes and inform user about it
this . egw ( ) . set _preference ( app , pref + "-size" , colSize ) ;
this . egw ( ) . message ( this . egw ( ) . lang ( "Saved column sizes to preferences." ) ) ;
}
2020-01-24 12:14:08 +01:00
this . egw ( ) . set _preference ( app , pref + "-size" , colSize ) ;
// No significant change (just normal columns shown) and no need to wait,
// but the grid still needs to be redrawn if a custom field was removed because
// the cell content changed. This is a cheaper refresh than the callback,
// this.controller.update(true)
if ( ( changed . length || custom _fields . length ) && ! cf _added )
this . applyFilters ( ) ;
} ;
et2 _nextmatch . prototype . _parseHeaderRow = function ( _row , _colData ) {
// Make sure there's a widget - cols disabled in template can be missing them, and the header really likes to have a widget
for ( var x = 0 ; x < _row . length ; x ++ ) {
if ( ! _row [ x ] . widget ) {
_row [ x ] . widget = et2 _createWidget ( "label" , { } ) ;
}
}
// Get column display preference
this . _applyUserPreferences ( _row , _colData ) ;
// Go over the header row and create the column entries
this . columns = new Array ( _row . length ) ;
var columnData = new Array ( _row . length ) ;
// No action columns in et2
var remove _action _index = null ;
for ( var x = 0 ; x < _row . length ; x ++ ) {
this . columns [ x ] = jQuery . extend ( {
"order" : _colData [ x ] && typeof _colData [ x ] . order !== 'undefined' ? _colData [ x ] . order : x ,
"widget" : _row [ x ] . widget
} , _colData [ x ] ) ;
var visibility = ( ! _colData [ x ] || _colData [ x ] . visible ) ?
2020-01-31 21:07:27 +01:00
et2 _dataview _model _columns _1 . et2 _dataview _column . ET2 _COL _VISIBILITY _VISIBLE :
et2 _dataview _model _columns _1 . et2 _dataview _column . ET2 _COL _VISIBILITY _INVISIBLE ;
2020-01-24 12:14:08 +01:00
if ( _colData [ x ] . disabled && _colData [ x ] . disabled !== '' &&
this . getArrayMgr ( "content" ) . parseBoolExpression ( _colData [ x ] . disabled ) ) {
2020-01-31 21:07:27 +01:00
visibility = et2 _dataview _model _columns _1 . et2 _dataview _column . ET2 _COL _VISIBILITY _DISABLED ;
2020-06-09 21:56:52 +02:00
this . columns [ x ] . visible = false ;
2020-01-24 12:14:08 +01:00
}
columnData [ x ] = {
"id" : "col_" + x ,
// @ts-ignore
"order" : this . columns [ x ] . order ,
"caption" : this . _genColumnCaption ( _row [ x ] . widget ) ,
"visibility" : visibility ,
"width" : _colData [ x ] ? _colData [ x ] . width : 0
} ;
if ( _colData [ x ] . width === 'auto' ) {
// Column manager does not understand 'auto', which grid widget
// uses if width is not set
columnData [ x ] . width = '100%' ;
}
if ( _colData [ x ] . minWidth ) {
columnData [ x ] . minWidth = _colData [ x ] . minWidth ;
}
if ( _colData [ x ] . maxWidth ) {
columnData [ x ] . maxWidth = _colData [ x ] . maxWidth ;
}
// No action columns in et2
var colName = this . _getColumnName ( _row [ x ] . widget ) ;
if ( colName == 'actions' || colName == 'legacy_actions' || colName == 'legacy_actions_check_all' ) {
remove _action _index = x ;
}
else if ( ! colName ) {
// Unnamed column cannot be toggled or saved
2020-04-01 19:46:05 +02:00
columnData [ x ] . visibility = et2 _dataview _model _columns _1 . et2 _dataview _column . ET2 _COL _VISIBILITY _ALWAYS _NOSELECT ;
this . columns [ x ] . visible = true ;
2020-01-24 12:14:08 +01:00
}
}
// Remove action column
if ( remove _action _index != null ) {
this . columns . splice ( remove _action _index , remove _action _index ) ;
columnData . splice ( remove _action _index , remove _action _index ) ;
_colData . splice ( remove _action _index , remove _action _index ) ;
}
// Create the column manager and update the grid container
2020-01-30 00:05:01 +01:00
this . dataview . setColumns ( columnData ) ;
2020-01-24 12:14:08 +01:00
for ( var x = 0 ; x < _row . length ; x ++ ) {
// Append the widget to this container
this . addChild ( _row [ x ] . widget ) ;
}
// Create the nextmatch row provider
2020-01-31 21:07:27 +01:00
this . rowProvider = new et2 _extension _nextmatch _rowProvider _1 . et2 _nextmatch _rowProvider ( this . dataview . rowProvider , this . _getSubgrid , this ) ;
2020-01-24 12:14:08 +01:00
// Register handler to update preferences when column properties are changed
var self = this ;
this . dataview . onUpdateColumns = function ( ) {
// Use apply to make sure context is there
self . _updateUserPreferences . apply ( self ) ;
// Allow column widgets a chance to resize
self . iterateOver ( function ( widget ) { widget . resize ( ) ; } , self , et2 _IResizeable ) ;
} ;
// Register handler for column selection popup, or disable
if ( this . selectPopup ) {
this . selectPopup . remove ( ) ;
this . selectPopup = null ;
}
if ( this . options . settings . no _columnselection ) {
this . dataview . selectColumnsClick = function ( ) { return false ; } ;
jQuery ( 'span.selectcols' , this . dataview . headTr ) . hide ( ) ;
}
else {
jQuery ( 'span.selectcols' , this . dataview . headTr ) . show ( ) ;
this . dataview . selectColumnsClick = function ( event ) {
self . _selectColumnsClick ( event ) ;
} ;
}
} ;
et2 _nextmatch . prototype . _parseDataRow = function ( _row , _rowData , _colData ) {
2020-03-23 17:05:46 +01:00
var columnWidgets = [ ] ;
_row . sort ( function ( a , b ) {
return a . colData . order - b . colData . order ;
} ) ;
for ( var x = 0 ; x < this . columns . length ; x ++ ) {
if ( ! this . columns [ x ] . visible ) {
continue ;
}
if ( typeof _row [ x ] != "undefined" && _row [ x ] . widget ) {
columnWidgets [ x ] = _row [ x ] . widget ;
// Append the widget to this container
this . addChild ( _row [ x ] . widget ) ;
}
else {
columnWidgets [ x ] = _row [ x ] . widget ;
}
// Pass along column alignment
if ( _row [ x ] . align && columnWidgets [ x ] ) {
columnWidgets [ x ] . align = _row [ x ] . align ;
}
}
this . rowProvider . setDataRowTemplate ( columnWidgets , _rowData , this ) ;
// Create the grid controller
this . controller = new et2 _extension _nextmatch _controller _1 . et2 _nextmatch _controller ( null , this . egw ( ) , this . getInstanceManager ( ) . etemplate _exec _id , this , null , this . dataview . grid , this . rowProvider , this . options . settings . action _links , null , this . options . actions ) ;
// Need to trigger empty row the first time
if ( total == 0 )
this . controller . _emptyRow ( ) ;
// Set data cache prefix to either provided custom or auto
if ( ! this . options . settings . dataStorePrefix && this . options . settings . get _rows ) {
// Use jsapi data module to update
var list = this . options . settings . get _rows . split ( '.' , 2 ) ;
if ( list . length < 2 )
list = this . options . settings . get _rows . split ( '_' ) ; // support "app_something::method"
this . options . settings . dataStorePrefix = list [ 0 ] ;
}
this . controller . setPrefix ( this . options . settings . dataStorePrefix ) ;
// Set the view
this . controller . _view = this . view ;
// Load the initial order
/ * t h i s . c o n t r o l l e r . l o a d I n i t i a l O r d e r ( t h i s . _ g e t I n i t i a l O r d e r (
this . options . settings . rows , this . options . settings . row _id
) ) ; * /
// Set the initial row count
var total = typeof this . options . settings . total != "undefined" ?
this . options . settings . total : 0 ;
// This triggers an invalidate, which updates the grid
this . dataview . grid . setTotalCount ( total ) ;
// Insert any data sent from server, so invalidate finds data already
2020-01-24 12:14:08 +01:00
if ( this . options . settings . rows && this . options . settings . num _rows ) {
this . controller . loadInitialData ( this . options . settings . dataStorePrefix , this . options . settings . row _id , this . options . settings . rows ) ;
// Remove, to prevent duplication
delete this . options . settings . rows ;
}
} ;
et2 _nextmatch . prototype . _parseGrid = function ( _grid ) {
// Search the rows for a header-row - if one is found, parse it
for ( var y = 0 ; y < _grid . rowData . length ; y ++ ) {
// Parse the first row as a header, need header to parse the data rows
if ( _grid . rowData [ y ] [ "class" ] == "th" || y == 0 ) {
this . _parseHeaderRow ( _grid . cells [ y ] , _grid . colData ) ;
}
else {
this . _parseDataRow ( _grid . cells [ y ] , _grid . rowData [ y ] , _grid . colData ) ;
}
}
this . dataview . table . resize ( ) ;
} ;
et2 _nextmatch . prototype . _getSubgrid = function ( _row , _data , _controller ) {
// Fetch the id of the element described by _data, this will be the
// parent_id of the elements in the subgrid
var rowId = _data . content [ this . options . settings . row _id ] ;
// Create a new grid with the row as parent and the dataview grid as
// parent grid
var grid = new et2 _dataview _grid ( _row , this . dataview . grid ) ;
// Create a new controller for the grid
2020-01-31 21:07:27 +01:00
var controller = new et2 _extension _nextmatch _controller _1 . et2 _nextmatch _controller ( _controller , this . egw ( ) , this . getInstanceManager ( ) . etemplate _exec _id , this , rowId , grid , this . rowProvider , this . options . settings . action _links , _controller . getObjectManager ( ) ) ;
2020-01-24 12:14:08 +01:00
controller . update ( ) ;
// Register inside the destruction callback of the grid
grid . setDestroyCallback ( function ( ) {
2020-01-31 21:07:27 +01:00
controller . destroy ( ) ;
2020-01-24 12:14:08 +01:00
} ) ;
return grid ;
} ;
et2 _nextmatch . prototype . _getInitialOrder = function ( _rows , _rowId ) {
var _order = [ ] ;
// Get the length of the non-numerical rows arra
var len = 0 ;
for ( var key in _rows ) {
if ( ! isNaN ( parseInt ( key ) ) && parseInt ( key ) > len )
len = parseInt ( key ) ;
}
// Iterate over the rows
for ( var i = 0 ; i < len ; i ++ ) {
// Get the uid from the data
2020-02-11 19:32:50 +01:00
var uid = this . egw ( ) . app _name ( ) + '::' + _rows [ i ] [ _rowId ] ;
2020-01-24 12:14:08 +01:00
// Store the data for that uid
this . egw ( ) . dataStoreUID ( uid , _rows [ i ] ) ;
// Push the uid onto the order array
_order . push ( uid ) ;
}
return _order ;
} ;
et2 _nextmatch . prototype . _selectColumnsClick = function ( e ) {
var self = this ;
var columnMgr = this . dataview . getColumnMgr ( ) ;
// ID for faking letter selection in column selection
var LETTERS = '~search_letter~' ;
var columns = { } ;
var columns _selected = [ ] ;
for ( var i = 0 ; i < columnMgr . columns . length ; i ++ ) {
var col = columnMgr . columns [ i ] ;
var widget = this . columns [ i ] . widget ;
2020-02-12 19:16:34 +01:00
if ( col . visibility == et2 _dataview _model _columns _1 . et2 _dataview _column . ET2 _COL _VISIBILITY _DISABLED ||
col . visibility == et2 _dataview _model _columns _1 . et2 _dataview _column . ET2 _COL _VISIBILITY _ALWAYS _NOSELECT ) {
continue ;
}
if ( col . caption ) {
2020-01-24 12:14:08 +01:00
columns [ col . id ] = col . caption ;
2020-02-11 00:00:14 +01:00
if ( col . visibility == et2 _dataview _model _columns _1 . et2 _dataview _column . ET2 _COL _VISIBILITY _VISIBLE )
2020-01-24 12:14:08 +01:00
columns _selected . push ( col . id ) ;
}
// Custom fields get listed separately
if ( widget . instanceOf ( et2 _nextmatch _customfields ) ) {
if ( jQuery . isEmptyObject ( widget . customfields ) ) {
// No customfields defined, don't show column
delete ( columns [ col . id ] ) ;
continue ;
}
for ( var field _name in widget . customfields ) {
2020-02-12 19:32:29 +01:00
columns [ et2 _nextmatch _customfields . PREFIX + field _name ] = " - " +
2020-01-24 12:14:08 +01:00
widget . customfields [ field _name ] . label ;
if ( widget . options . fields [ field _name ] )
2020-02-12 19:32:29 +01:00
columns _selected . push ( et2 _extension _customfields _1 . et2 _customfields _list . PREFIX + field _name ) ;
2020-01-24 12:14:08 +01:00
}
}
}
// Letter search
if ( this . options . settings . lettersearch ) {
columns [ LETTERS ] = egw . lang ( 'Search letter' ) ;
if ( this . header . lettersearch . is ( ':visible' ) )
columns _selected . push ( LETTERS ) ;
}
// Build the popup
if ( ! this . selectPopup ) {
2020-02-11 19:32:50 +01:00
var select _1 = et2 _createWidget ( "select" , {
2020-01-24 12:14:08 +01:00
multiple : true ,
2020-03-23 17:05:46 +01:00
rows : 8 ,
empty _label : this . egw ( ) . lang ( "select columns" ) ,
selected _first : false ,
value _class : "selcolumn_sortable_"
} , this ) ;
select _1 . set _select _options ( columns ) ;
select _1 . set _value ( columns _selected ) ;
var autoRefresh _1 = et2 _createWidget ( "select" , {
"empty_label" : "Refresh"
} , this ) ;
autoRefresh _1 . set _id ( "nm_autorefresh" ) ;
autoRefresh _1 . set _select _options ( {
// Cause [unknown] problems with mail
//30: "30 seconds",
//60: "1 Minute",
180 : "3 Minutes" ,
300 : "5 Minutes" ,
900 : "15 Minutes" ,
1800 : "30 Minutes"
} ) ;
autoRefresh _1 . set _value ( this . _get _autorefresh ( ) ) ;
autoRefresh _1 . set _statustext ( egw . lang ( "Automatically refresh list" ) ) ;
var defaultCheck = et2 _createWidget ( "select" , { "empty_label" : "Preference" } , this ) ;
defaultCheck . set _id ( 'nm_col_preference' ) ;
defaultCheck . set _select _options ( {
'default' : { label : 'Default' , title : 'Set these columns as the default' } ,
'reset' : { label : 'Reset' , title : "Reset all user's column preferences" } ,
'force' : { label : 'Force' , title : 'Force column preference so users cannot change it' }
} ) ;
defaultCheck . set _value ( this . options . settings . columns _forced ? 'force' : '' ) ;
var okButton = et2 _createWidget ( "buttononly" , { "background_image" : true , image : "check" } , this ) ;
2020-01-24 12:14:08 +01:00
okButton . set _label ( this . egw ( ) . lang ( "ok" ) ) ;
okButton . onclick = function ( ) {
// Update visibility
var visibility = { } ;
for ( var i = 0 ; i < columnMgr . columns . length ; i ++ ) {
2020-02-11 19:32:50 +01:00
var col _1 = columnMgr . columns [ i ] ;
if ( col _1 . caption && col _1 . visibility !== et2 _dataview _model _columns _1 . et2 _dataview _column . ET2 _COL _VISIBILITY _ALWAYS _NOSELECT &&
col _1 . visibility !== et2 _dataview _model _columns _1 . et2 _dataview _column . ET2 _COL _VISIBILITY _DISABLED ) {
visibility [ col _1 . id ] = { visible : false } ;
2020-01-24 12:14:08 +01:00
}
}
2020-02-11 19:32:50 +01:00
var value = select _1 . getValue ( ) ;
2020-01-24 12:14:08 +01:00
// Update & remove letter filter
if ( self . header . lettersearch ) {
var show _letters = true ;
if ( value . indexOf ( LETTERS ) >= 0 ) {
value . splice ( value . indexOf ( LETTERS ) , 1 ) ;
}
else {
show _letters = false ;
}
self . _set _lettersearch ( show _letters ) ;
}
var column = 0 ;
for ( var i = 0 ; i < value . length ; i ++ ) {
// Handle skipped columns
while ( value [ i ] != "col_" + column && column < columnMgr . columns . length ) {
column ++ ;
}
if ( visibility [ value [ i ] ] ) {
visibility [ value [ i ] ] . visible = true ;
}
// Custom fields are listed seperately in column list, but are only 1 column
if ( self . columns [ column ] && self . columns [ column ] . widget . instanceOf ( et2 _nextmatch _customfields ) ) {
var cf = self . columns [ column ] . widget . options . customfields ;
var visible = self . columns [ column ] . widget . options . fields ;
// Turn off all custom fields
for ( var field _name in cf ) {
visible [ field _name ] = false ;
}
// Turn on selected custom fields - start from 0 in case they're not in order
for ( var j = 0 ; j < value . length ; j ++ ) {
2020-02-12 19:32:29 +01:00
if ( value [ j ] . indexOf ( et2 _extension _customfields _1 . et2 _customfields _list . PREFIX ) != 0 )
2020-01-24 12:14:08 +01:00
continue ;
visible [ value [ j ] . substring ( 1 ) ] = true ;
i ++ ;
}
self . columns [ column ] . widget . set _visible ( visible ) ;
}
}
columnMgr . setColumnVisibilitySet ( visibility ) ;
this . sortedColumnsList = [ ] ;
2020-02-11 19:32:50 +01:00
jQuery ( select _1 . getDOMNode ( ) ) . find ( 'li[class^="selcolumn_sortable_"]' ) . each ( function ( i , v ) {
2020-01-24 12:14:08 +01:00
var data _id = v . getAttribute ( 'data-value' ) ;
2020-02-11 19:32:50 +01:00
var value = select _1 . getValue ( ) ;
2020-01-24 12:14:08 +01:00
if ( data _id . match ( /^col_/ ) && value . indexOf ( data _id ) != - 1 ) {
var col _id = data _id . replace ( 'col_' , '' ) ;
var col _widget = self . columns [ col _id ] . widget ;
if ( col _widget . customfields ) {
self . sortedColumnsList . push ( col _widget . id ) ;
2020-02-11 19:32:50 +01:00
for ( var field _name _1 in col _widget . customfields ) {
if ( jQuery . isEmptyObject ( col _widget . options . fields ) || col _widget . options . fields [ field _name _1 ] == true ) {
2020-02-12 19:32:29 +01:00
self . sortedColumnsList . push ( et2 _extension _customfields _1 . et2 _customfields _list . PREFIX + field _name _1 ) ;
2020-01-24 12:14:08 +01:00
}
}
}
else {
self . sortedColumnsList . push ( self . _getColumnName ( col _widget ) ) ;
}
}
} ) ;
// Hide popup
self . selectPopup . toggle ( ) ;
self . dataview . updateColumns ( ) ;
// Auto refresh
2020-02-11 19:32:50 +01:00
self . _set _autorefresh ( autoRefresh _1 . get _value ( ) ) ;
2020-01-24 12:14:08 +01:00
// Set default or clear forced
if ( show _letters ) {
self . activeFilters . selectcols . push ( 'lettersearch' ) ;
}
self . getInstanceManager ( ) . submit ( ) ;
self . selectPopup = null ;
} ;
var cancelButton = et2 _createWidget ( "buttononly" , { "background_image" : true , image : "cancel" } , this ) ;
cancelButton . set _label ( this . egw ( ) . lang ( "cancel" ) ) ;
cancelButton . onclick = function ( ) {
self . selectPopup . toggle ( ) ;
self . selectPopup = null ;
} ;
2020-02-11 19:32:50 +01:00
var $select = jQuery ( select _1 . getDOMNode ( ) ) ;
2020-01-24 12:14:08 +01:00
$select . find ( '.ui-multiselect-checkboxes' ) . sortable ( {
placeholder : 'ui-fav-sortable-placeholder' ,
items : 'li[class^="selcolumn_sortable_col"]' ,
cancel : 'li[class^="selcolumn_sortable_#"]' ,
cursor : "move" ,
tolerance : "pointer" ,
axis : 'y' ,
containment : "parent" ,
delay : 250 ,
beforeStop : function ( event , ui ) {
jQuery ( 'li[class^="selcolumn_sortable_#"]' , this ) . css ( {
opacity : 1
} ) ;
} ,
start : function ( event , ui ) {
jQuery ( 'li[class^="selcolumn_sortable_#"]' , this ) . css ( {
opacity : 0.5
} ) ;
} ,
sort : function ( event , ui ) {
jQuery ( this ) . sortable ( "refreshPositions" ) ;
}
} ) ;
$select . disableSelection ( ) ;
$select . find ( 'li[class^="selcolumn_sortable_"]' ) . each ( function ( i , v ) {
// @ts-ignore
jQuery ( v ) . attr ( 'data-value' , ( jQuery ( v ) . find ( 'input' ) [ 0 ] . value ) ) ;
} ) ;
var $footerWrap = jQuery ( document . createElement ( "div" ) )
. addClass ( 'dialogFooterToolbar' )
. append ( okButton . getDOMNode ( ) )
. append ( cancelButton . getDOMNode ( ) ) ;
this . selectPopup = jQuery ( document . createElement ( "div" ) )
. addClass ( "colselection ui-dialog ui-widget-content" )
2020-02-11 19:32:50 +01:00
. append ( select _1 . getDOMNode ( ) )
2020-01-24 12:14:08 +01:00
. append ( $footerWrap )
. appendTo ( this . innerDiv ) ;
// Add autorefresh
2020-02-11 19:32:50 +01:00
$footerWrap . append ( autoRefresh _1 . getSurroundings ( ) . getDOMNode ( autoRefresh _1 . getDOMNode ( ) ) ) ;
2020-01-24 12:14:08 +01:00
// Add default checkbox for admins
var apps = this . egw ( ) . user ( 'apps' ) ;
if ( apps [ 'admin' ] ) {
$footerWrap . append ( defaultCheck . getSurroundings ( ) . getDOMNode ( defaultCheck . getDOMNode ( ) ) ) ;
}
}
else {
this . selectPopup . toggle ( ) ;
}
var t _position = jQuery ( e . target ) . position ( ) ;
var s _position = this . div . position ( ) ;
var max _height = this . getDOMNode ( ) . getElementsByClassName ( 'egwGridView_outer' ) [ 0 ] [ 'tBodies' ] [ 0 ] . clientHeight -
( 2 * this . selectPopup . find ( '.dialogFooterToolbar' ) . height ( ) ) ;
this . selectPopup . find ( '.ui-multiselect-checkboxes' ) . css ( 'max-height' , max _height ) ;
this . selectPopup . css ( "top" , t _position . top )
. css ( "left" , s _position . left + this . div . width ( ) - this . selectPopup . width ( ) ) ;
} ;
/ * *
* Set the currently displayed columns , without updating user ' s preference
*
* @ param { string [ ] } column _list List of column names
* @ param { boolean } trigger _update = false - explicitly trigger an update
* /
et2 _nextmatch . prototype . set _columns = function ( column _list , trigger _update ) {
if ( trigger _update === void 0 ) { trigger _update = false ; }
var columnMgr = this . dataview . getColumnMgr ( ) ;
var visibility = { } ;
// Initialize to false
for ( var i = 0 ; i < columnMgr . columns . length ; i ++ ) {
var col = columnMgr . columns [ i ] ;
if ( col . caption && col . visibility != et2 _dataview _grid . ET2 _COL _VISIBILITY _ALWAYS _NOSELECT ) {
visibility [ col . id ] = { visible : false } ;
}
}
for ( var i = 0 ; i < this . columns . length ; i ++ ) {
var widget = this . columns [ i ] . widget ;
var colName = this . _getColumnName ( widget ) ;
if ( column _list . indexOf ( colName ) !== - 1 &&
typeof visibility [ columnMgr . columns [ i ] . id ] !== 'undefined' ) {
visibility [ columnMgr . columns [ i ] . id ] . visible = true ;
}
// Custom fields are listed seperately in column list, but are only 1 column
if ( widget && widget . instanceOf ( et2 _nextmatch _customfields ) ) {
// Just the ID for server side, not the whole nm name - some apps use it to skip custom fields
colName = widget . id ;
if ( column _list . indexOf ( colName ) !== - 1 ) {
visibility [ columnMgr . columns [ i ] . id ] . visible = true ;
}
var cf = this . columns [ i ] . widget . options . customfields ;
var visible = this . columns [ i ] . widget . options . fields ;
// Turn off all custom fields
for ( var field _name in cf ) {
visible [ field _name ] = false ;
}
// Turn on selected custom fields - start from 0 in case they're not in order
for ( var j = 0 ; j < column _list . length ; j ++ ) {
2020-02-12 19:32:29 +01:00
if ( column _list [ j ] . indexOf ( et2 _extension _customfields _1 . et2 _customfields _list . PREFIX ) != 0 )
2020-01-24 12:14:08 +01:00
continue ;
visible [ column _list [ j ] . substring ( 1 ) ] = true ;
}
widget . set _visible ( visible ) ;
}
}
columnMgr . setColumnVisibilitySet ( visibility ) ;
// We don't want to update user's preference, so directly update
this . dataview . _updateColumns ( ) ;
// Allow column widgets a chance to resize
this . iterateOver ( function ( widget ) { widget . resize ( ) ; } , this , et2 _IResizeable ) ;
} ;
/ * *
* Set the letter search preference , and update the UI
*
* @ param { boolean } letters _on
* /
et2 _nextmatch . prototype . _set _lettersearch = function ( letters _on ) {
if ( letters _on ) {
this . header . lettersearch . show ( ) ;
}
else {
this . header . lettersearch . hide ( ) ;
}
var lettersearch _preference = "nextmatch-" + this . options . settings . columnselection _pref + "-lettersearch" ;
2020-02-11 19:32:50 +01:00
this . egw ( ) . set _preference ( this . egw ( ) . app _name ( ) , lettersearch _preference , letters _on ) ;
2020-01-24 12:14:08 +01:00
} ;
/ * *
* Set the auto - refresh time period , and starts the timer if not started
*
* @ param time int Refresh period , in seconds
* /
et2 _nextmatch . prototype . _set _autorefresh = function ( time ) {
// Store preference
var refresh _preference = "nextmatch-" + this . options . settings . columnselection _pref + "-autorefresh" ;
2020-07-21 23:32:13 +02:00
var app = this . _get _appname ( ) ;
2020-01-24 12:14:08 +01:00
if ( this . _get _autorefresh ( ) != time ) {
2020-07-21 23:32:13 +02:00
this . egw ( ) . set _preference ( app , refresh _preference , time ) ;
2020-01-24 12:14:08 +01:00
}
// Start / update timer
if ( this . _autorefresh _timer ) {
window . clearInterval ( this . _autorefresh _timer ) ;
delete this . _autorefresh _timer ;
}
if ( time > 0 ) {
this . _autorefresh _timer = setInterval ( jQuery . proxy ( this . controller . update , this . controller ) , time * 1000 ) ;
// Bind to tab show/hide events, so that we don't bother refreshing in the background
jQuery ( this . getInstanceManager ( ) . DOMContainer . parentNode ) . on ( 'hide.et2_nextmatch' , jQuery . proxy ( function ( e ) {
// Stop
window . clearInterval ( this . _autorefresh _timer ) ;
jQuery ( e . target ) . off ( e ) ;
// If the autorefresh time is up, bind once to trigger a refresh
// (if needed) when tab is activated again
this . _autorefresh _timer = setTimeout ( jQuery . proxy ( function ( ) {
// Check in case it was stopped / destroyed since
if ( ! this . _autorefresh _timer || ! this . getInstanceManager ( ) )
return ;
jQuery ( this . getInstanceManager ( ) . DOMContainer . parentNode ) . one ( 'show.et2_nextmatch' ,
// Important to use anonymous function instead of just 'this.refresh' because
// of the parameters passed
jQuery . proxy ( function ( ) { this . refresh ( ) ; } , this ) ) ;
} , this ) , time * 1000 ) ;
} , this ) ) ;
jQuery ( this . getInstanceManager ( ) . DOMContainer . parentNode ) . on ( 'show.et2_nextmatch' , jQuery . proxy ( function ( e ) {
// Start normal autorefresh timer again
this . _set _autorefresh ( this . _get _autorefresh ( ) ) ;
jQuery ( e . target ) . off ( e ) ;
} , this ) ) ;
}
} ;
/ * *
* Get the auto - refresh timer
*
* @ return int Refresh period , in secods
* /
et2 _nextmatch . prototype . _get _autorefresh = function ( ) {
var refresh _preference = "nextmatch-" + this . options . settings . columnselection _pref + "-autorefresh" ;
2020-07-21 23:32:13 +02:00
return this . egw ( ) . preference ( refresh _preference , this . _get _appname ( ) ) ;
2020-01-24 12:14:08 +01:00
} ;
/ * *
* When the template attribute is set , the nextmatch widget tries to load
* that template and to fetch the grid which is inside of it . It then calls
*
2020-02-11 19:32:50 +01:00
* @ param { string } template _name Full template name in the form app . template [ . template ]
2020-01-24 12:14:08 +01:00
* /
et2 _nextmatch . prototype . set _template = function ( template _name ) {
2020-02-11 19:32:50 +01:00
var template = et2 _createWidget ( "template" , { "id" : template _name } , this ) ;
2020-01-24 12:14:08 +01:00
if ( this . template ) {
// Stop early to prevent unneeded processing, and prevent infinite
// loops if the server changes the template in get_rows
if ( this . template == template _name ) {
return ;
}
// Free the grid components - they'll be re-created as the template is processed
2020-01-31 21:07:27 +01:00
this . dataview . destroy ( ) ;
this . rowProvider . destroy ( ) ;
this . controller . destroy ( ) ;
2020-01-24 12:14:08 +01:00
// Free any children from previous template
// They may get left behind because of how detached nodes are processed
// We don't use iterateOver because it checks sub-children
for ( var i = this . _children . length - 1 ; i >= 0 ; i -- ) {
var _node = this . _children [ i ] ;
2020-03-23 17:05:46 +01:00
if ( _node != this . header && _node !== template ) {
2020-01-24 12:14:08 +01:00
this . removeChild ( _node ) ;
_node . destroy ( ) ;
}
}
// Clear this setting if it's the same as the template, or
// the columns will not be loaded
if ( this . template == this . options . settings . columnselection _pref ) {
this . options . settings . columnselection _pref = template _name ;
}
2020-01-31 21:07:27 +01:00
this . dataview = new et2 _dataview _1 . et2 _dataview ( this . innerDiv , this . egw ( ) ) ;
2020-01-24 12:14:08 +01:00
}
if ( ! template ) {
this . egw ( ) . debug ( "error" , "Error while loading definition template for " +
"nextmatch widget." , template _name ) ;
return ;
}
if ( this . options . disabled ) {
return ;
}
// Deferred parse function - template might not be fully loaded
var parse = function ( template ) {
// Keep the name of the template, as we'll free up the widget after parsing
this . template = template _name ;
// Fetch the grid element and parse it
var definitionGrid = template . getChildren ( ) [ 0 ] ;
if ( definitionGrid && definitionGrid instanceof et2 _grid ) {
this . _parseGrid ( definitionGrid ) ;
}
else {
this . egw ( ) . debug ( "error" , "Nextmatch widget expects a grid to be the " +
"first child of the defined template." ) ;
return ;
}
// Free the template again, but don't remove it
setTimeout ( function ( ) {
2020-01-29 22:29:06 +01:00
template . destroy ( ) ;
2020-01-24 12:14:08 +01:00
} , 1 ) ;
// Call the "setNextmatch" function of all registered
// INextmatchHeader widgets. This updates this.activeFilters.col_filters according
// to what's in the template.
this . iterateOver ( function ( _node ) {
_node . setNextmatch ( this ) ;
} , this , et2 _INextmatchHeader ) ;
// Set filters to current values
2020-01-29 22:29:06 +01:00
// TODO this.controller.setFilters(this.activeFilters);
2020-01-24 12:14:08 +01:00
// If no data was sent from the server, and num_rows is 0, the nm will be empty.
// This triggers a cache check.
2020-01-29 22:29:06 +01:00
if ( ! this . options . settings . num _rows && this . controller ) {
2020-01-24 12:14:08 +01:00
this . controller . update ( ) ;
}
// Load the default sort order
if ( this . options . settings . order && this . options . settings . sort ) {
this . sortBy ( this . options . settings . order , this . options . settings . sort == "ASC" , false ) ;
}
// Start auto-refresh
this . _set _autorefresh ( this . _get _autorefresh ( ) ) ;
} ;
// Template might not be loaded yet, defer parsing
var promise = [ ] ;
template . loadingFinished ( promise ) ;
// Wait until template (& children) are done
jQuery . when . apply ( null , promise ) . done ( jQuery . proxy ( function ( ) {
parse . call ( this , template ) ;
if ( ! this . dynheight ) {
this . dynheight = this . _getDynheight ( ) ;
}
this . dynheight . initialized = false ;
this . resize ( ) ;
} , this ) ) ;
} ;
// Some accessors to match conventions
et2 _nextmatch . prototype . set _hide _header = function ( hide ) {
( hide ? this . header . div . hide ( ) : this . header . div . show ( ) ) ;
} ;
et2 _nextmatch . prototype . set _header _left = function ( template ) {
this . header . _build _header ( "left" , template ) ;
} ;
et2 _nextmatch . prototype . set _header _right = function ( template ) {
this . header . _build _header ( "right" , template ) ;
} ;
et2 _nextmatch . prototype . set _header _row = function ( template ) {
this . header . _build _header ( "row" , template ) ;
} ;
et2 _nextmatch . prototype . set _no _filter = function ( bool , filter _name ) {
if ( typeof filter _name == 'undefined' ) {
filter _name = 'filter' ;
}
this . options [ 'no_' + filter _name ] = bool ;
var filter = this . header [ filter _name ] ;
if ( filter ) {
filter . set _disabled ( bool ) ;
}
else if ( bool ) {
filter = this . header . _build _select ( filter _name , 'select' , this . settings [ filter _name ] , this . settings [ filter _name + '_no_lang' ] ) ;
}
} ;
et2 _nextmatch . prototype . set _no _filter2 = function ( bool ) {
this . set _no _filter ( bool , 'filter2' ) ;
} ;
/ * *
* Directly change filter value , with no server query .
*
* This allows the server app code to change filter value , and have it
* updated in the client UI .
*
* @ param { String | number } value
* /
et2 _nextmatch . prototype . set _filter = function ( value ) {
var update = this . update _in _progress ;
this . update _in _progress = true ;
this . activeFilters . filter = value ;
// Update the header
this . header . setFilters ( this . activeFilters ) ;
this . update _in _progress = update ;
} ;
/ * *
* Directly change filter2 value , with no server query .
*
* This allows the server app code to change filter2 value , and have it
* updated in the client UI .
*
* @ param { String | number } value
* /
et2 _nextmatch . prototype . set _filter2 = function ( value ) {
var update = this . update _in _progress ;
this . update _in _progress = true ;
this . activeFilters . filter2 = value ;
// Update the header
this . header . setFilters ( this . activeFilters ) ;
this . update _in _progress = update ;
} ;
/ * *
* If nextmatch starts disabled , it will need a resize after being shown
* to get all the sizing correct . Override the parent to add the resize
* when enabling .
*
* @ param { boolean } _value
* /
et2 _nextmatch . prototype . set _disabled = function ( _value ) {
var previous = this . disabled ;
2020-01-31 21:07:27 +01:00
_super . prototype . set _disabled . call ( this , _value ) ;
2020-01-24 12:14:08 +01:00
if ( previous && ! _value ) {
this . resize ( ) ;
}
} ;
/ * *
* Actions are handled by the controller , so ignore these during init .
*
* @ param { object } actions
* /
et2 _nextmatch . prototype . set _actions = function ( actions ) {
if ( actions != this . options . actions && this . controller != null && this . controller . _actionManager ) {
for ( var i = this . controller . _actionManager . children . length - 1 ; i >= 0 ; i -- ) {
this . controller . _actionManager . children [ i ] . remove ( ) ;
}
this . options . actions = actions ;
this . options . settings . action _links = this . controller . _actionLinks = this . _get _action _links ( actions ) ;
this . controller . _initActions ( actions ) ;
}
} ;
/ * *
* Switch view between row and tile .
* This should be followed by a call to change the template to match , which
* will cause a reload of the grid using the new settings .
*
* @ param { string } view Either 'tile' or 'row'
* /
et2 _nextmatch . prototype . set _view = function ( view ) {
// Restrict to the only 2 accepted values
if ( view == 'tile' ) {
this . view = 'tile' ;
}
else {
this . view = 'row' ;
}
} ;
/ * *
* Set a different / additional handler for dropped files .
*
* File dropping doesn ' t work with the action system , so we handle it in the
* nextmatch by linking automatically to the target row . This allows an additional handler .
* It should accept a row UID and a File [ ] , and return a boolean Execute the default ( link ) action
*
* @ param { String | Function } handler
* /
et2 _nextmatch . prototype . set _onfiledrop = function ( handler ) {
this . options . onfiledrop = handler ;
} ;
/ * *
* Handle drops of files by linking to the row , if possible .
*
* HTML5 / native file drops conflict with jQueryUI draggable , which handles
* all our drop actions . So we side - step the issue by registering an additional
* drop handler on the rows parent . If the row / actions itself doesn ' t handle
* the drop , it should bubble and get handled here .
*
* @ param { object } event
* @ param { object } target
* /
et2 _nextmatch . prototype . handle _drop = function ( event , target ) {
// Check to see if we can handle the link
// First, find the UID
var row = this . controller . getRowByNode ( target ) ;
var uid = row . uid || null ;
// Get the file information
var files = [ ] ;
if ( event . originalEvent && event . originalEvent . dataTransfer &&
event . originalEvent . dataTransfer . files && event . originalEvent . dataTransfer . files . length > 0 ) {
files = event . originalEvent . dataTransfer . files ;
}
else {
return false ;
}
// Exectute the custom handler code
if ( this . options . onfiledrop && ! this . options . onfiledrop . call ( this , uid , files ) ) {
return false ;
}
event . stopPropagation ( ) ;
event . preventDefault ( ) ;
if ( ! row || ! row . uid )
return false ;
// Link the file to the row
// just use a link widget, it's all already done
var split = uid . split ( '::' ) ;
var link _value = {
to _app : split . shift ( ) ,
to _id : split . join ( '::' )
} ;
// Create widget and mangle to our needs
var link = et2 _createWidget ( "link-to" , { value : link _value } , this ) ;
link . loadingFinished ( ) ;
link . file _upload . set _drop _target ( false ) ;
if ( row . row . tr ) {
// Ignore most of the UI, just use the status indicators
2020-02-11 19:32:50 +01:00
var status _1 = jQuery ( document . createElement ( "div" ) )
2020-01-24 12:14:08 +01:00
. addClass ( 'et2_link_to' )
. width ( row . row . tr . width ( ) )
. position ( { my : "left top" , at : "left top" , of : row . row . tr } )
. append ( link . status _span )
. append ( link . file _upload . progress )
. appendTo ( row . row . tr ) ;
// Bind to link event so we can remove when done
link . div . on ( 'link.et2_link_to' , function ( e , linked ) {
if ( ! linked ) {
jQuery ( "li.success" , link . file _upload . progress )
. removeClass ( 'success' ) . addClass ( 'validation_error' ) ;
}
else {
// Update row
link . _parent . refresh ( uid , 'edit' ) ;
}
// Fade out nicely
2020-02-11 19:32:50 +01:00
status _1 . delay ( linked ? 1 : 2000 )
2020-01-24 12:14:08 +01:00
. fadeOut ( 500 , function ( ) {
2020-01-31 21:07:27 +01:00
link . destroy ( ) ;
2020-02-11 19:32:50 +01:00
status _1 . remove ( ) ;
2020-01-24 12:14:08 +01:00
} ) ;
} ) ;
}
// Upload and link - this triggers the upload, which triggers the link, which triggers the cleanup and refresh
link . file _upload . set _value ( files ) ;
} ;
et2 _nextmatch . prototype . getDOMNode = function ( _sender ) {
if ( _sender == this || typeof _sender === 'undefined' ) {
return this . div [ 0 ] ;
}
if ( _sender == this . header ) {
return this . header . div [ 0 ] ;
}
for ( var i = 0 ; i < this . columns . length ; i ++ ) {
if ( this . columns [ i ] && this . columns [ i ] . widget && _sender == this . columns [ i ] . widget ) {
return this . dataview . getHeaderContainerNode ( i ) ;
}
}
// Let header have a chance
if ( _sender && _sender . _parent && _sender . _parent == this ) {
return this . header . getDOMNode ( _sender ) ;
}
return null ;
} ;
// Input widget
/ * *
* Get the current 'value' for the nextmatch
* /
et2 _nextmatch . prototype . getValue = function ( ) {
var _ids = this . getSelection ( ) ;
// Translate the internal uids back to server uids
var idsArr = _ids . ids ;
for ( var i = 0 ; i < idsArr . length ; i ++ ) {
idsArr [ i ] = idsArr [ i ] . split ( "::" ) . pop ( ) ;
}
var value = {
"selected" : idsArr
} ;
jQuery . extend ( value , this . activeFilters , this . value ) ;
return value ;
} ;
et2 _nextmatch . prototype . resetDirty = function ( ) { } ;
2020-06-29 19:55:35 +02:00
et2 _nextmatch . prototype . isDirty = function ( ) { return false ; } ;
2020-01-24 12:14:08 +01:00
et2 _nextmatch . prototype . isValid = function ( ) { return true ; } ;
et2 _nextmatch . prototype . set _value = function ( _value ) {
this . value = _value ;
} ;
// Printing
/ * *
* Prepare for printing
*
* We check for un - loaded rows , and ask the user what they want to do about them .
* If they want to print them all , we ask the server and print when they ' re loaded .
* /
et2 _nextmatch . prototype . beforePrint = function ( ) {
// Add the class, if needed
this . div . addClass ( 'print' ) ;
// Trigger resize, so we can fit on a page
this . dynheight . outerNode . css ( 'max-width' , this . div . css ( 'max-width' ) ) ;
this . resize ( ) ;
// Reset height to auto (after width resize) so there's no restrictions
this . dynheight . innerNode . css ( 'height' , 'auto' ) ;
// Check for rows that aren't loaded yet, or lots of rows
var range = this . controller . _grid . getIndexRange ( ) ;
this . print . old _height = this . controller . _grid . _scrollHeight ;
var loaded _count = range . bottom - range . top + 1 ;
var total = this . controller . _grid . getTotalCount ( ) ;
// Defer the printing to ask about columns & rows
var defer = jQuery . Deferred ( ) ;
var pref = this . options . settings . columnselection _pref ;
if ( pref . indexOf ( 'nextmatch' ) == 0 ) {
pref = 'nextmatch-' + pref ;
}
var app = this . getInstanceManager ( ) . app ;
var columns = { } ;
var columnMgr = this . dataview . getColumnMgr ( ) ;
pref += '_print' ;
var columns _selected = [ ] ;
// Get column names
for ( var i = 0 ; i < columnMgr . columns . length ; i ++ ) {
var col = columnMgr . columns [ i ] ;
var widget = this . columns [ i ] . widget ;
var colName = this . _getColumnName ( widget ) ;
if ( col . caption && col . visibility !== et2 _dataview _grid . ET2 _COL _VISIBILITY _ALWAYS _NOSELECT &&
col . visibility !== et2 _dataview _grid . ET2 _COL _VISIBILITY _DISABLED ) {
columns [ colName ] = col . caption ;
if ( col . visibility === et2 _dataview _grid . ET2 _COL _VISIBILITY _VISIBLE )
columns _selected . push ( colName ) ;
}
// Custom fields get listed separately
if ( widget . instanceOf ( et2 _nextmatch _customfields ) ) {
delete ( columns [ colName ] ) ;
colName = widget . id ;
if ( col . visibility === et2 _dataview _grid . ET2 _COL _VISIBILITY _VISIBLE && ! jQuery . isEmptyObject ( widget . customfields ) ) {
columns [ colName ] = col . caption ;
for ( var field _name in widget . customfields ) {
2020-02-12 19:32:29 +01:00
columns [ et2 _nextmatch _customfields . PREFIX + field _name ] = " - " + widget . customfields [ field _name ] . label ;
2020-01-24 12:14:08 +01:00
if ( widget . options . fields [ field _name ] && columns _selected . indexOf ( colName ) >= 0 ) {
2020-02-12 19:32:29 +01:00
columns _selected . push ( et2 _nextmatch _customfields . PREFIX + field _name ) ;
2020-01-24 12:14:08 +01:00
}
}
}
}
}
// Preference exists? Set it now
if ( this . egw ( ) . preference ( pref , app ) ) {
this . set _columns ( jQuery . extend ( [ ] , this . egw ( ) . preference ( pref , app ) ) ) ;
}
var callback = jQuery . proxy ( function ( button , value ) {
if ( button === et2 _dialog . CANCEL _BUTTON ) {
// Give dialog a chance to close, or it will be in the print
2020-02-11 19:32:50 +01:00
window . setTimeout ( function ( ) {
defer . reject ( ) ;
} , 0 ) ;
2020-01-24 12:14:08 +01:00
return ;
}
// Set CSS for orientation
this . div . addClass ( value . orientation ) ;
this . egw ( ) . set _preference ( app , pref + '_orientation' , value . orientation ) ;
// Try to tell browser about orientation
var css = '@page { size: ' + value . orientation + '; }' , head = document . head || document . getElementsByTagName ( 'head' ) [ 0 ] , style = document . createElement ( 'style' ) ;
style . type = 'text/css' ;
style . media = 'print' ;
// @ts-ignore
if ( style . styleSheet ) {
// @ts-ignore
style . styleSheet . cssText = css ;
}
else {
style . appendChild ( document . createTextNode ( css ) ) ;
}
head . appendChild ( style ) ;
this . print . orientation _style = style ;
// Trigger resize, so we can fit on a page
this . dynheight . outerNode . css ( 'max-width' , this . div . css ( 'max-width' ) ) ;
// Handle columns
this . set _columns ( value . columns ) ;
this . egw ( ) . set _preference ( app , pref , value . columns ) ;
var rows = parseInt ( value . row _count ) ;
if ( rows > total ) {
rows = total ;
}
// If they want the whole thing, style it as all
if ( button === et2 _dialog . OK _BUTTON && rows == this . controller . _grid . getTotalCount ( ) ) {
// Add the class, gives more reliable sizing
this . div . addClass ( 'print' ) ;
// Show it all
jQuery ( '.egwGridView_scrollarea' , this . div ) . css ( 'height' , 'auto' ) ;
}
// We need more rows
if ( button === 'dialog[all]' || rows > loaded _count ) {
2020-02-11 19:32:50 +01:00
var count _1 = 0 ;
var fetchedCount _1 = 0 ;
var cancel _1 = false ;
var nm _1 = this ;
var dialog _1 = et2 _dialog . show _dialog (
2020-01-24 12:14:08 +01:00
// Abort the long task if they canceled the data load
2020-02-11 19:32:50 +01:00
function ( ) {
count _1 = total ;
cancel _1 = true ;
window . setTimeout ( function ( ) {
defer . reject ( ) ;
} , 0 ) ;
} , egw . lang ( 'Loading' ) , egw . lang ( 'please wait...' ) , { } , [
2020-01-24 12:14:08 +01:00
{ "button_id" : et2 _dialog . CANCEL _BUTTON , "text" : 'cancel' , id : 'dialog[cancel]' , image : 'cancel' }
] ) ;
2020-02-11 19:32:50 +01:00
// dataFetch() is asynchronous, so all these requests just get fired off...
2020-01-24 12:14:08 +01:00
// 200 rows chosen arbitrarily to reduce requests.
do {
var ctx = {
"self" : this . controller ,
2020-02-11 19:32:50 +01:00
"start" : count _1 ,
2020-01-24 12:14:08 +01:00
"count" : Math . min ( rows , 200 ) ,
"lastModification" : this . controller . _lastModification
} ;
2020-02-11 19:32:50 +01:00
if ( nm _1 . controller . dataStorePrefix ) {
2020-01-24 12:14:08 +01:00
// @ts-ignore
2020-02-11 19:32:50 +01:00
ctx . prefix = nm _1 . controller . dataStorePrefix ;
2020-01-24 12:14:08 +01:00
}
2020-02-11 19:32:50 +01:00
nm _1 . controller . dataFetch ( { start : count _1 , num _rows : Math . min ( rows , 200 ) } , function ( data ) {
2020-01-24 12:14:08 +01:00
// Keep track
if ( data && data . order ) {
2020-02-11 19:32:50 +01:00
fetchedCount _1 += data . order . length ;
2020-01-24 12:14:08 +01:00
}
2020-02-11 19:32:50 +01:00
nm _1 . controller . _fetchCallback . apply ( this , arguments ) ;
if ( fetchedCount _1 >= rows ) {
if ( cancel _1 ) {
dialog _1 . destroy ( ) ;
2020-01-24 12:14:08 +01:00
defer . reject ( ) ;
return ;
}
// Use CSS to hide all but the requested rows
// Prevents us from showing more than requested, if actual height was less than average
2020-02-11 19:32:50 +01:00
nm _1 . print _row _selector = ".egwGridView_grid > tbody > tr:not(:nth-child(-n+" + rows + "))" ;
egw . css ( nm _1 . print _row _selector , 'display: none' ) ;
2020-01-24 12:14:08 +01:00
// No scrollbar in print view
jQuery ( '.egwGridView_scrollarea' , this . div ) . css ( 'overflow-y' , 'hidden' ) ;
// Show it all
jQuery ( '.egwGridView_scrollarea' , this . div ) . css ( 'height' , 'auto' ) ;
// Grid needs to redraw before it can be printed, so wait
window . setTimeout ( jQuery . proxy ( function ( ) {
2020-02-11 19:32:50 +01:00
dialog _1 . destroy ( ) ;
2020-01-24 12:14:08 +01:00
// Should be OK to print now
defer . resolve ( ) ;
2020-02-11 19:32:50 +01:00
} , nm _1 ) , et2 _dataview _grid . ET2 _GRID _INVALIDATE _TIMEOUT ) ;
2020-01-24 12:14:08 +01:00
}
} , ctx ) ;
2020-02-11 19:32:50 +01:00
count _1 += 200 ;
} while ( count _1 < rows ) ;
nm _1 . controller . _grid . setScrollHeight ( nm _1 . controller . _grid . getAverageHeight ( ) * ( rows + 1 ) ) ;
2020-01-24 12:14:08 +01:00
}
else {
// Don't need more rows, limit to requested and finish
// Show it all
jQuery ( '.egwGridView_scrollarea' , this . div ) . css ( 'height' , 'auto' ) ;
// Use CSS to hide all but the requested rows
// Prevents us from showing more than requested, if actual height was less than average
this . print _row _selector = ".egwGridView_grid > tbody > tr:not(:nth-child(-n+" + rows + "))" ;
egw . css ( this . print _row _selector , 'display: none' ) ;
// No scrollbar in print view
jQuery ( '.egwGridView_scrollarea' , this . div ) . css ( 'overflow-y' , 'hidden' ) ;
// Give dialog a chance to close, or it will be in the print
2020-02-11 19:32:50 +01:00
window . setTimeout ( function ( ) {
defer . resolve ( ) ;
} , 0 ) ;
2020-01-24 12:14:08 +01:00
}
} , this ) ;
var value = {
content : {
row _count : Math . min ( 100 , total ) ,
columns : this . egw ( ) . preference ( pref , app ) || columns _selected ,
orientation : this . egw ( ) . preference ( pref + '_orientation' , app )
} ,
sel _options : {
columns : columns
}
} ;
this . _create _print _dialog . call ( this , value , callback ) ;
return defer ;
} ;
/ * *
* Create and show the print dialog , which calls the provided callback when
* done . Broken out for overriding if needed .
*
* @ param { Object } value Current settings and preferences , passed to the dialog for
* the template
* @ param { Object } value . content
* @ param { Object } value . sel _options
*
* @ param { function ( int , Object ) } callback - Process the dialog response ,
* format things according to the specified orientation and fetch any needed
* rows .
*
* /
et2 _nextmatch . prototype . _create _print _dialog = function ( value , callback ) {
var base _url = this . getInstanceManager ( ) . template _base _url ;
if ( base _url . substr ( base _url . length - 1 ) == '/' )
base _url = base _url . slice ( 0 , - 1 ) ; // otherwise we generate a url //api/templates, which is wrong
var tab = this . get _tab _info ( ) ;
// Get title for print dialog from settings or tab, if available
var title = this . options . settings . label ? this . options . settings . label : ( tab ? tab . label : '' ) ;
var dialog = et2 _createWidget ( "dialog" , {
// If you use a template, the second parameter will be the value of the template, as if it were submitted.
callback : callback ,
buttons : et2 _dialog . BUTTONS _OK _CANCEL ,
title : this . egw ( ) . lang ( 'Print' ) + ' ' + this . egw ( ) . lang ( title ) ,
template : this . egw ( ) . link ( base _url + '/api/templates/default/nm_print_dialog.xet' ) ,
value : value
} ) ;
} ;
/ * *
* Try to clean up the mess we made getting ready for printing
* in beforePrint ( )
* /
et2 _nextmatch . prototype . afterPrint = function ( ) {
this . div . removeClass ( 'print landscape portrait' ) ;
jQuery ( this . print . orientation _style ) . remove ( ) ;
delete this . print . orientation _style ;
// Put scrollbar back
jQuery ( '.egwGridView_scrollarea' , this . div ) . css ( 'overflow-y' , '' ) ;
// Correct size of grid, and trigger resize to fix it
this . controller . _grid . setScrollHeight ( this . print . old _height ) ;
delete this . print . old _height ;
// Remove CSS rule hiding extra rows
if ( this . print . row _selector ) {
2020-02-11 19:32:50 +01:00
egw . css ( this . print . row _selector , '' ) ;
2020-01-24 12:14:08 +01:00
delete this . print . row _selector ;
}
// Restore columns
var pref = [ ] ;
var app = this . getInstanceManager ( ) . app ;
if ( this . options . settings . columnselection _pref . indexOf ( 'nextmatch' ) == 0 ) {
pref = egw . preference ( this . options . settings . columnselection _pref , app ) ;
}
else {
// 'nextmatch-' prefix is there in preference name, but not in setting, so add it in
pref = egw . preference ( "nextmatch-" + this . options . settings . columnselection _pref , app ) ;
}
if ( pref ) {
if ( typeof pref === 'string' )
pref = pref . split ( ',' ) ;
2020-02-11 19:32:50 +01:00
// @ts-ignore
2020-01-24 12:14:08 +01:00
this . set _columns ( pref , app ) ;
}
this . dynheight . outerNode . css ( 'max-width' , 'inherit' ) ;
this . resize ( ) ;
} ;
et2 _nextmatch . _attributes = {
// These normally set in settings, but broken out into attributes to allow run-time changes
"template" : {
"name" : "Template" ,
"type" : "string" ,
"description" : "The id of the template which contains the grid layout."
} ,
"hide_header" : {
"name" : "Hide header" ,
"type" : "boolean" ,
"description" : "Hide the header" ,
"default" : false
} ,
"header_left" : {
"name" : "Left custom template" ,
"type" : "string" ,
"description" : "Customise the nextmatch - left side. Provided template becomes a child of nextmatch, and any input widgets are automatically bound to refresh the nextmatch on change. Any inputs with an onChange attribute can trigger the nextmatch to refresh by returning true." ,
"default" : ""
} ,
"header_right" : {
"name" : "Right custom template" ,
"type" : "string" ,
"description" : "Customise the nextmatch - right side. Provided template becomes a child of nextmatch, and any input widgets are automatically bound to refresh the nextmatch on change. Any inputs with an onChange attribute can trigger the nextmatch to refresh by returning true." ,
"default" : ""
} ,
"header_row" : {
"name" : "Inline custom template" ,
"type" : "string" ,
"description" : "Customise the nextmatch - inline, after row count. Provided template becomes a child of nextmatch, and any input widgets are automatically bound to refresh the nextmatch on change. Any inputs with an onChange attribute can trigger the nextmatch to refresh by returning true." ,
"default" : ""
} ,
"no_filter" : {
"name" : "No filter" ,
"type" : "boolean" ,
"description" : "Hide the first filter" ,
"default" : et2 _no _init
} ,
"no_filter2" : {
"name" : "No filter2" ,
"type" : "boolean" ,
"description" : "Hide the second filter" ,
"default" : et2 _no _init
} ,
"view" : {
"name" : "View" ,
"type" : "string" ,
"description" : "Display entries as either 'row' or 'tile'. A matching template must also be set after changing this." ,
"default" : et2 _no _init
} ,
"onselect" : {
"name" : "onselect" ,
"type" : "js" ,
"default" : et2 _no _init ,
"description" : "JS code which gets executed when rows are selected. Can also be a app.appname.func(selected) style method"
} ,
"onfiledrop" : {
"name" : "onFileDrop" ,
"type" : "js" ,
"default" : et2 _no _init ,
"description" : "JS code that gets executed when a _file_ is dropped on a row. Other drop interactions are handled by the action system. Return false to prevent the default link action."
} ,
2020-07-20 21:43:26 +02:00
"onadd" : {
"name" : "onAdd" ,
"type" : "js" ,
"default" : et2 _no _init ,
"description" : "JS code that gets executed when a new entry is added via refresh(). Allows apps to override the default handling. Return false to cancel the add."
} ,
2020-01-24 12:14:08 +01:00
"settings" : {
"name" : "Settings" ,
"type" : "any" ,
"description" : "The nextmatch settings" ,
"default" : { }
}
} ;
2020-01-24 14:43:08 +01:00
et2 _nextmatch . legacyOptions = [ "template" , "hide_header" , "header_left" , "header_right" ] ;
2020-01-24 12:14:08 +01:00
return et2 _nextmatch ;
} ( et2 _core _DOMWidget _1 . et2 _DOMWidget ) ) ;
exports . et2 _nextmatch = et2 _nextmatch ;
et2 _core _widget _1 . et2 _register _widget ( et2 _nextmatch , [ "nextmatch" ] ) ;
2011-09-23 21:09:52 +02:00
/ * *
* Standard nextmatch header bar , containing filters , search , record count , letter filters , etc .
2011-09-26 21:11:01 +02:00
*
* Unable to use an existing template for this because parent ( nm ) doesn 't, and template widget doesn' t
* actually load templates from the server .
2013-04-13 21:00:13 +02:00
* @ augments et2 _DOMWidget
2011-09-23 21:09:52 +02:00
* /
2020-01-31 21:07:27 +01:00
var et2 _nextmatch _header _bar = /** @class */ ( function ( _super ) {
_ _extends ( et2 _nextmatch _header _bar , _super ) ;
2020-01-24 12:14:08 +01:00
/ * *
* Constructor
*
2020-02-11 19:32:50 +01:00
* @ param _parent
* @ param _attrs
* @ param _child
2020-01-24 12:14:08 +01:00
* /
function et2 _nextmatch _header _bar ( _parent , _attrs , _child ) {
2020-01-31 21:07:27 +01:00
var _this = _super . call ( this , _parent , [ _parent , _parent . options . settings ] , et2 _core _inheritance _1 . ClassWithAttributes . extendAttributes ( et2 _nextmatch _header _bar . _attributes , _child || { } ) ) || this ;
2020-01-24 12:14:08 +01:00
_this . nextmatch = _parent ;
_this . div = jQuery ( document . createElement ( "div" ) )
. addClass ( "nextmatch_header" ) ;
_this . _createHeader ( ) ;
// Flag to avoid loops while updating filters
_this . update _in _progress = false ;
return _this ;
}
et2 _nextmatch _header _bar . prototype . destroy = function ( ) {
this . nextmatch = null ;
2020-01-31 21:07:27 +01:00
_super . prototype . destroy . call ( this ) ;
2020-01-24 12:14:08 +01:00
this . div = null ;
} ;
et2 _nextmatch _header _bar . prototype . setNextmatch = function ( nextmatch ) {
var create _once = ( this . nextmatch == null ) ;
this . nextmatch = nextmatch ;
if ( create _once ) {
this . _createHeader ( ) ;
}
// Bind row count
this . nextmatch . dataview . grid . setInvalidateCallback ( function ( ) {
this . count _total . text ( this . nextmatch . dataview . grid . getTotalCount ( ) + "" ) ;
} , this ) ;
} ;
/ * *
* Actions are handled by the controller , so ignore these
*
* @ param { object } actions
* /
et2 _nextmatch _header _bar . prototype . set _actions = function ( actions ) { } ;
et2 _nextmatch _header _bar . prototype . _createHeader = function ( ) {
var button ;
var self = this ;
var nm _div = this . nextmatch . getDOMNode ( ) ;
var settings = this . nextmatch . options . settings ;
this . div . prependTo ( nm _div ) ;
// Left & Right (& row) headers
this . headers = [
{ id : this . nextmatch . options . header _left } ,
{ id : this . nextmatch . options . header _right } ,
{ id : this . nextmatch . options . header _row }
] ;
// The rest of the header
this . header _div = this . row _div = jQuery ( document . createElement ( "div" ) )
. addClass ( "nextmatch_header_row" )
. appendTo ( this . div ) ;
this . filter _div = jQuery ( document . createElement ( "div" ) )
. addClass ( 'filtersContainer' )
. appendTo ( this . row _div ) ;
// Search
this . search _box = jQuery ( document . createElement ( "div" ) )
. addClass ( 'search' )
. prependTo ( egwIsMobile ( ) ? this . nextmatch . getDOMNode ( ) : this . row _div ) ;
// searchbox widget options
var searchbox _options = {
id : "search" ,
overlay : ( typeof settings . searchbox != 'undefined' && typeof settings . searchbox . overlay != 'undefined' ) ? settings . searchbox . overlay : false ,
onchange : function ( ) {
self . nextmatch . applyFilters ( { search : this . get _value ( ) } ) ;
} ,
value : settings . search ,
fix : ! egwIsMobile ( )
} ;
// searchbox widget
this . et2 _searchbox = et2 _createWidget ( 'searchbox' , searchbox _options , this ) ;
// Set activeFilters to current value
this . nextmatch . activeFilters . search = settings . search ;
this . et2 _searchbox . set _value ( settings . search ) ;
/ * *
* Mobile theme specific part for nm header
* nm header has very different behaivior for mobile theme and basically
* it has its own markup separately from nm header in normal templates .
* /
if ( egwIsMobile ( ) ) {
this . search _box . addClass ( 'nm-mob-header' ) ;
jQuery ( this . div ) . css ( { display : 'inline-block' } ) . addClass ( 'nm_header_hide' ) ;
//indicates appname in header
jQuery ( document . createElement ( 'div' ) )
. addClass ( 'nm_appname_header' )
. text ( egw . lang ( egw . app _name ( ) ) )
. appendTo ( this . search _box ) ;
this . delete _action = jQuery ( document . createElement ( 'div' ) )
. addClass ( 'nm_delete_action' )
. prependTo ( this . search _box ) ;
// toggle header
// add new button
this . fav _span = jQuery ( document . createElement ( 'div' ) )
. addClass ( 'nm_favorites_div' )
. prependTo ( this . search _box ) ;
// toggle header menu
this . toggle _header = jQuery ( document . createElement ( 'button' ) )
. addClass ( 'nm_toggle_header' )
. click ( function ( ) {
jQuery ( self . div ) . toggleClass ( 'nm_header_hide' ) ;
jQuery ( this ) . toggleClass ( 'nm_toggle_header_on' ) ;
window . setTimeout ( function ( ) { self . nextmatch . resize ( ) ; } , 800 ) ;
} )
. prependTo ( this . search _box ) ;
// Context menu
this . action _header = jQuery ( document . createElement ( 'button' ) )
. addClass ( 'nm_action_header' )
. hide ( )
. click ( function ( e ) {
// @ts-ignore
jQuery ( 'tr.selected' , self . nextmatch . getDOMNode ( ) ) . trigger ( { type : 'contextmenu' , which : 3 , originalEvent : e } ) ;
} )
. prependTo ( this . search _box ) ;
}
// Add category
if ( ! settings . no _cat ) {
if ( typeof settings . cat _id _label == 'undefined' )
settings . cat _id _label = '' ;
this . category = this . _build _select ( 'cat_id' , settings . cat _is _select ?
'select' : 'select-cat' , settings . cat _id , settings . cat _is _select !== true , {
multiple : false ,
tags : true ,
class : "select-cat" ,
value _class : settings . cat _id _class
} ) ;
}
// Filter 1
if ( ! settings . no _filter ) {
this . filter = this . _build _select ( 'filter' , 'select' , settings . filter , settings . filter _no _lang ) ;
}
// Filter 2
if ( ! settings . no _filter2 ) {
this . filter2 = this . _build _select ( 'filter2' , 'select' , settings . filter2 , settings . filter2 _no _lang , {
multiple : false ,
tags : settings . filter2 _tags ,
class : "select-cat" ,
value _class : settings . filter2 _class
} ) ;
}
// Other stuff
this . right _div = jQuery ( document . createElement ( "div" ) )
. addClass ( 'header_row_right' ) . appendTo ( this . row _div ) ;
// Record count
this . count = jQuery ( document . createElement ( "span" ) )
. addClass ( "header_count ui-corner-all" ) ;
// Need to figure out how to update this as grid scrolls
// this.count.append("? - ? ").append(egw.lang("of")).append(" ");
this . count _total = jQuery ( document . createElement ( "span" ) )
. appendTo ( this . count )
. text ( settings . total + "" ) ;
this . count . prependTo ( this . right _div ) ;
// Favorites
this . _setup _favorites ( settings [ 'favorites' ] ) ;
// Export
if ( typeof settings . csv _fields != "undefined" && settings . csv _fields != false ) {
2020-02-11 19:32:50 +01:00
var definition _1 = settings . csv _fields ;
2020-01-24 12:14:08 +01:00
if ( settings . csv _fields === true ) {
2020-02-11 19:32:50 +01:00
definition _1 = egw . preference ( 'nextmatch-export-definition' , this . nextmatch . egw ( ) . app _name ( ) ) ;
2020-01-24 12:14:08 +01:00
}
var button _1 = et2 _createWidget ( "buttononly" , { id : "export" , "statustext" : "Export" , image : "download" , "background_image" : true } , this ) ;
jQuery ( button _1 . getDOMNode ( ) )
. click ( this . nextmatch , function ( event ) {
// @ts-ignore
egw _openWindowCentered2 ( egw . link ( '/index.php' , {
'menuaction' : 'importexport.importexport_export_ui.export_dialog' ,
'appname' : event . data . egw ( ) . getAppName ( ) ,
2020-02-11 19:32:50 +01:00
'definition' : definition _1
2020-01-24 12:14:08 +01:00
} ) , '_blank' , 850 , 440 , 'yes' ) ;
} ) ;
}
// Another place to customize nextmatch
this . header _row = jQuery ( document . createElement ( "div" ) )
. addClass ( 'header_row' ) . appendTo ( this . right _div ) ;
// Letter search
var current _letter = this . nextmatch . options . settings . searchletter ?
this . nextmatch . options . settings . searchletter :
( this . nextmatch . activeFilters ? this . nextmatch . activeFilters . searchletter : false ) ;
if ( this . nextmatch . options . settings . lettersearch || current _letter ) {
this . lettersearch = jQuery ( document . createElement ( "table" ) )
. addClass ( 'nextmatch_lettersearch' )
. css ( "width" , "100%" )
. appendTo ( this . div ) ;
var tbody = jQuery ( document . createElement ( "tbody" ) ) . appendTo ( this . lettersearch ) ;
var row = jQuery ( document . createElement ( "tr" ) ) . appendTo ( tbody ) ;
// Capitals, A-Z
var letters = this . egw ( ) . lang ( 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' ) . split ( '' ) ;
for ( var i in letters ) {
button = jQuery ( document . createElement ( "td" ) )
. addClass ( "lettersearch" )
. appendTo ( row )
. attr ( "id" , letters [ i ] )
. text ( letters [ i ] ) ;
if ( letters [ i ] == current _letter )
button . addClass ( "lettersearch_active" ) ;
}
button = jQuery ( document . createElement ( "td" ) )
. addClass ( "lettersearch" )
. appendTo ( row )
. attr ( "id" , "" )
. text ( egw . lang ( "all" ) ) ;
if ( ! current _letter )
button . addClass ( "lettersearch_active" ) ;
this . lettersearch . click ( this . nextmatch , function ( event ) {
// this is the lettersearch table
jQuery ( "td" , this ) . removeClass ( "lettersearch_active" ) ;
jQuery ( event . target ) . addClass ( "lettersearch_active" ) ;
event . data . applyFilters ( { searchletter : event . target . id || false } ) ;
} ) ;
// Set activeFilters to current value
this . nextmatch . activeFilters . searchletter = current _letter ;
}
// Apply letter search preference
var lettersearch _preference = "nextmatch-" + this . nextmatch . options . settings . columnselection _pref + "-lettersearch" ;
2020-02-11 19:32:50 +01:00
if ( this . lettersearch && ! egw . preference ( lettersearch _preference , this . nextmatch . egw ( ) . app _name ( ) ) ) {
2020-01-24 12:14:08 +01:00
this . lettersearch . hide ( ) ;
}
} ;
/ * *
* Build & bind to a sub - template into the header
*
* @ param { string } location One of left , right , or row
* @ param { string } template _name Name of the template to load into the location
* /
et2 _nextmatch _header _bar . prototype . _build _header = function ( location , template _name ) {
var id = location == "left" ? 0 : ( location == "right" ? 1 : 2 ) ;
var existing = this . headers [ id ] ;
// @ts-ignore
if ( existing && existing . _type ) {
if ( existing . id == template _name )
return ;
existing . destroy ( ) ;
this . headers [ id ] = null ;
}
2020-01-24 14:43:08 +01:00
if ( ! template _name )
return ;
2020-01-24 12:14:08 +01:00
// Load the template
var self = this ;
var header = et2 _createWidget ( "template" , { "id" : template _name } , this ) ;
this . headers [ id ] = header ;
var deferred = [ ] ;
header . loadingFinished ( deferred ) ;
// Wait until all child widgets are loaded, then bind
jQuery . when . apply ( jQuery , deferred ) . then ( function ( ) {
// fix order in DOM by reattaching templates in correct position
switch ( id ) {
case 0 : // header_left: prepend
jQuery ( header . getDOMNode ( ) ) . prependTo ( self . header _div ) ;
break ;
case 1 : // header_right: before favorites and count
jQuery ( header . getDOMNode ( ) ) . prependTo ( self . header _div . find ( 'div.header_row_right' ) ) ;
break ;
case 2 : // header_row: after search
window . setTimeout ( function ( ) {
jQuery ( header . getDOMNode ( ) ) . insertAfter ( self . header _div . find ( 'div.search' ) ) ;
} , 1 ) ;
break ;
}
self . _bindHeaderInput ( header ) ;
} ) ;
} ;
/ * *
* Build the selectbox filters in the header bar
* Sets value , options , labels , and change handlers
*
* @ param { string } name
* @ param { string } type
* @ param { string } value
* @ param { string } lang
* @ param { object } extra
* /
et2 _nextmatch _header _bar . prototype . _build _select = function ( name , type , value , lang , extra ) {
2020-01-24 14:43:08 +01:00
var _a ;
2020-01-24 12:14:08 +01:00
var widget _options = jQuery . extend ( {
"id" : name ,
"label" : this . nextmatch . options . settings [ name + "_label" ] ,
"no_lang" : lang ,
"disabled" : this . nextmatch . options [ 'no_' + name ]
} , extra ) ;
// Set select options
// Check in content for options-<name>
var mgr = this . nextmatch . getArrayMgr ( "content" ) ;
var options = mgr . getEntry ( "options-" + name ) ;
// Look in sel_options
if ( ! options )
options = this . nextmatch . getArrayMgr ( "sel_options" ) . getEntry ( name ) ;
// Check parent sel_options, because those are usually global and don't get passed down
if ( ! options )
2020-01-24 14:43:08 +01:00
options = ( _a = this . nextmatch . getArrayMgr ( "sel_options" ) . getParentMgr ( ) ) === null || _a === void 0 ? void 0 : _a . getEntry ( name ) ;
2020-01-24 12:14:08 +01:00
// Sometimes legacy stuff puts it in here
if ( ! options )
options = mgr . getEntry ( 'rows[sel_options][' + name + ']' ) ;
// Maybe in a row, and options got stuck in ${row} instead of top level
var row _stuck = [ '${row}' , '{$row}' ] ;
for ( var i = 0 ; ! options && i < row _stuck . length ; i ++ ) {
var row _id = '' ;
if ( ( ! options || options . length == 0 ) && (
// perspectiveData.row in nm, data["${row}"] in an auto-repeat grid
this . nextmatch . getArrayMgr ( "sel_options" ) . perspectiveData . row || this . nextmatch . getArrayMgr ( "sel_options" ) . data [ row _stuck [ i ] ] ) ) {
2020-02-11 19:32:50 +01:00
row _id = name . replace ( /[0-9]+/ , row _stuck [ i ] ) ;
2020-01-24 12:14:08 +01:00
options = this . nextmatch . getArrayMgr ( "sel_options" ) . getEntry ( row _id ) ;
if ( ! options ) {
row _id = row _stuck [ i ] + "[" + name + "]" ;
options = this . nextmatch . getArrayMgr ( "sel_options" ) . getEntry ( row _id ) ;
}
}
if ( options ) {
this . egw ( ) . debug ( 'warn' , 'Nextmatch filter options in a weird place - "%s". Should be in sel_options[%s].' , row _id , name ) ;
}
}
// Legacy: Add in 'All' option for cat_id, if not provided.
if ( name == 'cat_id' && options != null && ( typeof options [ '' ] == 'undefined' && typeof options [ 0 ] != 'undefined' && options [ 0 ] . value != '' ) ) {
widget _options . empty _label = this . egw ( ) . lang ( 'All categories' ) ;
}
// Create widget
var select = et2 _createWidget ( type , widget _options , this ) ;
if ( options )
select . set _select _options ( options ) ;
// Set value
select . set _value ( value ) ;
// Set activeFilters to current value
this . nextmatch . activeFilters [ select . id ] = select . get _value ( ) ;
// Set onChange
var input = select . input ;
// Tell framework to ignore, or it will reset it to ''/empty when it does loadingFinished()
select . attributes . select _options . ignore = true ;
if ( this . nextmatch . options . settings [ name + "_onchange" ] ) {
// Get the onchange function string
2020-02-11 19:32:50 +01:00
var onchange _1 = this . nextmatch . options . settings [ name + "_onchange" ] ;
2020-01-24 12:14:08 +01:00
// Real submits cause all sorts of problems
2020-02-11 19:32:50 +01:00
if ( onchange _1 . match ( /this\.form\.submit/ ) ) {
2020-01-24 12:14:08 +01:00
this . egw ( ) . debug ( "warn" , "%s tries to submit form, which is not allowed. Filter changes automatically refresh data with no reload." , name ) ;
2020-02-11 19:32:50 +01:00
onchange _1 = onchange _1 . replace ( /this\.form\.submit\([^)]*\);?/ , 'return true;' ) ;
2020-01-24 12:14:08 +01:00
}
// Connect it to the onchange event of the input element - may submit
2020-02-11 19:32:50 +01:00
select . change = et2 _compileLegacyJS ( onchange _1 , this . nextmatch , select . getInputNode ( ) ) ;
2020-01-24 12:14:08 +01:00
this . _bindHeaderInput ( select ) ;
}
else // default request changed rows with new filters, previous this.form.submit()
{
input . change ( this . nextmatch , function ( event ) {
var set = { } ;
set [ name ] = select . getValue ( ) ;
event . data . applyFilters ( set ) ;
} ) ;
}
return select ;
} ;
/ * *
* Set up the favorites UI control
*
* @ param filters Array | boolean The nextmatch setting for favorites . Either true , or a list of
* additional fields / settings to add in to the favorite .
* /
et2 _nextmatch _header _bar . prototype . _setup _favorites = function ( filters ) {
if ( typeof filters == "undefined" || filters === false ) {
// No favorites configured
return ;
}
var widget _options = {
default _pref : "nextmatch-" + this . nextmatch . options . settings . columnselection _pref + "-favorite" ,
2020-05-22 18:50:36 +02:00
app : this . getInstanceManager ( ) . app ,
2020-01-24 12:14:08 +01:00
filters : filters ,
2020-05-22 18:50:36 +02:00
sidebox _target : 'favorite_sidebox_' + this . getInstanceManager ( ) . app
2020-01-24 12:14:08 +01:00
} ;
this . favorites = et2 _createWidget ( 'favorites' , widget _options , this ) ;
// Add into header
jQuery ( this . favorites . getDOMNode ( this . favorites ) ) . prependTo ( egwIsMobile ( ) ? this . search _box . find ( '.nm_favorites_div' ) . show ( ) : this . right _div ) ;
} ;
/ * *
* Updates all the filter elements in the header
*
* Does not actually refresh the data , just sets values to match those given .
* Called by et2 _nextmatch . applyFilters ( ) .
*
* @ param filters Array Key => Value pairs of current filters
* /
et2 _nextmatch _header _bar . prototype . setFilters = function ( filters ) {
// Avoid loops cause by change events
if ( this . update _in _progress )
return ;
this . update _in _progress = true ;
// Use an array mgr to hande non-simple IDs
var mgr = new et2 _arrayMgr ( filters ) ;
this . iterateOver ( function ( child ) {
// Skip favorites, don't want them in the filter
if ( typeof child . id != "undefined" && child . id . indexOf ( "favorite" ) == 0 )
return ;
var value = '' ;
if ( typeof child . set _value != "undefined" && child . id ) {
value = mgr . getEntry ( child . id ) ;
if ( value == null )
value = '' ;
/ * *
* Sometimes a filter value is not in current options . This can
* happen in a saved favorite , for example , or if server changes
* some filter options , and the order doesn ' t work out . The normal behaviour
* is to warn & not set it , but for nextmatch we ' ll just add it
* in , and let the server either set it properly , or ignore .
* /
if ( value && typeof value != 'object' && child . instanceOf ( et2 _widget _selectbox _1 . et2 _selectbox ) ) {
var found = typeof child . options . select _options [ value ] != 'undefined' ;
// options is array of objects with attribute value&label
if ( jQuery . isArray ( child . options . select _options ) ) {
for ( var o = 0 ; o < child . options . select _options . length ; ++ o ) {
if ( child . options . select _options [ o ] . value == value ) {
found = true ;
break ;
}
}
}
if ( ! found ) {
var old _options = child . options . select _options ;
// Actual label is not available, obviously, or it would be there
old _options [ value ] = child . egw ( ) . lang ( "Loading" ) ;
child . set _select _options ( old _options ) ;
}
}
child . set _value ( value ) ;
}
if ( typeof child . get _value == "function" && child . id ) {
// Put data in the proper place
var target = this ;
value = child . get _value ( ) ;
// Split up indexes
var indexes = child . id . replace ( /[/g , '[' ) . split ( '[' ) ;
for ( var i = 0 ; i < indexes . length ; i ++ ) {
indexes [ i ] = indexes [ i ] . replace ( /]/g , '' ) . replace ( ']' , '' ) ;
if ( i < indexes . length - 1 ) {
if ( typeof target [ indexes [ i ] ] == "undefined" )
target [ indexes [ i ] ] = { } ;
target = target [ indexes [ i ] ] ;
}
else {
target [ indexes [ i ] ] = value ;
}
}
}
} , filters ) ;
// Letter search
if ( this . nextmatch . options . settings . lettersearch ) {
jQuery ( "td" , this . lettersearch ) . removeClass ( "lettersearch_active" ) ;
jQuery ( filters . searchletter ? "td#" + filters . searchletter : "td.lettersearch[id='']" , this . lettersearch ) . addClass ( "lettersearch_active" ) ;
// Set activeFilters to current value
filters . searchletter = jQuery ( "td.lettersearch_active" , this . lettersearch ) . attr ( "id" ) || false ;
}
// Reset flag
this . update _in _progress = false ;
} ;
/ * *
* Help out nextmatch / widget stuff by checking to see if sender is part of header
*
* @ param { et2 _widget } _sender
* /
et2 _nextmatch _header _bar . prototype . getDOMNode = function ( _sender ) {
var filters = [ this . category , this . filter , this . filter2 ] ;
for ( var i = 0 ; i < filters . length ; i ++ ) {
if ( _sender == filters [ i ] ) {
// Give them the filter div
return this . filter _div [ 0 ] ;
}
}
if ( _sender == this . et2 _searchbox )
return this . search _box [ 0 ] ;
if ( _sender . id == 'export' )
return this . right _div [ 0 ] ;
if ( _sender && _sender . _type == "template" ) {
for ( var i = 0 ; i < this . headers . length ; i ++ ) {
if ( _sender . id == this . headers [ i ] . id && _sender . _parent == this )
return i == 2 ? this . header _row [ 0 ] : this . header _div [ 0 ] ;
}
}
return null ;
} ;
/ * *
* Bind all the inputs in the header sub - templates to update the filters
* on change , and update current filter with the inputs ' current values
*
* @ param { et2 _template } sub _header
* /
et2 _nextmatch _header _bar . prototype . _bindHeaderInput = function ( sub _header ) {
var header = this ;
var bind _change = function ( _widget ) {
// Previously set change function
var widget _change = _widget . change ;
var change = function ( _node ) {
// Call previously set change function
var result = widget _change . call ( _widget , _node ) ;
// Update filters, if we're not already doing so
if ( ( result || typeof result === 'undefined' ) && _widget . isDirty ( ) && ! header . update _in _progress ) {
// Update dirty
_widget . _oldValue = _widget . getValue ( ) ;
// Widget will not have an entry in getValues() because nulls
// are not returned, we remove it from activeFilters
if ( _widget . _oldValue == null ) {
var path = _widget . getArrayMgr ( 'content' ) . explodeKey ( _widget . id ) ;
if ( path . length > 0 ) {
var entry = header . nextmatch . activeFilters ;
var i = 0 ;
for ( ; i < path . length - 1 ; i ++ ) {
entry = entry [ path [ i ] ] ;
}
delete entry [ path [ i ] ] ;
}
header . nextmatch . applyFilters ( header . nextmatch . activeFilters ) ;
}
else {
// Not null is easy, just get values
2020-02-11 19:32:50 +01:00
var value _1 = this . getInstanceManager ( ) . getValues ( sub _header ) ;
header . nextmatch . applyFilters ( value _1 [ header . nextmatch . id ] ) ;
2020-01-24 12:14:08 +01:00
}
}
// In case this gets bound twice, it's important to return
return true ;
} ;
_widget . change = change ;
// Set activeFilters to current value
// Use an array mgr to hande non-simple IDs
var value = { } ;
value [ _widget . id ] = _widget . _oldValue = _widget . getValue ( ) ;
var mgr = new et2 _arrayMgr ( value ) ;
jQuery . extend ( true , this . nextmatch . activeFilters , mgr . data ) ;
} ;
if ( sub _header . instanceOf ( et2 _core _inputWidget _1 . et2 _inputWidget ) ) {
bind _change . call ( this , sub _header ) ;
}
else {
sub _header . iterateOver ( bind _change , this , et2 _core _inputWidget _1 . et2 _inputWidget ) ;
}
} ;
2020-02-05 00:07:50 +01:00
et2 _nextmatch _header _bar . _attributes = {
"filter_label" : {
"name" : "Filter label" ,
"type" : "string" ,
"description" : "Label for filter" ,
"default" : "" ,
"translate" : true
} ,
"filter_help" : {
"name" : "Filter help" ,
"type" : "string" ,
"description" : "Help message for filter" ,
"default" : "" ,
"translate" : true
} ,
"filter" : {
"name" : "Filter value" ,
"type" : "any" ,
"description" : "Current value for filter" ,
"default" : ""
} ,
"no_filter" : {
"name" : "No filter" ,
"type" : "boolean" ,
"description" : "Remove filter" ,
"default" : false
}
} ;
2020-01-24 12:14:08 +01:00
return et2 _nextmatch _header _bar ;
} ( et2 _core _DOMWidget _1 . et2 _DOMWidget ) ) ;
et2 _core _widget _1 . et2 _register _widget ( et2 _nextmatch _header _bar , [ "nextmatch_header_bar" ] ) ;
2011-08-25 15:35:53 +02:00
/ * *
* Classes for the nextmatch sortheaders etc .
2014-01-27 17:26:00 +01:00
*
2013-04-13 21:00:13 +02:00
* @ augments et2 _baseWidget
2011-08-25 15:35:53 +02:00
* /
2020-01-31 21:07:27 +01:00
var et2 _nextmatch _header = /** @class */ ( function ( _super ) {
_ _extends ( et2 _nextmatch _header , _super ) ;
2020-01-24 12:14:08 +01:00
/ * *
* Constructor
*
* @ memberOf et2 _nextmatch _header
* /
function et2 _nextmatch _header ( _parent , _attrs , _child ) {
2020-01-31 21:07:27 +01:00
var _this = _super . call ( this , _parent , _attrs , et2 _core _inheritance _1 . ClassWithAttributes . extendAttributes ( et2 _nextmatch _header . _attributes , _child || { } ) ) || this ;
2020-01-24 12:14:08 +01:00
_this . labelNode = jQuery ( document . createElement ( "span" ) ) ;
_this . nextmatch = null ;
_this . setDOMNode ( _this . labelNode [ 0 ] ) ;
return _this ;
}
/ * *
* Set nextmatch is the function which has to be implemented for the
* et2 _INextmatchHeader interface .
*
* @ param { et2 _nextmatch } _nextmatch
* /
et2 _nextmatch _header . prototype . setNextmatch = function ( _nextmatch ) {
this . nextmatch = _nextmatch ;
} ;
et2 _nextmatch _header . prototype . set _label = function ( _value ) {
this . label = _value ;
this . labelNode . text ( _value ) ;
// add class if label is empty
this . labelNode . toggleClass ( 'et2_label_empty' , ! _value ) ;
} ;
2020-02-05 00:07:50 +01:00
et2 _nextmatch _header . _attributes = {
"label" : {
"name" : "Caption" ,
"type" : "string" ,
"description" : "Caption for the nextmatch header" ,
"translate" : true
}
} ;
2020-01-24 12:14:08 +01:00
return et2 _nextmatch _header ;
} ( et2 _core _baseWidget _1 . et2 _baseWidget ) ) ;
exports . et2 _nextmatch _header = et2 _nextmatch _header ;
et2 _core _widget _1 . et2 _register _widget ( et2 _nextmatch _header , [ 'nextmatch-header' ] ) ;
2011-10-14 19:59:57 +02:00
/ * *
* Extend header to process customfields
2014-01-27 17:26:00 +01:00
*
2013-04-13 21:00:13 +02:00
* @ augments et2 _customfields _list
2020-01-24 13:57:05 +01:00
*
2020-01-24 14:43:08 +01:00
* TODO This should extend customfield widget when it ' s ready , put the whole column in constructor ( ) back too
2011-10-14 19:59:57 +02:00
* /
2020-01-31 21:07:27 +01:00
var et2 _nextmatch _customfields = /** @class */ ( function ( _super ) {
_ _extends ( et2 _nextmatch _customfields , _super ) ;
2020-01-24 12:14:08 +01:00
/ * *
* Constructor
*
* @ memberOf et2 _nextmatch _customfields
* /
function et2 _nextmatch _customfields ( _parent , _attrs , _child ) {
2020-02-12 19:16:34 +01:00
var _this = _super . call ( this , _parent , _attrs , et2 _core _inheritance _1 . ClassWithAttributes . extendAttributes ( et2 _nextmatch _customfields . _attributes , _child || { } ) ) || this ;
2020-01-24 12:14:08 +01:00
// Specifically take the whole column
2020-02-12 19:16:34 +01:00
_this . table . css ( "width" , "100%" ) ;
return _this ;
2020-01-24 12:14:08 +01:00
}
et2 _nextmatch _customfields . prototype . destroy = function ( ) {
this . nextmatch = null ;
2020-01-31 21:07:27 +01:00
_super . prototype . destroy . call ( this ) ;
2020-01-24 12:14:08 +01:00
} ;
et2 _nextmatch _customfields . prototype . transformAttributes = function ( _attrs ) {
2020-01-31 21:07:27 +01:00
_super . prototype . transformAttributes . call ( this , _attrs ) ;
2020-01-24 12:14:08 +01:00
// Add in settings that are objects
if ( ! _attrs . customfields ) {
// Check for custom stuff (unlikely)
var data = this . getArrayMgr ( "modifications" ) . getEntry ( this . id ) ;
// Check for global settings
if ( ! data )
data = this . getArrayMgr ( "modifications" ) . getRoot ( ) . getEntry ( '~custom_fields~' , true ) ;
for ( var key in data ) {
if ( typeof data [ key ] === 'object' && ! _attrs [ key ] )
_attrs [ key ] = data [ key ] ;
}
}
} ;
et2 _nextmatch _customfields . prototype . setNextmatch = function ( _nextmatch ) {
this . nextmatch = _nextmatch ;
this . loadFields ( ) ;
} ;
/ * *
* Build widgets for header - sortable for numeric , text , etc . , filterables for selectbox , radio
* /
et2 _nextmatch _customfields . prototype . loadFields = function ( ) {
2020-02-12 19:16:34 +01:00
if ( this . nextmatch == null ) {
2020-01-24 12:14:08 +01:00
// not ready yet
return ;
}
var columnMgr = this . nextmatch . dataview . getColumnMgr ( ) ;
var nm _column = null ;
var set _fields = { } ;
for ( var i = 0 ; i < this . nextmatch . columns . length ; i ++ ) {
2020-02-11 19:32:50 +01:00
// @ts-ignore
2020-01-24 12:14:08 +01:00
if ( this . nextmatch . columns [ i ] . widget == this ) {
nm _column = columnMgr . columns [ i ] ;
break ;
}
}
if ( ! nm _column )
return ;
// Check for global setting changes (visibility)
var global _data = this . getArrayMgr ( "modifications" ) . getRoot ( ) . getEntry ( '~custom_fields~' ) ;
if ( global _data != null && global _data . fields )
this . options . fields = global _data . fields ;
var apps = egw . link _app _list ( ) ;
for ( var field _name in this . options . customfields ) {
var field = this . options . customfields [ field _name ] ;
2020-02-12 19:32:29 +01:00
var cf _id = et2 _extension _customfields _1 . et2 _customfields _list . PREFIX + field _name ;
2020-01-24 12:14:08 +01:00
if ( this . rows [ field _name ] )
continue ;
// Table row
var row = jQuery ( document . createElement ( "tr" ) )
. appendTo ( this . tbody ) ;
var cf = jQuery ( document . createElement ( "td" ) )
. appendTo ( row ) ;
this . rows [ cf _id ] = cf [ 0 ] ;
// Create widget by type
var widget = null ;
if ( field . type == 'select' || field . type == 'select-account' ) {
if ( field . values && typeof field . values [ '' ] !== 'undefined' ) {
delete ( field . values [ '' ] ) ;
}
widget = et2 _createWidget ( field . type == 'select-account' ? 'nextmatch-accountfilter' : "nextmatch-filterheader" , {
id : cf _id ,
empty _label : field . label ,
select _options : field . values
} , this ) ;
}
else if ( apps [ field . type ] ) {
widget = et2 _createWidget ( "nextmatch-entryheader" , {
id : cf _id ,
only _app : field . type ,
blur : field . label
} , this ) ;
}
else {
widget = et2 _createWidget ( "nextmatch-sortheader" , {
id : cf _id ,
label : field . label
} , this ) ;
}
// If this is already attached, widget needs to be finished explicitly
if ( this . isAttached ( ) && ! widget . isAttached ( ) ) {
widget . loadingFinished ( ) ;
}
// Check for column filter
if ( ! jQuery . isEmptyObject ( this . options . fields ) && ( this . options . fields [ field _name ] == false || typeof this . options . fields [ field _name ] == 'undefined' ) ) {
cf . hide ( ) ;
}
else if ( jQuery . isEmptyObject ( this . options . fields ) ) {
// If we're showing it make sure it's set, but only after
set _fields [ field _name ] = true ;
}
}
jQuery . extend ( this . options . fields , set _fields ) ;
} ;
/ * *
* Override parent so we can update the nextmatch row too
*
* @ param { array } _fields
* /
et2 _nextmatch _customfields . prototype . set _visible = function ( _fields ) {
2020-01-31 21:07:27 +01:00
_super . prototype . set _visible . call ( this , _fields ) ;
2020-01-24 12:14:08 +01:00
// Find data row, and do it too
var self = this ;
if ( this . nextmatch ) {
this . nextmatch . iterateOver ( function ( widget ) {
if ( widget == self )
return ;
widget . set _visible ( _fields ) ;
2020-02-12 19:16:34 +01:00
} , this , et2 _extension _customfields _1 . et2 _customfields _list ) ;
2020-01-24 12:14:08 +01:00
}
} ;
/ * *
* Provide own column caption ( column selection )
*
* If only one custom field , just use that , otherwise use "custom fields"
* /
et2 _nextmatch _customfields . prototype . _genColumnCaption = function ( ) {
return egw . lang ( "Custom fields" ) ;
} ;
/ * *
* Provide own column naming , including only selected columns - only useful
* to nextmatch itself , not for sending server - side
* /
et2 _nextmatch _customfields . prototype . _getColumnName = function ( ) {
var name = this . id ;
var visible = [ ] ;
for ( var field _name in this . options . customfields ) {
if ( jQuery . isEmptyObject ( this . options . fields ) || this . options . fields [ field _name ] == true ) {
2020-02-12 19:32:29 +01:00
visible . push ( et2 _extension _customfields _1 . et2 _customfields _list . PREFIX + field _name ) ;
2020-01-24 12:14:08 +01:00
jQuery ( this . rows [ field _name ] ) . show ( ) ;
}
else if ( typeof this . rows [ field _name ] != "undefined" ) {
jQuery ( this . rows [ field _name ] ) . hide ( ) ;
}
}
if ( visible . length ) {
name += "_" + visible . join ( "_" ) ;
}
2020-01-29 22:29:06 +01:00
else if ( this . rows ) {
2020-01-24 12:14:08 +01:00
// None hidden means all visible
jQuery ( this . rows [ field _name ] ) . parent ( ) . parent ( ) . children ( ) . show ( ) ;
}
// Update global custom fields column(s) - widgets will check on their own
// Check for custom stuff (unlikely)
var data = this . getArrayMgr ( "modifications" ) . getEntry ( this . id ) ;
// Check for global settings
if ( ! data )
data = this . getArrayMgr ( "modifications" ) . getRoot ( ) . getEntry ( '~custom_fields~' , true ) || { } ;
if ( ! data . fields )
data . fields = { } ;
for ( var field in this . options . customfields ) {
data . fields [ field ] = ( this . options . fields == null || typeof this . options . fields [ field ] == 'undefined' ? false : this . options . fields [ field ] ) ;
}
return name ;
} ;
2020-02-05 00:07:50 +01:00
et2 _nextmatch _customfields . _attributes = {
'customfields' : {
'name' : 'Custom fields' ,
'description' : 'Auto filled'
} ,
'fields' : {
'name' : "Visible fields" ,
"description" : "Auto filled"
}
} ;
2020-01-24 12:14:08 +01:00
return et2 _nextmatch _customfields ;
2020-02-12 19:16:34 +01:00
} ( et2 _extension _customfields _1 . et2 _customfields _list ) ) ;
2020-01-24 12:14:08 +01:00
exports . et2 _nextmatch _customfields = et2 _nextmatch _customfields ;
et2 _core _widget _1 . et2 _register _widget ( et2 _nextmatch _customfields , [ 'nextmatch-customfields' ] ) ;
2013-04-13 21:00:13 +02:00
/ * *
* @ augments et2 _nextmatch _header
* /
2020-01-24 12:14:08 +01:00
// @ts-ignore
2020-01-31 21:07:27 +01:00
var et2 _nextmatch _sortheader = /** @class */ ( function ( _super ) {
_ _extends ( et2 _nextmatch _sortheader , _super ) ;
2020-01-24 12:14:08 +01:00
/ * *
* Constructor
*
* @ memberOf et2 _nextmatch _sortheader
* /
function et2 _nextmatch _sortheader ( _parent , _attrs , _child ) {
2020-01-31 21:07:27 +01:00
var _this = _super . call ( this , _parent , _attrs , et2 _core _inheritance _1 . ClassWithAttributes . extendAttributes ( et2 _nextmatch _sortheader . _attributes , _child || { } ) ) || this ;
2020-01-24 12:14:08 +01:00
_this . sortmode = "none" ;
_this . labelNode . addClass ( "nextmatch_sortheader none" ) ;
return _this ;
}
et2 _nextmatch _sortheader . prototype . click = function ( _event ) {
2020-01-31 21:07:27 +01:00
if ( this . nextmatch && _super . prototype . click . call ( this , _event ) ) {
2020-01-24 12:14:08 +01:00
// Send default sort mode if not sorted, otherwise send undefined to calculate
this . nextmatch . sortBy ( this . id , this . sortmode == "none" ? ! ( this . options . sortmode . toUpperCase ( ) == "DESC" ) : undefined ) ;
return true ;
}
return false ;
} ;
/ * *
* Wrapper to join up interface * framework
*
* @ param { string } _mode
* /
et2 _nextmatch _sortheader . prototype . set _sortmode = function ( _mode ) {
// Set via nextmatch after setup
if ( this . nextmatch )
return ;
this . setSortmode ( _mode ) ;
} ;
/ * *
* Function which implements the et2 _INextmatchSortable function .
*
* @ param { string } _mode
* /
et2 _nextmatch _sortheader . prototype . setSortmode = function ( _mode ) {
// Remove the last sortmode class and add the new one
this . labelNode . removeClass ( this . sortmode )
. addClass ( _mode ) ;
this . sortmode = _mode ;
} ;
2020-02-05 00:07:50 +01:00
et2 _nextmatch _sortheader . _attributes = {
"sortmode" : {
"name" : "Sort order" ,
"type" : "string" ,
"description" : "Default sort order" ,
"translate" : false
}
} ;
2020-01-24 12:14:08 +01:00
return et2 _nextmatch _sortheader ;
} ( et2 _nextmatch _header ) ) ;
exports . et2 _nextmatch _sortheader = et2 _nextmatch _sortheader ;
et2 _core _widget _1 . et2 _register _widget ( et2 _nextmatch _sortheader , [ 'nextmatch-sortheader' ] ) ;
2013-04-13 21:00:13 +02:00
/ * *
2020-02-05 00:07:50 +01:00
* Filter from a provided list of options
2013-04-13 21:00:13 +02:00
* /
2020-01-31 21:07:27 +01:00
var et2 _nextmatch _filterheader = /** @class */ ( function ( _super ) {
_ _extends ( et2 _nextmatch _filterheader , _super ) ;
2020-01-24 12:14:08 +01:00
function et2 _nextmatch _filterheader ( ) {
2020-01-31 21:07:27 +01:00
return _super !== null && _super . apply ( this , arguments ) || this ;
2020-01-24 12:14:08 +01:00
}
/ * *
* Override to add change handler
* /
et2 _nextmatch _filterheader . prototype . createInputWidget = function ( ) {
// Make sure there's an option for all
if ( ! this . options . empty _label && ( ! this . options . select _options || ! this . options . select _options [ "" ] ) ) {
this . options . empty _label = this . options . label ? this . options . label : egw . lang ( "All" ) ;
}
2020-01-31 21:07:27 +01:00
_super . prototype . createInputWidget . call ( this ) ;
2020-01-24 12:14:08 +01:00
jQuery ( this . getInputNode ( ) ) . change ( this , function ( event ) {
if ( typeof event . data . nextmatch == 'undefined' ) {
// Not fully set up yet
return ;
}
var col _filter = { } ;
col _filter [ event . data . id ] = event . data . input . val ( ) ;
// Set value so it's there for response (otherwise it gets cleared if options are updated)
event . data . set _value ( event . data . input . val ( ) ) ;
event . data . nextmatch . applyFilters ( { col _filter : col _filter } ) ;
} ) ;
} ;
/ * *
* Set nextmatch is the function which has to be implemented for the
* et2 _INextmatchHeader interface .
*
* @ param { et2 _nextmatch } _nextmatch
* /
et2 _nextmatch _filterheader . prototype . setNextmatch = function ( _nextmatch ) {
this . nextmatch = _nextmatch ;
// Set current filter value from nextmatch settings
if ( this . nextmatch . activeFilters . col _filter && typeof this . nextmatch . activeFilters . col _filter [ this . id ] != "undefined" ) {
this . set _value ( this . nextmatch . activeFilters . col _filter [ this . id ] ) ;
// Make sure it's set in the nextmatch
_nextmatch . activeFilters . col _filter [ this . id ] = this . getValue ( ) ;
}
} ;
// Make sure selectbox is not longer than the column
et2 _nextmatch _filterheader . prototype . resize = function ( ) {
this . input . css ( "max-width" , jQuery ( this . parentNode ) . innerWidth ( ) + "px" ) ;
} ;
return et2 _nextmatch _filterheader ;
} ( et2 _widget _selectbox _1 . et2 _selectbox ) ) ;
exports . et2 _nextmatch _filterheader = et2 _nextmatch _filterheader ;
et2 _core _widget _1 . et2 _register _widget ( et2 _nextmatch _filterheader , [ 'nextmatch-filterheader' ] ) ;
2013-04-13 21:00:13 +02:00
/ * *
2020-02-05 00:07:50 +01:00
* Filter by account
2013-04-13 21:00:13 +02:00
* /
2020-02-12 22:49:22 +01:00
var et2 _nextmatch _accountfilterheader = /** @class */ ( function ( _super ) {
_ _extends ( et2 _nextmatch _accountfilterheader , _super ) ;
function et2 _nextmatch _accountfilterheader ( ) {
return _super !== null && _super . apply ( this , arguments ) || this ;
}
/ * *
* Override to add change handler
*
* /
et2 _nextmatch _accountfilterheader . prototype . createInputWidget = function ( ) {
// Make sure there's an option for all
if ( ! this . options . empty _label && ! this . options . select _options [ "" ] ) {
this . options . empty _label = this . options . label ? this . options . label : egw . lang ( "All" ) ;
}
_super . prototype . createInputWidget . call ( this , this , arguments ) ;
this . input . change ( this , function ( event ) {
if ( typeof event . data . nextmatch == 'undefined' ) {
// Not fully set up yet
return ;
}
var col _filter = { } ;
col _filter [ event . data . id ] = event . data . getValue ( ) ;
event . data . nextmatch . applyFilters ( { col _filter : col _filter } ) ;
} ) ;
} ;
/ * *
* Set nextmatch is the function which has to be implemented for the
* et2 _INextmatchHeader interface .
*
* @ param { et2 _nextmatch } _nextmatch
* /
et2 _nextmatch _accountfilterheader . prototype . setNextmatch = function ( _nextmatch ) {
this . nextmatch = _nextmatch ;
// Set current filter value from nextmatch settings
if ( this . nextmatch . activeFilters . col _filter && this . nextmatch . activeFilters . col _filter [ this . id ] ) {
this . set _value ( this . nextmatch . activeFilters . col _filter [ this . id ] ) ;
}
} ;
// Make sure selectbox is not longer than the column
et2 _nextmatch _accountfilterheader . prototype . resize = function ( ) {
var max = jQuery ( this . parentNode ) . innerWidth ( ) - 4 ;
var surroundings = this . getSurroundings ( ) . _widgetSurroundings ;
for ( var i = 0 ; i < surroundings . length ; i ++ ) {
max -= jQuery ( surroundings [ i ] ) . outerWidth ( ) ;
}
this . input . css ( "max-width" , max + "px" ) ;
} ;
return et2 _nextmatch _accountfilterheader ;
} ( et2 _selectAccount ) ) ;
exports . et2 _nextmatch _accountfilterheader = et2 _nextmatch _accountfilterheader ;
et2 _core _widget _1 . et2 _register _widget ( et2 _nextmatch _accountfilterheader , [ 'nextmatch-accountfilter' ] ) ;
2016-05-16 16:58:40 +02:00
/ * *
* Filter allowing multiple values to be selected , base on a taglist instead
* of a regular selectbox
2016-06-17 12:29:23 +02:00
*
2016-05-16 16:58:40 +02:00
* @ augments et2 _taglist
* /
2020-02-12 22:49:22 +01:00
var et2 _nextmatch _taglistheader = /** @class */ ( function ( _super ) {
_ _extends ( et2 _nextmatch _taglistheader , _super ) ;
function et2 _nextmatch _taglistheader ( ) {
return _super !== null && _super . apply ( this , arguments ) || this ;
}
/ * *
* Override to add change handler
*
* @ memberOf et2 _nextmatch _filterheader
* /
et2 _nextmatch _taglistheader . prototype . createInputWidget = function ( ) {
// Make sure there's an option for all
if ( ! this . options . empty _label && ( ! this . options . select _options || ! this . options . select _options [ "" ] ) ) {
this . options . empty _label = this . options . label ? this . options . label : egw . lang ( "All" ) ;
}
_super . prototype . createInputWidget . call ( this ) ;
} ;
/ * *
* Disable toggle if there are 2 or less options
* @ param { Object [ ] } options
* /
et2 _nextmatch _taglistheader . prototype . set _select _options = function ( options ) {
if ( options && options . length <= 2 && this . options . multiple == 'toggle' ) {
this . set _multiple ( false ) ;
}
_super . prototype . set _select _options . call ( this , options ) ;
} ;
/ * *
* Set nextmatch is the function which has to be implemented for the
* et2 _INextmatchHeader interface .
*
* @ param { et2 _nextmatch } _nextmatch
* /
et2 _nextmatch _taglistheader . prototype . setNextmatch = function ( _nextmatch ) {
this . nextmatch = _nextmatch ;
// Set current filter value from nextmatch settings
if ( this . nextmatch . activeFilters . col _filter && typeof this . nextmatch . activeFilters . col _filter [ this . id ] != "undefined" ) {
this . set _value ( this . nextmatch . activeFilters . col _filter [ this . id ] ) ;
// Make sure it's set in the nextmatch
_nextmatch . activeFilters . col _filter [ this . id ] = this . getValue ( ) ;
}
} ;
// Make sure selectbox is not longer than the column
et2 _nextmatch _taglistheader . prototype . resize = function ( ) {
this . div . css ( "height" , '' ) ;
this . div . css ( "max-width" , jQuery ( this . parentNode ) . innerWidth ( ) + "px" ) ;
_super . prototype . resize . call ( this ) ;
} ;
et2 _nextmatch _taglistheader . _attributes = {
autocomplete _url : { default : '' } ,
multiple : { default : 'toggle' } ,
onchange : {
// @ts-ignore
default : function ( event ) {
if ( typeof this . nextmatch === 'undefined' ) {
// Not fully set up yet
return ;
}
var col _filter = { } ;
col _filter [ this . id ] = this . getValue ( ) ;
// Set value so it's there for response (otherwise it gets cleared if options are updated)
//event.data.set_value(event.data.input.val());
this . nextmatch . applyFilters ( { col _filter : col _filter } ) ;
}
} ,
rows : { default : 2 } ,
class : { default : 'nm_filterheader_taglist' }
} ;
return et2 _nextmatch _taglistheader ;
} ( et2 _taglist ) ) ;
et2 _core _widget _1 . et2 _register _widget ( et2 _nextmatch _taglistheader , [ 'nextmatch-taglistheader' ] ) ;
2013-04-13 21:00:13 +02:00
/ * *
2020-02-05 21:48:50 +01:00
* Nextmatch filter that can filter for a selected entry
2013-04-13 21:00:13 +02:00
* /
2020-01-31 21:07:27 +01:00
var et2 _nextmatch _entryheader = /** @class */ ( function ( _super ) {
_ _extends ( et2 _nextmatch _entryheader , _super ) ;
2020-01-24 12:14:08 +01:00
function et2 _nextmatch _entryheader ( ) {
2020-01-31 21:07:27 +01:00
return _super !== null && _super . apply ( this , arguments ) || this ;
2020-01-24 12:14:08 +01:00
}
/ * *
* Override to add change handler
*
* @ memberOf et2 _nextmatch _entryheader
* @ param { object } event
* @ param { object } selected
* /
et2 _nextmatch _entryheader . prototype . onchange = function ( event , selected ) {
var col _filter = { } ;
col _filter [ this . id ] = this . get _value ( ) ;
this . nextmatch . applyFilters . call ( this . nextmatch , { col _filter : col _filter } ) ;
} ;
/ * *
* Override to always return a string appname : id ( or just id ) for simple ( one real selection )
* cases , parent returns an object . If multiple are selected , or anything other than app and
* id , the original parent value is returned .
* /
et2 _nextmatch _entryheader . prototype . getValue = function ( ) {
2020-01-31 21:07:27 +01:00
var value = _super . prototype . getValue . call ( this ) ;
2020-01-24 12:14:08 +01:00
if ( typeof value == "object" && value != null ) {
if ( ! value . app || ! value . id )
return null ;
// If array with just one value, use a string instead for legacy server handling
if ( typeof value . id == 'object' && value . id . shift && value . id . length == 1 ) {
value . id = value . id . shift ( ) ;
}
// If simple value, format it legacy string style, otherwise
// we return full value
if ( typeof value . id == 'string' ) {
value = value . app + ":" + value . id ;
}
}
return value ;
} ;
/ * *
* Set nextmatch is the function which has to be implemented for the
* et2 _INextmatchHeader interface .
*
* @ param { et2 _nextmatch } _nextmatch
* /
et2 _nextmatch _entryheader . prototype . setNextmatch = function ( _nextmatch ) {
this . nextmatch = _nextmatch ;
// Set current filter value from nextmatch settings
if ( this . nextmatch . options . settings . col _filter && this . nextmatch . options . settings . col _filter [ this . id ] ) {
this . set _value ( this . nextmatch . options . settings . col _filter [ this . id ] ) ;
if ( this . getValue ( ) != this . nextmatch . activeFilters . col _filter [ this . id ] ) {
this . nextmatch . activeFilters . col _filter [ this . id ] = this . getValue ( ) ;
}
// Tell framework to ignore, or it will reset it to ''/empty when it does loadingFinished()
this . attributes . value . ignore = true ;
//this.attributes.select_options.ignore = true;
}
// Fire on lost focus, clear filter if user emptied box
} ;
return et2 _nextmatch _entryheader ;
} ( et2 _link _entry ) ) ;
et2 _core _widget _1 . et2 _register _widget ( et2 _nextmatch _entryheader , [ 'nextmatch-entryheader' ] ) ;
2013-04-13 21:00:13 +02:00
/ * *
* @ augments et2 _nextmatch _filterheader
* /
2020-01-31 21:07:27 +01:00
var et2 _nextmatch _customfilter = /** @class */ ( function ( _super ) {
_ _extends ( et2 _nextmatch _customfilter , _super ) ;
2020-01-24 12:14:08 +01:00
/ * *
* Constructor
*
* @ param _parent
* @ param _attrs
2020-02-11 19:32:50 +01:00
* @ param _child
2020-01-24 12:14:08 +01:00
* @ memberOf et2 _nextmatch _customfilter
* /
function et2 _nextmatch _customfilter ( _parent , _attrs , _child ) {
2020-01-31 21:07:27 +01:00
var _this = _super . call ( this , _parent , _attrs , et2 _core _inheritance _1 . ClassWithAttributes . extendAttributes ( et2 _nextmatch _customfilter . _attributes , _child || { } ) ) || this ;
2020-01-24 12:14:08 +01:00
switch ( _attrs . widget _type ) {
case "link-entry" :
_attrs . type = 'nextmatch-entryheader' ;
break ;
default :
if ( _attrs . widget _type . indexOf ( 'select' ) === 0 ) {
_attrs . type = 'nextmatch-filterheader' ;
}
else {
_attrs . type = _attrs . widget _type ;
}
}
jQuery . extend ( _attrs . widget _options , { id : _this . id } ) ;
_attrs . id = '' ;
2020-01-31 21:07:27 +01:00
_this = _super . call ( this , _parent , _attrs , et2 _core _inheritance _1 . ClassWithAttributes . extendAttributes ( et2 _nextmatch _customfilter . _attributes , _child || { } ) ) || this ;
2020-01-24 12:14:08 +01:00
_this . real _node = et2 _createWidget ( _attrs . type , _attrs . widget _options , _this . getParent ( ) ) ;
var select _options = [ ] ;
var correct _type = _attrs . type ;
_this . real _node [ 'type' ] = _attrs . widget _type ;
et2 _widget _selectbox _1 . et2 _selectbox . find _select _options ( _this . real _node , select _options , _attrs ) ;
_this . real _node [ "_type" ] = correct _type ;
if ( typeof _this . real _node . set _select _options === 'function' ) {
_this . real _node . set _select _options ( select _options ) ;
}
return _this ;
}
// Just pass the real DOM node through, in case anybody asks
et2 _nextmatch _customfilter . prototype . getDOMNode = function ( _sender ) {
return this . real _node ? this . real _node . getDOMNode ( _sender ) : null ;
} ;
// Also need to pass through real children
et2 _nextmatch _customfilter . prototype . getChildren = function ( ) {
return this . real _node . getChildren ( ) || [ ] ;
} ;
et2 _nextmatch _customfilter . prototype . setNextmatch = function ( _nextmatch ) {
if ( this . real _node && this . real _node . instanceOf ( et2 _INextmatchHeader ) ) {
return this . real _node . setNextmatch ( _nextmatch ) ;
}
} ;
2020-02-05 00:07:50 +01:00
et2 _nextmatch _customfilter . _attributes = {
"widget_type" : {
"name" : "Actual type" ,
"type" : "string" ,
"description" : "The actual type of widget you should use" ,
"no_lang" : 1
} ,
"widget_options" : {
"name" : "Actual options" ,
"type" : "any" ,
"description" : "The options for the actual widget" ,
"no_lang" : 1 ,
"default" : { }
}
} ;
2020-01-24 12:14:08 +01:00
return et2 _nextmatch _customfilter ;
} ( et2 _nextmatch _filterheader ) ) ;
et2 _core _widget _1 . et2 _register _widget ( et2 _nextmatch _customfilter , [ 'nextmatch-customfilter' ] ) ;
//# sourceMappingURL=et2_extension_nextmatch.js.map