2017-03-19 19:33:56 +00:00
/ * \
title : $ : / c o r e / m o d u l e s / w i d g e t s / d r o p p a b l e . j s
type : application / javascript
module - type : widget
Droppable widget
\ * /
( function ( ) {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict" ;
var Widget = require ( "$:/core/modules/widgets/widget.js" ) . widget ;
var DroppableWidget = function ( parseTreeNode , options ) {
this . initialise ( parseTreeNode , options ) ;
} ;
/ *
Inherit from the base widget class
* /
DroppableWidget . prototype = new Widget ( ) ;
/ *
Render this widget into the DOM
* /
DroppableWidget . prototype . render = function ( parent , nextSibling ) {
2020-11-22 21:13:24 +00:00
var self = this ,
tag = this . parseTreeNode . isBlock ? "div" : "span" ,
domNode ;
2017-03-19 19:33:56 +00:00
// Remember parent
this . parentDomNode = parent ;
// Compute attributes and execute state
this . computeAttributes ( ) ;
this . execute ( ) ;
2017-03-28 14:09:36 +00:00
if ( this . droppableTag && $tw . config . htmlUnsafeElements . indexOf ( this . droppableTag ) === - 1 ) {
tag = this . droppableTag ;
}
// Create element and assign classes
2020-11-22 21:13:24 +00:00
domNode = this . document . createElement ( tag ) ;
this . domNode = domNode ;
this . assignDomNodeClasses ( ) ;
2017-03-19 19:33:56 +00:00
// Add event handlers
2020-03-20 10:46:17 +00:00
if ( this . droppableEnable ) {
$tw . utils . addEventListeners ( domNode , [
{ name : "dragenter" , handlerObject : this , handlerMethod : "handleDragEnterEvent" } ,
{ name : "dragover" , handlerObject : this , handlerMethod : "handleDragOverEvent" } ,
{ name : "dragleave" , handlerObject : this , handlerMethod : "handleDragLeaveEvent" } ,
{ name : "drop" , handlerObject : this , handlerMethod : "handleDropEvent" }
] ) ;
2020-12-15 11:09:32 +00:00
} else {
$tw . utils . addClass ( this . domNode , this . disabledClass ) ;
2020-03-20 10:46:17 +00:00
}
2017-03-19 19:33:56 +00:00
// Insert element
parent . insertBefore ( domNode , nextSibling ) ;
this . renderChildren ( domNode , null ) ;
this . domNodes . push ( domNode ) ;
// Stack of outstanding enter/leave events
this . currentlyEntered = [ ] ;
} ;
DroppableWidget . prototype . enterDrag = function ( event ) {
if ( this . currentlyEntered . indexOf ( event . target ) === - 1 ) {
this . currentlyEntered . push ( event . target ) ;
}
// If we're entering for the first time we need to apply highlighting
$tw . utils . addClass ( this . domNodes [ 0 ] , "tc-dragover" ) ;
} ;
DroppableWidget . prototype . leaveDrag = function ( event ) {
var pos = this . currentlyEntered . indexOf ( event . target ) ;
if ( pos !== - 1 ) {
this . currentlyEntered . splice ( pos , 1 ) ;
}
// Remove highlighting if we're leaving externally. The hacky second condition is to resolve a problem with Firefox whereby there is an erroneous dragenter event if the node being dragged is within the dropzone
if ( this . currentlyEntered . length === 0 || ( this . currentlyEntered . length === 1 && this . currentlyEntered [ 0 ] === $tw . dragInProgress ) ) {
this . currentlyEntered = [ ] ;
2020-06-11 11:11:56 +00:00
if ( this . domNodes [ 0 ] ) {
$tw . utils . removeClass ( this . domNodes [ 0 ] , "tc-dragover" ) ;
}
2017-03-19 19:33:56 +00:00
}
} ;
DroppableWidget . prototype . handleDragEnterEvent = function ( event ) {
this . enterDrag ( event ) ;
// Tell the browser that we're ready to handle the drop
event . preventDefault ( ) ;
// Tell the browser not to ripple the drag up to any parent drop handlers
event . stopPropagation ( ) ;
2017-03-27 08:59:40 +00:00
return false ;
2017-03-19 19:33:56 +00:00
} ;
DroppableWidget . prototype . handleDragOverEvent = function ( event ) {
// Check for being over a TEXTAREA or INPUT
if ( [ "TEXTAREA" , "INPUT" ] . indexOf ( event . target . tagName ) !== - 1 ) {
return false ;
}
// Tell the browser that we're still interested in the drop
event . preventDefault ( ) ;
2017-03-28 14:09:36 +00:00
// Set the drop effect
event . dataTransfer . dropEffect = this . droppableEffect ;
2017-03-27 08:59:40 +00:00
return false ;
2017-03-19 19:33:56 +00:00
} ;
DroppableWidget . prototype . handleDragLeaveEvent = function ( event ) {
this . leaveDrag ( event ) ;
2017-03-27 08:59:40 +00:00
return false ;
2017-03-19 19:33:56 +00:00
} ;
DroppableWidget . prototype . handleDropEvent = function ( event ) {
var self = this ;
this . leaveDrag ( event ) ;
// Check for being over a TEXTAREA or INPUT
if ( [ "TEXTAREA" , "INPUT" ] . indexOf ( event . target . tagName ) !== - 1 ) {
return false ;
}
var dataTransfer = event . dataTransfer ;
// Remove highlighting
$tw . utils . removeClass ( this . domNodes [ 0 ] , "tc-dragover" ) ;
// Try to import the various data types we understand
$tw . utils . importDataTransfer ( dataTransfer , null , function ( fieldsArray ) {
fieldsArray . forEach ( function ( fields ) {
2017-03-27 08:59:40 +00:00
self . performActions ( fields . title || fields . text , event ) ;
2017-03-19 19:33:56 +00:00
} ) ;
} ) ;
// Tell the browser that we handled the drop
event . preventDefault ( ) ;
// Stop the drop ripple up to any parent handlers
event . stopPropagation ( ) ;
2017-03-27 08:59:40 +00:00
return false ;
2017-03-19 19:33:56 +00:00
} ;
DroppableWidget . prototype . performActions = function ( title , event ) {
2017-03-28 14:09:36 +00:00
if ( this . droppableActions ) {
2020-07-14 16:04:06 +00:00
var modifierKey = $tw . keyboardManager . getEventModifierKeyDescriptor ( event ) ;
2018-03-14 17:52:13 +00:00
this . invokeActionString ( this . droppableActions , this , event , { actionTiddler : title , modifier : modifierKey } ) ;
2017-03-19 19:33:56 +00:00
}
} ;
/ *
Compute the internal state of the widget
* /
DroppableWidget . prototype . execute = function ( ) {
2017-03-28 14:09:36 +00:00
this . droppableActions = this . getAttribute ( "actions" ) ;
this . droppableEffect = this . getAttribute ( "effect" , "copy" ) ;
this . droppableTag = this . getAttribute ( "tag" ) ;
2020-03-20 10:46:17 +00:00
this . droppableEnable = ( this . getAttribute ( "enable" ) || "yes" ) === "yes" ;
2020-12-15 11:09:32 +00:00
this . disabledClass = this . getAttribute ( "disabledClass" , "" ) ;
2017-03-19 19:33:56 +00:00
// Make child widgets
this . makeChildWidgets ( ) ;
} ;
2020-11-22 21:13:24 +00:00
DroppableWidget . prototype . assignDomNodeClasses = function ( ) {
var classes = this . getAttribute ( "class" , "" ) . split ( " " ) ;
classes . push ( "tc-droppable" ) ;
this . domNode . className = classes . join ( " " ) ;
} ;
2017-03-19 19:33:56 +00:00
/ *
Selectively refreshes the widget if needed . Returns true if the widget or any of its children needed re - rendering
* /
DroppableWidget . prototype . refresh = function ( changedTiddlers ) {
2017-03-28 14:09:36 +00:00
var changedAttributes = this . computeAttributes ( ) ;
2020-12-15 11:09:32 +00:00
if ( changedAttributes . tag || changedAttributes . enable || changedAttributes . disabledClass || changedAttributes . actions || changedAttributes . effect ) {
2017-03-28 14:09:36 +00:00
this . refreshSelf ( ) ;
return true ;
2020-11-22 21:13:24 +00:00
} else if ( changedAttributes [ "class" ] ) {
this . assignDomNodeClasses ( ) ;
2017-03-28 14:09:36 +00:00
}
2017-03-19 19:33:56 +00:00
return this . refreshChildren ( changedTiddlers ) ;
} ;
exports . droppable = DroppableWidget ;
2020-12-15 11:09:32 +00:00
} ) ( ) ;