Best JavaScript code snippet using root
ddwtos.js
Source:ddwtos.js
1// This file is part of Moodle - http://moodle.org/2//3// Moodle is free software: you can redistribute it and/or modify4// it under the terms of the GNU General Public License as published by5// the Free Software Foundation, either version 3 of the License, or6// (at your option) any later version.7//8// Moodle is distributed in the hope that it will be useful,9// but WITHOUT ANY WARRANTY; without even the implied warranty of10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the11// GNU General Public License for more details.12//13// You should have received a copy of the GNU General Public License14// along with Moodle. If not, see <http://www.gnu.org/licenses/>.15/**16 * JavaScript to make drag-drop into text questions work.17 *18 * Some vocabulary to help understand this code:19 *20 * The question text contains 'drops' - blanks into which the 'drags', the missing21 * words, can be put.22 *23 * The thing that can be moved into the drops are called 'drags'. There may be24 * multiple copies of the 'same' drag which does not really cause problems.25 * Each drag has a 'choice' number which is the value set on the drop's hidden26 * input when this drag is placed in a drop.27 *28 * These may be in separate 'groups', distinguished by colour.29 * Things can only interact with other things in the same group.30 * The groups are numbered from 1.31 *32 * The place where a given drag started from is called its 'home'.33 *34 * @module qtype_ddwtos/ddwtos35 * @copyright 2018 The Open University36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later37 * @since 3.638 */39define(['jquery', 'core/dragdrop', 'core/key_codes'], function($, dragDrop, keys) {40 "use strict";41 /**42 * Object to handle one drag-drop into text question.43 *44 * @param {String} containerId id of the outer div for this question.45 * @param {boolean} readOnly whether the question is being displayed read-only.46 * @constructor47 */48 function DragDropToTextQuestion(containerId, readOnly) {49 this.containerId = containerId;50 if (readOnly) {51 this.getRoot().addClass('qtype_ddwtos-readonly');52 }53 this.resizeAllDragsAndDrops();54 this.cloneDrags();55 this.positionDrags();56 }57 /**58 * In each group, resize all the items to be the same size.59 */60 DragDropToTextQuestion.prototype.resizeAllDragsAndDrops = function() {61 var thisQ = this;62 this.getRoot().find('.answercontainer > div').each(function(i, node) {63 thisQ.resizeAllDragsAndDropsInGroup(64 thisQ.getClassnameNumericSuffix($(node), 'draggrouphomes'));65 });66 };67 /**68 * In a given group, set all the drags and drops to be the same size.69 *70 * @param {int} group the group number.71 */72 DragDropToTextQuestion.prototype.resizeAllDragsAndDropsInGroup = function(group) {73 var thisQ = this,74 dragHomes = this.getRoot().find('.draggrouphomes' + group + ' span.draghome'),75 maxWidth = 0,76 maxHeight = 0;77 // Find the maximum size of any drag in this groups.78 dragHomes.each(function(i, drag) {79 maxWidth = Math.max(maxWidth, Math.ceil(drag.offsetWidth));80 maxHeight = Math.max(maxHeight, Math.ceil(0 + drag.offsetHeight));81 });82 // The size we will want to set is a bit bigger than this.83 maxWidth += 8;84 maxHeight += 2;85 // Set each drag home to that size.86 dragHomes.each(function(i, drag) {87 thisQ.setElementSize(drag, maxWidth, maxHeight);88 });89 // Set each drop to that size.90 this.getRoot().find('span.drop.group' + group).each(function(i, drop) {91 thisQ.setElementSize(drop, maxWidth, maxHeight);92 });93 };94 /**95 * Set a given DOM element to be a particular size.96 *97 * @param {HTMLElement} element98 * @param {int} width99 * @param {int} height100 */101 DragDropToTextQuestion.prototype.setElementSize = function(element, width, height) {102 $(element).width(width).height(height).css('lineHeight', height + 'px');103 };104 /**105 * Invisible 'drag homes' are output by the renderer. These have the same properties106 * as the drag items but are invisible. We clone these invisible elements to make the107 * actual drag items.108 */109 DragDropToTextQuestion.prototype.cloneDrags = function() {110 var thisQ = this;111 thisQ.getRoot().find('span.draghome').each(function(index, draghome) {112 var drag = $(draghome);113 var placeHolder = drag.clone();114 placeHolder.removeClass();115 placeHolder.addClass('draghome choice' +116 thisQ.getChoice(drag) + ' group' +117 thisQ.getGroup(drag) + ' dragplaceholder');118 drag.before(placeHolder);119 });120 };121 /**122 * Update the position of drags.123 */124 DragDropToTextQuestion.prototype.positionDrags = function() {125 var thisQ = this,126 root = this.getRoot();127 // First move all items back home.128 root.find('span.draghome').not('.dragplaceholder').each(function(i, dragNode) {129 var drag = $(dragNode),130 currentPlace = thisQ.getClassnameNumericSuffix(drag, 'inplace');131 drag.addClass('unplaced')132 .removeClass('placed');133 drag.removeAttr('tabindex');134 if (currentPlace !== null) {135 drag.removeClass('inplace' + currentPlace);136 }137 });138 // Then place the once that should be placed.139 root.find('input.placeinput').each(function(i, inputNode) {140 var input = $(inputNode),141 choice = input.val(),142 place = thisQ.getPlace(input);143 // Record the last known position of the drop.144 var drop = root.find('.drop.place' + place),145 dropPosition = drop.offset();146 drop.data('prev-top', dropPosition.top).data('prev-left', dropPosition.left);147 if (choice === '0') {148 // No item in this place.149 return;150 }151 // Get the unplaced drag.152 var unplacedDrag = thisQ.getUnplacedChoice(thisQ.getGroup(input), choice);153 // Get the clone of the drag.154 var hiddenDrag = thisQ.getDragClone(unplacedDrag);155 if (hiddenDrag.length) {156 if (unplacedDrag.hasClass('infinite')) {157 var noOfDrags = thisQ.noOfDropsInGroup(thisQ.getGroup(unplacedDrag));158 var cloneDrags = thisQ.getInfiniteDragClones(unplacedDrag, false);159 if (cloneDrags.length < noOfDrags) {160 var cloneDrag = unplacedDrag.clone();161 hiddenDrag.after(cloneDrag);162 questionManager.addEventHandlersToDrag(cloneDrag);163 } else {164 hiddenDrag.addClass('active');165 }166 } else {167 hiddenDrag.addClass('active');168 }169 }170 // Send the drag to drop.171 thisQ.sendDragToDrop(thisQ.getUnplacedChoice(thisQ.getGroup(input), choice), drop);172 });173 };174 /**175 * Handles the start of dragging an item.176 *177 * @param {Event} e the touch start or mouse down event.178 */179 DragDropToTextQuestion.prototype.handleDragStart = function(e) {180 var thisQ = this,181 drag = $(e.target).closest('.draghome');182 var info = dragDrop.prepare(e);183 if (!info.start) {184 return;185 }186 drag.addClass('beingdragged');187 var currentPlace = this.getClassnameNumericSuffix(drag, 'inplace');188 if (currentPlace !== null) {189 this.setInputValue(currentPlace, 0);190 drag.removeClass('inplace' + currentPlace);191 var hiddenDrop = thisQ.getDrop(drag, currentPlace);192 if (hiddenDrop.length) {193 hiddenDrop.addClass('active');194 drag.offset(hiddenDrop.offset());195 }196 } else {197 var hiddenDrag = thisQ.getDragClone(drag);198 if (hiddenDrag.length) {199 if (drag.hasClass('infinite')) {200 var noOfDrags = this.noOfDropsInGroup(this.getGroup(drag));201 var cloneDrags = this.getInfiniteDragClones(drag, false);202 if (cloneDrags.length < noOfDrags) {203 var cloneDrag = drag.clone();204 cloneDrag.removeClass('beingdragged');205 hiddenDrag.after(cloneDrag);206 questionManager.addEventHandlersToDrag(cloneDrag);207 drag.offset(cloneDrag.offset());208 } else {209 hiddenDrag.addClass('active');210 drag.offset(hiddenDrag.offset());211 }212 } else {213 hiddenDrag.addClass('active');214 drag.offset(hiddenDrag.offset());215 }216 }217 }218 dragDrop.start(e, drag, function(x, y, drag) {219 thisQ.dragMove(x, y, drag);220 }, function(x, y, drag) {221 thisQ.dragEnd(x, y, drag);222 });223 };224 /**225 * Called whenever the currently dragged items moves.226 *227 * @param {Number} pageX the x position.228 * @param {Number} pageY the y position.229 * @param {jQuery} drag the item being moved.230 */231 DragDropToTextQuestion.prototype.dragMove = function(pageX, pageY, drag) {232 var thisQ = this;233 this.getRoot().find('span.drop.group' + this.getGroup(drag)).each(function(i, dropNode) {234 var drop = $(dropNode);235 if (thisQ.isPointInDrop(pageX, pageY, drop)) {236 drop.addClass('valid-drag-over-drop');237 } else {238 drop.removeClass('valid-drag-over-drop');239 }240 });241 this.getRoot().find('span.draghome.placed.group' + this.getGroup(drag)).not('.beingdragged').each(function(i, dropNode) {242 var drop = $(dropNode);243 if (thisQ.isPointInDrop(pageX, pageY, drop) && !thisQ.isDragSameAsDrop(drag, drop)) {244 drop.addClass('valid-drag-over-drop');245 } else {246 drop.removeClass('valid-drag-over-drop');247 }248 });249 };250 /**251 * Called when user drops a drag item.252 *253 * @param {Number} pageX the x position.254 * @param {Number} pageY the y position.255 * @param {jQuery} drag the item being moved.256 */257 DragDropToTextQuestion.prototype.dragEnd = function(pageX, pageY, drag) {258 var thisQ = this,259 root = this.getRoot(),260 placed = false;261 root.find('span.drop.group' + this.getGroup(drag)).each(function(i, dropNode) {262 var drop = $(dropNode);263 if (!thisQ.isPointInDrop(pageX, pageY, drop)) {264 // Not this drop.265 return true;266 }267 // Now put this drag into the drop.268 drop.removeClass('valid-drag-over-drop');269 thisQ.sendDragToDrop(drag, drop);270 placed = true;271 return false; // Stop the each() here.272 });273 root.find('span.draghome.placed.group' + this.getGroup(drag)).not('.beingdragged').each(function(i, placedNode) {274 var placedDrag = $(placedNode);275 if (!thisQ.isPointInDrop(pageX, pageY, placedDrag) || thisQ.isDragSameAsDrop(drag, placedDrag)) {276 // Not this placed drag.277 return true;278 }279 // Now put this drag into the drop.280 placedDrag.removeClass('valid-drag-over-drop');281 var currentPlace = thisQ.getClassnameNumericSuffix(placedDrag, 'inplace');282 var drop = thisQ.getDrop(drag, currentPlace);283 thisQ.sendDragToDrop(drag, drop);284 placed = true;285 return false; // Stop the each() here.286 });287 if (!placed) {288 this.sendDragHome(drag);289 }290 };291 /**292 * Animate a drag item into a given place (or back home).293 *294 * @param {jQuery|null} drag the item to place. If null, clear the place.295 * @param {jQuery} drop the place to put it.296 */297 DragDropToTextQuestion.prototype.sendDragToDrop = function(drag, drop) {298 // Is there already a drag in this drop? if so, evict it.299 var oldDrag = this.getCurrentDragInPlace(this.getPlace(drop));300 if (oldDrag.length !== 0) {301 var currentPlace = this.getClassnameNumericSuffix(oldDrag, 'inplace');302 var hiddenDrop = this.getDrop(oldDrag, currentPlace);303 hiddenDrop.addClass('active');304 oldDrag.addClass('beingdragged');305 oldDrag.offset(hiddenDrop.offset());306 this.sendDragHome(oldDrag);307 }308 if (drag.length === 0) {309 this.setInputValue(this.getPlace(drop), 0);310 if (drop.data('isfocus')) {311 drop.focus();312 }313 } else {314 this.setInputValue(this.getPlace(drop), this.getChoice(drag));315 drag.removeClass('unplaced')316 .addClass('placed inplace' + this.getPlace(drop));317 drag.attr('tabindex', 0);318 this.animateTo(drag, drop);319 }320 };321 /**322 * Animate a drag back to its home.323 *324 * @param {jQuery} drag the item being moved.325 */326 DragDropToTextQuestion.prototype.sendDragHome = function(drag) {327 var currentPlace = this.getClassnameNumericSuffix(drag, 'inplace');328 if (currentPlace !== null) {329 drag.removeClass('inplace' + currentPlace);330 }331 drag.data('unplaced', true);332 this.animateTo(drag, this.getDragHome(this.getGroup(drag), this.getChoice(drag)));333 };334 /**335 * Handles keyboard events on drops.336 *337 * Drops are focusable. Once focused, right/down/space switches to the next choice, and338 * left/up switches to the previous. Escape clear.339 *340 * @param {KeyboardEvent} e341 */342 DragDropToTextQuestion.prototype.handleKeyPress = function(e) {343 var drop = $(e.target).closest('.drop');344 if (drop.length === 0) {345 var placedDrag = $(e.target);346 var currentPlace = this.getClassnameNumericSuffix(placedDrag, 'inplace');347 if (currentPlace !== null) {348 drop = this.getDrop(placedDrag, currentPlace);349 }350 }351 var currentDrag = this.getCurrentDragInPlace(this.getPlace(drop)),352 nextDrag = $();353 switch (e.keyCode) {354 case keys.space:355 case keys.arrowRight:356 case keys.arrowDown:357 nextDrag = this.getNextDrag(this.getGroup(drop), currentDrag);358 break;359 case keys.arrowLeft:360 case keys.arrowUp:361 nextDrag = this.getPreviousDrag(this.getGroup(drop), currentDrag);362 break;363 case keys.escape:364 break;365 default:366 questionManager.isKeyboardNavigation = false;367 return; // To avoid the preventDefault below.368 }369 if (nextDrag.length) {370 nextDrag.data('isfocus', true);371 nextDrag.addClass('beingdragged');372 var hiddenDrag = this.getDragClone(nextDrag);373 if (hiddenDrag.length) {374 if (nextDrag.hasClass('infinite')) {375 var noOfDrags = this.noOfDropsInGroup(this.getGroup(nextDrag));376 var cloneDrags = this.getInfiniteDragClones(nextDrag, false);377 if (cloneDrags.length < noOfDrags) {378 var cloneDrag = nextDrag.clone();379 cloneDrag.removeClass('beingdragged');380 cloneDrag.removeAttr('tabindex');381 hiddenDrag.after(cloneDrag);382 questionManager.addEventHandlersToDrag(cloneDrag);383 nextDrag.offset(cloneDrag.offset());384 } else {385 hiddenDrag.addClass('active');386 nextDrag.offset(hiddenDrag.offset());387 }388 } else {389 hiddenDrag.addClass('active');390 nextDrag.offset(hiddenDrag.offset());391 }392 }393 } else {394 drop.data('isfocus', true);395 }396 e.preventDefault();397 this.sendDragToDrop(nextDrag, drop);398 };399 /**400 * Choose the next drag in a group.401 *402 * @param {int} group which group.403 * @param {jQuery} drag current choice (empty jQuery if there isn't one).404 * @return {jQuery} the next drag in that group, or null if there wasn't one.405 */406 DragDropToTextQuestion.prototype.getNextDrag = function(group, drag) {407 var choice,408 numChoices = this.noOfChoicesInGroup(group);409 if (drag.length === 0) {410 choice = 1; // Was empty, so we want to select the first choice.411 } else {412 choice = this.getChoice(drag) + 1;413 }414 var next = this.getUnplacedChoice(group, choice);415 while (next.length === 0 && choice < numChoices) {416 choice++;417 next = this.getUnplacedChoice(group, choice);418 }419 return next;420 };421 /**422 * Choose the previous drag in a group.423 *424 * @param {int} group which group.425 * @param {jQuery} drag current choice (empty jQuery if there isn't one).426 * @return {jQuery} the next drag in that group, or null if there wasn't one.427 */428 DragDropToTextQuestion.prototype.getPreviousDrag = function(group, drag) {429 var choice;430 if (drag.length === 0) {431 choice = this.noOfChoicesInGroup(group);432 } else {433 choice = this.getChoice(drag) - 1;434 }435 var previous = this.getUnplacedChoice(group, choice);436 while (previous.length === 0 && choice > 1) {437 choice--;438 previous = this.getUnplacedChoice(group, choice);439 }440 // Does this choice exist?441 return previous;442 };443 /**444 * Animate an object to the given destination.445 *446 * @param {jQuery} drag the element to be animated.447 * @param {jQuery} target element marking the place to move it to.448 */449 DragDropToTextQuestion.prototype.animateTo = function(drag, target) {450 var currentPos = drag.offset(),451 targetPos = target.offset(),452 thisQ = this;453 M.util.js_pending('qtype_ddwtos-animate-' + thisQ.containerId);454 // Animate works in terms of CSS position, whereas locating an object455 // on the page works best with jQuery offset() function. So, to get456 // the right target position, we work out the required change in457 // offset() and then add that to the current CSS position.458 drag.animate(459 {460 left: parseInt(drag.css('left')) + targetPos.left - currentPos.left,461 top: parseInt(drag.css('top')) + targetPos.top - currentPos.top462 },463 {464 duration: 'fast',465 done: function() {466 $('body').trigger('qtype_ddwtos-dragmoved', [drag, target, thisQ]);467 M.util.js_complete('qtype_ddwtos-animate-' + thisQ.containerId);468 }469 }470 );471 };472 /**473 * Detect if a point is inside a given DOM node.474 *475 * @param {Number} pageX the x position.476 * @param {Number} pageY the y position.477 * @param {jQuery} drop the node to check (typically a drop).478 * @return {boolean} whether the point is inside the node.479 */480 DragDropToTextQuestion.prototype.isPointInDrop = function(pageX, pageY, drop) {481 var position = drop.offset();482 return pageX >= position.left && pageX < position.left + drop.width()483 && pageY >= position.top && pageY < position.top + drop.height();484 };485 /**486 * Set the value of the hidden input for a place, to record what is currently there.487 *488 * @param {int} place which place to set the input value for.489 * @param {int} choice the value to set.490 */491 DragDropToTextQuestion.prototype.setInputValue = function(place, choice) {492 this.getRoot().find('input.placeinput.place' + place).val(choice);493 };494 /**495 * Get the outer div for this question.496 *497 * @returns {jQuery} containing that div.498 */499 DragDropToTextQuestion.prototype.getRoot = function() {500 return $(document.getElementById(this.containerId));501 };502 /**503 * Get drag home for a given choice.504 *505 * @param {int} group the group.506 * @param {int} choice the choice number.507 * @returns {jQuery} containing that div.508 */509 DragDropToTextQuestion.prototype.getDragHome = function(group, choice) {510 if (!this.getRoot().find('.draghome.dragplaceholder.group' + group + '.choice' + choice).is(':visible')) {511 return this.getRoot().find('.draggrouphomes' + group +512 ' span.draghome.infinite' +513 '.choice' + choice +514 '.group' + group);515 }516 return this.getRoot().find('.draghome.dragplaceholder.group' + group + '.choice' + choice);517 };518 /**519 * Get an unplaced choice for a particular group.520 *521 * @param {int} group the group.522 * @param {int} choice the choice number.523 * @returns {jQuery} jQuery wrapping the unplaced choice. If there isn't one, the jQuery will be empty.524 */525 DragDropToTextQuestion.prototype.getUnplacedChoice = function(group, choice) {526 return this.getRoot().find('.draghome.group' + group + '.choice' + choice + '.unplaced').slice(0, 1);527 };528 /**529 * Get the drag that is currently in a given place.530 *531 * @param {int} place the place number.532 * @return {jQuery} the current drag (or an empty jQuery if none).533 */534 DragDropToTextQuestion.prototype.getCurrentDragInPlace = function(place) {535 return this.getRoot().find('span.draghome.inplace' + place);536 };537 /**538 * Return the number of blanks in a given group.539 *540 * @param {int} group the group number.541 * @returns {int} the number of drops.542 */543 DragDropToTextQuestion.prototype.noOfDropsInGroup = function(group) {544 return this.getRoot().find('.drop.group' + group).length;545 };546 /**547 * Return the number of choices in a given group.548 *549 * @param {int} group the group number.550 * @returns {int} the number of choices.551 */552 DragDropToTextQuestion.prototype.noOfChoicesInGroup = function(group) {553 return this.getRoot().find('.draghome.group' + group).length;554 };555 /**556 * Return the number at the end of the CSS class name with the given prefix.557 *558 * @param {jQuery} node559 * @param {String} prefix name prefix560 * @returns {Number|null} the suffix if found, else null.561 */562 DragDropToTextQuestion.prototype.getClassnameNumericSuffix = function(node, prefix) {563 var classes = node.attr('class');564 if (classes !== '') {565 var classesArr = classes.split(' ');566 for (var index = 0; index < classesArr.length; index++) {567 var patt1 = new RegExp('^' + prefix + '([0-9])+$');568 if (patt1.test(classesArr[index])) {569 var patt2 = new RegExp('([0-9])+$');570 var match = patt2.exec(classesArr[index]);571 return Number(match[0]);572 }573 }574 }575 return null;576 };577 /**578 * Get the choice number of a drag.579 *580 * @param {jQuery} drag the drag.581 * @returns {Number} the choice number.582 */583 DragDropToTextQuestion.prototype.getChoice = function(drag) {584 return this.getClassnameNumericSuffix(drag, 'choice');585 };586 /**587 * Given a DOM node that is significant to this question588 * (drag, drop, ...) get the group it belongs to.589 *590 * @param {jQuery} node a DOM node.591 * @returns {Number} the group it belongs to.592 */593 DragDropToTextQuestion.prototype.getGroup = function(node) {594 return this.getClassnameNumericSuffix(node, 'group');595 };596 /**597 * Get the place number of a drop, or its corresponding hidden input.598 *599 * @param {jQuery} node the DOM node.600 * @returns {Number} the place number.601 */602 DragDropToTextQuestion.prototype.getPlace = function(node) {603 return this.getClassnameNumericSuffix(node, 'place');604 };605 /**606 * Get drag clone for a given drag.607 *608 * @param {jQuery} drag the drag.609 * @returns {jQuery} the drag's clone.610 */611 DragDropToTextQuestion.prototype.getDragClone = function(drag) {612 return this.getRoot().find('.draggrouphomes' +613 this.getGroup(drag) +614 ' span.draghome' +615 '.choice' + this.getChoice(drag) +616 '.group' + this.getGroup(drag) +617 '.dragplaceholder');618 };619 /**620 * Get infinite drag clones for given drag.621 *622 * @param {jQuery} drag the drag.623 * @param {Boolean} inHome in the home area or not.624 * @returns {jQuery} the drag's clones.625 */626 DragDropToTextQuestion.prototype.getInfiniteDragClones = function(drag, inHome) {627 if (inHome) {628 return this.getRoot().find('.draggrouphomes' +629 this.getGroup(drag) +630 ' span.draghome' +631 '.choice' + this.getChoice(drag) +632 '.group' + this.getGroup(drag) +633 '.infinite').not('.dragplaceholder');634 }635 return this.getRoot().find('span.draghome' +636 '.choice' + this.getChoice(drag) +637 '.group' + this.getGroup(drag) +638 '.infinite').not('.dragplaceholder');639 };640 /**641 * Get drop for a given drag and place.642 *643 * @param {jQuery} drag the drag.644 * @param {Integer} currentPlace the current place of drag.645 * @returns {jQuery} the drop's clone.646 */647 DragDropToTextQuestion.prototype.getDrop = function(drag, currentPlace) {648 return this.getRoot().find('.drop.group' + this.getGroup(drag) + '.place' + currentPlace);649 };650 /**651 * Check that the drag is drop to it's clone.652 *653 * @param {jQuery} drag The drag.654 * @param {jQuery} drop The drop.655 * @returns {boolean}656 */657 DragDropToTextQuestion.prototype.isDragSameAsDrop = function(drag, drop) {658 return this.getChoice(drag) === this.getChoice(drop) && this.getGroup(drag) === this.getGroup(drop);659 };660 /**661 * Singleton that tracks all the DragDropToTextQuestions on this page, and deals662 * with event dispatching.663 *664 * @type {Object}665 */666 var questionManager = {667 /**668 * {boolean} used to ensure the event handlers are only initialised once per page.669 */670 eventHandlersInitialised: false,671 /**672 * {boolean} is keyboard navigation or not.673 */674 isKeyboardNavigation: false,675 /**676 * {DragDropToTextQuestion[]} all the questions on this page, indexed by containerId (id on the .que div).677 */678 questions: {},679 /**680 * Initialise questions.681 *682 * @param {String} containerId id of the outer div for this question.683 * @param {boolean} readOnly whether the question is being displayed read-only.684 */685 init: function(containerId, readOnly) {686 questionManager.questions[containerId] = new DragDropToTextQuestion(containerId, readOnly);687 if (!questionManager.eventHandlersInitialised) {688 questionManager.setupEventHandlers();689 questionManager.eventHandlersInitialised = true;690 }691 },692 /**693 * Set up the event handlers that make this question type work. (Done once per page.)694 */695 setupEventHandlers: function() {696 // We do not use the body event here to prevent the other event on Mobile device, such as scroll event.697 questionManager.addEventHandlersToDrag($('.que.ddwtos:not(.qtype_ddwtos-readonly) span.draghome'));698 $('body')699 .on('keydown',700 '.que.ddwtos:not(.qtype_ddwtos-readonly) span.drop',701 questionManager.handleKeyPress)702 .on('keydown',703 '.que.ddwtos:not(.qtype_ddwtos-readonly) span.draghome.placed:not(.beingdragged)',704 questionManager.handleKeyPress)705 .on('qtype_ddwtos-dragmoved', questionManager.handleDragMoved);706 },707 /**708 * Binding the drag/touch event again for newly created element.709 *710 * @param {jQuery} element Element to bind the event711 */712 addEventHandlersToDrag: function(element) {713 // Unbind all the mousedown and touchstart events to prevent double binding.714 element.unbind('mousedown touchstart');715 element.on('mousedown touchstart', questionManager.handleDragStart);716 },717 /**718 * Handle mouse down / touch start on drags.719 * @param {Event} e the DOM event.720 */721 handleDragStart: function(e) {722 e.preventDefault();723 var question = questionManager.getQuestionForEvent(e);724 if (question) {725 question.handleDragStart(e);726 }727 },728 /**729 * Handle key down / press on drops.730 * @param {KeyboardEvent} e731 */732 handleKeyPress: function(e) {733 if (questionManager.isKeyboardNavigation) {734 return;735 }736 questionManager.isKeyboardNavigation = true;737 var question = questionManager.getQuestionForEvent(e);738 if (question) {739 question.handleKeyPress(e);740 }741 },742 /**743 * Given an event, work out which question it affects.744 *745 * @param {Event} e the event.746 * @returns {DragDropToTextQuestion|undefined} The question, or undefined.747 */748 getQuestionForEvent: function(e) {749 var containerId = $(e.currentTarget).closest('.que.ddwtos').attr('id');750 return questionManager.questions[containerId];751 },752 /**753 * Handle when drag moved.754 *755 * @param {Event} e the event.756 * @param {jQuery} drag the drag757 * @param {jQuery} target the target758 * @param {DragDropToTextQuestion} thisQ the question.759 */760 handleDragMoved: function(e, drag, target, thisQ) {761 drag.removeClass('beingdragged');762 drag.css('top', '').css('left', '');763 target.after(drag);764 target.removeClass('active');765 if (typeof drag.data('unplaced') !== 'undefined' && drag.data('unplaced') === true) {766 drag.removeClass('placed').addClass('unplaced');767 drag.removeAttr('tabindex');768 drag.removeData('unplaced');769 if (drag.hasClass('infinite') && thisQ.getInfiniteDragClones(drag, true).length > 1) {770 thisQ.getInfiniteDragClones(drag, true).first().remove();771 }772 }773 if (typeof drag.data('isfocus') !== 'undefined' && drag.data('isfocus') === true) {774 drag.focus();775 drag.removeData('isfocus');776 }777 if (typeof target.data('isfocus') !== 'undefined' && target.data('isfocus') === true) {778 target.removeData('isfocus');779 }780 if (questionManager.isKeyboardNavigation) {781 questionManager.isKeyboardNavigation = false;782 }783 }784 };785 /**786 * @alias module:qtype_ddwtos/ddwtos787 */788 return {789 /**790 * Initialise one drag-drop into text question.791 *792 * @param {String} containerId id of the outer div for this question.793 * @param {boolean} readOnly whether the question is being displayed read-only.794 */795 init: questionManager.init796 };...
dnd.js
Source:dnd.js
1// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-2const Clutter = imports.gi.Clutter;3const GLib = imports.gi.GLib;4const Gtk = imports.gi.Gtk;5const St = imports.gi.St;6const Lang = imports.lang;7const Meta = imports.gi.Meta;8const Shell = imports.gi.Shell;9const Signals = imports.signals;10const Tweener = imports.ui.tweener;11const Main = imports.ui.main;12const Params = imports.misc.params;13// Time to scale down to maxDragActorSize14const SCALE_ANIMATION_TIME = 0.25;15// Time to animate to original position on cancel16const SNAP_BACK_ANIMATION_TIME = 0.25;17// Time to animate to original position on success18const REVERT_ANIMATION_TIME = 0.75;19const DragMotionResult = {20 NO_DROP: 0,21 COPY_DROP: 1,22 MOVE_DROP: 2,23 CONTINUE: 324};25const DRAG_CURSOR_MAP = {26 0: Meta.Cursor.DND_UNSUPPORTED_TARGET,27 1: Meta.Cursor.DND_COPY,28 2: Meta.Cursor.DND_MOVE29};30const DragDropResult = {31 FAILURE: 0,32 SUCCESS: 1,33 CONTINUE: 234};35let eventHandlerActor = null;36let currentDraggable = null;37let dragMonitors = [];38function _getEventHandlerActor() {39 if (!eventHandlerActor) {40 eventHandlerActor = new Clutter.Actor({ width: 0, height: 0 });41 Main.uiGroup.add_actor(eventHandlerActor);42 // We connect to 'event' rather than 'captured-event' because the capturing phase doesn't happen43 // when you've grabbed the pointer.44 eventHandlerActor.connect('event',45 function(actor, event) {46 return currentDraggable._onEvent(actor, event);47 });48 }49 return eventHandlerActor;50}51function addDragMonitor(monitor) {52 dragMonitors.push(monitor);53}54function removeDragMonitor(monitor) {55 for (let i = 0; i < dragMonitors.length; i++)56 if (dragMonitors[i] == monitor) {57 dragMonitors.splice(i, 1);58 return;59 }60}61const _Draggable = new Lang.Class({62 Name: 'Draggable',63 _init : function(actor, params) {64 params = Params.parse(params, { manualMode: false,65 restoreOnSuccess: false,66 dragActorMaxSize: undefined,67 dragActorOpacity: undefined });68 this.actor = actor;69 if (!params.manualMode)70 this.actor.connect('button-press-event',71 Lang.bind(this, this._onButtonPress));72 this.actor.connect('destroy', Lang.bind(this, function() {73 this._actorDestroyed = true;74 if (this._dragInProgress && this._dragCancellable)75 this._cancelDrag(global.get_current_time());76 this.disconnectAll();77 }));78 this._onEventId = null;79 this._restoreOnSuccess = params.restoreOnSuccess;80 this._dragActorMaxSize = params.dragActorMaxSize;81 this._dragActorOpacity = params.dragActorOpacity;82 this._buttonDown = false; // The mouse button has been pressed and has not yet been released.83 this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.84 this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).85 this._dragCancellable = true;86 this._eventsGrabbed = false;87 },88 _onButtonPress : function (actor, event) {89 if (event.get_button() != 1)90 return Clutter.EVENT_PROPAGATE;91 if (Tweener.getTweenCount(actor))92 return Clutter.EVENT_PROPAGATE;93 this._buttonDown = true;94 this._grabActor();95 let [stageX, stageY] = event.get_coords();96 this._dragStartX = stageX;97 this._dragStartY = stageY;98 return Clutter.EVENT_PROPAGATE;99 },100 _grabActor: function() {101 Clutter.grab_pointer(this.actor);102 this._onEventId = this.actor.connect('event',103 Lang.bind(this, this._onEvent));104 },105 _ungrabActor: function() {106 if (!this._onEventId)107 return;108 Clutter.ungrab_pointer();109 this.actor.disconnect(this._onEventId);110 this._onEventId = null;111 },112 _grabEvents: function() {113 if (!this._eventsGrabbed) {114 this._eventsGrabbed = Main.pushModal(_getEventHandlerActor());115 if (this._eventsGrabbed)116 Clutter.grab_pointer(_getEventHandlerActor());117 }118 },119 _ungrabEvents: function() {120 if (this._eventsGrabbed) {121 Clutter.ungrab_pointer();122 Main.popModal(_getEventHandlerActor());123 this._eventsGrabbed = false;124 }125 },126 _onEvent: function(actor, event) {127 // We intercept BUTTON_RELEASE event to know that the button was released in case we128 // didn't start the drag, to drop the draggable in case the drag was in progress, and129 // to complete the drag and ensure that whatever happens to be under the pointer does130 // not get triggered if the drag was cancelled with Esc.131 if (event.type() == Clutter.EventType.BUTTON_RELEASE) {132 this._buttonDown = false;133 if (this._dragInProgress) {134 return this._dragActorDropped(event);135 } else if (this._dragActor != null && !this._animationInProgress) {136 // Drag must have been cancelled with Esc.137 this._dragComplete();138 return Clutter.EVENT_STOP;139 } else {140 // Drag has never started.141 this._ungrabActor();142 return Clutter.EVENT_PROPAGATE;143 }144 // We intercept MOTION event to figure out if the drag has started and to draw145 // this._dragActor under the pointer when dragging is in progress146 } else if (event.type() == Clutter.EventType.MOTION) {147 if (this._dragInProgress) {148 return this._updateDragPosition(event);149 } else if (this._dragActor == null) {150 return this._maybeStartDrag(event);151 }152 // We intercept KEY_PRESS event so that we can process Esc key press to cancel153 // dragging and ignore all other key presses.154 } else if (event.type() == Clutter.EventType.KEY_PRESS && this._dragInProgress) {155 let symbol = event.get_key_symbol();156 if (symbol == Clutter.Escape) {157 this._cancelDrag(event.get_time());158 return Clutter.EVENT_STOP;159 }160 }161 return Clutter.EVENT_PROPAGATE;162 },163 /**164 * fakeRelease:165 *166 * Fake a release event.167 * Must be called if you want to intercept release events on draggable168 * actors for other purposes (for example if you're using169 * PopupMenu.ignoreRelease())170 */171 fakeRelease: function() {172 this._buttonDown = false;173 this._ungrabActor();174 },175 /**176 * startDrag:177 * @stageX: X coordinate of event178 * @stageY: Y coordinate of event179 * @time: Event timestamp180 *181 * Directly initiate a drag and drop operation from the given actor.182 * This function is useful to call if you've specified manualMode183 * for the draggable.184 */185 startDrag: function (stageX, stageY, time) {186 currentDraggable = this;187 this._dragInProgress = true;188 // Special-case St.Button: the pointer grab messes with the internal189 // state, so force a reset to a reasonable state here190 if (this.actor instanceof St.Button) {191 this.actor.fake_release();192 this.actor.hover = false;193 }194 this.emit('drag-begin', time);195 if (this._onEventId)196 this._ungrabActor();197 this._grabEvents();198 global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG);199 this._dragX = this._dragStartX = stageX;200 this._dragY = this._dragStartY = stageY;201 if (this.actor._delegate && this.actor._delegate.getDragActor) {202 this._dragActor = this.actor._delegate.getDragActor();203 Main.uiGroup.add_child(this._dragActor);204 this._dragActor.raise_top();205 Shell.util_set_hidden_from_pick(this._dragActor, true);206 // Drag actor does not always have to be the same as actor. For example drag actor207 // can be an image that's part of the actor. So to perform "snap back" correctly we need208 // to know what was the drag actor source.209 if (this.actor._delegate.getDragActorSource) {210 this._dragActorSource = this.actor._delegate.getDragActorSource();211 // If the user dragged from the source, then position212 // the dragActor over it. Otherwise, center it213 // around the pointer214 let [sourceX, sourceY] = this._dragActorSource.get_transformed_position();215 let x, y;216 if (stageX > sourceX && stageX <= sourceX + this._dragActor.width &&217 stageY > sourceY && stageY <= sourceY + this._dragActor.height) {218 x = sourceX;219 y = sourceY;220 } else {221 x = stageX - this._dragActor.width / 2;222 y = stageY - this._dragActor.height / 2;223 }224 this._dragActor.set_position(x, y);225 } else {226 this._dragActorSource = this.actor;227 }228 this._dragOrigParent = undefined;229 this._dragOffsetX = this._dragActor.x - this._dragStartX;230 this._dragOffsetY = this._dragActor.y - this._dragStartY;231 } else {232 this._dragActor = this.actor;233 this._dragActorSource = undefined;234 this._dragOrigParent = this.actor.get_parent();235 this._dragOrigX = this._dragActor.x;236 this._dragOrigY = this._dragActor.y;237 this._dragOrigScale = this._dragActor.scale_x;238 // Set the actor's scale such that it will keep the same239 // transformed size when it's reparented to the uiGroup240 let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();241 this._dragActor.set_scale(scaledWidth / this.actor.width,242 scaledHeight / this.actor.height);243 let [actorStageX, actorStageY] = this.actor.get_transformed_position();244 this._dragOffsetX = actorStageX - this._dragStartX;245 this._dragOffsetY = actorStageY - this._dragStartY;246 this._dragOrigParent.remove_actor(this._dragActor);247 Main.uiGroup.add_child(this._dragActor);248 this._dragActor.raise_top();249 Shell.util_set_hidden_from_pick(this._dragActor, true);250 }251 this._dragOrigOpacity = this._dragActor.opacity;252 if (this._dragActorOpacity != undefined)253 this._dragActor.opacity = this._dragActorOpacity;254 this._snapBackX = this._dragStartX + this._dragOffsetX;255 this._snapBackY = this._dragStartY + this._dragOffsetY;256 this._snapBackScale = this._dragActor.scale_x;257 if (this._dragActorMaxSize != undefined) {258 let [scaledWidth, scaledHeight] = this._dragActor.get_transformed_size();259 let currentSize = Math.max(scaledWidth, scaledHeight);260 if (currentSize > this._dragActorMaxSize) {261 let scale = this._dragActorMaxSize / currentSize;262 let origScale = this._dragActor.scale_x;263 let origDragOffsetX = this._dragOffsetX;264 let origDragOffsetY = this._dragOffsetY;265 // The position of the actor changes as we scale266 // around the drag position, but we can't just tween267 // to the final position because that tween would268 // fight with updates as the user continues dragging269 // the mouse; instead we do the position computations in270 // an onUpdate() function.271 Tweener.addTween(this._dragActor,272 { scale_x: scale * origScale,273 scale_y: scale * origScale,274 time: SCALE_ANIMATION_TIME,275 transition: 'easeOutQuad',276 onUpdate: function() {277 let currentScale = this._dragActor.scale_x / origScale;278 this._dragOffsetX = currentScale * origDragOffsetX;279 this._dragOffsetY = currentScale * origDragOffsetY;280 this._dragActor.set_position(this._dragX + this._dragOffsetX,281 this._dragY + this._dragOffsetY);282 },283 onUpdateScope: this });284 }285 }286 },287 _maybeStartDrag: function(event) {288 let [stageX, stageY] = event.get_coords();289 // See if the user has moved the mouse enough to trigger a drag290 let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold;291 if ((Math.abs(stageX - this._dragStartX) > threshold ||292 Math.abs(stageY - this._dragStartY) > threshold)) {293 this.startDrag(stageX, stageY, event.get_time());294 this._updateDragPosition(event);295 }296 return true;297 },298 _updateDragHover : function () {299 this._updateHoverId = 0;300 let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,301 this._dragX, this._dragY);302 let dragEvent = {303 x: this._dragX,304 y: this._dragY,305 dragActor: this._dragActor,306 source: this.actor._delegate,307 targetActor: target308 };309 for (let i = 0; i < dragMonitors.length; i++) {310 let motionFunc = dragMonitors[i].dragMotion;311 if (motionFunc) {312 let result = motionFunc(dragEvent);313 if (result != DragMotionResult.CONTINUE) {314 global.screen.set_cursor(DRAG_CURSOR_MAP[result]);315 return GLib.SOURCE_REMOVE;316 }317 }318 }319 while (target) {320 if (target._delegate && target._delegate.handleDragOver) {321 let [r, targX, targY] = target.transform_stage_point(this._dragX, this._dragY);322 // We currently loop through all parents on drag-over even if one of the children has handled it.323 // We can check the return value of the function and break the loop if it's true if we don't want324 // to continue checking the parents.325 let result = target._delegate.handleDragOver(this.actor._delegate,326 this._dragActor,327 targX,328 targY,329 0);330 if (result != DragMotionResult.CONTINUE) {331 global.screen.set_cursor(DRAG_CURSOR_MAP[result]);332 return GLib.SOURCE_REMOVE;333 }334 }335 target = target.get_parent();336 }337 global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG);338 return GLib.SOURCE_REMOVE;339 },340 _queueUpdateDragHover: function() {341 if (this._updateHoverId)342 return;343 this._updateHoverId = GLib.idle_add(GLib.PRIORITY_DEFAULT,344 Lang.bind(this, this._updateDragHover));345 GLib.Source.set_name_by_id(this._updateHoverId, '[gnome-shell] this._updateDragHover');346 },347 _updateDragPosition : function (event) {348 let [stageX, stageY] = event.get_coords();349 this._dragX = stageX;350 this._dragY = stageY;351 this._dragActor.set_position(stageX + this._dragOffsetX,352 stageY + this._dragOffsetY);353 this._queueUpdateDragHover();354 return true;355 },356 _dragActorDropped: function(event) {357 let [dropX, dropY] = event.get_coords();358 let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,359 dropX, dropY);360 // We call observers only once per motion with the innermost361 // target actor. If necessary, the observer can walk the362 // parent itself.363 let dropEvent = {364 dropActor: this._dragActor,365 targetActor: target,366 clutterEvent: event367 };368 for (let i = 0; i < dragMonitors.length; i++) {369 let dropFunc = dragMonitors[i].dragDrop;370 if (dropFunc)371 switch (dropFunc(dropEvent)) {372 case DragDropResult.FAILURE:373 case DragDropResult.SUCCESS:374 return true;375 case DragDropResult.CONTINUE:376 continue;377 }378 }379 // At this point it is too late to cancel a drag by destroying380 // the actor, the fate of which is decided by acceptDrop and its381 // side-effects382 this._dragCancellable = false;383 while (target) {384 if (target._delegate && target._delegate.acceptDrop) {385 let [r, targX, targY] = target.transform_stage_point(dropX, dropY);386 if (target._delegate.acceptDrop(this.actor._delegate,387 this._dragActor,388 targX,389 targY,390 event.get_time())) {391 // If it accepted the drop without taking the actor,392 // handle it ourselves.393 if (this._dragActor.get_parent() == Main.uiGroup) {394 if (this._restoreOnSuccess) {395 this._restoreDragActor(event.get_time());396 return true;397 } else398 this._dragActor.destroy();399 }400 this._dragInProgress = false;401 global.screen.set_cursor(Meta.Cursor.DEFAULT);402 this.emit('drag-end', event.get_time(), true);403 this._dragComplete();404 return true;405 }406 }407 target = target.get_parent();408 }409 this._cancelDrag(event.get_time());410 return true;411 },412 _getRestoreLocation: function() {413 let x, y, scale;414 if (this._dragActorSource && this._dragActorSource.visible) {415 // Snap the clone back to its source416 [x, y] = this._dragActorSource.get_transformed_position();417 let [sourceScaledWidth, sourceScaledHeight] = this._dragActorSource.get_transformed_size();418 scale = this._dragActor.width / sourceScaledWidth;419 } else if (this._dragOrigParent) {420 // Snap the actor back to its original position within421 // its parent, adjusting for the fact that the parent422 // may have been moved or scaled423 let [parentX, parentY] = this._dragOrigParent.get_transformed_position();424 let [parentWidth, parentHeight] = this._dragOrigParent.get_size();425 let [parentScaledWidth, parentScaledHeight] = this._dragOrigParent.get_transformed_size();426 let parentScale = 1.0;427 if (parentWidth != 0)428 parentScale = parentScaledWidth / parentWidth;429 x = parentX + parentScale * this._dragOrigX;430 y = parentY + parentScale * this._dragOrigY;431 scale = this._dragOrigScale * parentScale;432 } else {433 // Snap back actor to its original stage position434 x = this._snapBackX;435 y = this._snapBackY;436 scale = this._snapBackScale;437 }438 return [x, y, scale];439 },440 _cancelDrag: function(eventTime) {441 this.emit('drag-cancelled', eventTime);442 this._dragInProgress = false;443 let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();444 if (this._actorDestroyed) {445 global.screen.set_cursor(Meta.Cursor.DEFAULT);446 if (!this._buttonDown)447 this._dragComplete();448 this.emit('drag-end', eventTime, false);449 if (!this._dragOrigParent)450 this._dragActor.destroy();451 return;452 }453 this._animationInProgress = true;454 // No target, so snap back455 Tweener.addTween(this._dragActor,456 { x: snapBackX,457 y: snapBackY,458 scale_x: snapBackScale,459 scale_y: snapBackScale,460 opacity: this._dragOrigOpacity,461 time: SNAP_BACK_ANIMATION_TIME,462 transition: 'easeOutQuad',463 onComplete: this._onAnimationComplete,464 onCompleteScope: this,465 onCompleteParams: [this._dragActor, eventTime]466 });467 },468 _restoreDragActor: function(eventTime) {469 this._dragInProgress = false;470 [restoreX, restoreY, restoreScale] = this._getRestoreLocation();471 // fade the actor back in at its original location472 this._dragActor.set_position(restoreX, restoreY);473 this._dragActor.set_scale(restoreScale, restoreScale);474 this._dragActor.opacity = 0;475 this._animationInProgress = true;476 Tweener.addTween(this._dragActor,477 { opacity: this._dragOrigOpacity,478 time: REVERT_ANIMATION_TIME,479 transition: 'easeOutQuad',480 onComplete: this._onAnimationComplete,481 onCompleteScope: this,482 onCompleteParams: [this._dragActor, eventTime]483 });484 },485 _onAnimationComplete : function (dragActor, eventTime) {486 if (this._dragOrigParent) {487 Main.uiGroup.remove_child(this._dragActor);488 this._dragOrigParent.add_actor(this._dragActor);489 dragActor.set_scale(this._dragOrigScale, this._dragOrigScale);490 dragActor.set_position(this._dragOrigX, this._dragOrigY);491 } else {492 dragActor.destroy();493 }494 global.screen.set_cursor(Meta.Cursor.DEFAULT);495 this.emit('drag-end', eventTime, false);496 this._animationInProgress = false;497 if (!this._buttonDown)498 this._dragComplete();499 },500 _dragComplete: function() {501 if (!this._actorDestroyed)502 Shell.util_set_hidden_from_pick(this._dragActor, false);503 this._ungrabEvents();504 global.sync_pointer();505 if (this._updateHoverId) {506 GLib.source_remove(this._updateHoverId);507 this._updateHoverId = 0;508 }509 this._dragActor = undefined;510 currentDraggable = null;511 }512});513Signals.addSignalMethods(_Draggable.prototype);514/**515 * makeDraggable:516 * @actor: Source actor517 * @params: (optional) Additional parameters518 *519 * Create an object which controls drag and drop for the given actor.520 *521 * If %manualMode is %true in @params, do not automatically start522 * drag and drop on click523 *524 * If %dragActorMaxSize is present in @params, the drag actor will525 * be scaled down to be no larger than that size in pixels.526 *527 * If %dragActorOpacity is present in @params, the drag actor will528 * will be set to have that opacity during the drag.529 *530 * Note that when the drag actor is the source actor and the drop531 * succeeds, the actor scale and opacity aren't reset; if the drop532 * target wants to reuse the actor, it's up to the drop target to533 * reset these values.534 */535function makeDraggable(actor, params) {536 return new _Draggable(actor, params);...
abstractdragdrop.js
Source:abstractdragdrop.js
1goog.provide('goog.fx.AbstractDragDrop'); 2goog.provide('goog.fx.AbstractDragDrop.EventType'); 3goog.provide('goog.fx.DragDropEvent'); 4goog.provide('goog.fx.DragDropItem'); 5goog.require('goog.dom'); 6goog.require('goog.dom.classes'); 7goog.require('goog.events'); 8goog.require('goog.events.Event'); 9goog.require('goog.events.EventTarget'); 10goog.require('goog.events.EventType'); 11goog.require('goog.fx.Dragger'); 12goog.require('goog.fx.Dragger.EventType'); 13goog.require('goog.math.Box'); 14goog.require('goog.math.Coordinate'); 15goog.require('goog.style'); 16goog.fx.AbstractDragDrop = function() { 17 this.items_ =[]; 18 this.targets_ =[]; 19 this.scrollableContainers_ =[]; 20}; 21goog.inherits(goog.fx.AbstractDragDrop, goog.events.EventTarget); 22goog.fx.AbstractDragDrop.DUMMY_TARGET_MIN_SIZE_ = 10; 23goog.fx.AbstractDragDrop.prototype.isSource_ = false; 24goog.fx.AbstractDragDrop.prototype.isTarget_ = false; 25goog.fx.AbstractDragDrop.prototype.subtargetFunction_; 26goog.fx.AbstractDragDrop.prototype.activeSubtarget_; 27goog.fx.AbstractDragDrop.prototype.dragClass_; 28goog.fx.AbstractDragDrop.prototype.sourceClass_; 29goog.fx.AbstractDragDrop.prototype.targetClass_; 30goog.fx.AbstractDragDrop.prototype.scrollTarget_; 31goog.fx.AbstractDragDrop.prototype.dummyTarget_; 32goog.fx.AbstractDragDrop.prototype.initialized_ = false; 33goog.fx.AbstractDragDrop.EventType = { 34 DRAGOVER: 'dragover', 35 DRAGOUT: 'dragout', 36 DRAG: 'drag', 37 DROP: 'drop', 38 DRAGSTART: 'dragstart', 39 DRAGEND: 'dragend' 40}; 41goog.fx.AbstractDragDrop.initDragDistanceThreshold = 5; 42goog.fx.AbstractDragDrop.prototype.setDragClass = function(className) { 43 this.dragClass_ = className; 44}; 45goog.fx.AbstractDragDrop.prototype.setSourceClass = function(className) { 46 this.sourceClass_ = className; 47}; 48goog.fx.AbstractDragDrop.prototype.setTargetClass = function(className) { 49 this.targetClass_ = className; 50}; 51goog.fx.AbstractDragDrop.prototype.isInitialized = function() { 52 return this.initialized_; 53}; 54goog.fx.AbstractDragDrop.prototype.addItem = function(item) { 55 throw Error('Call to pure virtual method'); 56}; 57goog.fx.AbstractDragDrop.prototype.addTarget = function(target) { 58 this.targets_.push(target); 59 target.isTarget_ = true; 60 this.isSource_ = true; 61}; 62goog.fx.AbstractDragDrop.prototype.setScrollTarget = function(scrollTarget) { 63 this.scrollTarget_ = scrollTarget; 64}; 65goog.fx.AbstractDragDrop.prototype.init = function() { 66 if(this.initialized_) { 67 return; 68 } 69 for(var item, i = 0; item = this.items_[i]; i ++) { 70 this.initItem(item); 71 } 72 this.initialized_ = true; 73}; 74goog.fx.AbstractDragDrop.prototype.initItem = function(item) { 75 if(this.isSource_) { 76 goog.events.listen(item.element, goog.events.EventType.MOUSEDOWN, item.mouseDown_, false, item); 77 if(this.sourceClass_) { 78 goog.dom.classes.add(item.element, this.sourceClass_); 79 } 80 } 81 if(this.isTarget_ && this.targetClass_) { 82 goog.dom.classes.add(item.element, this.targetClass_); 83 } 84}; 85goog.fx.AbstractDragDrop.prototype.disposeItem = function(item) { 86 if(this.isSource_) { 87 goog.events.unlisten(item.element, goog.events.EventType.MOUSEDOWN, item.mouseDown_, false, item); 88 if(this.sourceClass_) { 89 goog.dom.classes.remove(item.element, this.sourceClass_); 90 } 91 } 92 if(this.isTarget_ && this.targetClass_) { 93 goog.dom.classes.remove(item.element, this.targetClass_); 94 } 95}; 96goog.fx.AbstractDragDrop.prototype.removeItems = function() { 97 for(var item, i = 0; item = this.items_[i]; i ++) { 98 this.disposeItem(item); 99 } 100 this.items_.length = 0; 101}; 102goog.fx.AbstractDragDrop.prototype.maybeStartDrag = function(event, item) { 103 item.maybeStartDrag_(event, item.element); 104}; 105goog.fx.AbstractDragDrop.prototype.startDrag = function(event, item) { 106 if(this.dragItem_) { 107 return; 108 } 109 this.dragItem_ = item; 110 var dragStartEvent = new goog.fx.DragDropEvent(goog.fx.AbstractDragDrop.EventType.DRAGSTART, this, this.dragItem_); 111 if(this.dispatchEvent(dragStartEvent) == false) { 112 dragStartEvent.dispose(); 113 this.dragItem_ = null; 114 return; 115 } 116 dragStartEvent.dispose(); 117 var el = item.getCurrentDragElement(); 118 this.dragEl_ = this.createDragElement(el); 119 var doc = goog.dom.getOwnerDocument(el); 120 doc.body.appendChild(this.dragEl_); 121 this.dragger_ = this.createDraggerFor(el, this.dragEl_, event); 122 this.dragger_.setScrollTarget(this.scrollTarget_); 123 goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.DRAG, this.moveDrag_, false, this); 124 goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.END, this.endDrag, false, this); 125 goog.events.listen(doc.body, goog.events.EventType.SELECTSTART, this.suppressSelect_); 126 this.recalculateDragTargets(); 127 this.activeTarget_ = null; 128 this.initScrollableContainers_(); 129 this.dragger_.startDrag(event); 130 event.preventDefault(); 131}; 132goog.fx.AbstractDragDrop.prototype.recalculateDragTargets = function() { 133 this.targetList_ =[]; 134 for(var target, i = 0; target = this.targets_[i]; i ++) { 135 for(var itm, j = 0; itm = target.items_[j]; j ++) { 136 this.addDragTarget_(target, itm); 137 } 138 } 139 if(! this.targetBox_) { 140 this.targetBox_ = new goog.math.Box(0, 0, 0, 0); 141 } 142}; 143goog.fx.AbstractDragDrop.prototype.createDraggerFor = function(sourceEl, el, event) { 144 var pos = this.getDragElementPosition(sourceEl, el, event); 145 el.style.position = 'absolute'; 146 el.style.left = pos.x + 'px'; 147 el.style.top = pos.y + 'px'; 148 return new goog.fx.Dragger(el); 149}; 150goog.fx.AbstractDragDrop.prototype.endDrag = function(event) { 151 var activeTarget = event.dragCanceled ? null: this.activeTarget_; 152 if(activeTarget && activeTarget.target_) { 153 var clientX = event.clientX; 154 var clientY = event.clientY; 155 var scroll = this.getScrollPos(); 156 var x = clientX + scroll.x; 157 var y = clientY + scroll.y; 158 var subtarget; 159 if(this.subtargetFunction_) { 160 subtarget = this.subtargetFunction_(activeTarget.item_, activeTarget.box_, x, y); 161 } 162 var dragEvent = new goog.fx.DragDropEvent(goog.fx.AbstractDragDrop.EventType.DRAG, this, this.dragItem_, activeTarget.target_, activeTarget.item_, activeTarget.element_, clientX, clientY, x, y); 163 this.dispatchEvent(dragEvent); 164 dragEvent.dispose(); 165 var dropEvent = new goog.fx.DragDropEvent(goog.fx.AbstractDragDrop.EventType.DROP, this, this.dragItem_, activeTarget.target_, activeTarget.item_, activeTarget.element_, clientX, clientY, x, y, subtarget); 166 activeTarget.target_.dispatchEvent(dropEvent); 167 dropEvent.dispose(); 168 } 169 var dragEndEvent = new goog.fx.DragDropEvent(goog.fx.AbstractDragDrop.EventType.DRAGEND, this, this.dragItem_); 170 this.dispatchEvent(dragEndEvent); 171 dragEndEvent.dispose(); 172 goog.events.unlisten(this.dragger_, goog.fx.Dragger.EventType.DRAG, this.moveDrag_, false, this); 173 goog.events.unlisten(this.dragger_, goog.fx.Dragger.EventType.END, this.endDrag, false, this); 174 var doc = goog.dom.getOwnerDocument(this.dragItem_.getCurrentDragElement()); 175 goog.events.unlisten(doc.body, goog.events.EventType.SELECTSTART, this.suppressSelect_); 176 this.afterEndDrag(this.activeTarget_ ? this.activeTarget_.item_: null); 177}; 178goog.fx.AbstractDragDrop.prototype.afterEndDrag = function(opt_dropTarget) { 179 this.disposeDrag(); 180}; 181goog.fx.AbstractDragDrop.prototype.disposeDrag = function() { 182 this.disposeScrollableContainerListeners_(); 183 this.dragger_.dispose(); 184 goog.dom.removeNode(this.dragEl_); 185 delete this.dragItem_; 186 delete this.dragEl_; 187 delete this.dragger_; 188 delete this.targetList_; 189 delete this.activeTarget_; 190}; 191goog.fx.AbstractDragDrop.prototype.moveDrag_ = function(event) { 192 var x = event.clientX; 193 var y = event.clientY; 194 var scroll = this.getScrollPos(); 195 x += scroll.x; 196 y += scroll.y; 197 var activeTarget = this.activeTarget_; 198 var subtarget; 199 if(activeTarget) { 200 if(this.subtargetFunction_ && activeTarget.target_) { 201 subtarget = this.subtargetFunction_(activeTarget.item_, activeTarget.box_, x, y); 202 } 203 if(this.isInside_(x, y, activeTarget.box_) && subtarget == this.activeSubtarget_) { 204 return; 205 } 206 if(activeTarget.target_) { 207 var sourceDragOutEvent = new goog.fx.DragDropEvent(goog.fx.AbstractDragDrop.EventType.DRAGOUT, this, this.dragItem_, activeTarget.target_, activeTarget.item_, activeTarget.element_); 208 this.dispatchEvent(sourceDragOutEvent); 209 sourceDragOutEvent.dispose(); 210 var targetDragOutEvent = new goog.fx.DragDropEvent(goog.fx.AbstractDragDrop.EventType.DRAGOUT, this, this.dragItem_, activeTarget.target_, activeTarget.item_, activeTarget.element_, undefined, undefined, undefined, undefined, this.activeSubtarget_); 211 activeTarget.target_.dispatchEvent(targetDragOutEvent); 212 targetDragOutEvent.dispose(); 213 } 214 this.activeSubtarget_ = subtarget; 215 this.activeTarget_ = null; 216 } 217 if(this.isInside_(x, y, this.targetBox_)) { 218 activeTarget = this.activeTarget_ = this.getTargetFromPosition_(x, y); 219 if(activeTarget && activeTarget.target_) { 220 if(this.subtargetFunction_) { 221 subtarget = this.subtargetFunction_(activeTarget.item_, activeTarget.box_, x, y); 222 } 223 var sourceDragOverEvent = new goog.fx.DragDropEvent(goog.fx.AbstractDragDrop.EventType.DRAGOVER, this, this.dragItem_, activeTarget.target_, activeTarget.item_, activeTarget.element_); 224 sourceDragOverEvent.subtarget = subtarget; 225 this.dispatchEvent(sourceDragOverEvent); 226 sourceDragOverEvent.dispose(); 227 var targetDragOverEvent = new goog.fx.DragDropEvent(goog.fx.AbstractDragDrop.EventType.DRAGOVER, this, this.dragItem_, activeTarget.target_, activeTarget.item_, activeTarget.element_, event.clientX, event.clientY, undefined, undefined, subtarget); 228 activeTarget.target_.dispatchEvent(targetDragOverEvent); 229 targetDragOverEvent.dispose(); 230 } else if(! activeTarget) { 231 this.activeTarget_ = this.maybeCreateDummyTargetForPosition_(x, y); 232 } 233 } 234}; 235goog.fx.AbstractDragDrop.prototype.suppressSelect_ = function(event) { 236 return false; 237}; 238goog.fx.AbstractDragDrop.prototype.initScrollableContainers_ = function() { 239 var container, i, j, target; 240 for(i = 0; container = this.scrollableContainers_[i]; i ++) { 241 goog.events.listen(container.element_, goog.events.EventType.SCROLL, this.containerScrollHandler_, false, this); 242 container.containedTargets_ =[]; 243 container.savedScrollLeft_ = container.element_.scrollLeft; 244 container.savedScrollTop_ = container.element_.scrollTop; 245 var pos = goog.style.getPageOffset(container.element_); 246 var size = goog.style.getSize(container.element_); 247 container.box_ = new goog.math.Box(pos.y, pos.x + size.width, pos.y + size.height, pos.x); 248 } 249 for(i = 0; target = this.targetList_[i]; i ++) { 250 for(j = 0; container = this.scrollableContainers_[j]; j ++) { 251 if(goog.dom.contains(container.element_, target.element_)) { 252 container.containedTargets_.push(target); 253 target.scrollableContainer_ = container; 254 } 255 } 256 } 257}; 258goog.fx.AbstractDragDrop.prototype.disposeScrollableContainerListeners_ = function() { 259 for(var i = 0, container; container = this.scrollableContainers_[i]; i ++) { 260 goog.events.unlisten(container.element_, 'scroll', this.containerScrollHandler_, false, this); 261 container.containedTargets_ =[]; 262 } 263}; 264goog.fx.AbstractDragDrop.prototype.addScrollableContainer = function(element) { 265 this.scrollableContainers_.push(new goog.fx.ScrollableContainer_(element)); 266}; 267goog.fx.AbstractDragDrop.prototype.containerScrollHandler_ = function(e) { 268 for(var i = 0, container; container = this.scrollableContainers_[i]; i ++) { 269 if(e.target == container.element_) { 270 var deltaTop = container.savedScrollTop_ - container.element_.scrollTop; 271 var deltaLeft = container.savedScrollLeft_ - container.element_.scrollLeft; 272 container.savedScrollTop_ = container.element_.scrollTop; 273 container.savedScrollLeft_ = container.element_.scrollLeft; 274 for(var j = 0, target; target = container.containedTargets_[j]; j ++) { 275 var box = target.box_; 276 box.top += deltaTop; 277 box.left += deltaLeft; 278 box.bottom += deltaTop; 279 box.right += deltaLeft; 280 this.calculateTargetBox_(box); 281 } 282 } 283 } 284}; 285goog.fx.AbstractDragDrop.prototype.setSubtargetFunction = function(f) { 286 this.subtargetFunction_ = f; 287}; 288goog.fx.AbstractDragDrop.prototype.createDragElement = function(sourceEl) { 289 var dragEl = this.cloneNode_(sourceEl); 290 if(this.dragClass_) { 291 goog.dom.classes.add(dragEl, this.dragClass_); 292 } 293 return dragEl; 294}; 295goog.fx.AbstractDragDrop.prototype.getDragElementPosition = function(el, dragEl, event) { 296 var pos = goog.style.getPageOffset(el); 297 var marginBox = goog.style.getMarginBox(el); 298 pos.x +=(marginBox.left || 0) * 2; 299 pos.y +=(marginBox.top || 0) * 2; 300 return pos; 301}; 302goog.fx.AbstractDragDrop.prototype.getDragger = function() { 303 return this.dragger_; 304}; 305goog.fx.AbstractDragDrop.prototype.cloneNode_ = function(sourceEl) { 306 var clonedEl =(sourceEl.cloneNode(true)); 307 switch(sourceEl.tagName.toLowerCase()) { 308 case 'tr': 309 return goog.dom.createDom('table', null, goog.dom.createDom('tbody', null, clonedEl)); 310 case 'td': 311 case 'th': 312 return goog.dom.createDom('table', null, goog.dom.createDom('tbody', null, goog.dom.createDom('tr', null, clonedEl))); 313 default: 314 return clonedEl; 315 } 316}; 317goog.fx.AbstractDragDrop.prototype.addDragTarget_ = function(target, item) { 318 var draggableElements = item.getDraggableElements(); 319 var targetList = this.targetList_; 320 for(var i = 0; i < draggableElements.length; i ++) { 321 var draggableElement = draggableElements[i]; 322 var pos = goog.style.getPageOffset(draggableElement); 323 var size = goog.style.getSize(draggableElement); 324 var box = new goog.math.Box(pos.y, pos.x + size.width, pos.y + size.height, pos.x); 325 targetList.push(new goog.fx.ActiveDropTarget_(box, target, item, draggableElement)); 326 this.calculateTargetBox_(box); 327 } 328}; 329goog.fx.AbstractDragDrop.prototype.calculateTargetBox_ = function(box) { 330 if(this.targetList_.length == 1) { 331 this.targetBox_ = new goog.math.Box(box.top, box.right, box.bottom, box.left); 332 } else { 333 var tb = this.targetBox_; 334 tb.left = Math.min(box.left, tb.left); 335 tb.right = Math.max(box.right, tb.right); 336 tb.top = Math.min(box.top, tb.top); 337 tb.bottom = Math.max(box.bottom, tb.bottom); 338 } 339}; 340goog.fx.AbstractDragDrop.prototype.maybeCreateDummyTargetForPosition_ = function(x, y) { 341 if(! this.dummyTarget_) { 342 this.dummyTarget_ = new goog.fx.ActiveDropTarget_(this.targetBox_.clone()); 343 } 344 var fakeTargetBox = this.dummyTarget_.box_; 345 fakeTargetBox.top = this.targetBox_.top; 346 fakeTargetBox.right = this.targetBox_.right; 347 fakeTargetBox.bottom = this.targetBox_.bottom; 348 fakeTargetBox.left = this.targetBox_.left; 349 for(var i = 0, target; target = this.targetList_[i]; i ++) { 350 var box = target.box_; 351 var horizontalClip = - 1; 352 if(x >= box.right) { 353 horizontalClip = box.right > fakeTargetBox.left ? box.right: fakeTargetBox.left; 354 } else if(x < box.left) { 355 horizontalClip = box.left < fakeTargetBox.right ? box.left: fakeTargetBox.right; 356 } 357 var verticalClip = - 1; 358 if(y >= box.bottom) { 359 verticalClip = box.bottom > fakeTargetBox.top ? box.bottom: fakeTargetBox.top; 360 } else if(y < box.top) { 361 verticalClip = box.top < fakeTargetBox.bottom ? box.top: fakeTargetBox.bottom; 362 } 363 if(horizontalClip >= 0 && verticalClip >= 0) { 364 if(Math.abs(horizontalClip - x) > Math.abs(verticalClip - y)) { 365 verticalClip = - 1; 366 } else { 367 horizontalClip = - 1; 368 } 369 } 370 if(horizontalClip >= 0) { 371 if(horizontalClip <= x) { 372 fakeTargetBox.left = horizontalClip; 373 } else { 374 fakeTargetBox.right = horizontalClip; 375 } 376 } else if(verticalClip >= 0) { 377 if(verticalClip <= y) { 378 fakeTargetBox.top = verticalClip; 379 } else { 380 fakeTargetBox.bottom = verticalClip; 381 } 382 } 383 } 384 return(fakeTargetBox.right - fakeTargetBox.left) *(fakeTargetBox.bottom - fakeTargetBox.top) >= goog.fx.AbstractDragDrop.DUMMY_TARGET_MIN_SIZE_ ? this.dummyTarget_: null; 385}; 386goog.fx.AbstractDragDrop.prototype.getTargetFromPosition_ = function(x, y) { 387 for(var target, i = 0; target = this.targetList_[i]; i ++) { 388 if(this.isInside_(x, y, target.box_)) { 389 if(target.scrollableContainer_) { 390 var box = target.scrollableContainer_.box_; 391 if(this.isInside_(x, y, box)) { 392 return target; 393 } 394 } else { 395 return target; 396 } 397 } 398 } 399 return null; 400}; 401goog.fx.AbstractDragDrop.prototype.isInside_ = function(x, y, box) { 402 return x >= box.left && x < box.right && y >= box.top && y < box.bottom; 403}; 404goog.fx.AbstractDragDrop.prototype.getScrollPos = function() { 405 return goog.dom.getDomHelper(this.dragEl_).getDocumentScroll(); 406}; 407goog.fx.AbstractDragDrop.prototype.disposeInternal = function() { 408 goog.fx.AbstractDragDrop.superClass_.disposeInternal.call(this); 409 this.removeItems(); 410}; 411goog.fx.DragDropEvent = function(type, source, sourceItem, opt_target, opt_targetItem, opt_targetElement, opt_clientX, opt_clientY, opt_x, opt_y, opt_subtarget) { 412 goog.events.Event.call(this, type); 413 this.dragSource = source; 414 this.dragSourceItem = sourceItem; 415 this.dropTarget = opt_target; 416 this.dropTargetItem = opt_targetItem; 417 this.dropTargetElement = opt_targetElement; 418 this.clientX = opt_clientX; 419 this.clientY = opt_clientY; 420 this.viewportX = opt_x; 421 this.viewportY = opt_y; 422 this.subtarget = opt_subtarget; 423}; 424goog.inherits(goog.fx.DragDropEvent, goog.events.Event); 425goog.fx.DragDropEvent.prototype.disposeInternal = function() { 426 goog.fx.DragDropEvent.superClass_.disposeInternal.call(this); 427 delete this.dragSource; 428 delete this.dragSourceItem; 429 delete this.dropTarget; 430 delete this.dropTargetItem; 431 delete this.dropTargetElement; 432}; 433goog.fx.DragDropItem = function(element, opt_data) { 434 this.element = goog.dom.getElement(element); 435 this.data = opt_data; 436 this.parent_ = null; 437 if(! this.element) { 438 throw Error('Invalid argument'); 439 } 440}; 441goog.inherits(goog.fx.DragDropItem, goog.events.EventTarget); 442goog.fx.DragDropItem.prototype.currentDragElement_ = null; 443goog.fx.DragDropItem.prototype.getData = function() { 444 return this.data; 445}; 446goog.fx.DragDropItem.prototype.getDraggableElement = function(target) { 447 return target; 448}; 449goog.fx.DragDropItem.prototype.getCurrentDragElement = function() { 450 return this.currentDragElement_; 451}; 452goog.fx.DragDropItem.prototype.getDraggableElements = function() { 453 return[this.element]; 454}; 455goog.fx.DragDropItem.prototype.mouseDown_ = function(event) { 456 var element = this.getDraggableElement((event.target)); 457 if(element) { 458 this.maybeStartDrag_(event, element); 459 } 460}; 461goog.fx.DragDropItem.prototype.setParent = function(parent) { 462 this.parent_ = parent; 463}; 464goog.fx.DragDropItem.prototype.maybeStartDrag_ = function(event, element) { 465 goog.events.listen(element, goog.events.EventType.MOUSEMOVE, this.mouseMove_, false, this); 466 goog.events.listen(element, goog.events.EventType.MOUSEOUT, this.mouseMove_, false, this); 467 goog.events.listen(element, goog.events.EventType.MOUSEUP, this.mouseUp_, false, this); 468 this.currentDragElement_ = element; 469 this.startPosition_ = new goog.math.Coordinate(event.clientX, event.clientY); 470 event.preventDefault(); 471}; 472goog.fx.DragDropItem.prototype.mouseMove_ = function(event) { 473 var distance = Math.abs(event.clientX - this.startPosition_.x) + Math.abs(event.clientY - this.startPosition_.y); 474 if(distance > goog.fx.AbstractDragDrop.initDragDistanceThreshold) { 475 var currentDragElement = this.currentDragElement_; 476 goog.events.unlisten(currentDragElement, goog.events.EventType.MOUSEMOVE, this.mouseMove_, false, this); 477 goog.events.unlisten(currentDragElement, goog.events.EventType.MOUSEOUT, this.mouseMove_, false, this); 478 goog.events.unlisten(currentDragElement, goog.events.EventType.MOUSEUP, this.mouseUp_, false, this); 479 this.parent_.startDrag(event, this); 480 } 481}; 482goog.fx.DragDropItem.prototype.mouseUp_ = function(event) { 483 var currentDragElement = this.currentDragElement_; 484 goog.events.unlisten(currentDragElement, goog.events.EventType.MOUSEMOVE, this.mouseMove_, false, this); 485 goog.events.unlisten(currentDragElement, goog.events.EventType.MOUSEOUT, this.mouseMove_, false, this); 486 goog.events.unlisten(currentDragElement, goog.events.EventType.MOUSEUP, this.mouseUp_, false, this); 487 delete this.startPosition_; 488 this.currentDragElement_ = null; 489}; 490goog.fx.ActiveDropTarget_ = function(box, opt_target, opt_item, opt_element) { 491 this.box_ = box; 492 this.target_ = opt_target; 493 this.item_ = opt_item; 494 this.element_ = opt_element; 495}; 496goog.fx.ActiveDropTarget_.prototype.scrollableContainer_ = null; 497goog.fx.ScrollableContainer_ = function(element) { 498 this.containedTargets_ =[]; 499 this.element_ = element; 500 this.savedScrollLeft_ = 0; 501 this.savedScrollTop_ = 0; 502 this.box_ = null; ...
draglistgroup.js
Source:draglistgroup.js
1goog.provide('goog.fx.DragListDirection'); 2goog.provide('goog.fx.DragListGroup'); 3goog.provide('goog.fx.DragListGroup.EventType'); 4goog.provide('goog.fx.DragListGroupEvent'); 5goog.require('goog.asserts'); 6goog.require('goog.dom'); 7goog.require('goog.dom.NodeType'); 8goog.require('goog.dom.classes'); 9goog.require('goog.events.Event'); 10goog.require('goog.events.EventHandler'); 11goog.require('goog.events.EventTarget'); 12goog.require('goog.events.EventType'); 13goog.require('goog.fx.Dragger'); 14goog.require('goog.fx.Dragger.EventType'); 15goog.require('goog.math.Coordinate'); 16goog.require('goog.style'); 17goog.fx.DragListGroup = function() { 18 goog.events.EventTarget.call(this); 19 this.dragLists_ =[]; 20 this.dragItems_ =[]; 21 this.dragItemForHandle_ = { }; 22 this.eventHandler_ = new goog.events.EventHandler(this); 23 this.isInitialized_ = false; 24 this.isCurrDragItemAlwaysDisplayed_ = false; 25 this.updateWhileDragging_ = true; 26}; 27goog.inherits(goog.fx.DragListGroup, goog.events.EventTarget); 28goog.fx.DragListDirection = { 29 DOWN: 0, 30 RIGHT: 2, 31 LEFT: 3, 32 RIGHT_2D: 4, 33 LEFT_2D: 5 34}; 35goog.fx.DragListGroup.EventType = { 36 BEFOREDRAGSTART: 'beforedragstart', 37 DRAGSTART: 'dragstart', 38 BEFOREDRAGMOVE: 'beforedragmove', 39 DRAGMOVE: 'dragmove', 40 BEFOREDRAGEND: 'beforedragend', 41 DRAGEND: 'dragend' 42}; 43goog.fx.DragListGroup.prototype.dragItemHoverClasses_; 44goog.fx.DragListGroup.prototype.dragItemHandleHoverClasses_; 45goog.fx.DragListGroup.prototype.currDragItemClasses_; 46goog.fx.DragListGroup.prototype.draggerElClass_; 47goog.fx.DragListGroup.prototype.currDragItem_; 48goog.fx.DragListGroup.prototype.currHoverList_; 49goog.fx.DragListGroup.prototype.origList_; 50goog.fx.DragListGroup.prototype.origNextItem_; 51goog.fx.DragListGroup.prototype.currHoverItem_; 52goog.fx.DragListGroup.prototype.draggerEl_; 53goog.fx.DragListGroup.prototype.dragger_; 54goog.fx.DragListGroup.prototype.setIsCurrDragItemAlwaysDisplayed = function() { 55 this.isCurrDragItemAlwaysDisplayed_ = true; 56}; 57goog.fx.DragListGroup.prototype.setNoUpdateWhileDragging = function() { 58 this.updateWhileDragging_ = false; 59}; 60goog.fx.DragListGroup.prototype.addDragList = function(dragListElement, growthDirection, opt_unused, opt_dragHoverClass) { 61 goog.asserts.assert(! this.isInitialized_); 62 dragListElement.dlgGrowthDirection_ = growthDirection; 63 dragListElement.dlgDragHoverClass_ = opt_dragHoverClass; 64 this.dragLists_.push(dragListElement); 65}; 66goog.fx.DragListGroup.prototype.setFunctionToGetHandleForDragItem = function(getHandleForDragItemFn) { 67 goog.asserts.assert(! this.isInitialized_); 68 this.getHandleForDragItem_ = getHandleForDragItemFn; 69}; 70goog.fx.DragListGroup.prototype.setDragItemHoverClass = function(var_args) { 71 goog.asserts.assert(! this.isInitialized_); 72 this.dragItemHoverClasses_ = goog.array.slice(arguments, 0); 73}; 74goog.fx.DragListGroup.prototype.setDragItemHandleHoverClass = function(var_args) { 75 goog.asserts.assert(! this.isInitialized_); 76 this.dragItemHandleHoverClasses_ = goog.array.slice(arguments, 0); 77}; 78goog.fx.DragListGroup.prototype.setCurrDragItemClass = function(var_args) { 79 goog.asserts.assert(! this.isInitialized_); 80 this.currDragItemClasses_ = goog.array.slice(arguments, 0); 81}; 82goog.fx.DragListGroup.prototype.setDraggerElClass = function(draggerElClass) { 83 goog.asserts.assert(! this.isInitialized_); 84 this.draggerElClass_ = draggerElClass; 85}; 86goog.fx.DragListGroup.prototype.init = function() { 87 if(this.isInitialized_) { 88 return; 89 } 90 for(var i = 0, numLists = this.dragLists_.length; i < numLists; i ++) { 91 var dragList = this.dragLists_[i]; 92 var dragItems = goog.dom.getChildren(dragList); 93 for(var j = 0, numItems = dragItems.length; j < numItems; ++ j) { 94 var dragItem = dragItems[j]; 95 var dragItemHandle = this.getHandleForDragItem_(dragItem); 96 var uid = goog.getUid(dragItemHandle); 97 this.dragItemForHandle_[uid]= dragItem; 98 if(this.dragItemHoverClasses_) { 99 this.eventHandler_.listen(dragItem, goog.events.EventType.MOUSEOVER, this.handleDragItemMouseover_); 100 this.eventHandler_.listen(dragItem, goog.events.EventType.MOUSEOUT, this.handleDragItemMouseout_); 101 } 102 if(this.dragItemHandleHoverClasses_) { 103 this.eventHandler_.listen(dragItemHandle, goog.events.EventType.MOUSEOVER, this.handleDragItemHandleMouseover_); 104 this.eventHandler_.listen(dragItemHandle, goog.events.EventType.MOUSEOUT, this.handleDragItemHandleMouseout_); 105 } 106 this.dragItems_.push(dragItem); 107 this.eventHandler_.listen(dragItemHandle, goog.events.EventType.MOUSEDOWN, this.handleDragStart_); 108 } 109 } 110 this.isInitialized_ = true; 111}; 112goog.fx.DragListGroup.prototype.disposeInternal = function() { 113 this.eventHandler_.dispose(); 114 for(var i = 0, n = this.dragLists_.length; i < n; i ++) { 115 var dragList = this.dragLists_[i]; 116 dragList.dlgGrowthDirection_ = undefined; 117 dragList.dlgDragHoverClass_ = undefined; 118 } 119 this.dragLists_.length = 0; 120 this.dragItems_.length = 0; 121 this.dragItemForHandle_ = null; 122 this.cleanupDragDom_(); 123 goog.fx.DragListGroup.superClass_.disposeInternal.call(this); 124}; 125goog.fx.DragListGroup.prototype.recacheListAndItemBounds_ = function(currDragItem) { 126 for(var i = 0, n = this.dragLists_.length; i < n; i ++) { 127 var dragList = this.dragLists_[i]; 128 dragList.dlgBounds_ = goog.style.getBounds(dragList); 129 } 130 for(var i = 0, n = this.dragItems_.length; i < n; i ++) { 131 var dragItem = this.dragItems_[i]; 132 if(dragItem != currDragItem) { 133 dragItem.dlgBounds_ = goog.style.getBounds(dragItem); 134 } 135 } 136}; 137goog.fx.DragListGroup.prototype.handleDragStart_ = function(e) { 138 if(! e.isMouseActionButton()) { 139 e.preventDefault(); 140 return; 141 } 142 var uid = goog.getUid((e.currentTarget)); 143 var currDragItem =(this.dragItemForHandle_[uid]); 144 var rv = this.dispatchEvent(new goog.fx.DragListGroupEvent(goog.fx.DragListGroup.EventType.BEFOREDRAGSTART, this, e, currDragItem, null, null)); 145 if(! rv) { 146 e.preventDefault(); 147 return; 148 } 149 this.currDragItem_ = currDragItem; 150 this.origList_ =(currDragItem.parentNode); 151 this.origNextItem_ = goog.dom.getNextElementSibling(currDragItem); 152 this.currHoverItem_ = this.origNextItem_; 153 this.currHoverList_ = this.origList_; 154 var draggerEl = this.cloneNode_(currDragItem); 155 this.draggerEl_ = draggerEl; 156 if(this.currDragItemClasses_) { 157 goog.dom.classes.add.apply(null, goog.array.concat(currDragItem, this.currDragItemClasses_)); 158 } else { 159 currDragItem.style.visibility = 'hidden'; 160 } 161 if(this.draggerElClass_) { 162 goog.dom.classes.add(draggerEl, this.draggerElClass_); 163 } 164 draggerEl.style.margin = '0px'; 165 draggerEl.style.position = 'absolute'; 166 goog.dom.getOwnerDocument(currDragItem).body.appendChild(draggerEl); 167 var currDragItemPos = goog.style.getPageOffset(currDragItem); 168 goog.style.setPageOffset(draggerEl, currDragItemPos); 169 var draggerElSize = goog.style.getSize(draggerEl); 170 draggerEl.halfWidth = draggerElSize.width / 2; 171 draggerEl.halfHeight = draggerElSize.height / 2; 172 if(this.updateWhileDragging_) { 173 currDragItem.style.display = 'none'; 174 } 175 this.recacheListAndItemBounds_(currDragItem); 176 currDragItem.style.display = ''; 177 this.dragger_ = new goog.fx.Dragger(draggerEl); 178 this.eventHandler_.listen(this.dragger_, goog.fx.Dragger.EventType.DRAG, this.handleDragMove_); 179 this.eventHandler_.listen(this.dragger_, goog.fx.Dragger.EventType.END, this.handleDragEnd_); 180 this.dragger_.startDrag(e); 181 this.dispatchEvent(new goog.fx.DragListGroupEvent(goog.fx.DragListGroup.EventType.DRAGSTART, this, e, currDragItem, draggerEl, this.dragger_)); 182}; 183goog.fx.DragListGroup.prototype.handleDragMove_ = function(dragEvent) { 184 var draggerElPos = goog.style.getPageOffset(this.draggerEl_); 185 var draggerElCenter = new goog.math.Coordinate(draggerElPos.x + this.draggerEl_.halfWidth, draggerElPos.y + this.draggerEl_.halfHeight); 186 var hoverList = this.getHoverDragList_(draggerElCenter); 187 var hoverNextItem = hoverList ? this.getHoverNextItem_(hoverList, draggerElCenter): null; 188 var rv = this.dispatchEvent(new goog.fx.DragListGroupEvent(goog.fx.DragListGroup.EventType.BEFOREDRAGMOVE, this, dragEvent, this.currDragItem_, this.draggerEl_, this.dragger_, draggerElCenter, hoverList, hoverNextItem)); 189 if(! rv) { 190 return false; 191 } 192 if(hoverList) { 193 if(this.updateWhileDragging_) { 194 this.insertCurrDragItem_(hoverList, hoverNextItem); 195 } else { 196 this.updateCurrHoverItem(hoverNextItem, draggerElCenter); 197 } 198 this.currDragItem_.style.display = ''; 199 if(hoverList.dlgDragHoverClass_) { 200 goog.dom.classes.add(hoverList, hoverList.dlgDragHoverClass_); 201 } 202 } else { 203 if(! this.isCurrDragItemAlwaysDisplayed_) { 204 this.currDragItem_.style.display = 'none'; 205 } 206 for(var i = 0, n = this.dragLists_.length; i < n; i ++) { 207 var dragList = this.dragLists_[i]; 208 if(dragList.dlgDragHoverClass_) { 209 goog.dom.classes.remove(dragList, dragList.dlgDragHoverClass_); 210 } 211 } 212 } 213 if(hoverList != this.currHoverList_) { 214 this.currHoverList_ = hoverList; 215 this.recacheListAndItemBounds_(this.currDragItem_); 216 } 217 this.dispatchEvent(new goog.fx.DragListGroupEvent(goog.fx.DragListGroup.EventType.DRAGMOVE, this, dragEvent,(this.currDragItem_), this.draggerEl_, this.dragger_, draggerElCenter, hoverList, hoverNextItem)); 218 return false; 219}; 220goog.fx.DragListGroup.prototype.handleDragEnd_ = function(dragEvent) { 221 var rv = this.dispatchEvent(new goog.fx.DragListGroupEvent(goog.fx.DragListGroup.EventType.BEFOREDRAGEND, this, dragEvent,(this.currDragItem_), this.draggerEl_, this.dragger_)); 222 if(! rv) { 223 return false; 224 } 225 if(! this.updateWhileDragging_) { 226 this.insertCurrHoverItem(); 227 } 228 this.cleanupDragDom_(); 229 this.dispatchEvent(new goog.fx.DragListGroupEvent(goog.fx.DragListGroup.EventType.DRAGEND, this, dragEvent, this.currDragItem_, this.draggerEl_, this.dragger_)); 230 this.currDragItem_ = null; 231 this.currHoverList_ = null; 232 this.origList_ = null; 233 this.origNextItem_ = null; 234 this.draggerEl_ = null; 235 this.dragger_ = null; 236 for(var i = 0, n = this.dragLists_.length; i < n; i ++) { 237 this.dragLists_[i].dlgBounds_ = null; 238 } 239 for(var i = 0, n = this.dragItems_.length; i < n; i ++) { 240 this.dragItems_[i].dlgBounds_ = null; 241 } 242 return true; 243}; 244goog.fx.DragListGroup.prototype.cleanupDragDom_ = function() { 245 goog.dispose(this.dragger_); 246 if(this.draggerEl_) { 247 goog.dom.removeNode(this.draggerEl_); 248 } 249 if(this.currDragItem_ && this.currDragItem_.style.display == 'none') { 250 this.origList_.insertBefore(this.currDragItem_, this.origNextItem_); 251 this.currDragItem_.style.display = ''; 252 } 253 if(this.currDragItemClasses_ && this.currDragItem_) { 254 goog.dom.classes.remove.apply(null, goog.array.concat(this.currDragItem_, this.currDragItemClasses_)); 255 } else if(this.currDragItem_) { 256 this.currDragItem_.style.visibility = 'visible'; 257 } 258 for(var i = 0, n = this.dragLists_.length; i < n; i ++) { 259 var dragList = this.dragLists_[i]; 260 if(dragList.dlgDragHoverClass_) { 261 goog.dom.classes.remove(dragList, dragList.dlgDragHoverClass_); 262 } 263 } 264}; 265goog.fx.DragListGroup.prototype.getHandleForDragItem_ = function(dragItem) { 266 return dragItem; 267}; 268goog.fx.DragListGroup.prototype.handleDragItemMouseover_ = function(e) { 269 goog.dom.classes.add.apply(null, goog.array.concat((e.currentTarget), this.dragItemHoverClasses_)); 270}; 271goog.fx.DragListGroup.prototype.handleDragItemMouseout_ = function(e) { 272 goog.dom.classes.remove.apply(null, goog.array.concat((e.currentTarget), this.dragItemHoverClasses_)); 273}; 274goog.fx.DragListGroup.prototype.handleDragItemHandleMouseover_ = function(e) { 275 goog.dom.classes.add.apply(null, goog.array.concat((e.currentTarget), this.dragItemHandleHoverClasses_)); 276}; 277goog.fx.DragListGroup.prototype.handleDragItemHandleMouseout_ = function(e) { 278 goog.dom.classes.remove.apply(null, goog.array.concat((e.currentTarget), this.dragItemHandleHoverClasses_)); 279}; 280goog.fx.DragListGroup.prototype.getHoverDragList_ = function(draggerElCenter) { 281 var prevHoverList = null; 282 if(this.currDragItem_.style.display != 'none') { 283 prevHoverList =(this.currDragItem_.parentNode); 284 var prevHoverListBounds = goog.style.getBounds(prevHoverList); 285 if(this.isInRect_(draggerElCenter, prevHoverListBounds)) { 286 return prevHoverList; 287 } 288 } 289 for(var i = 0, n = this.dragLists_.length; i < n; i ++) { 290 var dragList = this.dragLists_[i]; 291 if(dragList == prevHoverList) { 292 continue; 293 } 294 if(this.isInRect_(draggerElCenter, dragList.dlgBounds_)) { 295 return dragList; 296 } 297 } 298 return null; 299}; 300goog.fx.DragListGroup.prototype.isInRect_ = function(pos, rect) { 301 return pos.x > rect.left && pos.x < rect.left + rect.width && pos.y > rect.top && pos.y < rect.top + rect.height; 302}; 303goog.fx.DragListGroup.prototype.updateCurrHoverItem = function(hoverNextItem, opt_draggerElCenter) { 304 if(goog.isDefAndNotNull(hoverNextItem)) { 305 this.currHoverItem_ = hoverNextItem; 306 } 307}; 308goog.fx.DragListGroup.prototype.insertCurrHoverItem = function() { 309 this.origList_.insertBefore(this.currDragItem_, this.currHoverItem_); 310}; 311goog.fx.DragListGroup.prototype.getHoverNextItem_ = function(hoverList, draggerElCenter) { 312 if(hoverList == null) { 313 throw Error('getHoverNextItem_ called with null hoverList.'); 314 } 315 var relevantCoord; 316 var getRelevantBoundFn; 317 var isBeforeFn; 318 var pickClosestRow = false; 319 var distanceToClosestRow = undefined; 320 switch(hoverList.dlgGrowthDirection_) { 321 case goog.fx.DragListDirection.DOWN: 322 relevantCoord = draggerElCenter.y; 323 getRelevantBoundFn = goog.fx.DragListGroup.getBottomBound_; 324 isBeforeFn = goog.fx.DragListGroup.isLessThan_; 325 break; 326 case goog.fx.DragListDirection.RIGHT_2D: 327 pickClosestRow = true; 328 case goog.fx.DragListDirection.RIGHT: 329 relevantCoord = draggerElCenter.x; 330 getRelevantBoundFn = goog.fx.DragListGroup.getRightBound_; 331 isBeforeFn = goog.fx.DragListGroup.isLessThan_; 332 break; 333 case goog.fx.DragListDirection.LEFT_2D: 334 pickClosestRow = true; 335 case goog.fx.DragListDirection.LEFT: 336 relevantCoord = draggerElCenter.x; 337 getRelevantBoundFn = goog.fx.DragListGroup.getLeftBound_; 338 isBeforeFn = goog.fx.DragListGroup.isGreaterThan_; 339 break; 340 } 341 var earliestAfterItem = null; 342 var earliestAfterItemRelevantBound; 343 var hoverListItems = goog.dom.getChildren(hoverList); 344 for(var i = 0, n = hoverListItems.length; i < n; i ++) { 345 var item = hoverListItems[i]; 346 if(item == this.currDragItem_) { 347 continue; 348 } 349 var relevantBound = getRelevantBoundFn(item.dlgBounds_); 350 if(pickClosestRow) { 351 var distanceToRow = goog.fx.DragListGroup.verticalDistanceFromItem_(item, draggerElCenter); 352 if(! goog.isDef(distanceToClosestRow)) { 353 distanceToClosestRow = distanceToRow; 354 } 355 if(isBeforeFn(relevantCoord, relevantBound) &&(earliestAfterItemRelevantBound == undefined ||(distanceToRow < distanceToClosestRow) ||((distanceToRow == distanceToClosestRow) &&(isBeforeFn(relevantBound, earliestAfterItemRelevantBound) || relevantBound == earliestAfterItemRelevantBound)))) { 356 earliestAfterItem = item; 357 earliestAfterItemRelevantBound = relevantBound; 358 } 359 if(distanceToRow < distanceToClosestRow) { 360 distanceToClosestRow = distanceToRow; 361 } 362 } else if(isBeforeFn(relevantCoord, relevantBound) &&(earliestAfterItemRelevantBound == undefined || isBeforeFn(relevantBound, earliestAfterItemRelevantBound))) { 363 earliestAfterItem = item; 364 earliestAfterItemRelevantBound = relevantBound; 365 } 366 } 367 if(! goog.isNull(earliestAfterItem) && goog.fx.DragListGroup.verticalDistanceFromItem_(earliestAfterItem, draggerElCenter) > distanceToClosestRow) { 368 return null; 369 } else { 370 return earliestAfterItem; 371 } 372}; 373goog.fx.DragListGroup.verticalDistanceFromItem_ = function(item, target) { 374 var itemBounds = item.dlgBounds_; 375 var itemCenterY = itemBounds.top +(itemBounds.height - 1) / 2; 376 return Math.abs(target.y - itemCenterY); 377}; 378goog.fx.DragListGroup.getBottomBound_ = function(itemBounds) { 379 return itemBounds.top + itemBounds.height - 1; 380}; 381goog.fx.DragListGroup.getRightBound_ = function(itemBounds) { 382 return itemBounds.left + itemBounds.width - 1; 383}; 384goog.fx.DragListGroup.getLeftBound_ = function(itemBounds) { 385 return itemBounds.left || 0; 386}; 387goog.fx.DragListGroup.isLessThan_ = function(a, b) { 388 return a < b; 389}; 390goog.fx.DragListGroup.isGreaterThan_ = function(a, b) { 391 return a > b; 392}; 393goog.fx.DragListGroup.prototype.insertCurrDragItem_ = function(hoverList, hoverNextItem) { 394 if(this.currDragItem_.parentNode != hoverList || goog.dom.getNextElementSibling(this.currDragItem_) != hoverNextItem) { 395 hoverList.insertBefore(this.currDragItem_, hoverNextItem); 396 } 397}; 398goog.fx.DragListGroup.prototype.cloneNode_ = function(sourceEl) { 399 var clonedEl =(sourceEl.cloneNode(true)); 400 switch(sourceEl.tagName.toLowerCase()) { 401 case 'tr': 402 return goog.dom.createDom('table', null, goog.dom.createDom('tbody', null, clonedEl)); 403 case 'td': 404 case 'th': 405 return goog.dom.createDom('table', null, goog.dom.createDom('tbody', null, goog.dom.createDom('tr', null, clonedEl))); 406 default: 407 return clonedEl; 408 } 409}; 410goog.fx.DragListGroupEvent = function(type, dragListGroup, event, currDragItem, draggerEl, dragger, opt_draggerElCenter, opt_hoverList, opt_hoverNextItem) { 411 goog.events.Event.call(this, type); 412 this.dragListGroup = dragListGroup; 413 this.event = event; 414 this.currDragItem = currDragItem; 415 this.draggerEl = draggerEl; 416 this.dragger = dragger; 417 this.draggerElCenter = opt_draggerElCenter; 418 this.hoverList = opt_hoverList; 419 this.hoverNextItem = opt_hoverNextItem; 420}; ...
MultiDrag.js
Source:MultiDrag.js
1import {2 toggleClass,3 getRect,4 index,5 closest,6 on,7 off,8 clone,9 css,10 setRect,11 unsetRect,12 matrix,13 expando14} from '../../src/utils.js';15import dispatchEvent from '../../src/EventDispatcher.js';16let multiDragElements = [],17 multiDragClones = [],18 lastMultiDragSelect, // for selection with modifier key down (SHIFT)19 multiDragSortable,20 initialFolding = false, // Initial multi-drag fold when drag started21 folding = false, // Folding any other time22 dragStarted = false,23 dragEl,24 clonesFromRect,25 clonesHidden;26function MultiDragPlugin() {27 function MultiDrag(sortable) {28 // Bind all private methods29 for (let fn in this) {30 if (fn.charAt(0) === '_' && typeof this[fn] === 'function') {31 this[fn] = this[fn].bind(this);32 }33 }34 if (sortable.options.supportPointer) {35 on(document, 'pointerup', this._deselectMultiDrag);36 } else {37 on(document, 'mouseup', this._deselectMultiDrag);38 on(document, 'touchend', this._deselectMultiDrag);39 }40 on(document, 'keydown', this._checkKeyDown);41 on(document, 'keyup', this._checkKeyUp);42 this.defaults = {43 selectedClass: 'sortable-selected',44 multiDragKey: null,45 setData(dataTransfer, dragEl) {46 let data = '';47 if (multiDragElements.length && multiDragSortable === sortable) {48 multiDragElements.forEach((multiDragElement, i) => {49 data += (!i ? '' : ', ') + multiDragElement.textContent;50 });51 } else {52 data = dragEl.textContent;53 }54 dataTransfer.setData('Text', data);55 }56 };57 }58 MultiDrag.prototype = {59 multiDragKeyDown: false,60 isMultiDrag: false,61 delayStartGlobal({ dragEl: dragged }) {62 dragEl = dragged;63 },64 delayEnded() {65 this.isMultiDrag = ~multiDragElements.indexOf(dragEl);66 },67 setupClone({ sortable, cancel }) {68 if (!this.isMultiDrag) return;69 for (let i = 0; i < multiDragElements.length; i++) {70 multiDragClones.push(clone(multiDragElements[i]));71 multiDragClones[i].sortableIndex = multiDragElements[i].sortableIndex;72 multiDragClones[i].draggable = false;73 multiDragClones[i].style['will-change'] = '';74 toggleClass(multiDragClones[i], this.options.selectedClass, false);75 multiDragElements[i] === dragEl && toggleClass(multiDragClones[i], this.options.chosenClass, false);76 }77 sortable._hideClone();78 cancel();79 },80 clone({ sortable, rootEl, dispatchSortableEvent, cancel }) {81 if (!this.isMultiDrag) return;82 if (!this.options.removeCloneOnHide) {83 if (multiDragElements.length && multiDragSortable === sortable) {84 insertMultiDragClones(true, rootEl);85 dispatchSortableEvent('clone');86 cancel();87 }88 }89 },90 showClone({ cloneNowShown, rootEl, cancel }) {91 if (!this.isMultiDrag) return;92 insertMultiDragClones(false, rootEl);93 multiDragClones.forEach(clone => {94 css(clone, 'display', '');95 });96 cloneNowShown();97 clonesHidden = false;98 cancel();99 },100 hideClone({ sortable, cloneNowHidden, cancel }) {101 if (!this.isMultiDrag) return;102 multiDragClones.forEach(clone => {103 css(clone, 'display', 'none');104 if (this.options.removeCloneOnHide && clone.parentNode) {105 clone.parentNode.removeChild(clone);106 }107 });108 cloneNowHidden();109 clonesHidden = true;110 cancel();111 },112 dragStartGlobal({ sortable }) {113 if (!this.isMultiDrag && multiDragSortable) {114 multiDragSortable.multiDrag._deselectMultiDrag();115 }116 multiDragElements.forEach(multiDragElement => {117 multiDragElement.sortableIndex = index(multiDragElement);118 });119 // Sort multi-drag elements120 multiDragElements = multiDragElements.sort(function(a, b) {121 return a.sortableIndex - b.sortableIndex;122 });123 dragStarted = true;124 },125 dragStarted({ sortable }) {126 if (!this.isMultiDrag) return;127 if (this.options.sort) {128 // Capture rects,129 // hide multi drag elements (by positioning them absolute),130 // set multi drag elements rects to dragRect,131 // show multi drag elements,132 // animate to rects,133 // unset rects & remove from DOM134 sortable.captureAnimationState();135 if (this.options.animation) {136 multiDragElements.forEach(multiDragElement => {137 if (multiDragElement === dragEl) return;138 css(multiDragElement, 'position', 'absolute');139 });140 let dragRect = getRect(dragEl, false, true, true);141 multiDragElements.forEach(multiDragElement => {142 if (multiDragElement === dragEl) return;143 setRect(multiDragElement, dragRect);144 });145 folding = true;146 initialFolding = true;147 }148 }149 sortable.animateAll(() => {150 folding = false;151 initialFolding = false;152 if (this.options.animation) {153 multiDragElements.forEach(multiDragElement => {154 unsetRect(multiDragElement);155 });156 }157 // Remove all auxiliary multidrag items from el, if sorting enabled158 if (this.options.sort) {159 removeMultiDragElements();160 }161 });162 },163 dragOver({ target, completed, cancel }) {164 if (folding && ~multiDragElements.indexOf(target)) {165 completed(false);166 cancel();167 }168 },169 revert({ fromSortable, rootEl, sortable, dragRect }) {170 if (multiDragElements.length > 1) {171 // Setup unfold animation172 multiDragElements.forEach(multiDragElement => {173 sortable.addAnimationState({174 target: multiDragElement,175 rect: folding ? getRect(multiDragElement) : dragRect176 });177 unsetRect(multiDragElement);178 multiDragElement.fromRect = dragRect;179 fromSortable.removeAnimationState(multiDragElement);180 });181 folding = false;182 insertMultiDragElements(!this.options.removeCloneOnHide, rootEl);183 }184 },185 dragOverCompleted({ sortable, isOwner, insertion, activeSortable, parentEl, putSortable }) {186 let options = this.options;187 if (insertion) {188 // Clones must be hidden before folding animation to capture dragRectAbsolute properly189 if (isOwner) {190 activeSortable._hideClone();191 }192 initialFolding = false;193 // If leaving sort:false root, or already folding - Fold to new location194 if (options.animation && multiDragElements.length > 1 && (folding || !isOwner && !activeSortable.options.sort && !putSortable)) {195 // Fold: Set all multi drag elements's rects to dragEl's rect when multi-drag elements are invisible196 let dragRectAbsolute = getRect(dragEl, false, true, true);197 multiDragElements.forEach(multiDragElement => {198 if (multiDragElement === dragEl) return;199 setRect(multiDragElement, dragRectAbsolute);200 // Move element(s) to end of parentEl so that it does not interfere with multi-drag clones insertion if they are inserted201 // while folding, and so that we can capture them again because old sortable will no longer be fromSortable202 parentEl.appendChild(multiDragElement);203 });204 folding = true;205 }206 // Clones must be shown (and check to remove multi drags) after folding when interfering multiDragElements are moved out207 if (!isOwner) {208 // Only remove if not folding (folding will remove them anyways)209 if (!folding) {210 removeMultiDragElements();211 }212 if (multiDragElements.length > 1) {213 let clonesHiddenBefore = clonesHidden;214 activeSortable._showClone(sortable);215 // Unfold animation for clones if showing from hidden216 if (activeSortable.options.animation && !clonesHidden && clonesHiddenBefore) {217 multiDragClones.forEach(clone => {218 activeSortable.addAnimationState({219 target: clone,220 rect: clonesFromRect221 });222 clone.fromRect = clonesFromRect;223 clone.thisAnimationDuration = null;224 });225 }226 } else {227 activeSortable._showClone(sortable);228 }229 }230 }231 },232 dragOverAnimationCapture({ dragRect, isOwner, activeSortable }) {233 multiDragElements.forEach(multiDragElement => {234 multiDragElement.thisAnimationDuration = null;235 });236 if (activeSortable.options.animation && !isOwner && activeSortable.multiDrag.isMultiDrag) {237 clonesFromRect = Object.assign({}, dragRect);238 let dragMatrix = matrix(dragEl, true);239 clonesFromRect.top -= dragMatrix.f;240 clonesFromRect.left -= dragMatrix.e;241 }242 },243 dragOverAnimationComplete() {244 if (folding) {245 folding = false;246 removeMultiDragElements();247 }248 },249 drop({ originalEvent: evt, rootEl, parentEl, sortable, dispatchSortableEvent, oldIndex, putSortable }) {250 let toSortable = (putSortable || this.sortable);251 if (!evt) return;252 let options = this.options,253 children = parentEl.children;254 // Multi-drag selection255 if (!dragStarted) {256 if (options.multiDragKey && !this.multiDragKeyDown) {257 this._deselectMultiDrag();258 }259 toggleClass(dragEl, options.selectedClass, !~multiDragElements.indexOf(dragEl));260 if (!~multiDragElements.indexOf(dragEl)) {261 multiDragElements.push(dragEl);262 dispatchEvent({263 sortable,264 rootEl,265 name: 'select',266 targetEl: dragEl,267 originalEvt: evt268 });269 // Modifier activated, select from last to dragEl270 if (evt.shiftKey && lastMultiDragSelect && sortable.el.contains(lastMultiDragSelect)) {271 let lastIndex = index(lastMultiDragSelect),272 currentIndex = index(dragEl);273 if (~lastIndex && ~currentIndex && lastIndex !== currentIndex) {274 // Must include lastMultiDragSelect (select it), in case modified selection from no selection275 // (but previous selection existed)276 let n, i;277 if (currentIndex > lastIndex) {278 i = lastIndex;279 n = currentIndex;280 } else {281 i = currentIndex;282 n = lastIndex + 1;283 }284 for (; i < n; i++) {285 if (~multiDragElements.indexOf(children[i])) continue;286 toggleClass(children[i], options.selectedClass, true);287 multiDragElements.push(children[i]);288 dispatchEvent({289 sortable,290 rootEl,291 name: 'select',292 targetEl: children[i],293 originalEvt: evt294 });295 }296 }297 } else {298 lastMultiDragSelect = dragEl;299 }300 multiDragSortable = toSortable;301 } else {302 multiDragElements.splice(multiDragElements.indexOf(dragEl), 1);303 lastMultiDragSelect = null;304 dispatchEvent({305 sortable,306 rootEl,307 name: 'deselect',308 targetEl: dragEl,309 originalEvt: evt310 });311 }312 }313 // Multi-drag drop314 if (dragStarted && this.isMultiDrag) {315 // Do not "unfold" after around dragEl if reverted316 if ((parentEl[expando].options.sort || parentEl !== rootEl) && multiDragElements.length > 1) {317 let dragRect = getRect(dragEl),318 multiDragIndex = index(dragEl, ':not(.' + this.options.selectedClass + ')');319 if (!initialFolding && options.animation) dragEl.thisAnimationDuration = null;320 toSortable.captureAnimationState();321 if (!initialFolding) {322 if (options.animation) {323 dragEl.fromRect = dragRect;324 multiDragElements.forEach(multiDragElement => {325 multiDragElement.thisAnimationDuration = null;326 if (multiDragElement !== dragEl) {327 let rect = folding ? getRect(multiDragElement) : dragRect;328 multiDragElement.fromRect = rect;329 // Prepare unfold animation330 toSortable.addAnimationState({331 target: multiDragElement,332 rect: rect333 });334 }335 });336 }337 // Multi drag elements are not necessarily removed from the DOM on drop, so to reinsert338 // properly they must all be removed339 removeMultiDragElements();340 multiDragElements.forEach(multiDragElement => {341 if (children[multiDragIndex]) {342 parentEl.insertBefore(multiDragElement, children[multiDragIndex]);343 } else {344 parentEl.appendChild(multiDragElement);345 }346 multiDragIndex++;347 });348 // If initial folding is done, the elements may have changed position because they are now349 // unfolding around dragEl, even though dragEl may not have his index changed, so update event350 // must be fired here as Sortable will not.351 if (oldIndex === index(dragEl)) {352 let update = false;353 multiDragElements.forEach(multiDragElement => {354 if (multiDragElement.sortableIndex !== index(multiDragElement)) {355 update = true;356 return;357 }358 });359 if (update) {360 dispatchSortableEvent('update');361 }362 }363 }364 // Must be done after capturing individual rects (scroll bar)365 multiDragElements.forEach(multiDragElement => {366 unsetRect(multiDragElement);367 });368 toSortable.animateAll();369 }370 multiDragSortable = toSortable;371 }372 // Remove clones if necessary373 if (rootEl === parentEl || (putSortable && putSortable.lastPutMode !== 'clone')) {374 multiDragClones.forEach(clone => {375 clone.parentNode && clone.parentNode.removeChild(clone);376 });377 }378 },379 nullingGlobal() {380 this.isMultiDrag =381 dragStarted = false;382 multiDragClones.length = 0;383 },384 destroyGlobal() {385 this._deselectMultiDrag();386 off(document, 'pointerup', this._deselectMultiDrag);387 off(document, 'mouseup', this._deselectMultiDrag);388 off(document, 'touchend', this._deselectMultiDrag);389 off(document, 'keydown', this._checkKeyDown);390 off(document, 'keyup', this._checkKeyUp);391 },392 _deselectMultiDrag(evt) {393 if (typeof dragStarted !== "undefined" && dragStarted) return;394 // Only deselect if selection is in this sortable395 if (multiDragSortable !== this.sortable) return;396 // Only deselect if target is not item in this sortable397 if (evt && closest(evt.target, this.options.draggable, this.sortable.el, false)) return;398 // Only deselect if left click399 if (evt && evt.button !== 0) return;400 while (multiDragElements.length) {401 let el = multiDragElements[0];402 toggleClass(el, this.options.selectedClass, false);403 multiDragElements.shift();404 dispatchEvent({405 sortable: this.sortable,406 rootEl: this.sortable.el,407 name: 'deselect',408 targetEl: el,409 originalEvt: evt410 });411 }412 },413 _checkKeyDown(evt) {414 if (evt.key === this.options.multiDragKey) {415 this.multiDragKeyDown = true;416 }417 },418 _checkKeyUp(evt) {419 if (evt.key === this.options.multiDragKey) {420 this.multiDragKeyDown = false;421 }422 }423 };424 return Object.assign(MultiDrag, {425 // Static methods & properties426 pluginName: 'multiDrag',427 utils: {428 /**429 * Selects the provided multi-drag item430 * @param {HTMLElement} el The element to be selected431 */432 select(el) {433 let sortable = el.parentNode[expando];434 if (!sortable || !sortable.options.multiDrag || ~multiDragElements.indexOf(el)) return;435 if (multiDragSortable && multiDragSortable !== sortable) {436 multiDragSortable.multiDrag._deselectMultiDrag();437 multiDragSortable = sortable;438 }439 toggleClass(el, sortable.options.selectedClass, true);440 multiDragElements.push(el);441 },442 /**443 * Deselects the provided multi-drag item444 * @param {HTMLElement} el The element to be deselected445 */446 deselect(el) {447 let sortable = el.parentNode[expando],448 index = multiDragElements.indexOf(el);449 if (!sortable || !sortable.options.multiDrag || !~index) return;450 toggleClass(el, sortable.options.selectedClass, false);451 multiDragElements.splice(index, 1);452 }453 },454 eventProperties() {455 const oldIndicies = [],456 newIndicies = [];457 multiDragElements.forEach(multiDragElement => {458 oldIndicies.push({459 multiDragElement,460 index: multiDragElement.sortableIndex461 });462 // multiDragElements will already be sorted if folding463 let newIndex;464 if (folding && multiDragElement !== dragEl) {465 newIndex = -1;466 } else if (folding) {467 newIndex = index(multiDragElement, ':not(.' + this.options.selectedClass + ')');468 } else {469 newIndex = index(multiDragElement);470 }471 newIndicies.push({472 multiDragElement,473 index: newIndex474 });475 });476 return {477 items: [...multiDragElements],478 clones: [...multiDragClones],479 oldIndicies,480 newIndicies481 };482 },483 optionListeners: {484 multiDragKey(key) {485 key = key.toLowerCase();486 if (key === 'ctrl') {487 key = 'Control';488 } else if (key.length > 1) {489 key = key.charAt(0).toUpperCase() + key.substr(1);490 }491 return key;492 }493 }494 });495}496function insertMultiDragElements(clonesInserted, rootEl) {497 multiDragElements.forEach((multiDragElement, i) => {498 let target = rootEl.children[multiDragElement.sortableIndex + (clonesInserted ? Number(i) : 0)];499 if (target) {500 rootEl.insertBefore(multiDragElement, target);501 } else {502 rootEl.appendChild(multiDragElement);503 }504 });505}506/**507 * Insert multi-drag clones508 * @param {[Boolean]} elementsInserted Whether the multi-drag elements are inserted509 * @param {HTMLElement} rootEl510 */511function insertMultiDragClones(elementsInserted, rootEl) {512 multiDragClones.forEach((clone, i) => {513 let target = rootEl.children[clone.sortableIndex + (elementsInserted ? Number(i) : 0)];514 if (target) {515 rootEl.insertBefore(clone, target);516 } else {517 rootEl.appendChild(clone);518 }519 });520}521function removeMultiDragElements() {522 multiDragElements.forEach(multiDragElement => {523 if (multiDragElement === dragEl) return;524 multiDragElement.parentNode && multiDragElement.parentNode.removeChild(multiDragElement);525 });526}...
drag.js
Source:drag.js
1/* global $q, $create, Blyde */2"use strict";3{4 let mouseX = 0, mouseY = 0;5 window.on('mousemove', (e) => {6 mouseX = e.clientX;7 mouseY = e.clientY;8 });9 window.on('touchmove', (e) => {10 mouseX = e.touches[0].clientX;11 mouseY = e.touches[0].clientY;12 });13 window.on('touchstart', (e) => {14 mouseX = e.touches[0].clientX;15 mouseY = e.touches[0].clientY;16 });1718 const createCase = (node) => {19 let dragCase = $create('dragcase');20 let dragHolder = $create('dragHolder');21 let clientStyle = node.getBoundingClientRect();22 let computedStyle = window.getComputedStyle(node);23 dragCase.dragger = {24 posX: clientStyle.left,25 posY: clientStyle.top,26 deg: 027 };28 dragCase.style.position = 'fixed';29 dragCase.style.display = 'block';30 dragCase.style.left = dragCase.dragger.posX + 'px';31 dragCase.style.top = dragCase.dragger.posY + 'px';32 dragHolder.style.display = computedStyle.display;33 dragHolder.style.float = computedStyle.float;34 dragCase.append(dragHolder);35 node.dragCase = dragCase;36 node.dragHolder = dragHolder;37 return dragCase;38 };3940 const drag = function() {41 let diffX = 0, diffY = 0;4243 if (this.dragging) {44 diffX = mouseX - this.clientWidth/2 - this.dragger.posX;45 diffY = mouseY - this.dragger.posY + 40;46 } else {47 diffX = this.dragger.targetX - this.dragger.posX;48 diffY = this.dragger.targetY - this.dragger.posY;49 if ((Math.pow(diffX, 2) + Math.pow(diffY, 2)) < 0.05) {50 this.style.left = this.dragger.targetX + 'px';51 this.style.top = this.dragger.targetY + 'px';52 this.style.transform = `rotate(0deg)`;53 window.cancelAnimationFrame(this.dragID);54 this.dragger.dragID = null;55 if (this.dragger.callback) this.dragger.callback();56 return;57 }58 }5960 let diffDeg = Math.tan(diffX/(Math.abs(this.dragger.posY - mouseY) + Math.sqrt(Math.pow(this.clientHeight, 2) + Math.pow(this.clientWidth, 2))*2 + 100 + Math.abs(diffX)))/Math.PI*180;61 if (diffDeg > 60) {62 diffDeg = 60;63 } else if (diffDeg < -60) {64 diffDeg = -60;65 }66 diffDeg = diffDeg - this.dragger.deg;67 this.dragger.posX = this.dragger.posX + diffX/8;68 this.dragger.posY = this.dragger.posY + diffY/8;69 this.dragger.deg = this.dragger.deg + diffDeg/8;70 this.style.left = this.dragger.posX + 'px';71 this.style.top = this.dragger.posY + 'px';72 this.style.transform = `rotate(${this.dragger.deg}deg)`;7374 this.dragger.dragID = window.requestAnimationFrame(drag.bind(this));75 };7677 Blyde.fn('Dragjs', {78 node: {79 startDrag(width, height) {80 if (this.parentNode.dragging || this.tagName.toUpperCase() === 'DRAGCASE') return this;81 let dragCase = '';82 if (this.parentNode.tagName.toUpperCase() === 'DRAGCASE') {83 dragCase = this.parentNode;84 } else {85 dragCase = createCase(this);86 if (width) dragCase.style.width = width;87 if (height) dragCase.style.height = height;88 this.swap(this.dragHolder);89 }90 $q('body').appendChild(dragCase);91 dragCase.dragging = true;92 if (!dragCase.dragger.dragID) {93 drag.call(dragCase);94 }95 return this;96 },97 dragRelease(cb) {98 if (this.tagName.toUpperCase() === 'DRAGCASE') return this;99 if (this.parentNode.tagName.toUpperCase() === 'DRAGCASE' && this.parentNode.dragging === true) {100 this.dragCase.dragger.callback = cb;101 this.dragCase.dragger.targetX = mouseX - this.dragCase.clientWidth/2;102 this.dragCase.dragger.targetY = mouseY;103 this.dragCase.dragging = false;104 }105 return this;106 },107 stopDrag(cb) {108 if (this.tagName.toUpperCase() === 'DRAGCASE') return this;109 if (this.dragCase) {110 if (this.dragCase.dragger.dragID) {111 window.cancelAnimationFrame(this.dragCase.dragger.dragID);112 }113 if (cb) {114 cb();115 } else {116 this.dragHolder.swap(this);117 }118 this.dragHolder.remove();119 this.dragCase.remove();120 this.dragHolder = null;121 this.dragCase = null;122 }123 return this;124 },125 moveTo(x, y, cb) {126 if (this.tagName.toUpperCase() === 'DRAGCASE') return this;127 this.startDrag().dragRelease(cb);128 this.dragCase.dragger.targetX = x;129 this.dragCase.dragger.targetY = y;130 return this;131 }132 }133 }, true);
...
DragAndDrop.js
Source:DragAndDrop.js
1/*2 Copyright (c) 2004-2006, The Dojo Foundation3 All Rights Reserved.4 Licensed under the Academic Free License version 2.1 or above OR the5 modified BSD license. For more information on Dojo licensing, see:6 http://dojotoolkit.org/community/licensing.shtml7*/8dojo.require("dojo.lang.common");9dojo.require("dojo.lang.declare");10dojo.provide("dojo.dnd.DragAndDrop");11dojo.declare("dojo.dnd.DragSource", null, {12 type: "",13 onDragEnd: function(){14 },15 onDragStart: function(){16 },17 /*18 * This function gets called when the DOM element was 19 * selected for dragging by the HtmlDragAndDropManager.20 */21 onSelected: function(){22 },23 unregister: function(){24 dojo.dnd.dragManager.unregisterDragSource(this);25 },26 reregister: function(){27 dojo.dnd.dragManager.registerDragSource(this);28 }29}, function(){30 //dojo.profile.start("DragSource");31 var dm = dojo.dnd.dragManager;32 if(dm["registerDragSource"]){ // side-effect prevention33 dm.registerDragSource(this);34 }35 //dojo.profile.end("DragSource");36});37dojo.declare("dojo.dnd.DragObject", null, {38 type: "",39 onDragStart: function(){40 // gets called directly after being created by the DragSource41 // default action is to clone self as icon42 },43 onDragMove: function(){44 // this changes the UI for the drag icon45 // "it moves itself"46 },47 onDragOver: function(){48 },49 onDragOut: function(){50 },51 onDragEnd: function(){52 },53 // normal aliases54 onDragLeave: this.onDragOut,55 onDragEnter: this.onDragOver,56 // non-camel aliases57 ondragout: this.onDragOut,58 ondragover: this.onDragOver59}, function(){60 var dm = dojo.dnd.dragManager;61 if(dm["registerDragObject"]){ // side-effect prevention62 dm.registerDragObject(this);63 }64});65dojo.declare("dojo.dnd.DropTarget", null, {66 acceptsType: function(type){67 if(!dojo.lang.inArray(this.acceptedTypes, "*")){ // wildcard68 if(!dojo.lang.inArray(this.acceptedTypes, type)) { return false; }69 }70 return true;71 },72 accepts: function(dragObjects){73 if(!dojo.lang.inArray(this.acceptedTypes, "*")){ // wildcard74 for (var i = 0; i < dragObjects.length; i++) {75 if (!dojo.lang.inArray(this.acceptedTypes,76 dragObjects[i].type)) { return false; }77 }78 }79 return true;80 },81 unregister: function(){82 dojo.dnd.dragManager.unregisterDropTarget(this);83 },84 onDragOver: function(){85 },86 onDragOut: function(){87 },88 onDragMove: function(){89 },90 onDropStart: function(){91 },92 onDrop: function(){93 },94 onDropEnd: function(){95 }96}, function(){97 if (this.constructor == dojo.dnd.DropTarget) { return; } // need to be subclassed98 this.acceptedTypes = [];99 dojo.dnd.dragManager.registerDropTarget(this);100});101// NOTE: this interface is defined here for the convenience of the DragManager102// implementor. It is expected that in most cases it will be satisfied by103// extending a native event (DOM event in HTML and SVG).104dojo.dnd.DragEvent = function(){105 this.dragSource = null;106 this.dragObject = null;107 this.target = null;108 this.eventStatus = "success";109 //110 // can be one of:111 // [ "dropSuccess", "dropFailure", "dragMove",112 // "dragStart", "dragEnter", "dragLeave"]113 //114}115/*116 * The DragManager handles listening for low-level events and dispatching117 * them to higher-level primitives like drag sources and drop targets. In118 * order to do this, it must keep a list of the items.119 */120dojo.declare("dojo.dnd.DragManager", null, {121 selectedSources: [],122 dragObjects: [],123 dragSources: [],124 registerDragSource: function(){},125 dropTargets: [],126 registerDropTarget: function(){},127 lastDragTarget: null,128 currentDragTarget: null,129 onKeyDown: function(){},130 onMouseOut: function(){},131 onMouseMove: function(){},132 onMouseUp: function(){}133});134// NOTE: despite the existance of the DragManager class, there will be a135// singleton drag manager provided by the renderer-specific D&D support code.136// It is therefore sane for us to assign instance variables to the DragManager137// prototype138// The renderer-specific file will define the following object:...
HtmlDragCopy.js
Source:HtmlDragCopy.js
1/*2 Copyright (c) 2004-2006, The Dojo Foundation3 All Rights Reserved.4 Licensed under the Academic Free License version 2.1 or above OR the5 modified BSD license. For more information on Dojo licensing, see:6 http://dojotoolkit.org/community/licensing.shtml7*/8dojo.provide("dojo.dnd.HtmlDragCopy");9dojo.require("dojo.dnd.*");10dojo.declare("dojo.dnd.HtmlDragCopySource", dojo.dnd.HtmlDragSource,11function(node, type, copyOnce){12 this.copyOnce = copyOnce;13 this.makeCopy = true;14},15{16 onDragStart: function(){17 var dragObj = new dojo.dnd.HtmlDragCopyObject(this.dragObject, this.type, this);18 if(this.dragClass) { dragObj.dragClass = this.dragClass; }19 if (this.constrainToContainer) {20 dragObj.constrainTo(this.constrainingContainer || this.domNode.parentNode);21 }22 return dragObj;23 },24 onSelected: function() {25 for (var i=0; i<this.dragObjects.length; i++) {26 dojo.dnd.dragManager.selectedSources.push(new dojo.dnd.HtmlDragCopySource(this.dragObjects[i]));27 }28 }29});30dojo.declare("dojo.dnd.HtmlDragCopyObject", dojo.dnd.HtmlDragObject,31function(dragObject, type, source){32 this.copySource = source;33},34{35 onDragStart: function(e) {36 dojo.dnd.HtmlDragCopyObject.superclass.onDragStart.apply(this, arguments);37 if(this.copySource.makeCopy) {38 this.sourceNode = this.domNode;39 this.domNode = this.domNode.cloneNode(true);40 }41 },42 onDragEnd: function(e){43 switch(e.dragStatus){44 case "dropFailure": // slide back to the start45 var startCoords = dojo.html.getAbsolutePosition(this.dragClone, true);46 // offset the end so the effect can be seen47 var endCoords = { left: this.dragStartPosition.x + 1,48 top: this.dragStartPosition.y + 1};49 // animate50 var anim = dojo.lfx.slideTo(this.dragClone, endCoords, 500, dojo.lfx.easeOut);51 var dragObject = this;52 dojo.event.connect(anim, "onEnd", function (e) {53 // pause for a second (not literally) and disappear54 dojo.lang.setTimeout(function() {55 dojo.html.removeNode(dragObject.dragClone);56 dragObject.dragClone = null;57 if(dragObject.copySource.makeCopy) {58 dojo.html.removeNode(dragObject.domNode);59 dragObject.domNode = dragObject.sourceNode;60 dragObject.sourceNode = null;61 }62 },63 200);64 });65 anim.play();66 dojo.event.topic.publish('dragEnd', { source: this } );67 return;68 }69 dojo.dnd.HtmlDragCopyObject.superclass.onDragEnd.apply(this, arguments);70 this.copySource.dragObject = this.domNode;71 if(this.copySource.copyOnce){72 this.copySource.makeCopy = false;73 }74 new dojo.dnd.HtmlDragCopySource(this.sourceNode, this.type, this.copySource.copyOnce);75 this.sourceNode = null;76 }...
Using AI Code Generation
1var drag = require('drag');2var rootview = Ti.UI.createView({3});4rootview.addEventListener('click', function(e) {5 Ti.API.info('rootview clicked');6});7rootview.addEventListener('touchstart', function(e) {8 Ti.API.info('rootview touchstart');9});10rootview.addEventListener('touchend', function(e) {11 Ti.API.info('rootview touchend');12});13rootview.addEventListener('touchmove', function(e) {14 Ti.API.info('rootview touchmove');15});16rootview.addEventListener('touchcancel', function(e) {17 Ti.API.info('rootview touchcancel');18});19rootview.addEventListener('singletap', function(e) {20 Ti.API.info('rootview singletap');21});22rootview.addEventListener('dblclick', function(e) {23 Ti.API.info('rootview dblclick');24});25rootview.addEventListener('doubletap', function(e) {26 Ti.API.info('rootview doubletap');27});28rootview.addEventListener('longpress', function(e) {29 Ti.API.info('rootview longpress');30});31drag(rootview);32var win = Ti.UI.createWindow({33});34win.add(rootview);35win.open();
Using AI Code Generation
1describe('Drag and Drop Demo', function() {2 it('Drag and Drop Demo', function() {3 browser.waitForAngularEnabled(false);4 browser.manage().window().maximize();5 browser.manage().timeouts().implicitlyWait(5000);6 browser.sleep(5000);7 });8});9dragAndDropBy(source, xOffset, yOffset)10describe('Drag and Drop Demo', function() {11 it('Drag and Drop Demo', function() {12 browser.waitForAngularEnabled(false);13 browser.manage().window().maximize();14 browser.manage().timeouts().implicitlyWait(5000);
Using AI Code Generation
1var win = Ti.UI.createWindow({2});3var rootview = Ti.UI.createView({4});5var view1 = Ti.UI.createView({6});7var view2 = Ti.UI.createView({8});9rootview.add(view1);10rootview.add(view2);11win.add(rootview);12win.open();13rootview.addEventListener('touchstart', function(e) {14 Ti.API.info('touchstart ' + e.source);15 var x = e.x;16 var y = e.y;17 rootview.addEventListener('touchmove', function(e) {18 Ti.API.info('touchmove ' + e.source);19 var dx = e.x - x;20 var dy = e.y - y;21 rootview.left += dx;22 rootview.top += dy;23 x = e.x;24 y = e.y;25 });26});27rootview.addEventListener('touchend', function(e) {28 Ti.API.info('touchend ' + e.source);29 rootview.removeEventListener('touchmove');30});
Using AI Code Generation
1var root = element(by.css('body'));2browser.actions().dragAndDrop(root, {x: 100, y: 100}).perform();3var child = element(by.css('body > div'));4browser.actions().dragAndDrop(child, {x: 100, y: 100}).perform();5var child = element(by.css('body > div'));6browser.actions().dragAndDrop(child, {x: 100, y: 100}).perform();7var child = element(by.css('body > div'));8browser.actions().dragAndDrop(child, {x: 100, y: 100}).perform();9var child = element(by.css('body > div'));10browser.actions().dragAndDrop(child, {x: 100, y: 100}).perform();11var child = element(by.css('body > div'));12browser.actions().dragAndDrop(child, {x: 100, y: 100}).perform();13var child = element(by.css('body > div'));14browser.actions().dragAndDrop(child, {x: 100, y: 100}).perform();15var child = element(by.css('body > div'));16browser.actions().dragAndDrop(child, {x: 100, y: 100}).perform();17var child = element(by.css('body > div'));18browser.actions().dragAndDrop(child, {x: 100, y: 100}).perform();19var child = element(by.css('body > div'));20browser.actions().dragAndDrop(child, {x: 100, y: 100}).perform();21var child = element(by.css('body > div'));22browser.actions().dragAndDrop(child, {x: 100, y: 100}).perform();23var child = element(by.css('body > div'));24browser.actions().dragAndDrop(child, {x: 100, y:
Using AI Code Generation
1var rootview = ui("$");2rootview.drag({action:"x",xRange:[0,200]});3var layout1 = ui("layout1");4layout1.drag({action:"x",xRange:[0,200]});5var layout2 = ui("layout2");6layout2.drag({action:"x",xRange:[0,200]});7var layout3 = ui("layout3");8layout3.drag({action:"x",xRange:[0,200]});9var layout4 = ui("layout4");10layout4.drag({action:"x",xRange:[0,200]});11var layout5 = ui("layout5");12layout5.drag({action:"x",xRange:[0,200]});13var layout6 = ui("layout6");14layout6.drag({action:"x",xRange:[0,200]});15var layout7 = ui("layout7");16layout7.drag({action:"x",xRange:[0,200]});17var layout8 = ui("layout8");18layout8.drag({action:"x",xRange:[0,200]});19var layout9 = ui("layout9");20layout9.drag({action:"x",xRange:[0,200]});21var layout10 = ui("layout10");22layout10.drag({action:"x",xRange:[0,200]});23var layout11 = ui("layout11");24layout11.drag({action:"x",xRange:[0,200]});25var layout12 = ui("layout12");26layout12.drag({action:"x",xRange:[0,200]});27var layout13 = ui("layout13");28layout13.drag({action:"x",xRange:[0,200]});29var layout14 = ui("layout14");
Using AI Code Generation
1var drag = new Drag('root', {2 onStart: function(){3 },4 onDrag: function(){5 },6 onComplete: function(){7 }8});9var drag = new Drag('root', {10 onStart: function(){11 },12 onDrag: function(){13 },14 onComplete: function(){15 }16});17var drag = new Drag('root', {18 onStart: function(){19 },20 onDrag: function(){21 },22 onComplete: function(){23 }24});25var drag = new Drag('root', {26 onStart: function(){27 },28 onDrag: function(){29 },30 onComplete: function(){31 }32});33var drag = new Drag('root', {34 onStart: function(){35 },36 onDrag: function(){37 },38 onComplete: function(){39 }40});41var drag = new Drag('root', {42 onStart: function(){43 },44 onDrag: function(){45 },46 onComplete: function(){47 }48});49var drag = new Drag('root', {50 onStart: function(){51 },52 onDrag: function(){53 },54 onComplete: function(){55 }56});57var drag = new Drag('root', {58 onStart: function(){59 },60 onDrag: function(){61 },62 onComplete: function(){63 }64});
Using AI Code Generation
1var drag = require('drag');2var view = Ti.UI.createView({backgroundColor:'red', width:100, height:100});3drag.init(view);4rootView.add(view);5exports.init = function(view) {6 var x, y;7 view.addEventListener('touchstart', function(e) {8 x = e.x;9 y = e.y;10 });11 view.addEventListener('touchmove', function(e) {12 view.left = e.x - x;13 view.top = e.y - y;14 });15};
Using AI Code Generation
1var drag = require('drag');2drag(rootView, function(e) {3 Ti.API.info("dragging");4});5var drag = require('drag');6var view = Ti.UI.createView({7});8drag(view, function(e) {9 Ti.API.info("dragging");10});11rootView.add(view);12Alloy.Globals.drag = require('drag');13function doClick(e) {14 Alloy.Globals.drag($.view, function(e) {15 Ti.API.info("dragging");16 });17}
Using AI Code Generation
1var drag = new Drag({2 limit: {3 },4 onMove: function () {5 console.log('dragging');6 },7 onEnd: function () {8 console.log('drag end');9 }10});11drag.start();12var drag = new Drag({13 limit: {14 },15 onMove: function () {16 console.log('dragging');17 },18 onEnd: function () {19 console.log('drag end');20 }21});22drag.start();23var drag = new Drag({24 limit: {25 },26 onMove: function () {27 console.log('dragging');28 },29 onEnd: function () {30 console.log('drag end');31 }32});33drag.start();34var drag = new Drag({35 limit: {36 },37 onMove: function () {38 console.log('dragging');39 },40 onEnd: function () {41 console.log('drag end');42 }43});44drag.start();45var drag = new Drag({46 limit: {47 },48 onMove: function () {49 console.log('dragging');50 },51 onEnd: function () {52 console.log('drag end');53 }54});55drag.start();56var drag = new Drag({57 limit: {58 },59 onMove: function () {60 console.log('dragging');
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!