JAVASCRIPT 11
Dragdrop.js Guest on 21st November 2020 10:03:19 AM
  1. /**
  2.  * trida zajistuje tahani predanych prvku nad prvky, ktere mohou tahnuty element prijmout
  3.  *
  4.  * @signal dragdrop-start
  5.  * @signal dragdrop-end
  6.  * @signal dragdrop-mousemove
  7.  * @signal dragdrop-change
  8.  */
  9. JAK.DragDrop = JAK.ClassMaker.makeClass({
  10.         NAME: 'JAK.DragDrop',
  11.         VERSION: '1.0',
  12.         IMPLEMENT: [JAK.ISignals]
  13. });
  14.  
  15. /**
  16.  * konstruktor
  17.  * @param {object} [opt]
  18.  * @param {string || function} opt.helper - zda pri tazeni budem tahnout s originalnim prvkem, nebo jeho klonem, nebo to bude nejaka funkce resit
  19.  * @param {bool} opt.revert - pokud je true, pak pri neupusteni do droppable, se premisti zpet na sve misto
  20.  * @param {object} opt.callbackObject
  21.  * @param {string || function} opt.callbackMethod
  22.  * @param {string} opt.draggableClass - trida, predavana prvku, ktery je pri tazeni pod mysi
  23.  * @param {int} opt.revertAnimationSpeed - rycholost animace navratu elementu pri neuspesnem pusteni, udava se v PX/20ms
  24.  * @param {int} opt.minDistance - nejmensi vzdalenost (v pixelech), po jejimz potazeni je vytvoren klon
  25.  */
  26. JAK.DragDrop.prototype.$constructor = function(opt) {
  27.         this.options = {
  28.                 helper: "clone", //original, clone, func
  29.                 revert: false, //true, vraci v pripade nepusteni do droppable
  30.                 callbackObject: null,
  31.                 callbackMethod: null,
  32.                 draggableClass: "draggable",
  33.                 revertAnimationSpeed: 3, // 3px/20ms rychlost animace navratu elementu na puvodni misto
  34.                 minDistance: 5
  35.         };
  36.  
  37.         for (var p in opt) {
  38.                 this.options[p] = opt[p];
  39.         }
  40.  
  41.         if (this.options.revert && !JAK.CSSInterpolator) { throw new Error("Cannot find JAK.CSSInterpolator."); }
  42.  
  43.         this.ec = []; /* event cache, jen pro sdilene udalosti */
  44.  
  45.         this.draggable = []; //pole poli pridanych tahacich elementu, kazdy prvek obsahuje pole [elm, handle, event], handle je to na co navesuji udalost, pokud neni zadane, handle = elm; event je ID mousedown eventu
  46.         this.droppable = []; //pole elementu, kde mohu pustit
  47.         this.activeDroppables = [];  // pole elementu kde mohu pustit, nad kterymi se nachazi prave mys pri tazeni
  48.  
  49.         this.dragging = {
  50.                 left: false,            //posunuti oproti poslednimu mousemove
  51.                 top: false,                     //posunuti oproti poslednimu mousemove
  52.                 x: false,                       //posledni pozice kurzoru
  53.                 y: false,                       //posledni pozice kurzoru
  54.                 initTop: false,         //puvodni pozice elementu
  55.                 initLeft: false,        //puvodni pozice elementu
  56.                 originalElm : null, //element, ktery vyvolal tazeni
  57.                 originalElmParent : null,               //pokud tahnu originalem a delam revert, musim ho pastnout do spravneho rodice
  58.                 originalElmNextSibling : null,  //a pred spravneho sourozence
  59.                 cloneElm : null,        //element, ktery tahnu (clone), nebo original
  60.                 mouseup : false,    //udalost mouseup navesena pri mousedown
  61.                 mousemove: false    //udalost mousemove navesena pri mousedown
  62.         };
  63. };
  64.  
  65. /**
  66.  * destruktor
  67.  */
  68. JAK.DragDrop.prototype.$destructor = function() {
  69.         JAK.Events.removeListeners(this.ec);
  70.        
  71.         this.removeAllDraggables();
  72.         this.removeAllDroppables();
  73.         this.activeDroppables = [];
  74.  
  75.         this.dragging.originalElm = null;
  76.         this.dragging.originalElmParent = null;
  77.         this.dragging.originalElmNextSibling = null;
  78.         this.dragging.cloneElm = null;
  79. };
  80.  
  81. /**
  82.  * pridani prvku pro tazeni, druhym volitelnym parametrem jde urcit cast, za kterou pujde tahnout
  83.  * @param elm
  84.  * @param handle
  85.  */
  86. JAK.DragDrop.prototype.addDraggable = function(elm, handle) {
  87.         var h = handle || elm;
  88.         var e = JAK.Events.addListener(h, "mousedown", this, "_mousedown");
  89.         this.draggable.push([elm, h, e]);
  90. };
  91.  
  92. /**
  93.  * Odebrani jednoho tahaciho prvku
  94.  */
  95. JAK.DragDrop.prototype.removeDraggable = function(elm) {
  96.         var index = -1;
  97.         for (var i=0;i<this.draggable.length;i++) {
  98.                 if (this.draggable[i][0] == elm) { index = i; }
  99.         }
  100.         if (index == -1) { throw new Error("Cannot remove draggable "+elm); }
  101.        
  102.         var e = this.draggable[index][2];
  103.         JAK.Events.removeListener(e);
  104.         this.draggable.splice(index, 1);
  105. }
  106.  
  107. /**
  108.  * Zruseni vsech tahacich prvku
  109.  */
  110. JAK.DragDrop.prototype.removeAllDraggables = function() {
  111.         while (this.draggable.length) { this.removeDraggable(this.draggable[0][0]); }
  112. }
  113.  
  114. /**
  115.  * pridani prvku pro pusteni
  116.  * @param elm
  117.  */
  118. JAK.DragDrop.prototype.addDroppable = function(elm) {
  119.         this.droppable.push(elm);
  120. };
  121.  
  122. /**
  123.  * odstraneni vsech droppable elementu
  124.  */
  125. JAK.DragDrop.prototype.removeAllDroppables = function() {
  126.         this.droppable = [];
  127. };
  128.  
  129. /**
  130.  * Odstraneni prvku pro pusteni
  131.  * @param elm
  132.  */
  133. JAK.DragDrop.prototype.removeDroppable = function(elm) {
  134.         var index = this.droppable.indexOf(elm);
  135.         if (index == -1) { throw new Error("Cannot remove droppable "+elm); }
  136.         this.droppable.splice(index, 1);
  137. };
  138.  
  139. /**
  140.  * metoda je navesena na vsechny draggable elementy
  141.  * @param e
  142.  * @param elm
  143.  */
  144. JAK.DragDrop.prototype._mousedown = function(e, elm) {
  145.         JAK.Events.cancelDef(e);
  146.  
  147.         this.dragging.mouseup = JAK.Events.addListener(document, "mouseup", this, "_mouseup");
  148.         this.dragging.mousemove = JAK.Events.addListener(document, "mousemove", this, "_mousemove");
  149.  
  150.         var container = null;
  151.         for (var i = 0; i < this.draggable.length; i++) {
  152.                 if (this.draggable[i][1] == elm) {
  153.                         container = this.draggable[i][0];
  154.                         break;
  155.                 }
  156.         }
  157.  
  158.         if (container) {
  159.                 this.dragging.x = e.clientX;
  160.                 this.dragging.y = e.clientY;
  161.                 this.dragging.originalElm = container;
  162.                 this.dragging.originalElmParent = container.parentNode;
  163.                 this.dragging.originalElmNextSibling = container.nextSibling;
  164.                 // pro pozici shodna s puvodnim prvkem zjistim jeste pred zobrazenim klona
  165.                 this.dragging.pos = JAK.DOM.getPortBoxPosition(this.dragging.originalElm);
  166.                 this.dragging.scroll = JAK.DOM.getScrollPos();
  167.         }
  168. };
  169.  
  170. /**
  171.  * pro mousedown navesena tato metoda, abychom zachytili konec tazeni
  172.  * @param e
  173.  * @param elm
  174.  */
  175. JAK.DragDrop.prototype._mouseup = function (e, elm) {
  176.         //zruseni udalosti
  177.         if (this.dragging.mouseup) {
  178.                 JAK.Events.removeListener(this.dragging.mouseup);
  179.                 this.dragging.mouseup = null;
  180.         }
  181.         if (this.dragging.mousemove) {
  182.                 JAK.Events.removeListener(this.dragging.mousemove);
  183.                 this.dragging.mousemove = null;
  184.         }
  185.  
  186.         //pokud nastal klik, tak nenastalo tazeni, tudiz tento element je prazdny a my netahneme
  187.         if (!this.dragging.cloneElm) { return; }
  188.  
  189.         var drops = this._getActiveDropboxes(e.clientX, e.clientY);
  190.         //pustenim jsme zasahli drop elementy
  191.         if (drops.length > 0) {
  192.                 //pokud mame pozitivni pripad nadjeti, informujeme callbackem, o objektu, ktery byl pretahovan a jake dropy ho mohou obslouzit
  193.                 if (this.options.callbackObject && this.options.callbackMethod) {
  194.                         if (typeof(this.options.callbackMethod) == 'string') {
  195.                                 this.options.callbackObject[this.options.callbackMethod](this.dragging.originalElm, this.dragging.cloneElm, drops);
  196.                         } else {
  197.                                 this.options.callbackMethod.call(this.options.callbackObject, this.dragging.originalElm, this.dragging.cloneElm, drops);
  198.                         }
  199.                 }
  200.                 this.dragging.cloneElm = null;
  201.         } else { //pustili jsme mimo
  202.                 //musime hezky vyanimovat na puvodni misto
  203.                 if (this.options.revert) {
  204.                          this._revert();
  205.                 } else {
  206.                         this._clearCloneElm(this.dragging.cloneElm, this.dragging.originalElmParent, this.dragging.originalElmNextSibling);
  207.                 }
  208.         }
  209.  
  210.         this.makeEvent('dragdrop-end', {droppable: drops, draggedElm: this.dragging.originalElm, coords:{x: e.clientX, y: e.clientY}});
  211. };
  212.  
  213. /**
  214.  * metoda zajistujici vytvoreni iluze tahnuti, vytvoreni ducha, a obsluhu jeho posouvani za kurzorem
  215.  * @param e
  216.  * @param elm
  217.  */
  218. JAK.DragDrop.prototype._mousemove = function(e, elm) {
  219.         JAK.Events.cancelDef(e);
  220.  
  221.         //zruseni vyberu textu pri tazeni elementem
  222.         if (window.getSelection) {
  223.                 try {
  224.                         window.getSelection().removeAllRanges();
  225.                 } catch (e) {}
  226.         } else if (document.selection) {
  227.                 document.selection.empty();
  228.         }
  229.  
  230.         //vytvoreni elementu, ktery budeme tahat, pri prvnim zatazeni
  231.         if (!this.dragging.cloneElm) {
  232.                 this.makeEvent("dragdrop-start", {draggedElm: this.dragging.originalElm});
  233.  
  234.                 if (this.options.helper == "original") {
  235.                         this.dragging.cloneElm = this.dragging.originalElm;
  236.                 } else {
  237.                         var dx = e.clientX - this.dragging.x;
  238.                         var dy = e.clientY - this.dragging.y;
  239.                         var dist = Math.sqrt(dx*dx+dy*dy);
  240.                         if (dist < this.options.minDistance) { return; } /* klon/helper nevyrabime, dokud uzivatel nepotahnul dostatecne daleko */
  241.  
  242.                         if (this.options.helper == "clone") {
  243.                                 this.dragging.cloneElm = this.dragging.originalElm.cloneNode(true);
  244.                                 this._removeId(this.dragging.cloneElm);
  245.                         } else {
  246.                                 this.dragging.cloneElm = this.options.helper(this.dragging.originalElm, e.clientX, e.clientY);
  247.                         }
  248.                 }
  249.  
  250.                 this.dragging.cloneElm.style.position = "absolute";
  251.                 JAK.DOM.addClass(this.dragging.cloneElm, this.options.draggableClass);
  252.                 document.body.appendChild(this.dragging.cloneElm);
  253.  
  254.                 this.dragging.left = this.dragging.pos.left + this.dragging.scroll.x;
  255.                 this.dragging.top = this.dragging.pos.top + this.dragging.scroll.y;
  256.                 this.dragging.initLeft = this.dragging.pos.left + this.dragging.scroll.x;
  257.                 this.dragging.initTop = this.dragging.pos.top + this.dragging.scroll.y;
  258.         }
  259.  
  260.         //o tolik jsme se posunuli oproti minule
  261.         this.dragging.left += e.clientX - this.dragging.x;
  262.         this.dragging.top += e.clientY - this.dragging.y;
  263.         //zaznamenat posledni pozici kurzoru
  264.         this.dragging.x = e.clientX;
  265.         this.dragging.y = e.clientY;
  266.        
  267.         //zmena polohy ducha
  268.         this.dragging.cloneElm.style.top = this.dragging.top + "px";
  269.         this.dragging.cloneElm.style.left = this.dragging.left + "px";
  270.  
  271.         //zjisteni kde jsem a vyslani signalu o zmene dropu
  272.         var drops = this._getActiveDropboxes(e.clientX, e.clientY);
  273.         if (drops.length != this.activeDroppables.length) {   //@todo: ne vzdy takhle jednoduchy test staci, asi bude nutne projit pole a porovnat prvek proti prvku
  274.                 this.makeEvent('dragdrop-change', {droppable: drops, draggedElm: this.dragging.originalElm, coords:{x: e.clientX, y: e.clientY}});
  275.                 this.activeDroppables = drops;
  276.         }
  277.         this.makeEvent('dragdrop-mousemove', {droppable: drops, draggedElm: this.dragging.originalElm, coords:{x: e.clientX, y: e.clientY}});
  278. };
  279.  
  280. /**
  281.  * pri klonovani originalniho prvku musim jeho klon zbavit vsech IDcek, jinak by to rozbilo selektory
  282.  * @param elm
  283.  */
  284. JAK.DragDrop.prototype._removeId = function(elm) {
  285.         elm.id = '';
  286.         var allChilds = elm.getElementsByTagName('*');
  287.         for (var i = 0; i < allChilds.length; i++) {
  288.                 allChilds[i].id = '';
  289.         }
  290. };
  291.  
  292. /**
  293.  * zjisteni, zda se kurzorem pri pusteni mysi nachazime nad nekterym z dropboxu
  294.  * @param {int} x pozice kurzoru mysi
  295.  * @param {int} y pozice kurzoru mysi
  296.  * @return Array pole dropu, ktere jsou aktivni kurzorem
  297.  */
  298. JAK.DragDrop.prototype._getActiveDropboxes = function(x, y) {
  299.         var ret = [];
  300.  
  301.         for (var i = 0; i < this.droppable.length; i++) {
  302.                 var d = this.droppable[i];
  303.                 var pos = JAK.DOM.getPortBoxPosition(d);
  304.  
  305.                 if (pos.top < y && pos.top + d.offsetHeight > y && pos.left < x && pos.left + d.offsetWidth > x) {
  306.                         ret.push(d);
  307.                 }
  308.         }
  309.  
  310.         return ret;
  311. };
  312.  
  313. /**
  314.  * metoda zacina animaci navratu elementu na puvodni misto
  315.  * animace je provadena pomoci vnitrni funkce _revert (closure stavu zacatku animace)
  316.  */
  317. JAK.DragDrop.prototype._revert = function() {
  318.         //pocatecni a cilove hodnoty
  319.         var pos = JAK.DOM.getPortBoxPosition(this.dragging.cloneElm);
  320.         var scroll = JAK.DOM.getScrollPos();
  321.  
  322.         var x = pos.left;
  323.         var y = pos.top;
  324.  
  325.         var ix = this.dragging.initLeft;
  326.         var iy = this.dragging.initTop;
  327.  
  328.         //vypocet vzdalenosti a rychlosti
  329.         var lx = (pos.left + scroll.x - this.dragging.initLeft);
  330.         var ly = (pos.top + scroll.y - this.dragging.initTop);
  331.         var routeLength = Math.sqrt(Math.pow(lx,2) + Math.pow(ly,2));
  332.         var time = Math.floor(routeLength / this.options.revertAnimationSpeed);
  333.  
  334.         //uzavera na elms a this
  335.         var elm = this.dragging.cloneElm;
  336.         var parent = this.dragging.originalElmParent;
  337.         var nextElm = this.dragging.originalElmNextSibling;
  338.         var that = this;
  339.  
  340.         var interpolator = new JAK.CSSInterpolator(elm, time, {interpolation: JAK.Interpolator.SIN, endCallback: function(){that._clearCloneElm(elm, parent, nextElm);}});
  341.         interpolator.addProperty('top', y, iy, 'px');
  342.         interpolator.addProperty('left', x, ix, 'px');
  343.         interpolator.start();
  344. };
  345.  
  346. /**
  347.  * schovani elementu, ktery byl tahan za mysi pri neuspesnem tazeni.
  348.  * jelikoz je volano i z animace navratu, proto jena poslednim radku test smazani, mohl jsem zacit tahnout uz jiny
  349.  * parametr 2 a 3 pouzity pouze pri u
  350.  * @param elm - element, ktery je pod kurzorem
  351.  * @param parent - rodic puvodniho tazeneho prvku
  352.  * @param next - nasledujici sourozenec puvodniho tazeneho prvku
  353.  */
  354. JAK.DragDrop.prototype._clearCloneElm = function(elm, parent, next) {
  355.         if (this.options.helper == 'original') {
  356.                 elm.style.position = '';
  357.                 parent.insertBefore(elm, next);
  358.         } else {
  359.                 elm.parentNode.removeChild(elm);
  360.         }
  361.        
  362.         JAK.DOM.removeClass(elm, this.options.draggableClass);
  363.         if (this.dragging.cloneElm == elm) {
  364.                 this.dragging.cloneElm = null;
  365.         }
  366. };

Paste is for source code and general debugging text.

Login or Register to edit, delete and keep track of your pastes and more.

Raw Paste

Login or Register to edit or fork this paste. It's free.