self.options.responsive) {
85 | //Init Drag Board
86 | self.drakeBoard = self
87 | .dragula([self.container], {
88 | moves: function (el, source, handle, sibling) {
89 | if (!self.options.dragBoards) return false
90 | return (
91 | handle.classList.contains('kanban-board-header') ||
92 | handle.classList.contains('kanban-title-board')
93 | )
94 | },
95 | accepts: function (el, target, source, sibling) {
96 | return target.classList.contains('kanban-container')
97 | },
98 | revertOnSpill: true,
99 | direction: 'horizontal'
100 | })
101 | .on('drag', function (el, source) {
102 | el.classList.add('is-moving')
103 | self.options.dragBoard(el, source)
104 | if (typeof el.dragfn === 'function') el.dragfn(el, source)
105 | })
106 | .on('dragend', function (el) {
107 | __updateBoardsOrder()
108 | el.classList.remove('is-moving')
109 | self.options.dragendBoard(el)
110 | if (typeof el.dragendfn === 'function') el.dragendfn(el)
111 | })
112 | .on('drop', function (el, target, source, sibling) {
113 | el.classList.remove('is-moving')
114 | self.options.dropBoard(el, target, source, sibling)
115 | if (typeof el.dropfn === 'function')
116 | el.dropfn(el, target, source, sibling)
117 | })
118 |
119 | //Init Drag Item
120 | self.drake = self
121 | .dragula(self.boardContainer, {
122 | moves: function (el, source, handle, sibling) {
123 | return self.__getCanMove(handle)
124 | },
125 | revertOnSpill: true
126 | })
127 | .on('cancel', function (el, container, source) {
128 | self.enableAllBoards()
129 | })
130 | .on('drag', function (el, source) {
131 | var elClass = el.getAttribute('class')
132 | if (elClass !== '' && elClass.indexOf('not-draggable') > -1) {
133 | self.drake.cancel(true)
134 | return
135 | }
136 |
137 | el.classList.add('is-moving')
138 |
139 | self.options.dragEl(el, source)
140 |
141 | var boardJSON = __findBoardJSON(source.parentNode.dataset.id)
142 | if (boardJSON.dragTo !== undefined) {
143 | self.options.boards.map(function (board) {
144 | if (
145 | boardJSON.dragTo.indexOf(board.id) === -1 &&
146 | board.id !== source.parentNode.dataset.id
147 | ) {
148 | self.findBoard(board.id).classList.add('disabled-board')
149 | }
150 | })
151 | }
152 |
153 | if (el !== null && typeof el.dragfn === 'function')
154 | el.dragfn(el, source)
155 | })
156 | .on('dragend', function (el) {
157 | self.options.dragendEl(el)
158 | if (el !== null && typeof el.dragendfn === 'function')
159 | el.dragendfn(el)
160 | })
161 | .on('drop', function (el, target, source, sibling) {
162 | self.enableAllBoards()
163 |
164 | var boardJSON = __findBoardJSON(source.parentNode.dataset.id)
165 | if (boardJSON.dragTo !== undefined) {
166 | if (
167 | boardJSON.dragTo.indexOf(target.parentNode.dataset.id) === -1 &&
168 | target.parentNode.dataset.id !== source.parentNode.dataset.id
169 | ) {
170 | self.drake.cancel(true)
171 | }
172 | }
173 | if (el !== null) {
174 | var result = self.options.dropEl(el, target, source, sibling)
175 | if (result === false) {
176 | self.drake.cancel(true)
177 | }
178 | el.classList.remove('is-moving')
179 | if (typeof el.dropfn === 'function')
180 | el.dropfn(el, target, source, sibling)
181 | }
182 | })
183 | }
184 | }
185 |
186 | this.enableAllBoards = function () {
187 | var allB = document.querySelectorAll('.kanban-board')
188 | if (allB.length > 0 && allB !== undefined) {
189 | for (var i = 0; i < allB.length; i++) {
190 | allB[i].classList.remove('disabled-board')
191 | }
192 | }
193 | }
194 |
195 | this.addElement = function (boardID, element, position) {
196 | if (typeof position === 'undefined') {
197 | position = -1
198 | }
199 | var board = self.element.querySelector(
200 | '[data-id="' + boardID + '"] .kanban-drag'
201 | )
202 | var refElement = board.childNodes[position]
203 | var nodeItem = document.createElement('div')
204 | nodeItem.classList.add('kanban-item')
205 | if (typeof element.id !== 'undefined' && element.id !== '') {
206 | nodeItem.setAttribute('data-eid', element.id)
207 | }
208 | if (element.class && Array.isArray(element.class)) {
209 | element.class.forEach(function (cl) {
210 | nodeItem.classList.add(cl)
211 | })
212 | }
213 | nodeItem.innerHTML = __buildItemCard(element)
214 | //add function
215 | nodeItem.clickfn = element.click
216 | nodeItem.contextfn = element.context;
217 | nodeItem.dragfn = element.drag
218 | nodeItem.dragendfn = element.dragend
219 | nodeItem.dropfn = element.drop
220 | __appendCustomProperties(nodeItem, element)
221 | __onclickHandler(nodeItem)
222 | __onContextHandler(nodeItem)
223 | if (self.options.itemHandleOptions.enabled) {
224 | nodeItem.style.cursor = 'default'
225 | }
226 | board.insertBefore(nodeItem, refElement)
227 | return self
228 | }
229 |
230 | this.addForm = function (boardID, formItem) {
231 | var board = self.element.querySelector(
232 | '[data-id="' + boardID + '"] .kanban-drag'
233 | )
234 | var _attribute = formItem.getAttribute('class')
235 | formItem.setAttribute('class', _attribute + ' not-draggable')
236 | board.appendChild(formItem)
237 | return self
238 | }
239 |
240 | this.addBoards = function (boards, isInit) {
241 | if (self.options.responsivePercentage) {
242 | self.container.style.width = '100%'
243 | self.options.gutter = '1%'
244 | if (window.innerWidth > self.options.responsive) {
245 | var boardWidth = (100 - boards.length * 2) / boards.length
246 | } else {
247 | var boardWidth = 100 - boards.length * 2
248 | }
249 | } else {
250 | var boardWidth = self.options.widthBoard
251 | }
252 | var addButton = self.options.itemAddOptions.enabled
253 | var buttonContent = self.options.itemAddOptions.content
254 | var buttonClass = self.options.itemAddOptions.class
255 | var buttonFooter = self.options.itemAddOptions.footer
256 |
257 | //for on all the boards
258 | for (var boardkey in boards) {
259 | // single board
260 | var board = boards[boardkey]
261 | if (!isInit) {
262 | self.options.boards.push(board)
263 | }
264 |
265 | if (!self.options.responsivePercentage) {
266 | //add width to container
267 | if (self.container.style.width === '') {
268 | self.container.style.width =
269 | parseInt(boardWidth) + parseInt(self.options.gutter) * 2 + 'px'
270 | } else {
271 | self.container.style.width =
272 | parseInt(self.container.style.width) +
273 | parseInt(boardWidth) +
274 | parseInt(self.options.gutter) * 2 +
275 | 'px'
276 | }
277 | }
278 | //create node
279 | var boardNode = document.createElement('div')
280 | boardNode.dataset.id = board.id
281 | boardNode.dataset.order = self.container.childNodes.length + 1
282 | boardNode.classList.add('kanban-board')
283 | //set style
284 | if (self.options.responsivePercentage) {
285 | boardNode.style.width = boardWidth + '%'
286 | } else {
287 | boardNode.style.width = boardWidth
288 | }
289 | boardNode.style.marginLeft = self.options.gutter
290 | boardNode.style.marginRight = self.options.gutter
291 | // header board
292 | var headerBoard = document.createElement('header')
293 | if (board.class !== '' && board.class !== undefined)
294 | var allClasses = board.class.split(',')
295 | else allClasses = []
296 | headerBoard.classList.add('kanban-board-header')
297 | allClasses.map(function (value) {
298 | // Remove empty spaces
299 | value = value.replace(/^[ ]+/g, '')
300 | headerBoard.classList.add(value)
301 | })
302 | headerBoard.innerHTML =
303 | '' + board.title + '
'
304 | //content board
305 | var contentBoard = document.createElement('main')
306 | contentBoard.classList.add('kanban-drag')
307 | if (board.bodyClass !== '' && board.bodyClass !== undefined)
308 | var bodyClasses = board.bodyClass.split(',')
309 | else bodyClasses = []
310 | bodyClasses.map(function (value) {
311 | contentBoard.classList.add(value)
312 | })
313 | //add drag to array for dragula
314 | self.boardContainer.push(contentBoard)
315 | for (var itemkey in board.item) {
316 | //create item
317 | var itemKanban = board.item[itemkey]
318 | var nodeItem = document.createElement('div')
319 | nodeItem.classList.add('kanban-item')
320 | if (itemKanban.id) {
321 | nodeItem.dataset.eid = itemKanban.id
322 | }
323 | if (itemKanban.class && Array.isArray(itemKanban.class)) {
324 | itemKanban.class.forEach(function (cl) {
325 | nodeItem.classList.add(cl)
326 | })
327 | }
328 | nodeItem.innerHTML = __buildItemCard(itemKanban)
329 | //add function
330 | nodeItem.clickfn = itemKanban.click
331 | nodeItem.contextfn = itemKanban.context
332 | nodeItem.dragfn = itemKanban.drag
333 | nodeItem.dragendfn = itemKanban.dragend
334 | nodeItem.dropfn = itemKanban.drop
335 | __appendCustomProperties(nodeItem, itemKanban)
336 | //add click handler of item
337 | __onclickHandler(nodeItem)
338 | __onContextHandler(nodeItem)
339 | if (self.options.itemHandleOptions.enabled) {
340 | nodeItem.style.cursor = 'default'
341 | }
342 | contentBoard.appendChild(nodeItem)
343 | }
344 | //footer board
345 | var footerBoard = document.createElement('footer')
346 | // if add button is true, add button to the board
347 | if (addButton) {
348 | var btn = document.createElement('BUTTON')
349 | var t = document.createTextNode(buttonContent ? buttonContent : '+')
350 | btn.setAttribute(
351 | 'class',
352 | buttonClass ? buttonClass : 'kanban-title-button btn btn-default btn-xs'
353 | )
354 | btn.appendChild(t)
355 | //var buttonHtml = ''+buttonContent+' '
356 | if (buttonFooter) {
357 | footerBoard.appendChild(btn)
358 | } else {
359 | headerBoard.appendChild(btn)
360 | }
361 | __onButtonClickHandler(btn, board.id)
362 | }
363 | //board assembly
364 | boardNode.appendChild(headerBoard)
365 | boardNode.appendChild(contentBoard)
366 | boardNode.appendChild(footerBoard)
367 | //board add
368 | self.container.appendChild(boardNode)
369 | }
370 | return self
371 | }
372 |
373 | this.findBoard = function (id) {
374 | var el = self.element.querySelector('[data-id="' + id + '"]')
375 | return el
376 | }
377 |
378 | this.getParentBoardID = function (el) {
379 | if (typeof el === 'string') {
380 | el = self.element.querySelector('[data-eid="' + el + '"]')
381 | }
382 | if (el === null) {
383 | return null
384 | }
385 | return el.parentNode.parentNode.dataset.id
386 | }
387 |
388 | this.moveElement = function (targetBoardID, elementID, element) {
389 | if (targetBoardID === this.getParentBoardID(elementID)) {
390 | return
391 | }
392 |
393 | this.removeElement(elementID)
394 | return this.addElement(targetBoardID, element)
395 | }
396 |
397 | this.replaceElement = function (el, element) {
398 | var nodeItem = el
399 | if (typeof nodeItem === 'string') {
400 | nodeItem = self.element.querySelector('[data-eid="' + el + '"]')
401 | }
402 | nodeItem.innerHTML = __buildItemCard(element)
403 | // add function
404 | nodeItem.clickfn = element.click
405 | nodeItem.contextfn = element.context
406 | nodeItem.dragfn = element.drag
407 | nodeItem.dragendfn = element.dragend
408 | nodeItem.dropfn = element.drop
409 | __appendCustomProperties(nodeItem, element)
410 | __onclickHandler(nodeItem)
411 | __onContextHandler(nodeItem)
412 | return self
413 | }
414 |
415 | this.findElement = function (id) {
416 | var el = self.element.querySelector('[data-eid="' + id + '"]')
417 | return el
418 | }
419 |
420 | this.getBoardElements = function (id) {
421 | var board = self.element.querySelector(
422 | '[data-id="' + id + '"] .kanban-drag'
423 | )
424 | return board.childNodes
425 | }
426 |
427 | this.removeElement = function (el) {
428 | if (typeof el === 'string')
429 | el = self.element.querySelector('[data-eid="' + el + '"]')
430 | if (el !== null) {
431 | //fallback for IE
432 | if (typeof el.remove == 'function') {
433 | el.remove()
434 | } else {
435 | el.parentNode.removeChild(el)
436 | }
437 | }
438 | return self
439 | }
440 |
441 | this.removeBoard = function (board) {
442 | var boardElement = null
443 | if (typeof board === 'string')
444 | boardElement = self.element.querySelector('[data-id="' + board + '"]')
445 | if (boardElement !== null) {
446 | //fallback for IE
447 | if (typeof boardElement.remove == 'function') {
448 | boardElement.remove()
449 | } else {
450 | boardElement.parentNode.removeChild(boardElement)
451 | }
452 | }
453 |
454 | // remove thboard in options.boards
455 | for (var i = 0; i < self.options.boards.length; i++) {
456 | if (self.options.boards[i].id === board) {
457 | self.options.boards.splice(i, 1)
458 | break
459 | }
460 | }
461 |
462 | return self
463 | }
464 |
465 | // board button on click function
466 | this.onButtonClick = function (el) {}
467 |
468 | //PRIVATE FUNCTION
469 | function __extendDefaults (source, properties) {
470 | var property
471 | for (property in properties) {
472 | if (properties.hasOwnProperty(property)) {
473 | source[property] = properties[property]
474 | }
475 | }
476 | return source
477 | }
478 |
479 | function __setBoard () {
480 | self.element = document.querySelector(self.options.element)
481 | //create container
482 | var boardContainer = document.createElement('div')
483 | boardContainer.classList.add('kanban-container')
484 | self.container = boardContainer
485 | //add boards
486 |
487 | if (document.querySelector(self.options.element).dataset.hasOwnProperty('board')) {
488 | url = document.querySelector(self.options.element).dataset.board
489 | window.fetch(url, {
490 | method: 'GET',
491 | headers: { 'Content-Type': 'application/json' }
492 | })
493 | .then(function (response) {
494 | // log response text
495 | response.json().then(function (data) {
496 | self.options.boards = data
497 | self.addBoards(self.options.boards, true)
498 | })
499 |
500 | })
501 | .catch(function (error) {
502 | console.log('Error: ', error)
503 | })
504 | } else {
505 | self.addBoards(self.options.boards, true)
506 | }
507 |
508 | //appends to container
509 | self.element.appendChild(self.container)
510 | }
511 |
512 | function __onclickHandler (nodeItem, clickfn) {
513 | nodeItem.addEventListener('click', function (e) {
514 | if (!self.options.propagationHandlers.includes('click')) e.preventDefault()
515 | self.options.click(this)
516 | if (typeof this.clickfn === 'function') this.clickfn(this)
517 | })
518 | }
519 |
520 | function __onContextHandler(nodeItem, contextfn) {
521 | if (nodeItem.addEventListener) {
522 | nodeItem.addEventListener('contextmenu', function (e) {
523 | if (!self.options.propagationHandlers.includes('context')) e.preventDefault()
524 | self.options.context(this, e)
525 | if (typeof this.contextfn === 'function') this.contextfn(this, e)
526 | }, false)
527 | } else {
528 | nodeItem.attachEvent('oncontextmenu', function () {
529 | self.options.context(this)
530 | if (typeof this.contextfn === 'function') this.contextfn(this)
531 | if (!self.options.propagationHandlers.includes('context')) window.event.returnValue = false
532 | })
533 | }
534 | }
535 |
536 | function __onButtonClickHandler (nodeItem, boardId) {
537 | nodeItem.addEventListener('click', function (e) {
538 | e.preventDefault()
539 | self.options.buttonClick(this, boardId)
540 | // if(typeof(this.clickfn) === 'function')
541 | // this.clickfn(this);
542 | })
543 | }
544 |
545 | function __findBoardJSON (id) {
546 | var el = []
547 | self.options.boards.map(function (board) {
548 | if (board.id === id) {
549 | return el.push(board)
550 | }
551 | })
552 | return el[0]
553 | }
554 |
555 | function __appendCustomProperties (element, parentObject) {
556 | for (var propertyName in parentObject) {
557 | if (self._disallowedItemProperties.indexOf(propertyName) > -1) {
558 | continue
559 | }
560 |
561 | element.setAttribute(
562 | 'data-' + propertyName,
563 | parentObject[propertyName]
564 | )
565 | }
566 | }
567 |
568 | function __updateBoardsOrder () {
569 | var index = 1
570 | for (var i = 0; i < self.container.childNodes.length; i++) {
571 | self.container.childNodes[i].dataset.order = index++
572 | }
573 | }
574 |
575 | function __buildItemCard(item) {
576 | var result = 'title' in item ? item.title : '';
577 |
578 | if (self.options.itemHandleOptions.enabled) {
579 | if ((self.options.itemHandleOptions.customHandler || undefined) === undefined) {
580 | var customCssHandler = self.options.itemHandleOptions.customCssHandler
581 | var customCssIconHandler = self.options.itemHandleOptions.customCssIconHandler
582 | var customItemLayout = self.options.itemHandleOptions.customItemLayout
583 | if ((customCssHandler || undefined) === undefined) {
584 | customCssHandler = 'drag_handler';
585 | }
586 |
587 | if ((customCssIconHandler || undefined) === undefined) {
588 | customCssIconHandler = customCssHandler + '_icon';
589 | }
590 |
591 | if ((customItemLayout || undefined) === undefined) {
592 | customItemLayout = '';
593 | }
594 |
595 | result = '
' + result + '
'
596 | } else {
597 | result = ' ' + self.options.itemHandleOptions.customHandler.replace(/%([^%]+)%/g, function (match, key)
598 | { return item[key] !== undefined ? item[key] : '' }) + '
'
599 | return result
600 | }
601 | }
602 |
603 | return result
604 | }
605 |
606 | //init plugin
607 | this.init()
608 | }
609 | })()
610 |
611 | },{"dragula":9}],2:[function(require,module,exports){
612 | module.exports = function atoa (a, n) { return Array.prototype.slice.call(a, n); }
613 |
614 | },{}],3:[function(require,module,exports){
615 | 'use strict';
616 |
617 | var ticky = require('ticky');
618 |
619 | module.exports = function debounce (fn, args, ctx) {
620 | if (!fn) { return; }
621 | ticky(function run () {
622 | fn.apply(ctx || null, args || []);
623 | });
624 | };
625 |
626 | },{"ticky":11}],4:[function(require,module,exports){
627 | 'use strict';
628 |
629 | var atoa = require('atoa');
630 | var debounce = require('./debounce');
631 |
632 | module.exports = function emitter (thing, options) {
633 | var opts = options || {};
634 | var evt = {};
635 | if (thing === undefined) { thing = {}; }
636 | thing.on = function (type, fn) {
637 | if (!evt[type]) {
638 | evt[type] = [fn];
639 | } else {
640 | evt[type].push(fn);
641 | }
642 | return thing;
643 | };
644 | thing.once = function (type, fn) {
645 | fn._once = true; // thing.off(fn) still works!
646 | thing.on(type, fn);
647 | return thing;
648 | };
649 | thing.off = function (type, fn) {
650 | var c = arguments.length;
651 | if (c === 1) {
652 | delete evt[type];
653 | } else if (c === 0) {
654 | evt = {};
655 | } else {
656 | var et = evt[type];
657 | if (!et) { return thing; }
658 | et.splice(et.indexOf(fn), 1);
659 | }
660 | return thing;
661 | };
662 | thing.emit = function () {
663 | var args = atoa(arguments);
664 | return thing.emitterSnapshot(args.shift()).apply(this, args);
665 | };
666 | thing.emitterSnapshot = function (type) {
667 | var et = (evt[type] || []).slice(0);
668 | return function () {
669 | var args = atoa(arguments);
670 | var ctx = this || thing;
671 | if (type === 'error' && opts.throws !== false && !et.length) { throw args.length === 1 ? args[0] : args; }
672 | et.forEach(function emitter (listen) {
673 | if (opts.async) { debounce(listen, args, ctx); } else { listen.apply(ctx, args); }
674 | if (listen._once) { thing.off(type, listen); }
675 | });
676 | return thing;
677 | };
678 | };
679 | return thing;
680 | };
681 |
682 | },{"./debounce":3,"atoa":2}],5:[function(require,module,exports){
683 | (function (global){(function (){
684 | 'use strict';
685 |
686 | var customEvent = require('custom-event');
687 | var eventmap = require('./eventmap');
688 | var doc = global.document;
689 | var addEvent = addEventEasy;
690 | var removeEvent = removeEventEasy;
691 | var hardCache = [];
692 |
693 | if (!global.addEventListener) {
694 | addEvent = addEventHard;
695 | removeEvent = removeEventHard;
696 | }
697 |
698 | module.exports = {
699 | add: addEvent,
700 | remove: removeEvent,
701 | fabricate: fabricateEvent
702 | };
703 |
704 | function addEventEasy (el, type, fn, capturing) {
705 | return el.addEventListener(type, fn, capturing);
706 | }
707 |
708 | function addEventHard (el, type, fn) {
709 | return el.attachEvent('on' + type, wrap(el, type, fn));
710 | }
711 |
712 | function removeEventEasy (el, type, fn, capturing) {
713 | return el.removeEventListener(type, fn, capturing);
714 | }
715 |
716 | function removeEventHard (el, type, fn) {
717 | var listener = unwrap(el, type, fn);
718 | if (listener) {
719 | return el.detachEvent('on' + type, listener);
720 | }
721 | }
722 |
723 | function fabricateEvent (el, type, model) {
724 | var e = eventmap.indexOf(type) === -1 ? makeCustomEvent() : makeClassicEvent();
725 | if (el.dispatchEvent) {
726 | el.dispatchEvent(e);
727 | } else {
728 | el.fireEvent('on' + type, e);
729 | }
730 | function makeClassicEvent () {
731 | var e;
732 | if (doc.createEvent) {
733 | e = doc.createEvent('Event');
734 | e.initEvent(type, true, true);
735 | } else if (doc.createEventObject) {
736 | e = doc.createEventObject();
737 | }
738 | return e;
739 | }
740 | function makeCustomEvent () {
741 | return new customEvent(type, { detail: model });
742 | }
743 | }
744 |
745 | function wrapperFactory (el, type, fn) {
746 | return function wrapper (originalEvent) {
747 | var e = originalEvent || global.event;
748 | e.target = e.target || e.srcElement;
749 | e.preventDefault = e.preventDefault || function preventDefault () { e.returnValue = false; };
750 | e.stopPropagation = e.stopPropagation || function stopPropagation () { e.cancelBubble = true; };
751 | e.which = e.which || e.keyCode;
752 | fn.call(el, e);
753 | };
754 | }
755 |
756 | function wrap (el, type, fn) {
757 | var wrapper = unwrap(el, type, fn) || wrapperFactory(el, type, fn);
758 | hardCache.push({
759 | wrapper: wrapper,
760 | element: el,
761 | type: type,
762 | fn: fn
763 | });
764 | return wrapper;
765 | }
766 |
767 | function unwrap (el, type, fn) {
768 | var i = find(el, type, fn);
769 | if (i) {
770 | var wrapper = hardCache[i].wrapper;
771 | hardCache.splice(i, 1); // free up a tad of memory
772 | return wrapper;
773 | }
774 | }
775 |
776 | function find (el, type, fn) {
777 | var i, item;
778 | for (i = 0; i < hardCache.length; i++) {
779 | item = hardCache[i];
780 | if (item.element === el && item.type === type && item.fn === fn) {
781 | return i;
782 | }
783 | }
784 | }
785 |
786 | }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
787 | },{"./eventmap":6,"custom-event":7}],6:[function(require,module,exports){
788 | (function (global){(function (){
789 | 'use strict';
790 |
791 | var eventmap = [];
792 | var eventname = '';
793 | var ron = /^on/;
794 |
795 | for (eventname in global) {
796 | if (ron.test(eventname)) {
797 | eventmap.push(eventname.slice(2));
798 | }
799 | }
800 |
801 | module.exports = eventmap;
802 |
803 | }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
804 | },{}],7:[function(require,module,exports){
805 | (function (global){(function (){
806 |
807 | var NativeCustomEvent = global.CustomEvent;
808 |
809 | function useNative () {
810 | try {
811 | var p = new NativeCustomEvent('cat', { detail: { foo: 'bar' } });
812 | return 'cat' === p.type && 'bar' === p.detail.foo;
813 | } catch (e) {
814 | }
815 | return false;
816 | }
817 |
818 | /**
819 | * Cross-browser `CustomEvent` constructor.
820 | *
821 | * https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent.CustomEvent
822 | *
823 | * @public
824 | */
825 |
826 | module.exports = useNative() ? NativeCustomEvent :
827 |
828 | // IE >= 9
829 | 'undefined' !== typeof document && 'function' === typeof document.createEvent ? function CustomEvent (type, params) {
830 | var e = document.createEvent('CustomEvent');
831 | if (params) {
832 | e.initCustomEvent(type, params.bubbles, params.cancelable, params.detail);
833 | } else {
834 | e.initCustomEvent(type, false, false, void 0);
835 | }
836 | return e;
837 | } :
838 |
839 | // IE <= 8
840 | function CustomEvent (type, params) {
841 | var e = document.createEventObject();
842 | e.type = type;
843 | if (params) {
844 | e.bubbles = Boolean(params.bubbles);
845 | e.cancelable = Boolean(params.cancelable);
846 | e.detail = params.detail;
847 | } else {
848 | e.bubbles = false;
849 | e.cancelable = false;
850 | e.detail = void 0;
851 | }
852 | return e;
853 | }
854 |
855 | }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
856 | },{}],8:[function(require,module,exports){
857 | 'use strict';
858 |
859 | var cache = {};
860 | var start = '(?:^|\\s)';
861 | var end = '(?:\\s|$)';
862 |
863 | function lookupClass (className) {
864 | var cached = cache[className];
865 | if (cached) {
866 | cached.lastIndex = 0;
867 | } else {
868 | cache[className] = cached = new RegExp(start + className + end, 'g');
869 | }
870 | return cached;
871 | }
872 |
873 | function addClass (el, className) {
874 | var current = el.className;
875 | if (!current.length) {
876 | el.className = className;
877 | } else if (!lookupClass(className).test(current)) {
878 | el.className += ' ' + className;
879 | }
880 | }
881 |
882 | function rmClass (el, className) {
883 | el.className = el.className.replace(lookupClass(className), ' ').trim();
884 | }
885 |
886 | module.exports = {
887 | add: addClass,
888 | rm: rmClass
889 | };
890 |
891 | },{}],9:[function(require,module,exports){
892 | (function (global){(function (){
893 | 'use strict';
894 |
895 | var emitter = require('contra/emitter');
896 | var crossvent = require('crossvent');
897 | var classes = require('./classes');
898 | var doc = document;
899 | var documentElement = doc.documentElement;
900 |
901 | function dragula (initialContainers, options) {
902 | var len = arguments.length;
903 | if (len === 1 && Array.isArray(initialContainers) === false) {
904 | options = initialContainers;
905 | initialContainers = [];
906 | }
907 | var _mirror; // mirror image
908 | var _source; // source container
909 | var _item; // item being dragged
910 | var _offsetX; // reference x
911 | var _offsetY; // reference y
912 | var _moveX; // reference move x
913 | var _moveY; // reference move y
914 | var _initialSibling; // reference sibling when grabbed
915 | var _currentSibling; // reference sibling now
916 | var _copy; // item used for copying
917 | var _renderTimer; // timer for setTimeout renderMirrorImage
918 | var _lastDropTarget = null; // last container item was over
919 | var _grabbed; // holds mousedown context until first mousemove
920 |
921 | var o = options || {};
922 | if (o.moves === void 0) { o.moves = always; }
923 | if (o.accepts === void 0) { o.accepts = always; }
924 | if (o.invalid === void 0) { o.invalid = invalidTarget; }
925 | if (o.containers === void 0) { o.containers = initialContainers || []; }
926 | if (o.isContainer === void 0) { o.isContainer = never; }
927 | if (o.copy === void 0) { o.copy = false; }
928 | if (o.copySortSource === void 0) { o.copySortSource = false; }
929 | if (o.revertOnSpill === void 0) { o.revertOnSpill = false; }
930 | if (o.removeOnSpill === void 0) { o.removeOnSpill = false; }
931 | if (o.direction === void 0) { o.direction = 'vertical'; }
932 | if (o.ignoreInputTextSelection === void 0) { o.ignoreInputTextSelection = true; }
933 | if (o.mirrorContainer === void 0) { o.mirrorContainer = doc.body; }
934 |
935 | var drake = emitter({
936 | containers: o.containers,
937 | start: manualStart,
938 | end: end,
939 | cancel: cancel,
940 | remove: remove,
941 | destroy: destroy,
942 | canMove: canMove,
943 | dragging: false
944 | });
945 |
946 | if (o.removeOnSpill === true) {
947 | drake.on('over', spillOver).on('out', spillOut);
948 | }
949 |
950 | events();
951 |
952 | return drake;
953 |
954 | function isContainer (el) {
955 | return drake.containers.indexOf(el) !== -1 || o.isContainer(el);
956 | }
957 |
958 | function events (remove) {
959 | var op = remove ? 'remove' : 'add';
960 | touchy(documentElement, op, 'mousedown', grab);
961 | touchy(documentElement, op, 'mouseup', release);
962 | }
963 |
964 | function eventualMovements (remove) {
965 | var op = remove ? 'remove' : 'add';
966 | touchy(documentElement, op, 'mousemove', startBecauseMouseMoved);
967 | }
968 |
969 | function movements (remove) {
970 | var op = remove ? 'remove' : 'add';
971 | crossvent[op](documentElement, 'selectstart', preventGrabbed); // IE8
972 | crossvent[op](documentElement, 'click', preventGrabbed);
973 | }
974 |
975 | function destroy () {
976 | events(true);
977 | release({});
978 | }
979 |
980 | function preventGrabbed (e) {
981 | if (_grabbed) {
982 | e.preventDefault();
983 | }
984 | }
985 |
986 | function grab (e) {
987 | _moveX = e.clientX;
988 | _moveY = e.clientY;
989 |
990 | var ignore = whichMouseButton(e) !== 1 || e.metaKey || e.ctrlKey;
991 | if (ignore) {
992 | return; // we only care about honest-to-god left clicks and touch events
993 | }
994 | var item = e.target;
995 | var context = canStart(item);
996 | if (!context) {
997 | return;
998 | }
999 | _grabbed = context;
1000 | eventualMovements();
1001 | if (e.type === 'mousedown') {
1002 | if (isInput(item)) { // see also: https://github.com/bevacqua/dragula/issues/208
1003 | item.focus(); // fixes https://github.com/bevacqua/dragula/issues/176
1004 | } else {
1005 | e.preventDefault(); // fixes https://github.com/bevacqua/dragula/issues/155
1006 | }
1007 | }
1008 | }
1009 |
1010 | function startBecauseMouseMoved (e) {
1011 | if (!_grabbed) {
1012 | return;
1013 | }
1014 | if (whichMouseButton(e) === 0) {
1015 | release({});
1016 | return; // when text is selected on an input and then dragged, mouseup doesn't fire. this is our only hope
1017 | }
1018 |
1019 | // truthy check fixes #239, equality fixes #207, fixes #501
1020 | if ((e.clientX !== void 0 && Math.abs(e.clientX - _moveX) <= (o.slideFactorX || 0)) &&
1021 | (e.clientY !== void 0 && Math.abs(e.clientY - _moveY) <= (o.slideFactorY || 0))) {
1022 | return;
1023 | }
1024 |
1025 | if (o.ignoreInputTextSelection) {
1026 | var clientX = getCoord('clientX', e) || 0;
1027 | var clientY = getCoord('clientY', e) || 0;
1028 | var elementBehindCursor = doc.elementFromPoint(clientX, clientY);
1029 | if (isInput(elementBehindCursor)) {
1030 | return;
1031 | }
1032 | }
1033 |
1034 | var grabbed = _grabbed; // call to end() unsets _grabbed
1035 | eventualMovements(true);
1036 | movements();
1037 | end();
1038 | start(grabbed);
1039 |
1040 | var offset = getOffset(_item);
1041 | _offsetX = getCoord('pageX', e) - offset.left;
1042 | _offsetY = getCoord('pageY', e) - offset.top;
1043 |
1044 | classes.add(_copy || _item, 'gu-transit');
1045 | renderMirrorImage();
1046 | drag(e);
1047 | }
1048 |
1049 | function canStart (item) {
1050 | if (drake.dragging && _mirror) {
1051 | return;
1052 | }
1053 | if (isContainer(item)) {
1054 | return; // don't drag container itself
1055 | }
1056 | var handle = item;
1057 | while (getParent(item) && isContainer(getParent(item)) === false) {
1058 | if (o.invalid(item, handle)) {
1059 | return;
1060 | }
1061 | item = getParent(item); // drag target should be a top element
1062 | if (!item) {
1063 | return;
1064 | }
1065 | }
1066 | var source = getParent(item);
1067 | if (!source) {
1068 | return;
1069 | }
1070 | if (o.invalid(item, handle)) {
1071 | return;
1072 | }
1073 |
1074 | var movable = o.moves(item, source, handle, nextEl(item));
1075 | if (!movable) {
1076 | return;
1077 | }
1078 |
1079 | return {
1080 | item: item,
1081 | source: source
1082 | };
1083 | }
1084 |
1085 | function canMove (item) {
1086 | return !!canStart(item);
1087 | }
1088 |
1089 | function manualStart (item) {
1090 | var context = canStart(item);
1091 | if (context) {
1092 | start(context);
1093 | }
1094 | }
1095 |
1096 | function start (context) {
1097 | if (isCopy(context.item, context.source)) {
1098 | _copy = context.item.cloneNode(true);
1099 | drake.emit('cloned', _copy, context.item, 'copy');
1100 | }
1101 |
1102 | _source = context.source;
1103 | _item = context.item;
1104 | _initialSibling = _currentSibling = nextEl(context.item);
1105 |
1106 | drake.dragging = true;
1107 | drake.emit('drag', _item, _source);
1108 | }
1109 |
1110 | function invalidTarget () {
1111 | return false;
1112 | }
1113 |
1114 | function end () {
1115 | if (!drake.dragging) {
1116 | return;
1117 | }
1118 | var item = _copy || _item;
1119 | drop(item, getParent(item));
1120 | }
1121 |
1122 | function ungrab () {
1123 | _grabbed = false;
1124 | eventualMovements(true);
1125 | movements(true);
1126 | }
1127 |
1128 | function release (e) {
1129 | ungrab();
1130 |
1131 | if (!drake.dragging) {
1132 | return;
1133 | }
1134 | var item = _copy || _item;
1135 | var clientX = getCoord('clientX', e) || 0;
1136 | var clientY = getCoord('clientY', e) || 0;
1137 | var elementBehindCursor = getElementBehindPoint(_mirror, clientX, clientY);
1138 | var dropTarget = findDropTarget(elementBehindCursor, clientX, clientY);
1139 | if (dropTarget && ((_copy && o.copySortSource) || (!_copy || dropTarget !== _source))) {
1140 | drop(item, dropTarget);
1141 | } else if (o.removeOnSpill) {
1142 | remove();
1143 | } else {
1144 | cancel();
1145 | }
1146 | }
1147 |
1148 | function drop (item, target) {
1149 | var parent = getParent(item);
1150 | if (_copy && o.copySortSource && target === _source) {
1151 | parent.removeChild(_item);
1152 | }
1153 | if (isInitialPlacement(target)) {
1154 | drake.emit('cancel', item, _source, _source);
1155 | } else {
1156 | drake.emit('drop', item, target, _source, _currentSibling);
1157 | }
1158 | cleanup();
1159 | }
1160 |
1161 | function remove () {
1162 | if (!drake.dragging) {
1163 | return;
1164 | }
1165 | var item = _copy || _item;
1166 | var parent = getParent(item);
1167 | if (parent) {
1168 | parent.removeChild(item);
1169 | }
1170 | drake.emit(_copy ? 'cancel' : 'remove', item, parent, _source);
1171 | cleanup();
1172 | }
1173 |
1174 | function cancel (revert) {
1175 | if (!drake.dragging) {
1176 | return;
1177 | }
1178 | var reverts = arguments.length > 0 ? revert : o.revertOnSpill;
1179 | var item = _copy || _item;
1180 | var parent = getParent(item);
1181 | var initial = isInitialPlacement(parent);
1182 | if (initial === false && reverts) {
1183 | if (_copy) {
1184 | if (parent) {
1185 | parent.removeChild(_copy);
1186 | }
1187 | } else {
1188 | _source.insertBefore(item, _initialSibling);
1189 | }
1190 | }
1191 | if (initial || reverts) {
1192 | drake.emit('cancel', item, _source, _source);
1193 | } else {
1194 | drake.emit('drop', item, parent, _source, _currentSibling);
1195 | }
1196 | cleanup();
1197 | }
1198 |
1199 | function cleanup () {
1200 | var item = _copy || _item;
1201 | ungrab();
1202 | removeMirrorImage();
1203 | if (item) {
1204 | classes.rm(item, 'gu-transit');
1205 | }
1206 | if (_renderTimer) {
1207 | clearTimeout(_renderTimer);
1208 | }
1209 | drake.dragging = false;
1210 | if (_lastDropTarget) {
1211 | drake.emit('out', item, _lastDropTarget, _source);
1212 | }
1213 | drake.emit('dragend', item);
1214 | _source = _item = _copy = _initialSibling = _currentSibling = _renderTimer = _lastDropTarget = null;
1215 | }
1216 |
1217 | function isInitialPlacement (target, s) {
1218 | var sibling;
1219 | if (s !== void 0) {
1220 | sibling = s;
1221 | } else if (_mirror) {
1222 | sibling = _currentSibling;
1223 | } else {
1224 | sibling = nextEl(_copy || _item);
1225 | }
1226 | return target === _source && sibling === _initialSibling;
1227 | }
1228 |
1229 | function findDropTarget (elementBehindCursor, clientX, clientY) {
1230 | var target = elementBehindCursor;
1231 | while (target && !accepted()) {
1232 | target = getParent(target);
1233 | }
1234 | return target;
1235 |
1236 | function accepted () {
1237 | var droppable = isContainer(target);
1238 | if (droppable === false) {
1239 | return false;
1240 | }
1241 |
1242 | var immediate = getImmediateChild(target, elementBehindCursor);
1243 | var reference = getReference(target, immediate, clientX, clientY);
1244 | var initial = isInitialPlacement(target, reference);
1245 | if (initial) {
1246 | return true; // should always be able to drop it right back where it was
1247 | }
1248 | return o.accepts(_item, target, _source, reference);
1249 | }
1250 | }
1251 |
1252 | function drag (e) {
1253 | if (!_mirror) {
1254 | return;
1255 | }
1256 | e.preventDefault();
1257 |
1258 | var clientX = getCoord('clientX', e) || 0;
1259 | var clientY = getCoord('clientY', e) || 0;
1260 | var x = clientX - _offsetX;
1261 | var y = clientY - _offsetY;
1262 |
1263 | _mirror.style.left = x + 'px';
1264 | _mirror.style.top = y + 'px';
1265 |
1266 | var item = _copy || _item;
1267 | var elementBehindCursor = getElementBehindPoint(_mirror, clientX, clientY);
1268 | var dropTarget = findDropTarget(elementBehindCursor, clientX, clientY);
1269 | var changed = dropTarget !== null && dropTarget !== _lastDropTarget;
1270 | if (changed || dropTarget === null) {
1271 | out();
1272 | _lastDropTarget = dropTarget;
1273 | over();
1274 | }
1275 | var parent = getParent(item);
1276 | if (dropTarget === _source && _copy && !o.copySortSource) {
1277 | if (parent) {
1278 | parent.removeChild(item);
1279 | }
1280 | return;
1281 | }
1282 | var reference;
1283 | var immediate = getImmediateChild(dropTarget, elementBehindCursor);
1284 | if (immediate !== null) {
1285 | reference = getReference(dropTarget, immediate, clientX, clientY);
1286 | } else if (o.revertOnSpill === true && !_copy) {
1287 | reference = _initialSibling;
1288 | dropTarget = _source;
1289 | } else {
1290 | if (_copy && parent) {
1291 | parent.removeChild(item);
1292 | }
1293 | return;
1294 | }
1295 | if (
1296 | (reference === null && changed) ||
1297 | reference !== item &&
1298 | reference !== nextEl(item)
1299 | ) {
1300 | _currentSibling = reference;
1301 | dropTarget.insertBefore(item, reference);
1302 | drake.emit('shadow', item, dropTarget, _source);
1303 | }
1304 | function moved (type) { drake.emit(type, item, _lastDropTarget, _source); }
1305 | function over () { if (changed) { moved('over'); } }
1306 | function out () { if (_lastDropTarget) { moved('out'); } }
1307 | }
1308 |
1309 | function spillOver (el) {
1310 | classes.rm(el, 'gu-hide');
1311 | }
1312 |
1313 | function spillOut (el) {
1314 | if (drake.dragging) { classes.add(el, 'gu-hide'); }
1315 | }
1316 |
1317 | function renderMirrorImage () {
1318 | if (_mirror) {
1319 | return;
1320 | }
1321 | var rect = _item.getBoundingClientRect();
1322 | _mirror = _item.cloneNode(true);
1323 | _mirror.style.width = getRectWidth(rect) + 'px';
1324 | _mirror.style.height = getRectHeight(rect) + 'px';
1325 | classes.rm(_mirror, 'gu-transit');
1326 | classes.add(_mirror, 'gu-mirror');
1327 | o.mirrorContainer.appendChild(_mirror);
1328 | touchy(documentElement, 'add', 'mousemove', drag);
1329 | classes.add(o.mirrorContainer, 'gu-unselectable');
1330 | drake.emit('cloned', _mirror, _item, 'mirror');
1331 | }
1332 |
1333 | function removeMirrorImage () {
1334 | if (_mirror) {
1335 | classes.rm(o.mirrorContainer, 'gu-unselectable');
1336 | touchy(documentElement, 'remove', 'mousemove', drag);
1337 | getParent(_mirror).removeChild(_mirror);
1338 | _mirror = null;
1339 | }
1340 | }
1341 |
1342 | function getImmediateChild (dropTarget, target) {
1343 | var immediate = target;
1344 | while (immediate !== dropTarget && getParent(immediate) !== dropTarget) {
1345 | immediate = getParent(immediate);
1346 | }
1347 | if (immediate === documentElement) {
1348 | return null;
1349 | }
1350 | return immediate;
1351 | }
1352 |
1353 | function getReference (dropTarget, target, x, y) {
1354 | var horizontal = o.direction === 'horizontal';
1355 | var reference = target !== dropTarget ? inside() : outside();
1356 | return reference;
1357 |
1358 | function outside () { // slower, but able to figure out any position
1359 | var len = dropTarget.children.length;
1360 | var i;
1361 | var el;
1362 | var rect;
1363 | for (i = 0; i < len; i++) {
1364 | el = dropTarget.children[i];
1365 | rect = el.getBoundingClientRect();
1366 | if (horizontal && (rect.left + rect.width / 2) > x) { return el; }
1367 | if (!horizontal && (rect.top + rect.height / 2) > y) { return el; }
1368 | }
1369 | return null;
1370 | }
1371 |
1372 | function inside () { // faster, but only available if dropped inside a child element
1373 | var rect = target.getBoundingClientRect();
1374 | if (horizontal) {
1375 | return resolve(x > rect.left + getRectWidth(rect) / 2);
1376 | }
1377 | return resolve(y > rect.top + getRectHeight(rect) / 2);
1378 | }
1379 |
1380 | function resolve (after) {
1381 | return after ? nextEl(target) : target;
1382 | }
1383 | }
1384 |
1385 | function isCopy (item, container) {
1386 | return typeof o.copy === 'boolean' ? o.copy : o.copy(item, container);
1387 | }
1388 | }
1389 |
1390 | function touchy (el, op, type, fn) {
1391 | var touch = {
1392 | mouseup: 'touchend',
1393 | mousedown: 'touchstart',
1394 | mousemove: 'touchmove'
1395 | };
1396 | var pointers = {
1397 | mouseup: 'pointerup',
1398 | mousedown: 'pointerdown',
1399 | mousemove: 'pointermove'
1400 | };
1401 | var microsoft = {
1402 | mouseup: 'MSPointerUp',
1403 | mousedown: 'MSPointerDown',
1404 | mousemove: 'MSPointerMove'
1405 | };
1406 | if (global.navigator.pointerEnabled) {
1407 | crossvent[op](el, pointers[type], fn);
1408 | } else if (global.navigator.msPointerEnabled) {
1409 | crossvent[op](el, microsoft[type], fn);
1410 | } else {
1411 | crossvent[op](el, touch[type], fn);
1412 | crossvent[op](el, type, fn);
1413 | }
1414 | }
1415 |
1416 | function whichMouseButton (e) {
1417 | if (e.touches !== void 0) { return e.touches.length; }
1418 | if (e.which !== void 0 && e.which !== 0) { return e.which; } // see https://github.com/bevacqua/dragula/issues/261
1419 | if (e.buttons !== void 0) { return e.buttons; }
1420 | var button = e.button;
1421 | if (button !== void 0) { // see https://github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/event.js#L573-L575
1422 | return button & 1 ? 1 : button & 2 ? 3 : (button & 4 ? 2 : 0);
1423 | }
1424 | }
1425 |
1426 | function getOffset (el) {
1427 | var rect = el.getBoundingClientRect();
1428 | return {
1429 | left: rect.left + getScroll('scrollLeft', 'pageXOffset'),
1430 | top: rect.top + getScroll('scrollTop', 'pageYOffset')
1431 | };
1432 | }
1433 |
1434 | function getScroll (scrollProp, offsetProp) {
1435 | if (typeof global[offsetProp] !== 'undefined') {
1436 | return global[offsetProp];
1437 | }
1438 | if (documentElement.clientHeight) {
1439 | return documentElement[scrollProp];
1440 | }
1441 | return doc.body[scrollProp];
1442 | }
1443 |
1444 | function getElementBehindPoint (point, x, y) {
1445 | point = point || {};
1446 | var state = point.className || '';
1447 | var el;
1448 | point.className += ' gu-hide';
1449 | el = doc.elementFromPoint(x, y);
1450 | point.className = state;
1451 | return el;
1452 | }
1453 |
1454 | function never () { return false; }
1455 | function always () { return true; }
1456 | function getRectWidth (rect) { return rect.width || (rect.right - rect.left); }
1457 | function getRectHeight (rect) { return rect.height || (rect.bottom - rect.top); }
1458 | function getParent (el) { return el.parentNode === doc ? null : el.parentNode; }
1459 | function isInput (el) { return el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT' || isEditable(el); }
1460 | function isEditable (el) {
1461 | if (!el) { return false; } // no parents were editable
1462 | if (el.contentEditable === 'false') { return false; } // stop the lookup
1463 | if (el.contentEditable === 'true') { return true; } // found a contentEditable element in the chain
1464 | return isEditable(getParent(el)); // contentEditable is set to 'inherit'
1465 | }
1466 |
1467 | function nextEl (el) {
1468 | return el.nextElementSibling || manually();
1469 | function manually () {
1470 | var sibling = el;
1471 | do {
1472 | sibling = sibling.nextSibling;
1473 | } while (sibling && sibling.nodeType !== 1);
1474 | return sibling;
1475 | }
1476 | }
1477 |
1478 | function getEventHost (e) {
1479 | // on touchend event, we have to use `e.changedTouches`
1480 | // see http://stackoverflow.com/questions/7192563/touchend-event-properties
1481 | // see https://github.com/bevacqua/dragula/issues/34
1482 | if (e.targetTouches && e.targetTouches.length) {
1483 | return e.targetTouches[0];
1484 | }
1485 | if (e.changedTouches && e.changedTouches.length) {
1486 | return e.changedTouches[0];
1487 | }
1488 | return e;
1489 | }
1490 |
1491 | function getCoord (coord, e) {
1492 | var host = getEventHost(e);
1493 | var missMap = {
1494 | pageX: 'clientX', // IE8
1495 | pageY: 'clientY' // IE8
1496 | };
1497 | if (coord in missMap && !(coord in host) && missMap[coord] in host) {
1498 | coord = missMap[coord];
1499 | }
1500 | return host[coord];
1501 | }
1502 |
1503 | module.exports = dragula;
1504 |
1505 | }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
1506 | },{"./classes":8,"contra/emitter":4,"crossvent":5}],10:[function(require,module,exports){
1507 | // shim for using process in browser
1508 | var process = module.exports = {};
1509 |
1510 | // cached from whatever global is present so that test runners that stub it
1511 | // don't break things. But we need to wrap it in a try catch in case it is
1512 | // wrapped in strict mode code which doesn't define any globals. It's inside a
1513 | // function because try/catches deoptimize in certain engines.
1514 |
1515 | var cachedSetTimeout;
1516 | var cachedClearTimeout;
1517 |
1518 | function defaultSetTimout() {
1519 | throw new Error('setTimeout has not been defined');
1520 | }
1521 | function defaultClearTimeout () {
1522 | throw new Error('clearTimeout has not been defined');
1523 | }
1524 | (function () {
1525 | try {
1526 | if (typeof setTimeout === 'function') {
1527 | cachedSetTimeout = setTimeout;
1528 | } else {
1529 | cachedSetTimeout = defaultSetTimout;
1530 | }
1531 | } catch (e) {
1532 | cachedSetTimeout = defaultSetTimout;
1533 | }
1534 | try {
1535 | if (typeof clearTimeout === 'function') {
1536 | cachedClearTimeout = clearTimeout;
1537 | } else {
1538 | cachedClearTimeout = defaultClearTimeout;
1539 | }
1540 | } catch (e) {
1541 | cachedClearTimeout = defaultClearTimeout;
1542 | }
1543 | } ())
1544 | function runTimeout(fun) {
1545 | if (cachedSetTimeout === setTimeout) {
1546 | //normal enviroments in sane situations
1547 | return setTimeout(fun, 0);
1548 | }
1549 | // if setTimeout wasn't available but was latter defined
1550 | if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
1551 | cachedSetTimeout = setTimeout;
1552 | return setTimeout(fun, 0);
1553 | }
1554 | try {
1555 | // when when somebody has screwed with setTimeout but no I.E. maddness
1556 | return cachedSetTimeout(fun, 0);
1557 | } catch(e){
1558 | try {
1559 | // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
1560 | return cachedSetTimeout.call(null, fun, 0);
1561 | } catch(e){
1562 | // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
1563 | return cachedSetTimeout.call(this, fun, 0);
1564 | }
1565 | }
1566 |
1567 |
1568 | }
1569 | function runClearTimeout(marker) {
1570 | if (cachedClearTimeout === clearTimeout) {
1571 | //normal enviroments in sane situations
1572 | return clearTimeout(marker);
1573 | }
1574 | // if clearTimeout wasn't available but was latter defined
1575 | if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
1576 | cachedClearTimeout = clearTimeout;
1577 | return clearTimeout(marker);
1578 | }
1579 | try {
1580 | // when when somebody has screwed with setTimeout but no I.E. maddness
1581 | return cachedClearTimeout(marker);
1582 | } catch (e){
1583 | try {
1584 | // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
1585 | return cachedClearTimeout.call(null, marker);
1586 | } catch (e){
1587 | // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
1588 | // Some versions of I.E. have different rules for clearTimeout vs setTimeout
1589 | return cachedClearTimeout.call(this, marker);
1590 | }
1591 | }
1592 |
1593 |
1594 |
1595 | }
1596 | var queue = [];
1597 | var draining = false;
1598 | var currentQueue;
1599 | var queueIndex = -1;
1600 |
1601 | function cleanUpNextTick() {
1602 | if (!draining || !currentQueue) {
1603 | return;
1604 | }
1605 | draining = false;
1606 | if (currentQueue.length) {
1607 | queue = currentQueue.concat(queue);
1608 | } else {
1609 | queueIndex = -1;
1610 | }
1611 | if (queue.length) {
1612 | drainQueue();
1613 | }
1614 | }
1615 |
1616 | function drainQueue() {
1617 | if (draining) {
1618 | return;
1619 | }
1620 | var timeout = runTimeout(cleanUpNextTick);
1621 | draining = true;
1622 |
1623 | var len = queue.length;
1624 | while(len) {
1625 | currentQueue = queue;
1626 | queue = [];
1627 | while (++queueIndex < len) {
1628 | if (currentQueue) {
1629 | currentQueue[queueIndex].run();
1630 | }
1631 | }
1632 | queueIndex = -1;
1633 | len = queue.length;
1634 | }
1635 | currentQueue = null;
1636 | draining = false;
1637 | runClearTimeout(timeout);
1638 | }
1639 |
1640 | process.nextTick = function (fun) {
1641 | var args = new Array(arguments.length - 1);
1642 | if (arguments.length > 1) {
1643 | for (var i = 1; i < arguments.length; i++) {
1644 | args[i - 1] = arguments[i];
1645 | }
1646 | }
1647 | queue.push(new Item(fun, args));
1648 | if (queue.length === 1 && !draining) {
1649 | runTimeout(drainQueue);
1650 | }
1651 | };
1652 |
1653 | // v8 likes predictible objects
1654 | function Item(fun, array) {
1655 | this.fun = fun;
1656 | this.array = array;
1657 | }
1658 | Item.prototype.run = function () {
1659 | this.fun.apply(null, this.array);
1660 | };
1661 | process.title = 'browser';
1662 | process.browser = true;
1663 | process.env = {};
1664 | process.argv = [];
1665 | process.version = ''; // empty string to avoid regexp issues
1666 | process.versions = {};
1667 |
1668 | function noop() {}
1669 |
1670 | process.on = noop;
1671 | process.addListener = noop;
1672 | process.once = noop;
1673 | process.off = noop;
1674 | process.removeListener = noop;
1675 | process.removeAllListeners = noop;
1676 | process.emit = noop;
1677 | process.prependListener = noop;
1678 | process.prependOnceListener = noop;
1679 |
1680 | process.listeners = function (name) { return [] }
1681 |
1682 | process.binding = function (name) {
1683 | throw new Error('process.binding is not supported');
1684 | };
1685 |
1686 | process.cwd = function () { return '/' };
1687 | process.chdir = function (dir) {
1688 | throw new Error('process.chdir is not supported');
1689 | };
1690 | process.umask = function() { return 0; };
1691 |
1692 | },{}],11:[function(require,module,exports){
1693 | (function (setImmediate){(function (){
1694 | var si = typeof setImmediate === 'function', tick;
1695 | if (si) {
1696 | tick = function (fn) { setImmediate(fn); };
1697 | } else {
1698 | tick = function (fn) { setTimeout(fn, 0); };
1699 | }
1700 |
1701 | module.exports = tick;
1702 | }).call(this)}).call(this,require("timers").setImmediate)
1703 | },{"timers":12}],12:[function(require,module,exports){
1704 | (function (setImmediate,clearImmediate){(function (){
1705 | var nextTick = require('process/browser.js').nextTick;
1706 | var apply = Function.prototype.apply;
1707 | var slice = Array.prototype.slice;
1708 | var immediateIds = {};
1709 | var nextImmediateId = 0;
1710 |
1711 | // DOM APIs, for completeness
1712 |
1713 | exports.setTimeout = function() {
1714 | return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout);
1715 | };
1716 | exports.setInterval = function() {
1717 | return new Timeout(apply.call(setInterval, window, arguments), clearInterval);
1718 | };
1719 | exports.clearTimeout =
1720 | exports.clearInterval = function(timeout) { timeout.close(); };
1721 |
1722 | function Timeout(id, clearFn) {
1723 | this._id = id;
1724 | this._clearFn = clearFn;
1725 | }
1726 | Timeout.prototype.unref = Timeout.prototype.ref = function() {};
1727 | Timeout.prototype.close = function() {
1728 | this._clearFn.call(window, this._id);
1729 | };
1730 |
1731 | // Does not start the time, just sets up the members needed.
1732 | exports.enroll = function(item, msecs) {
1733 | clearTimeout(item._idleTimeoutId);
1734 | item._idleTimeout = msecs;
1735 | };
1736 |
1737 | exports.unenroll = function(item) {
1738 | clearTimeout(item._idleTimeoutId);
1739 | item._idleTimeout = -1;
1740 | };
1741 |
1742 | exports._unrefActive = exports.active = function(item) {
1743 | clearTimeout(item._idleTimeoutId);
1744 |
1745 | var msecs = item._idleTimeout;
1746 | if (msecs >= 0) {
1747 | item._idleTimeoutId = setTimeout(function onTimeout() {
1748 | if (item._onTimeout)
1749 | item._onTimeout();
1750 | }, msecs);
1751 | }
1752 | };
1753 |
1754 | // That's not how node.js implements it but the exposed api is the same.
1755 | exports.setImmediate = typeof setImmediate === "function" ? setImmediate : function(fn) {
1756 | var id = nextImmediateId++;
1757 | var args = arguments.length < 2 ? false : slice.call(arguments, 1);
1758 |
1759 | immediateIds[id] = true;
1760 |
1761 | nextTick(function onNextTick() {
1762 | if (immediateIds[id]) {
1763 | // fn.call() is faster so we optimize for the common use-case
1764 | // @see http://jsperf.com/call-apply-segu
1765 | if (args) {
1766 | fn.apply(null, args);
1767 | } else {
1768 | fn.call(null);
1769 | }
1770 | // Prevent ids from leaking
1771 | exports.clearImmediate(id);
1772 | }
1773 | });
1774 |
1775 | return id;
1776 | };
1777 |
1778 | exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) {
1779 | delete immediateIds[id];
1780 | };
1781 | }).call(this)}).call(this,require("timers").setImmediate,require("timers").clearImmediate)
1782 | },{"process/browser.js":10,"timers":12}]},{},[1]);
1783 |
--------------------------------------------------------------------------------
/dist/jkanban.min.css:
--------------------------------------------------------------------------------
1 | .kanban-container{position:relative;box-sizing:border-box;width:auto}.kanban-container *{box-sizing:border-box}.kanban-container:after{clear:both;display:block;content:""}.kanban-board{position:relative;float:left;background:#e2e4e6;transition:all .3s cubic-bezier(.23,1,.32,1)}.kanban-board.disabled-board{opacity:.3}.kanban-board.is-moving.gu-mirror{transform:rotate(3deg)}.kanban-board.is-moving.gu-mirror .kanban-drag{overflow:hidden;padding-right:50px}.kanban-board header{font-size:16px;padding:15px}.kanban-board header .kanban-title-board{font-weight:700;margin:0;padding:0;display:inline}.kanban-board header .kanban-title-button{float:right}.kanban-board .kanban-drag{min-height:200px;padding:20px}.kanban-board:after{clear:both;display:block;content:""}.kanban-item{background:#fff;padding:15px;margin-bottom:20px;transition:all .3s cubic-bezier(.23,1,.32,1);animation:append-animate .3s cubic-bezier(.23,1,.32,1)}@keyframes append-animate{from{transform:translateY(-20px)}to{transform:translateY(0)}}.kanban-item:hover{cursor:move}.kanban-item:last-child{margin:0}.kanban-item.is-moving.gu-mirror{transform:rotate(3deg);height:auto!important}.gu-mirror{position:fixed!important;margin:0!important;z-index:9999!important}.gu-hide{display:none!important}.gu-unselectable{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.gu-transit{opacity:.2!important;transform:rotate(0)!important}.drag_handler{background:#fff;border-radius:50%;width:24px;height:24px;position:relative;float:left;top:-3px;margin-right:4px}.drag_handler:hover{cursor:move}.drag_handler_icon{position:relative;display:block;background:#000;width:24px;height:2px;top:12px;transition:.5s ease-in-out}.drag_handler_icon:after,.drag_handler_icon:before{background:#000;content:'';display:block;width:100%;height:100%;position:absolute;transition:.5s ease-in-out}.drag_handler_icon:before{top:6px}.drag_handler_icon:after{bottom:6px}
--------------------------------------------------------------------------------
/dist/jkanban.min.js:
--------------------------------------------------------------------------------
1 | !function(){return function e(t,n,o){function i(a,c){if(!n[a]){if(!t[a]){var d="function"==typeof require&&require;if(!c&&d)return d(a,!0);if(r)return r(a,!0);var s=new Error("Cannot find module '"+a+"'");throw s.code="MODULE_NOT_FOUND",s}var l=n[a]={exports:{}};t[a][0].call(l.exports,function(e){return i(t[a][1][e]||e)},l,l.exports,e,t,n,o)}return n[a].exports}for(var r="function"==typeof require&&require,a=0;a-1||t.setAttribute("data-"+o,n[o])}function l(t){var n="title"in t?t.title:"";if(e.options.itemHandleOptions.enabled){if(void 0!==(e.options.itemHandleOptions.customHandler||void 0))return n=" "+e.options.itemHandleOptions.customHandler.replace(/%([^%]+)%/g,function(e,n){return void 0!==t[n]?t[n]:""})+"
";var o=e.options.itemHandleOptions.customCssHandler,i=e.options.itemHandleOptions.customCssIconHandler,r=e.options.itemHandleOptions.customItemLayout;void 0===(o||void 0)&&(o="drag_handler"),void 0===(i||void 0)&&(i=o+"_icon"),void 0===(r||void 0)&&(r=""),n="
"+n+"
"}return n}arguments[0]&&"object"==typeof arguments[0]&&(this.options=function(e,t){var n;for(n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}(i,arguments[0])),this.__getCanMove=function(t){return e.options.itemHandleOptions.enabled?e.options.itemHandleOptions.handleClass?t.classList.contains(e.options.itemHandleOptions.handleClass):t.classList.contains("item_handle"):!!e.options.dragItems},this.init=function(){!function(){e.element=document.querySelector(e.options.element);var t=document.createElement("div");t.classList.add("kanban-container"),e.container=t,document.querySelector(e.options.element).dataset.hasOwnProperty("board")?(url=document.querySelector(e.options.element).dataset.board,window.fetch(url,{method:"GET",headers:{"Content-Type":"application/json"}}).then(function(t){t.json().then(function(t){e.options.boards=t,e.addBoards(e.options.boards,!0)})}).catch(function(e){console.log("Error: ",e)})):e.addBoards(e.options.boards,!0);e.element.appendChild(e.container)}(),window.innerWidth>e.options.responsive&&(e.drakeBoard=e.dragula([e.container],{moves:function(t,n,o,i){return!!e.options.dragBoards&&(o.classList.contains("kanban-board-header")||o.classList.contains("kanban-title-board"))},accepts:function(e,t,n,o){return t.classList.contains("kanban-container")},revertOnSpill:!0,direction:"horizontal"}).on("drag",function(t,n){t.classList.add("is-moving"),e.options.dragBoard(t,n),"function"==typeof t.dragfn&&t.dragfn(t,n)}).on("dragend",function(t){!function(){for(var t=1,n=0;n-1)e.drake.cancel(!0);else{t.classList.add("is-moving"),e.options.dragEl(t,n);var i=d(n.parentNode.dataset.id);void 0!==i.dragTo&&e.options.boards.map(function(t){-1===i.dragTo.indexOf(t.id)&&t.id!==n.parentNode.dataset.id&&e.findBoard(t.id).classList.add("disabled-board")}),null!==t&&"function"==typeof t.dragfn&&t.dragfn(t,n)}}).on("dragend",function(t){e.options.dragendEl(t),null!==t&&"function"==typeof t.dragendfn&&t.dragendfn(t)}).on("drop",function(t,n,o,i){e.enableAllBoards();var r=d(o.parentNode.dataset.id);(void 0!==r.dragTo&&-1===r.dragTo.indexOf(n.parentNode.dataset.id)&&n.parentNode.dataset.id!==o.parentNode.dataset.id&&e.drake.cancel(!0),null!==t)&&(!1===e.options.dropEl(t,n,o,i)&&e.drake.cancel(!0),t.classList.remove("is-moving"),"function"==typeof t.dropfn&&t.dropfn(t,n,o,i))}))},this.enableAllBoards=function(){var e=document.querySelectorAll(".kanban-board");if(e.length>0&&void 0!==e)for(var t=0;te.options.responsive)var o=(100-2*t.length)/t.length;else o=100-2*t.length;else o=e.options.widthBoard;var i=e.options.itemAddOptions.enabled,d=e.options.itemAddOptions.content,u=e.options.itemAddOptions.class,f=e.options.itemAddOptions.footer;for(var p in t){var v=t[p];n||e.options.boards.push(v),e.options.responsivePercentage||(""===e.container.style.width?e.container.style.width=parseInt(o)+2*parseInt(e.options.gutter)+"px":e.container.style.width=parseInt(e.container.style.width)+parseInt(o)+2*parseInt(e.options.gutter)+"px");var m=document.createElement("div");m.dataset.id=v.id,m.dataset.order=e.container.childNodes.length+1,m.classList.add("kanban-board"),e.options.responsivePercentage?m.style.width=o+"%":m.style.width=o,m.style.marginLeft=e.options.gutter,m.style.marginRight=e.options.gutter;var h=document.createElement("header");if(""!==v.class&&void 0!==v.class)var g=v.class.split(",");else g=[];h.classList.add("kanban-board-header"),g.map(function(e){e=e.replace(/^[ ]+/g,""),h.classList.add(e)}),h.innerHTML=''+v.title+"
";var y=document.createElement("main");if(y.classList.add("kanban-drag"),""!==v.bodyClass&&void 0!==v.bodyClass)var b=v.bodyClass.split(",");else b=[];for(var w in b.map(function(e){y.classList.add(e)}),e.boardContainer.push(y),v.item){var E=v.item[w],T=document.createElement("div");T.classList.add("kanban-item"),E.id&&(T.dataset.eid=E.id),E.class&&Array.isArray(E.class)&&E.class.forEach(function(e){T.classList.add(e)}),T.innerHTML=l(E),T.clickfn=E.click,T.contextfn=E.context,T.dragfn=E.drag,T.dragendfn=E.dragend,T.dropfn=E.drop,s(T,E),r(T),a(T),e.options.itemHandleOptions.enabled&&(T.style.cursor="default"),y.appendChild(T)}var x=document.createElement("footer");if(i){var C=document.createElement("BUTTON"),O=document.createTextNode(d||"+");C.setAttribute("class",u||"kanban-title-button btn btn-default btn-xs"),C.appendChild(O),f?x.appendChild(C):h.appendChild(C),c(C,v.id)}m.appendChild(h),m.appendChild(y),m.appendChild(x),e.container.appendChild(m)}return e},this.findBoard=function(t){return e.element.querySelector('[data-id="'+t+'"]')},this.getParentBoardID=function(t){return"string"==typeof t&&(t=e.element.querySelector('[data-eid="'+t+'"]')),null===t?null:t.parentNode.parentNode.dataset.id},this.moveElement=function(e,t,n){if(e!==this.getParentBoardID(t))return this.removeElement(t),this.addElement(e,n)},this.replaceElement=function(t,n){var o=t;return"string"==typeof o&&(o=e.element.querySelector('[data-eid="'+t+'"]')),o.innerHTML=l(n),o.clickfn=n.click,o.contextfn=n.context,o.dragfn=n.drag,o.dragendfn=n.dragend,o.dropfn=n.drop,s(o,n),r(o),a(o),e},this.findElement=function(t){return e.element.querySelector('[data-eid="'+t+'"]')},this.getBoardElements=function(t){return e.element.querySelector('[data-id="'+t+'"] .kanban-drag').childNodes},this.removeElement=function(t){return"string"==typeof t&&(t=e.element.querySelector('[data-eid="'+t+'"]')),null!==t&&("function"==typeof t.remove?t.remove():t.parentNode.removeChild(t)),e},this.removeBoard=function(t){var n=null;"string"==typeof t&&(n=e.element.querySelector('[data-id="'+t+'"]')),null!==n&&("function"==typeof n.remove?n.remove():n.parentNode.removeChild(n));for(var o=0;o0?e:A.revertOnSpill,n=L||E,o=h(n),i=$(o);!1===i&&t&&(L?o&&o.removeChild(L):w.insertBefore(n,k)),i||t?_.emit("cancel",n,w,w):_.emit("drop",n,o,w,S),G()}}function G(){var e=L||E;U(),n&&(r.rm(A.mirrorContainer,"gu-unselectable"),d(c,"remove","mousemove",Q),h(n).removeChild(n),n=null),e&&r.rm(e,"gu-transit"),B&&clearTimeout(B),_.dragging=!1,I&&_.emit("out",e,I,w),_.emit("dragend",e),w=E=L=k=S=B=I=null}function $(e,t){var o;return o=void 0!==t?t:n?S:y(L||E),e===w&&o===k}function J(e,t,n){for(var o=e;o&&!i();)o=h(o);return o;function i(){if(!1===H(o))return!1;var i=Z(o,e),r=ee(o,i,t,n);return!!$(o,r)||A.accepts(E,o,w,r)}}function Q(e){if(n){e.preventDefault();var t=b("clientX",e)||0,o=b("clientY",e)||0,i=t-T,r=o-x;n.style.left=i+"px",n.style.top=r+"px";var a=L||E,c=u(n,t,o),d=J(c,t,o),s=null!==d&&d!==I;(s||null===d)&&(I&&v("out"),I=d,s&&v("over"));var l=h(a);if(d!==w||!L||A.copySortSource){var f,p=Z(d,c);if(null!==p)f=ee(d,p,t,o);else{if(!0!==A.revertOnSpill||L)return void(L&&l&&l.removeChild(a));f=k,d=w}(null===f&&s||f!==a&&f!==y(a))&&(S=f,d.insertBefore(a,f),_.emit("shadow",a,d,w))}else l&&l.removeChild(a)}function v(e){_.emit(e,a,I,w)}}function Z(e,t){for(var n=t;n!==e&&h(n)!==e;)n=h(n);return n===c?null:n}function ee(e,t,n,o){var i,r="horizontal"===A.direction;return t!==e?(i=t.getBoundingClientRect(),a(r?n>i.left+v(i)/2:o>i.top+m(i)/2)):function(){var t,i,a,c=e.children.length;for(t=0;tn)return i;if(!r&&a.top+a.height/2>o)return i}return null}();function a(e){return e?y(t):t}}}}).call(this)}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./classes":8,"contra/emitter":4,crossvent:5}],10:[function(e,t,n){var o,i,r=t.exports={};function a(){throw new Error("setTimeout has not been defined")}function c(){throw new Error("clearTimeout has not been defined")}function d(e){if(o===setTimeout)return setTimeout(e,0);if((o===a||!o)&&setTimeout)return o=setTimeout,setTimeout(e,0);try{return o(e,0)}catch(t){try{return o.call(null,e,0)}catch(t){return o.call(this,e,0)}}}!function(){try{o="function"==typeof setTimeout?setTimeout:a}catch(e){o=a}try{i="function"==typeof clearTimeout?clearTimeout:c}catch(e){i=c}}();var s,l=[],u=!1,f=-1;function p(){u&&s&&(u=!1,s.length?l=s.concat(l):f=-1,l.length&&v())}function v(){if(!u){var e=d(p);u=!0;for(var t=l.length;t;){for(s=l,l=[];++f1)for(var n=1;n=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},n.setImmediate="function"==typeof t?t:function(e){var t=d++,o=!(arguments.length<2)&&a.call(arguments,1);return c[t]=!0,i(function(){c[t]&&(o?e.apply(null,o):e.call(null),n.clearImmediate(t))}),t},n.clearImmediate="function"==typeof o?o:function(e){delete c[e]}}).call(this)}).call(this,e("timers").setImmediate,e("timers").clearImmediate)},{"process/browser.js":10,timers:12}]},{},[1]);
--------------------------------------------------------------------------------
/example/drag_auto_scroll.js:
--------------------------------------------------------------------------------
1 | function drag_auto_scroll(el_drg) {
2 | //Thanks to StackOverFlow
3 | //Thanks to Peter-Paul Koch for Some of his concepts borrowed from http://www.quirksmode.org/js/dragdrop.html
4 | this.addEventSimple = function (obj, evt, fn) {
5 | if (obj.addEventListener)
6 | obj.addEventListener(evt, fn, false);
7 | else if (obj.attachEvent)
8 | obj.attachEvent('on' + evt, fn);
9 | };
10 | this.removeEventSimple = function (obj, evt, fn) {
11 | if (obj.removeEventListener)
12 | obj.removeEventListener(evt, fn, false);
13 | else if (obj.detachEvent)
14 | obj.detachEvent('on' + evt, fn);
15 | };
16 | this.fade = function (el_f, o) {
17 | var oo = o / 100;
18 | el_f.style.opacity = oo;
19 | el_f.style['-ms-filter'] = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + o + ")";
20 | el_f.style['-khtml-opacity'] = oo;
21 | el_f.style['-moz-opacity'] = oo;
22 | el_f.style.filter = "Alpha(Opacity=" + o + "); -moz-opacity:" + oo + "; opacity:" + oo + ";-khtml-opacity:" + oo + ";";
23 | if (o >= 100) {
24 | el_f.style.filter = '';
25 | }
26 | };
27 | this.intersectRect = function (r1, r2) {
28 | return !(r2.left > r1.right ||
29 | r2.right < r1.left ||
30 | r2.top > r1.bottom ||
31 | r2.bottom < r1.top);
32 | };
33 | this.getposition=function(el){
34 | var rect = el.getBoundingClientRect(),
35 | scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
36 | scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
37 | var l, t, w, h, sw, sh, sbw, sbh;
38 | if (el == document.body) {
39 | l = 0;
40 | t = 0;
41 | w = document.body.clientWidth || document.documentElement.clientWidth || window.innerWidth ;//don't change this order
42 | h = document.body.clientHeight || document.documentElement.clientHeight || window.innerHeight; //don't change this order
43 | sw = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
44 | sh = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
45 | sbw = (document.documentElement.clientWidth - document.body.offsetWidth); //16
46 | sbh = (document.documentElement.clientHeight - document.body.offsetHeight); //16
47 | } else {
48 | l=rect.left + scrollLeft;
49 | t=rect.top + scrollTop ;
50 | w=rect.right - rect.left;
51 | h = rect.bottom - rect.top;
52 | sw = el.scrollWidth;
53 | sh = el.scrollHeight;
54 | sbw = el.offsetWidth - el.clientWidth; //17
55 | sbh = el.offsetHeight - el.clientHeight; //17
56 | }
57 | return { left: l, top: t
58 | , width: w, height: h
59 | , right: l + w
60 | , bottom: t + h
61 | , scrollLeft: scrollLeft
62 | , scrollTop: scrollTop
63 | , scrollWidth: sw
64 | , scrollHeight: sh
65 | };
66 | };
67 | this.getoffset=function(){
68 | var el=arguments[0];
69 | var rect = el.getBoundingClientRect();
70 | var includescroll=(arguments.length==1 || arguments[1]!==false);
71 | var l,t,w, h,scrollLeft=0,scrollTop=0,isbody=false,sw,sh,sbw,sbh;
72 | if (el == document.body) {
73 | scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft;
74 | scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
75 | isbody = true;
76 | l = (includescroll==true?scrollLeft:0);//scrollLeft//
77 | t= (includescroll==true?scrollTop:0) ;//scrollTop//
78 | w = document.body.clientWidth || document.documentElement.clientWidth || window.innerWidth ;//don't change this order
79 | h = document.body.clientHeight || document.documentElement.clientHeight || window.innerHeight; //don't change this order
80 | sw = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
81 | sh = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
82 | sbw = (document.documentElement.clientWidth - document.body.offsetWidth); //16
83 | sbh = (document.documentElement.clientHeight - document.body.offsetHeight); //16
84 | } else {
85 | scrollLeft = el.parentNode.scrollLeft;
86 | scrollTop = el.parentNode.scrollTop;
87 | l=rect.left + (includescroll==true?scrollLeft:0);
88 | t=rect.top + (includescroll==true?scrollTop:0) ;
89 | w=rect.right - rect.left;
90 | h = rect.bottom - rect.top;
91 | sw = el.parentNode.scrollWidth;
92 | sh = el.parentNode.scrollHeight;
93 | sbw = el.parentNode.offsetWidth - el.parentNode.clientWidth; //17
94 | sbh = el.parentNode.offsetHeight - el.parentNode.clientHeight; //17
95 | }
96 | return { left: l, top: t
97 | , width: w, height: h
98 | , offsetLeft: (isbody == true ?0:rect.left), offsetTop:(isbody == true ?0: rect.top)
99 | , right: l + w
100 | , bottom: t + h
101 | , scrollLeft: scrollLeft
102 | , scrollTop: scrollTop
103 | , isBody: isbody
104 | , scrollWidth: sw
105 | , scrollHeight: sh
106 | , scrollBarWidth: sbw
107 | , scrollBarHeight: sbh
108 | };
109 | };
110 | this.rectoverlap = function (target, drgpos, xpos, ypos) {
111 | var targetpos = {};
112 | if (target.getBoundingClientRect) {
113 | targetpos = target.getBoundingClientRect();
114 | }
115 | var x_overlap = Math.max(0, Math.min(drgpos.right, targetpos.right) - Math.max(drgpos.left, targetpos.left))
116 | var y_overlap = Math.max(0, Math.min(drgpos.bottom, targetpos.bottom) - Math.max(drgpos.top, targetpos.top));
117 | var lp = Math.floor(x_overlap / Math.min((drgpos.right - drgpos.left), (targetpos.right - targetpos.left)) * 100);
118 | var tp = Math.floor(y_overlap / Math.min((drgpos.bottom - drgpos.top), (targetpos.bottom - targetpos.top)) * 100);
119 | if (lp > 0 && tp > 0) {
120 | // if (this.intersectRect(drgpos, targetpos) == true) {
121 | return { target: target, lp: lp, tp: tp };
122 | }
123 | return null;
124 | }; /*rectoverlap*/
125 | this.hittest = function (target, x, y ) {
126 | var offset=this.getposition(target);
127 | x+=offset.scrollLeft;
128 | y+=offset.scrollTop;
129 | //info.innerHTML=scrollLeft+'x'+scrollTop;
130 | //info.innerHTML+=' '+JSON.stringify(offset);
131 | //info.innerHTML+=' '+x+'x'+y;
132 | if ((x > offset.left && y > offset.top) && (x < offset.right && y < offset.bottom)) {
133 | var lp = Math.floor(Math.min(x-offset.left,offset.right-x )*2/ offset.width * 100);
134 | var tp = Math.floor(Math.min(y-offset.top,offset.bottom-y )*2 / offset.height * 100);
135 | //info.innerHTML+=' '+lp+'x'+tp;
136 | return { target: target, lp: lp, tp: tp };
137 | }
138 | return null;
139 | }; /*hittest*/
140 | this.getevent = function (e) {
141 | e = e || window.event;
142 | // if (e.originalEvent && e.originalEvent.touches) {
143 | // var oe = e.originalEvent;
144 | // e = oe.touches.length ? oe.touches[0] : oe.changedTouches[0];
145 | // }
146 | if (e.touches) {
147 | e = e.touches[0];
148 | }
149 | };
150 | this.scrollLeft = function (el_s, left, top) {
151 | if (el_s == document.body || el_s == null) {
152 | if (left == undefined) {
153 | //return (window.pageXOffset !== undefined) ? window.pageXOffset : (document.body || document.documentElement || document.body.parentNode).scrollLeft;
154 | return window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft;
155 | } else {
156 | document.body.scrollLeft = left; //Chrome
157 | document.documentElement.scrollLeft = left; //IE8
158 | // window.scrollTo(left, top); //All
159 | }
160 | } else {
161 | if (left == undefined) {
162 | return el_s.scrollLeft;
163 | } else {
164 | el_s.scrollLeft = left;
165 | }
166 | }
167 | };
168 | this.scrollTop = function (el_s, top, left) {
169 | if (el_s == document.body || el_s == null) {
170 | if (top == undefined) {
171 | //return (window.pageYOffset !== undefined) ? window.pageYOffset : (document.body || document.documentElement || document.body.parentNode).scrollTop;
172 | return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
173 | } else {
174 | document.body.scrollTop = top; //Chrome
175 | document.documentElement.scrollTop = top; //IE8
176 | // window.scrollTo(left, top); //All
177 | }
178 | } else {
179 | if (top == undefined) {
180 | return el_s.scrollTop;
181 | } else {
182 | el_s.scrollTop = top;
183 | }
184 | }
185 | };
186 | this.extend = function extend(obj, src) {
187 | for (var key in src) {
188 | try {
189 | // if (src.hasOwnProperty(key)) obj[key] = src[key];
190 | obj[key] = src[key];
191 | } catch (e) { }
192 | }
193 | return obj;
194 | };
195 | this.autoscroll = function (offset, poffset, parentNode) {
196 | //info.innerHTML=JSON.stringify(offset);
197 | //info.innerHTML+=' '+JSON.stringify(poffset);
198 | // var xb = this.xcell;
199 | // var yb = this.ycell;
200 | var xb = 0;
201 | var yb = 0;
202 | // var xb = -this.xcell;
203 | // var yb = -this.ycell;
204 | if (poffset.isBody == true) {
205 | var scrollLeft = poffset.scrollLeft;
206 | var scrollTop = poffset.scrollTop;
207 | //var scrollbarwidth = window.pageXOffset ? (document.documentElement.scrollWidth - document.body.clientWidth) : (document.documentElement.clientWidth - document.body.offsetWidth); //Chrome & IE 16
208 | // info.innerHTML = ' ' + (document.documentElement.scrollWidth - document.body.clientWidth);
209 | // info.innerHTML += ' ' + (document.documentElement.clientWidth - document.body.offsetWidth);//IE
210 | var scrollbarwidth = (document.documentElement.clientWidth - document.body.offsetWidth); //All
211 | var scrollspeed = (offset.right + xb) - (poffset.right + scrollbarwidth);
212 | //info.innerHTML+=' '+scrollspeed;
213 | if (scrollspeed > 0) {
214 | this.scrollLeft(parentNode, scrollLeft + scrollspeed);
215 | }
216 | scrollspeed = offset.left - (xb);
217 | if (scrollspeed < 0) {
218 | this.scrollLeft(parentNode, scrollLeft + scrollspeed);
219 | }
220 | scrollspeed = (offset.bottom + yb) - (poffset.bottom);
221 | //info.innerHTML+=' '+scrollspeed;
222 | if (scrollspeed > 0) {
223 | this.scrollTop(parentNode, scrollTop + scrollspeed);
224 | }
225 | scrollspeed = offset.top - (yb);
226 | if (scrollspeed < 0) {
227 | this.scrollTop(parentNode, scrollTop + scrollspeed);
228 | }
229 | } else {
230 | var scrollLeft = offset.scrollLeft;
231 | var scrollTop = offset.scrollTop;
232 | var scrollbarwidth = parentNode.offsetWidth - parentNode.clientWidth; //17
233 | var scrollbarheight = parentNode.offsetHeight - parentNode.clientHeight; //17
234 | var scrollspeed = (offset.right + xb) - (poffset.right - scrollbarwidth);
235 | //info.innerHTML+=' '+scrollspeed;
236 | if (scrollspeed > 0) {
237 | this.scrollLeft(parentNode, scrollLeft + scrollspeed);
238 | }
239 | scrollspeed = offset.left - (xb + poffset.left);
240 | if (scrollspeed < 0) {
241 | this.scrollLeft(parentNode, scrollLeft + scrollspeed);
242 | }
243 | scrollspeed = (offset.bottom + scrollbarheight + yb) - (poffset.bottom);
244 | //info.innerHTML+=' '+scrollspeed;
245 | if (scrollspeed > 0) {
246 | this.scrollTop(parentNode, scrollTop + scrollspeed);
247 | }
248 | scrollspeed = offset.top - (yb + poffset.top);
249 | if (scrollspeed < 0) {
250 | this.scrollTop(parentNode, scrollTop + scrollspeed);
251 | }
252 | }
253 | };
254 |
255 | this.el = el_drg;
256 | this.dragstarted = false;
257 | this.dragmoved = false;
258 | this.droppables = [];
259 | this.dropinfo = null;
260 | this.helper = null;
261 | this.xcell = 1;//snap to grid x must be >=1
262 | this.ycell = 1; //snap to grid y must be >=1
263 | this.parentNode = null;
264 | this.forceobjectmove = false;
265 | this.dragforce = 1;
266 | this.restricttoscrollview = true;
267 | this.restrictlimit = null;
268 | this.draghandle = null;
269 | this.axis = 'both'; //'both,x,y
270 | this.keyboardcontrol = false;
271 | this.keySpeed = 10;
272 | this.keys_on = false;
273 | this.start = function (e) {
274 | e = e || window.event;
275 | var relTarg = e.relatedTarget || e.fromElement;
276 | var relTarg = e.relatedTarget || e.toElement;
277 | this.mx = e.clientX;
278 | this.my = e.clientY;
279 | // e.preventDefault();
280 | (e.preventDefault) ? e.preventDefault() : e.returnValue = false;
281 | if (this.dragstarted == true) {
282 | return this;
283 | }
284 | this.dragstarted = true;
285 | this.dragmoved = false;
286 | document.onselectstart = function (e) {
287 | return false;
288 | };
289 | if (this.mousedown) {
290 | this.mousedown(e);
291 | }
292 | this.kx = 0;
293 | this.ky = 0;
294 | if (this.keyboardcontrol == true) {
295 | // this.starte = e;
296 | this.cur_e = this.extend({}, e);
297 | this.starte = null;
298 | // this.cur_e = null;
299 | this.canmove = true;
300 | this.keys_on = false;
301 | this.l_l = true;
302 | this.w_l = true;
303 | this.t_l = true;
304 | this.h_l = true;
305 | this.el.focus();
306 | }
307 | return false;
308 | };
309 | this.move = function (e) {
310 | e = e || window.event;
311 | //if (this.dragstarted == true) {
312 | // this.el.innerHTML = 'move ' + e.clientX;
313 | //}
314 | if (this.keys_on == true) {
315 | if (!e.keys_on) {
316 | return;
317 | }
318 | }
319 | if (this.dragstarted == true && (this.dragmoved == false && !e.keys_on ? (Math.abs(this.mx - e.clientX) > this.dragforce || Math.abs(this.my - e.clientY) > this.dragforce) : true)) {
320 | // this.kx = e.clientX;
321 | // this.ky = e.clientY;
322 | // e.preventDefault();
323 | if (!e.keys_on) {
324 | (e.preventDefault) ? e.preventDefault() : e.returnValue = false;
325 | }
326 | if (this.dragmoved == false) {
327 | var offset = {};
328 | var parentNode;
329 | var eld;
330 | if (this.helper) {
331 | eld = this.helper;
332 | if (this.helper.style.left == '') {
333 | offset = this.getposition(this.el);
334 | if (this.helper.parentNode != document.body) {
335 | var poffset = this.helper.parentNode.getBoundingClientRect();
336 | offset.left -= poffset.left + offset.scrollLeft;
337 | offset.top -= poffset.top + offset.scrollTop;
338 | }
339 | var scrollLeft = this.scrollLeft(this.el.parentNode);
340 | var scrollTop = this.scrollTop(this.el.parentNode);
341 | offset.left += scrollLeft;
342 | offset.top += scrollTop;
343 | this.helper.style.left = (offset.left) + 'px'; //+this.scrollLeft(this.el.parentNode)
344 | this.helper.style.top = (offset.top) + 'px'; //+this.scrollTop(this.el.parentNode)
345 | }
346 | this.helper.style.display = '';
347 | } else {
348 | eld = this.el;
349 | }
350 | if (this.draghandle) {
351 | eld = this.draghandle;
352 | }
353 | parentNode = eld.parentNode;
354 | offset = eld.getBoundingClientRect();
355 | if (this.parentNode) {
356 | parentNode = this.parentNode;
357 | }
358 | var ol = 0, ot = 0;
359 | this.parents = [];
360 | if (parentNode != document.body) {
361 | var poffset = parentNode.getBoundingClientRect();
362 | ol = poffset.left;
363 | ot = poffset.top;
364 | var obj = parentNode;
365 | while (obj.offsetParent) {
366 | this.parents.push(obj);
367 | if (obj == document.body) {
368 | break;
369 | }
370 | obj = obj.offsetParent;
371 | }
372 | // if (obj == document.body) {
373 | // this.parents.push(obj);
374 | // }
375 | }
376 | var scrollLeft = this.scrollLeft(parentNode);
377 | var scrollTop = this.scrollTop(parentNode);
378 | this.initialMouseX = scrollLeft + e.clientX;
379 | this.initialMouseY = scrollTop + e.clientY;
380 | this.startX = offset.left + scrollLeft;
381 | this.startY = offset.top + scrollTop;
382 | this.ol = ol;
383 | this.ot = ot;
384 | var dx = this.startX - this.ol + scrollLeft + e.clientX - this.initialMouseX;
385 | var dy = this.startY - this.ot + scrollTop + e.clientY - this.initialMouseY;
386 | dx = Math.floor(dx / this.xcell) * this.xcell;
387 | dy = Math.floor(dy / this.ycell) * this.ycell;
388 | if (parentNode == document.body) {
389 | this.sw = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
390 | this.sh = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
391 | } else {
392 | this.sw = parentNode.scrollWidth;
393 | this.sh = parentNode.scrollHeight;
394 | }
395 | this.dx = dx;
396 | this.dy = dy;
397 | if (this.beforedrag) {
398 | if (this.beforedrag(dx, dy, scrollLeft + e.clientX, scrollTop + e.clientY, e) === false) {
399 | this.dragmoved = true;
400 | this.end(e);
401 | return false;
402 | }
403 | }
404 | if (this.keyboardcontrol == true) {
405 | this.starte = e;
406 | this.cur_e = this.extend({}, this.starte);
407 | }
408 | } //this.dragmoved==false
409 | this.dragmoved = true;
410 | var parentNode;
411 | var eld;
412 | if (this.helper) {
413 | eld = this.helper;
414 | } else {
415 | eld = this.el;
416 | }
417 | if (this.draghandle) {
418 | eld = this.draghandle;
419 | }
420 | parentNode = eld.parentNode;
421 | if (this.parentNode) {
422 | parentNode = this.parentNode;
423 | }
424 | var offset = this.getoffset(eld, false);
425 | var poffset = this.getoffset(parentNode, false);
426 | for (var i = 0; i < this.parents.length; i++) {
427 | var o1 = this.getoffset(this.parents[i], false);
428 | var o2 = this.getoffset(this.parents[i].parentNode, false);
429 | o1.left = offset.left;
430 | o1.right = offset.right;
431 | o1.top = offset.top;
432 | o1.bottom = offset.bottom;
433 | var scrollspeed = (o1.right + this.xcell) - (o2.right - 16);
434 | this.autoscroll(o1, o2, this.parents[i].parentNode);
435 | }
436 | if (this.parents.length > 0) {
437 | //offset = this.getoffset(eld,false);
438 | //poffset = this.getoffset(parentNode,false);
439 | if (parentNode != document.body) {
440 | var poffset = parentNode.getBoundingClientRect();
441 | this.ol = poffset.left;
442 | this.ot = poffset.top;
443 | }
444 | }
445 | var scrollLeft = this.scrollLeft(parentNode);
446 | var scrollTop = this.scrollTop(parentNode);
447 | var dx = this.startX - this.ol + scrollLeft + e.clientX - this.initialMouseX;
448 | var dy = this.startY - this.ot + scrollTop + e.clientY - this.initialMouseY;
449 | dx = Math.floor(dx / this.xcell) * this.xcell;
450 | dy = Math.floor(dy / this.ycell) * this.ycell;
451 | var b = true;
452 | this.l_l = false;
453 | this.w_l = false;
454 | this.t_l = false;
455 | this.h_l = false;
456 | if (this.restricttoscrollview == true) {
457 | //info.innerHTML = (dx + offset.width - this.xcell) + 'x' + (this.sw);
458 | if ((dx + offset.width - this.xcell) > (this.sw)) {
459 | // b = false;
460 | dx = this.sw - offset.width;
461 | // this.kx -= (this.keySpeed*1);
462 | this.w_l = true;
463 | }
464 | if (dx < 0) {
465 | // b = false;
466 | dx = 0;
467 | // this.kx += (this.keySpeed * 1);
468 | this.l_l = true;
469 | }
470 | if ((dy + offset.height - this.ycell) > (this.sh)) {
471 | // b = false;
472 | dy = this.sh - offset.height;
473 | // this.ky -= (this.keySpeed * 1);
474 | this.h_l = true;
475 | }
476 | if (dy < 0) {
477 | // b = false;
478 | dy = 0;
479 | // this.ky += (this.keySpeed * 1);
480 | this.t_l = true;
481 | }
482 | }
483 | if (this.restrictlimit) {
484 | if ((dx + offset.width - this.xcell) > (this.restrictlimit.width)) {
485 | dx = this.restrictlimit.width - offset.width;
486 | // this.kx -= (this.keySpeed * 2);
487 | this.w_l = true;
488 | }
489 | if (dx < this.restrictlimit.left) {
490 | dx = this.restrictlimit.left;
491 | // this.kx += (this.keySpeed * 2);
492 | this.l_l = true;
493 | }
494 | if ((dy + offset.height - this.ycell) > (this.restrictlimit.height)) {
495 | dy = this.restrictlimit.height - offset.height;
496 | // this.ky -= (this.keySpeed * 2);
497 | this.h_l = true;
498 | }
499 | if (dy < this.restrictlimit.top) {
500 | dy = this.restrictlimit.top;
501 | // this.ky += (this.keySpeed * 2);
502 | this.t_l = true;
503 | }
504 | }
505 | if (this.keyboardcontrol == true) {
506 | // this.kx = this.startX - this.ol + scrollLeft + e.clientX - this.initialMouseX;
507 | // this.ky = this.startY - this.ot + scrollTop + e.clientY - this.initialMouseY;
508 | // info.innerHTML = e.clientX + 'x' + (dx - (this.startX - this.ol + scrollLeft - this.initialMouseX));
509 | // this.kx = dx - (this.startX + this.ol + scrollLeft - this.initialMouseX);
510 | // this.ky = dy - (this.startY + this.ot + scrollTop - this.initialMouseY);
511 | // this.kx = dx - (this.startX - this.initialMouseX+ this.ol + scrollLeft);
512 | // this.ky = dy - (this.startY - this.initialMouseY+ this.ot + scrollTop);
513 | // if (this.kx < (poffset.left - (this.starte.clientX))) {
514 | // this.kx = (poffset.left - (this.starte.clientX));
515 | // }
516 | this.cur_e.clientX=(dx - (this.startX - this.ol + scrollLeft - this.initialMouseX));
517 | }
518 | if (b == true) {
519 | if (this.axis == 'both' || this.axis == 'x') {
520 | eld.style.left = dx + 'px';
521 | }
522 | if (this.axis == 'both' || this.axis == 'y') {
523 | eld.style.top = dy + 'px';
524 | }
525 | this.autoscroll(offset, poffset, parentNode);
526 | this.dx = dx;
527 | this.dy = dy;
528 | this.dropinfo = null;
529 | for (var n = this.droppables.length - 1; n >= 0; n--) {
530 | // this.droppables[n].style.border = '1px solid black';
531 | if (this.droppables[n] != this.el) {
532 | //var x=dx+this.initialMouseX-this.startX-this.ol;
533 | //var y=dy+this.initialMouseY-this.startY-this.ot;
534 | var x = e.clientX;
535 | var y = e.clientY;
536 | this.dropinfo = this.hittest(this.droppables[n], x, y);
537 | if (this.dropinfo !== null) {
538 | // dropped.target.style.border = '1px solid red';
539 | break;
540 | }
541 | }
542 | }
543 | if (this.dragover) {
544 | this.dragover(this.dropinfo, this.el, dx, dy, scrollLeft + e.clientX, scrollTop + e.clientY, e);
545 | }
546 | }
547 | }
548 | };
549 | this.end = function (e) {
550 | e = e || window.event;
551 | //this.el.innerHTML = 'end ' + e.clientX;
552 | if (this.dragstarted == true) {
553 | this.dragstarted = false;
554 | document.onselectstart = null;
555 | if (this.dragmoved == true) {
556 | (e.preventDefault) ? e.preventDefault() : e.returnValue = false;
557 | e.cancelBubble = true;
558 | if (e.stopPropagation) e.stopPropagation();
559 | if (this.helper) {
560 | if (this.forceobjectmove == true && this.dragmoved == true) {
561 | this.el.style.left = this.dx + 'px';
562 | this.el.style.top = this.dy + 'px';
563 | }
564 | this.helper.style.display = 'none';
565 | }
566 | if (this.dragdrop) {
567 | this.dragdrop(this.dropinfo, this.el, this.dx, this.dy, e);
568 | }
569 | this.dragmoved = false;
570 | this.canmove = false;
571 | return false;
572 | } else {
573 | if (this.click) {
574 | this.click(e);
575 | }
576 | }
577 | }
578 | if (this.mouseup) {
579 | this.mouseup(e);
580 | }
581 | };
582 | this.keycontrol = function (e) {
583 | e = e || window.event;
584 | if (this.canmove !== true) {
585 | return true;
586 | }
587 | this.dragstarted = true;
588 | this.keys_on = true;
589 | if (this.starte) {
590 | var key = e.keyCode;
591 | switch (key) {
592 | case 37: case 63234: // left
593 | if (this.l_l == false) {
594 | // this.kx -= ;
595 | this.cur_e.clientX -= this.keySpeed ;
596 | }
597 | break;
598 | case 39: case 63235: // right
599 | if (this.w_l == false) {
600 | // this.kx += this.keySpeed;
601 | this.cur_e.clientX += this.keySpeed ;
602 | }
603 | break;
604 | case 38: case 63232: // up
605 | if (this.t_l == false) {
606 | // this.ky -= this.keySpeed;
607 | this.cur_e.clientY -= this.keySpeed ;
608 | }
609 | break;
610 | case 40: case 63233: // down
611 | if (this.h_l == false) {
612 | // this.ky += this.keySpeed;
613 | this.cur_e.clientY += this.keySpeed ;
614 | }
615 | break;
616 | case 13: case 27:
617 | this.end(e);
618 | return false;
619 | default:
620 | return true;
621 | }
622 | // var ne = this.extend({}, this.starte);
623 | // ne.keys_on = true;
624 | // ne.clientX += this.kx;
625 | // ne.clientY += this.ky;
626 | // this.cur_e.clientX = this.starte.clientX + this.kx;
627 | // this.cur_e.clientY = this.starte.clientY + this.ky;
628 | }
629 | this.cur_e.keys_on = true;
630 | this.move(this.cur_e);
631 | (e.preventDefault) ? e.preventDefault() : e.returnValue = false;
632 | };
633 | this.bindevents = function (b) {
634 | var fn;
635 | if (b == true) {
636 | fn = this.addEventSimple;
637 | } else {
638 | fn = this.removeEventSimple;
639 | }
640 | var _this = this;
641 | fn(this.el, 'mousedown', function (e) {
642 | return _this.start(e);
643 | });
644 | fn(this.el, 'mousemove', function (e) {
645 | return _this.move(e);
646 | });
647 | fn(this.el, 'mouseup', function (e) {
648 | return _this.end(e);
649 | });
650 | fn(document, 'mousemove', function (e) {
651 | return _this.move(e);
652 | });
653 | fn(document, 'mouseup', function (e) {
654 | return _this.end(e);
655 | });
656 | fn(document, 'click', function (e) {
657 | return _this.end(e);
658 | });
659 | this.addEventSimple(this.el, 'selectstart', function (e) {
660 | return false;
661 | });
662 | if (this.el.addEventListener) {
663 | this.el.addEventListener('touchstart', function (e) {
664 | //_this.el.innerHTML = 'start ' + e.touches[0].clientX;
665 | e.preventDefault();
666 | _this.start(e.touches[0]);
667 | });
668 | this.el.addEventListener('touchmove', function (e) {
669 | //_this.el.innerHTML = 'move ' + e.touches[0].clientX;
670 | e.preventDefault();
671 | _this.move(e.touches[0]);
672 | });
673 | this.el.addEventListener('touchend', function (e) {
674 | //_this.el.innerHTML = 'end ' + e.touches[0].clientX;
675 | e.stopPropagation();
676 | e.preventDefault();
677 | _this.end(e.touches[0]);
678 | });
679 | document.addEventListener('touchmove', function (e) {
680 | e.preventDefault();
681 | _this.move(e.touches[0]);
682 | });
683 | document.addEventListener('touchend', function (e) {
684 | e.stopPropagation();
685 | e.preventDefault();
686 | _this.end(e.touches[0]);
687 | });
688 | }
689 | if (this.keyboardcontrol == true) {
690 | var tabIndex = this.el.getAttribute('tabIndex');
691 | if (!tabIndex) {
692 | tabIndex = 0;
693 | }
694 | if (b == true) {
695 | this.el.setAttribute('tabIndex', 0);
696 | } else {
697 | if (tabIndex == 0) {
698 | this.el.removeAttribute('tabIndex');
699 | }
700 | }
701 | fn(this.el, 'keydown', function (e) {
702 | return _this.keycontrol(e);
703 | });
704 | }
705 | };
706 | this.attach=function(){
707 | this.bindevents(true);
708 | };
709 | this.detach=function(){
710 | this.bindevents(false);
711 | };
712 | this.reattach=function(){
713 | this.bindevents(false);
714 | this.bindevents(true);
715 | };
716 | this.attach();
717 | return this;
718 | } /*drag_auto_scroll*/
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Title
8 |
9 |
13 |
14 |
54 |
55 |
56 |
57 | Add "Default" board
58 |
59 | Add element in "To Do" Board
60 |
61 | Add element in "To Do" Board at position 2
62 |
63 | Remove "Done" Board
64 |
65 | Remove "My Task Test"
66 |
67 |
68 |
226 |
227 |
228 |
--------------------------------------------------------------------------------
/jest-unit-config.js:
--------------------------------------------------------------------------------
1 | const config = require('./jest.config')
2 | config.testMatch = ['**/*.spec.js']
3 | module.exports = config
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | coverageDirectory: 'coverage',
3 | testEnvironment: 'node',
4 | collectCoverageFrom: ['jkanban.js']
5 | }
--------------------------------------------------------------------------------
/jkanban.css:
--------------------------------------------------------------------------------
1 | .kanban-container {
2 | position: relative;
3 | box-sizing: border-box;
4 | width: auto;
5 | }
6 |
7 | .kanban-container * {
8 | box-sizing: border-box;
9 | }
10 |
11 | .kanban-container:after {
12 | clear: both;
13 | display: block;
14 | content: "";
15 | }
16 |
17 | .kanban-board {
18 | position: relative;
19 | float: left;
20 | background: #e2e4e6;
21 | transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
22 | }
23 |
24 | .kanban-board.disabled-board {
25 | opacity: 0.3;
26 | }
27 |
28 | .kanban-board.is-moving.gu-mirror {
29 | transform: rotate(3deg);
30 | }
31 |
32 | .kanban-board.is-moving.gu-mirror .kanban-drag {
33 | overflow: hidden;
34 | padding-right: 50px;
35 | }
36 |
37 | .kanban-board header {
38 | font-size: 16px;
39 | padding: 15px;
40 | }
41 |
42 | .kanban-board header .kanban-title-board {
43 | font-weight: 700;
44 | margin: 0;
45 | padding: 0;
46 | display: inline;
47 | }
48 |
49 | .kanban-board header .kanban-title-button {
50 | float: right;
51 | }
52 |
53 | .kanban-board .kanban-drag {
54 | min-height: 200px;
55 | padding: 20px;
56 | }
57 |
58 | .kanban-board:after {
59 | clear: both;
60 | display: block;
61 | content: "";
62 | }
63 |
64 | .kanban-item {
65 | background: #fff;
66 | padding: 15px;
67 | margin-bottom: 20px;
68 | transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
69 | animation: append-animate 0.3s cubic-bezier(0.23, 1, 0.32, 1);
70 | }
71 |
72 | @keyframes append-animate {
73 | from {
74 | transform: translateY(-20px);
75 | }
76 | to {
77 | transform: translateY(0px);
78 | }
79 | }
80 |
81 | .kanban-item:hover {
82 | cursor: move;
83 | }
84 |
85 | .kanban-item:last-child {
86 | margin: 0;
87 | }
88 |
89 | .kanban-item.is-moving.gu-mirror {
90 | transform: rotate(3deg);
91 | height: auto !important;
92 | }
93 |
94 | /* Dragula CSS */
95 | .gu-mirror {
96 | position: fixed !important;
97 | margin: 0 !important;
98 | z-index: 9999 !important;
99 | }
100 |
101 | .gu-hide {
102 | display: none !important;
103 | }
104 |
105 | .gu-unselectable {
106 | -webkit-user-select: none !important;
107 | -moz-user-select: none !important;
108 | -ms-user-select: none !important;
109 | user-select: none !important;
110 | }
111 |
112 | .gu-transit {
113 | opacity: 0.2 !important;
114 | transform: rotate(0deg) !important;
115 | }
116 |
117 | .drag_handler {
118 | background: #fff;
119 | border-radius: 50%;
120 | width: 24px;
121 | height: 24px;
122 | position: relative;
123 | float: left;
124 | top: -3px;
125 | margin-right: 4px;
126 | }
127 |
128 | .drag_handler:hover {
129 | cursor: move;
130 | }
131 |
132 | .drag_handler_icon {
133 | position: relative;
134 | display: block;
135 | background: #000;
136 | width: 24px;
137 | height: 2px;
138 | top: 12px;
139 | transition: .5s ease-in-out;
140 | }
141 |
142 | .drag_handler_icon:before,
143 | .drag_handler_icon:after {
144 | background: #000;
145 | content: '';
146 | display: block;
147 | width: 100%;
148 | height: 100%;
149 | position: absolute;
150 | transition: .5s ease-in-out;
151 | }
152 |
153 | .drag_handler_icon:before {
154 | top: 6px;
155 | }
156 |
157 | .drag_handler_icon:after {
158 | bottom: 6px;
159 | }
--------------------------------------------------------------------------------
/jkanban.js:
--------------------------------------------------------------------------------
1 | /**
2 | * jKanban
3 | * Vanilla Javascript plugin for manage kanban boards
4 | *
5 | * @site: http://www.riccardotartaglia.it/jkanban/
6 | * @author: Riccardo Tartaglia
7 | */
8 |
9 | //Require dragula
10 | var dragula = require('dragula');
11 |
12 | (function () {
13 | this.jKanban = function () {
14 | var self = this
15 | var __DEFAULT_ITEM_HANDLE_OPTIONS = {
16 | enabled: false
17 | }
18 | var __DEFAULT_ITEM_ADD_OPTIONS = {
19 | enabled: false
20 | }
21 | this._disallowedItemProperties = [
22 | 'id',
23 | 'title',
24 | 'click',
25 | 'context',
26 | 'drag',
27 | 'dragend',
28 | 'drop',
29 | 'order'
30 | ]
31 | this.element = ''
32 | this.container = ''
33 | this.boardContainer = []
34 | this.handlers = []
35 | this.dragula = dragula
36 | this.drake = ''
37 | this.drakeBoard = ''
38 | this.itemAddOptions = __DEFAULT_ITEM_ADD_OPTIONS
39 | this.itemHandleOptions = __DEFAULT_ITEM_HANDLE_OPTIONS
40 | var defaults = {
41 | element: '',
42 | gutter: '15px',
43 | widthBoard: '250px',
44 | responsive: '700',
45 | responsivePercentage: false,
46 | boards: [],
47 | dragBoards: true,
48 | dragItems: true, //whether can drag cards or not, useful when set permissions on it.
49 | itemAddOptions: __DEFAULT_ITEM_ADD_OPTIONS,
50 | itemHandleOptions: __DEFAULT_ITEM_HANDLE_OPTIONS,
51 | dragEl: function (el, source) {},
52 | dragendEl: function (el) {},
53 | dropEl: function (el, target, source, sibling) {},
54 | dragBoard: function (el, source) {},
55 | dragendBoard: function (el) {},
56 | dropBoard: function (el, target, source, sibling) {},
57 | click: function (el) {},
58 | context: function (el, e) {},
59 | buttonClick: function (el, boardId) {},
60 | propagationHandlers: [],
61 | }
62 |
63 | if (arguments[0] && typeof arguments[0] === 'object') {
64 | this.options = __extendDefaults(defaults, arguments[0])
65 | }
66 |
67 | this.__getCanMove = function (handle) {
68 | if (!self.options.itemHandleOptions.enabled) {
69 | return !!self.options.dragItems
70 | }
71 |
72 | if (self.options.itemHandleOptions.handleClass) {
73 | return handle.classList.contains(self.options.itemHandleOptions.handleClass)
74 | }
75 |
76 | return handle.classList.contains('item_handle')
77 | }
78 |
79 | this.init = function () {
80 | //set initial boards
81 | __setBoard()
82 | //set drag with dragula
83 | if (window.innerWidth > self.options.responsive) {
84 | //Init Drag Board
85 | self.drakeBoard = self
86 | .dragula([self.container], {
87 | moves: function (el, source, handle, sibling) {
88 | if (!self.options.dragBoards) return false
89 | return (
90 | handle.classList.contains('kanban-board-header') ||
91 | handle.classList.contains('kanban-title-board')
92 | )
93 | },
94 | accepts: function (el, target, source, sibling) {
95 | return target.classList.contains('kanban-container')
96 | },
97 | revertOnSpill: true,
98 | direction: 'horizontal'
99 | })
100 | .on('drag', function (el, source) {
101 | el.classList.add('is-moving')
102 | self.options.dragBoard(el, source)
103 | if (typeof el.dragfn === 'function') el.dragfn(el, source)
104 | })
105 | .on('dragend', function (el) {
106 | __updateBoardsOrder()
107 | el.classList.remove('is-moving')
108 | self.options.dragendBoard(el)
109 | if (typeof el.dragendfn === 'function') el.dragendfn(el)
110 | })
111 | .on('drop', function (el, target, source, sibling) {
112 | el.classList.remove('is-moving')
113 | self.options.dropBoard(el, target, source, sibling)
114 | if (typeof el.dropfn === 'function')
115 | el.dropfn(el, target, source, sibling)
116 | })
117 |
118 | //Init Drag Item
119 | self.drake = self
120 | .dragula(self.boardContainer, {
121 | moves: function (el, source, handle, sibling) {
122 | return self.__getCanMove(handle)
123 | },
124 | revertOnSpill: true
125 | })
126 | .on('cancel', function (el, container, source) {
127 | self.enableAllBoards()
128 | })
129 | .on('drag', function (el, source) {
130 | var elClass = el.getAttribute('class')
131 | if (elClass !== '' && elClass.indexOf('not-draggable') > -1) {
132 | self.drake.cancel(true)
133 | return
134 | }
135 |
136 | el.classList.add('is-moving')
137 |
138 | self.options.dragEl(el, source)
139 |
140 | var boardJSON = __findBoardJSON(source.parentNode.dataset.id)
141 | if (boardJSON.dragTo !== undefined) {
142 | self.options.boards.map(function (board) {
143 | if (
144 | boardJSON.dragTo.indexOf(board.id) === -1 &&
145 | board.id !== source.parentNode.dataset.id
146 | ) {
147 | self.findBoard(board.id).classList.add('disabled-board')
148 | }
149 | })
150 | }
151 |
152 | if (el !== null && typeof el.dragfn === 'function')
153 | el.dragfn(el, source)
154 | })
155 | .on('dragend', function (el) {
156 | self.options.dragendEl(el)
157 | if (el !== null && typeof el.dragendfn === 'function')
158 | el.dragendfn(el)
159 | })
160 | .on('drop', function (el, target, source, sibling) {
161 | self.enableAllBoards()
162 |
163 | var boardJSON = __findBoardJSON(source.parentNode.dataset.id)
164 | if (boardJSON.dragTo !== undefined) {
165 | if (
166 | boardJSON.dragTo.indexOf(target.parentNode.dataset.id) === -1 &&
167 | target.parentNode.dataset.id !== source.parentNode.dataset.id
168 | ) {
169 | self.drake.cancel(true)
170 | }
171 | }
172 | if (el !== null) {
173 | var result = self.options.dropEl(el, target, source, sibling)
174 | if (result === false) {
175 | self.drake.cancel(true)
176 | }
177 | el.classList.remove('is-moving')
178 | if (typeof el.dropfn === 'function')
179 | el.dropfn(el, target, source, sibling)
180 | }
181 | })
182 | }
183 | }
184 |
185 | this.enableAllBoards = function () {
186 | var allB = document.querySelectorAll('.kanban-board')
187 | if (allB.length > 0 && allB !== undefined) {
188 | for (var i = 0; i < allB.length; i++) {
189 | allB[i].classList.remove('disabled-board')
190 | }
191 | }
192 | }
193 |
194 | this.addElement = function (boardID, element, position) {
195 | if (typeof position === 'undefined') {
196 | position = -1
197 | }
198 | var board = self.element.querySelector(
199 | '[data-id="' + boardID + '"] .kanban-drag'
200 | )
201 | var refElement = board.childNodes[position]
202 | var nodeItem = document.createElement('div')
203 | nodeItem.classList.add('kanban-item')
204 | if (typeof element.id !== 'undefined' && element.id !== '') {
205 | nodeItem.setAttribute('data-eid', element.id)
206 | }
207 | if (element.class && Array.isArray(element.class)) {
208 | element.class.forEach(function (cl) {
209 | nodeItem.classList.add(cl)
210 | })
211 | }
212 | nodeItem.innerHTML = __buildItemCard(element)
213 | //add function
214 | nodeItem.clickfn = element.click
215 | nodeItem.contextfn = element.context;
216 | nodeItem.dragfn = element.drag
217 | nodeItem.dragendfn = element.dragend
218 | nodeItem.dropfn = element.drop
219 | __appendCustomProperties(nodeItem, element)
220 | __onclickHandler(nodeItem)
221 | __onContextHandler(nodeItem)
222 | if (self.options.itemHandleOptions.enabled) {
223 | nodeItem.style.cursor = 'default'
224 | }
225 | board.insertBefore(nodeItem, refElement)
226 | return self
227 | }
228 |
229 | this.addForm = function (boardID, formItem) {
230 | var board = self.element.querySelector(
231 | '[data-id="' + boardID + '"] .kanban-drag'
232 | )
233 | var _attribute = formItem.getAttribute('class')
234 | formItem.setAttribute('class', _attribute + ' not-draggable')
235 | board.appendChild(formItem)
236 | return self
237 | }
238 |
239 | this.addBoards = function (boards, isInit) {
240 | if (self.options.responsivePercentage) {
241 | self.container.style.width = '100%'
242 | self.options.gutter = '1%'
243 | if (window.innerWidth > self.options.responsive) {
244 | var boardWidth = (100 - boards.length * 2) / boards.length
245 | } else {
246 | var boardWidth = 100 - boards.length * 2
247 | }
248 | } else {
249 | var boardWidth = self.options.widthBoard
250 | }
251 | var addButton = self.options.itemAddOptions.enabled
252 | var buttonContent = self.options.itemAddOptions.content
253 | var buttonClass = self.options.itemAddOptions.class
254 | var buttonFooter = self.options.itemAddOptions.footer
255 |
256 | //for on all the boards
257 | for (var boardkey in boards) {
258 | // single board
259 | var board = boards[boardkey]
260 | if (!isInit) {
261 | self.options.boards.push(board)
262 | }
263 |
264 | if (!self.options.responsivePercentage) {
265 | //add width to container
266 | if (self.container.style.width === '') {
267 | self.container.style.width =
268 | parseInt(boardWidth) + parseInt(self.options.gutter) * 2 + 'px'
269 | } else {
270 | self.container.style.width =
271 | parseInt(self.container.style.width) +
272 | parseInt(boardWidth) +
273 | parseInt(self.options.gutter) * 2 +
274 | 'px'
275 | }
276 | }
277 | //create node
278 | var boardNode = document.createElement('div')
279 | boardNode.dataset.id = board.id
280 | boardNode.dataset.order = self.container.childNodes.length + 1
281 | boardNode.classList.add('kanban-board')
282 | //set style
283 | if (self.options.responsivePercentage) {
284 | boardNode.style.width = boardWidth + '%'
285 | } else {
286 | boardNode.style.width = boardWidth
287 | }
288 | boardNode.style.marginLeft = self.options.gutter
289 | boardNode.style.marginRight = self.options.gutter
290 | // header board
291 | var headerBoard = document.createElement('header')
292 | if (board.class !== '' && board.class !== undefined)
293 | var allClasses = board.class.split(',')
294 | else allClasses = []
295 | headerBoard.classList.add('kanban-board-header')
296 | allClasses.map(function (value) {
297 | // Remove empty spaces
298 | value = value.replace(/^[ ]+/g, '')
299 | headerBoard.classList.add(value)
300 | })
301 | headerBoard.innerHTML =
302 | '' + board.title + '
'
303 | //content board
304 | var contentBoard = document.createElement('main')
305 | contentBoard.classList.add('kanban-drag')
306 | if (board.bodyClass !== '' && board.bodyClass !== undefined)
307 | var bodyClasses = board.bodyClass.split(',')
308 | else bodyClasses = []
309 | bodyClasses.map(function (value) {
310 | contentBoard.classList.add(value)
311 | })
312 | //add drag to array for dragula
313 | self.boardContainer.push(contentBoard)
314 | for (var itemkey in board.item) {
315 | //create item
316 | var itemKanban = board.item[itemkey]
317 | var nodeItem = document.createElement('div')
318 | nodeItem.classList.add('kanban-item')
319 | if (itemKanban.id) {
320 | nodeItem.dataset.eid = itemKanban.id
321 | }
322 | if (itemKanban.class && Array.isArray(itemKanban.class)) {
323 | itemKanban.class.forEach(function (cl) {
324 | nodeItem.classList.add(cl)
325 | })
326 | }
327 | nodeItem.innerHTML = __buildItemCard(itemKanban)
328 | //add function
329 | nodeItem.clickfn = itemKanban.click
330 | nodeItem.contextfn = itemKanban.context
331 | nodeItem.dragfn = itemKanban.drag
332 | nodeItem.dragendfn = itemKanban.dragend
333 | nodeItem.dropfn = itemKanban.drop
334 | __appendCustomProperties(nodeItem, itemKanban)
335 | //add click handler of item
336 | __onclickHandler(nodeItem)
337 | __onContextHandler(nodeItem)
338 | if (self.options.itemHandleOptions.enabled) {
339 | nodeItem.style.cursor = 'default'
340 | }
341 | contentBoard.appendChild(nodeItem)
342 | }
343 | //footer board
344 | var footerBoard = document.createElement('footer')
345 | // if add button is true, add button to the board
346 | if (addButton) {
347 | var btn = document.createElement('BUTTON')
348 | var t = document.createTextNode(buttonContent ? buttonContent : '+')
349 | btn.setAttribute(
350 | 'class',
351 | buttonClass ? buttonClass : 'kanban-title-button btn btn-default btn-xs'
352 | )
353 | btn.appendChild(t)
354 | //var buttonHtml = ''+buttonContent+' '
355 | if (buttonFooter) {
356 | footerBoard.appendChild(btn)
357 | } else {
358 | headerBoard.appendChild(btn)
359 | }
360 | __onButtonClickHandler(btn, board.id)
361 | }
362 | //board assembly
363 | boardNode.appendChild(headerBoard)
364 | boardNode.appendChild(contentBoard)
365 | boardNode.appendChild(footerBoard)
366 | //board add
367 | self.container.appendChild(boardNode)
368 | }
369 | return self
370 | }
371 |
372 | this.findBoard = function (id) {
373 | var el = self.element.querySelector('[data-id="' + id + '"]')
374 | return el
375 | }
376 |
377 | this.getParentBoardID = function (el) {
378 | if (typeof el === 'string') {
379 | el = self.element.querySelector('[data-eid="' + el + '"]')
380 | }
381 | if (el === null) {
382 | return null
383 | }
384 | return el.parentNode.parentNode.dataset.id
385 | }
386 |
387 | this.moveElement = function (targetBoardID, elementID, element) {
388 | if (targetBoardID === this.getParentBoardID(elementID)) {
389 | return
390 | }
391 |
392 | this.removeElement(elementID)
393 | return this.addElement(targetBoardID, element)
394 | }
395 |
396 | this.replaceElement = function (el, element) {
397 | var nodeItem = el
398 | if (typeof nodeItem === 'string') {
399 | nodeItem = self.element.querySelector('[data-eid="' + el + '"]')
400 | }
401 | nodeItem.innerHTML = __buildItemCard(element)
402 | // add function
403 | nodeItem.clickfn = element.click
404 | nodeItem.contextfn = element.context
405 | nodeItem.dragfn = element.drag
406 | nodeItem.dragendfn = element.dragend
407 | nodeItem.dropfn = element.drop
408 | __appendCustomProperties(nodeItem, element)
409 | __onclickHandler(nodeItem)
410 | __onContextHandler(nodeItem)
411 | return self
412 | }
413 |
414 | this.findElement = function (id) {
415 | var el = self.element.querySelector('[data-eid="' + id + '"]')
416 | return el
417 | }
418 |
419 | this.getBoardElements = function (id) {
420 | var board = self.element.querySelector(
421 | '[data-id="' + id + '"] .kanban-drag'
422 | )
423 | return board.childNodes
424 | }
425 |
426 | this.removeElement = function (el) {
427 | if (typeof el === 'string')
428 | el = self.element.querySelector('[data-eid="' + el + '"]')
429 | if (el !== null) {
430 | //fallback for IE
431 | if (typeof el.remove == 'function') {
432 | el.remove()
433 | } else {
434 | el.parentNode.removeChild(el)
435 | }
436 | }
437 | return self
438 | }
439 |
440 | this.removeBoard = function (board) {
441 | var boardElement = null
442 | if (typeof board === 'string')
443 | boardElement = self.element.querySelector('[data-id="' + board + '"]')
444 | if (boardElement !== null) {
445 | //fallback for IE
446 | if (typeof boardElement.remove == 'function') {
447 | boardElement.remove()
448 | } else {
449 | boardElement.parentNode.removeChild(boardElement)
450 | }
451 | }
452 |
453 | // remove thboard in options.boards
454 | for (var i = 0; i < self.options.boards.length; i++) {
455 | if (self.options.boards[i].id === board) {
456 | self.options.boards.splice(i, 1)
457 | break
458 | }
459 | }
460 |
461 | return self
462 | }
463 |
464 | // board button on click function
465 | this.onButtonClick = function (el) {}
466 |
467 | //PRIVATE FUNCTION
468 | function __extendDefaults (source, properties) {
469 | var property
470 | for (property in properties) {
471 | if (properties.hasOwnProperty(property)) {
472 | source[property] = properties[property]
473 | }
474 | }
475 | return source
476 | }
477 |
478 | function __setBoard () {
479 | self.element = document.querySelector(self.options.element)
480 | //create container
481 | var boardContainer = document.createElement('div')
482 | boardContainer.classList.add('kanban-container')
483 | self.container = boardContainer
484 | //add boards
485 |
486 | if (document.querySelector(self.options.element).dataset.hasOwnProperty('board')) {
487 | url = document.querySelector(self.options.element).dataset.board
488 | window.fetch(url, {
489 | method: 'GET',
490 | headers: { 'Content-Type': 'application/json' }
491 | })
492 | .then(function (response) {
493 | // log response text
494 | response.json().then(function (data) {
495 | self.options.boards = data
496 | self.addBoards(self.options.boards, true)
497 | })
498 |
499 | })
500 | .catch(function (error) {
501 | console.log('Error: ', error)
502 | })
503 | } else {
504 | self.addBoards(self.options.boards, true)
505 | }
506 |
507 | //appends to container
508 | self.element.appendChild(self.container)
509 | }
510 |
511 | function __onclickHandler (nodeItem, clickfn) {
512 | nodeItem.addEventListener('click', function (e) {
513 | if (!self.options.propagationHandlers.includes('click')) e.preventDefault()
514 | self.options.click(this)
515 | if (typeof this.clickfn === 'function') this.clickfn(this)
516 | })
517 | }
518 |
519 | function __onContextHandler(nodeItem, contextfn) {
520 | if (nodeItem.addEventListener) {
521 | nodeItem.addEventListener('contextmenu', function (e) {
522 | if (!self.options.propagationHandlers.includes('context')) e.preventDefault()
523 | self.options.context(this, e)
524 | if (typeof this.contextfn === 'function') this.contextfn(this, e)
525 | }, false)
526 | } else {
527 | nodeItem.attachEvent('oncontextmenu', function () {
528 | self.options.context(this)
529 | if (typeof this.contextfn === 'function') this.contextfn(this)
530 | if (!self.options.propagationHandlers.includes('context')) window.event.returnValue = false
531 | })
532 | }
533 | }
534 |
535 | function __onButtonClickHandler (nodeItem, boardId) {
536 | nodeItem.addEventListener('click', function (e) {
537 | e.preventDefault()
538 | self.options.buttonClick(this, boardId)
539 | // if(typeof(this.clickfn) === 'function')
540 | // this.clickfn(this);
541 | })
542 | }
543 |
544 | function __findBoardJSON (id) {
545 | var el = []
546 | self.options.boards.map(function (board) {
547 | if (board.id === id) {
548 | return el.push(board)
549 | }
550 | })
551 | return el[0]
552 | }
553 |
554 | function __appendCustomProperties (element, parentObject) {
555 | for (var propertyName in parentObject) {
556 | if (self._disallowedItemProperties.indexOf(propertyName) > -1) {
557 | continue
558 | }
559 |
560 | element.setAttribute(
561 | 'data-' + propertyName,
562 | parentObject[propertyName]
563 | )
564 | }
565 | }
566 |
567 | function __updateBoardsOrder () {
568 | var index = 1
569 | for (var i = 0; i < self.container.childNodes.length; i++) {
570 | self.container.childNodes[i].dataset.order = index++
571 | }
572 | }
573 |
574 | function __buildItemCard(item) {
575 | var result = 'title' in item ? item.title : '';
576 |
577 | if (self.options.itemHandleOptions.enabled) {
578 | if ((self.options.itemHandleOptions.customHandler || undefined) === undefined) {
579 | var customCssHandler = self.options.itemHandleOptions.customCssHandler
580 | var customCssIconHandler = self.options.itemHandleOptions.customCssIconHandler
581 | var customItemLayout = self.options.itemHandleOptions.customItemLayout
582 | if ((customCssHandler || undefined) === undefined) {
583 | customCssHandler = 'drag_handler';
584 | }
585 |
586 | if ((customCssIconHandler || undefined) === undefined) {
587 | customCssIconHandler = customCssHandler + '_icon';
588 | }
589 |
590 | if ((customItemLayout || undefined) === undefined) {
591 | customItemLayout = '';
592 | }
593 |
594 | result = '
' + result + '
'
595 | } else {
596 | result = ' ' + self.options.itemHandleOptions.customHandler.replace(/%([^%]+)%/g, function (match, key)
597 | { return item[key] !== undefined ? item[key] : '' }) + '
'
598 | return result
599 | }
600 | }
601 |
602 | return result
603 | }
604 |
605 | //init plugin
606 | this.init()
607 | }
608 | })()
609 |
--------------------------------------------------------------------------------
/jkanban.spec.js:
--------------------------------------------------------------------------------
1 | require('./jkanban');
2 |
3 | const initializeDom = () => {
4 | document.body.innerHTML = '
';
5 | }
6 |
7 | beforeEach(() => {
8 | initializeDom();
9 | })
10 |
11 | const makeSut = (additionalParams) => {
12 | let options = {
13 | element: "#test"
14 | }
15 | if (additionalParams !== undefined) {
16 | for (var prop in additionalParams) {
17 | options[prop] = additionalParams[prop];
18 | }
19 | }
20 |
21 | let jkanban = new jKanban(options);
22 | return jkanban;
23 | }
24 |
25 | describe('jKanban TestCase', () => {
26 | test('Should init jKanban', async () => {
27 | const sut = makeSut()
28 |
29 | const expected = document.createElement("div")
30 | expected.setAttribute("id", "test")
31 | expected.innerHTML = "
"
32 |
33 | expect(sut.element).toStrictEqual(expected);
34 | })
35 |
36 | test('Should add a board with no items', async () => {
37 | const boardName = "test-board"
38 | const sut = makeSut({
39 | boards: [
40 | {
41 | "id": boardName
42 | }
43 | ]
44 | })
45 |
46 | expect(sut.findBoard(boardName)).not.toBeUndefined()
47 | expect(sut.getBoardElements(boardName).length).toEqual(0)
48 | })
49 | });
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jkanban",
3 | "version": "1.3.1",
4 | "description": "Javascript plugin for Kanban boards",
5 | "main": "jkanban.js",
6 | "watch": {
7 | "build": [
8 | "jkanban.js",
9 | "jkanban.css"
10 | ]
11 | },
12 | "scripts": {
13 | "watch_build": "npm-watch build",
14 | "build": "npm run scripts && npm run styles",
15 | "scripts": "browserify ./jkanban.js -o dist/jkanban.js && uglifyjs -m -c -o dist/jkanban.min.js dist/jkanban.js",
16 | "styles": "npm-css ./jkanban.css > dist/jkanban.css && cleancss dist/jkanban.css -o dist/jkanban.min.css",
17 | "test": "jest --env=jsdom"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "https://github.com/riktar/jkanban.git"
22 | },
23 | "homepage": "http://www.riccardotartaglia.it/jkanban",
24 | "keywords": [
25 | "kanban",
26 | "js",
27 | "drag",
28 | "todo list",
29 | "javascript",
30 | "plugin"
31 | ],
32 | "author": "Riccardo Tartaglia",
33 | "license": "Apache 2.0",
34 | "dependencies": {
35 | "dragula": "^3.7.3",
36 | "npm-watch": "^0.7.0"
37 | },
38 | "devDependencies": {
39 | "browserify": "^17.0.0",
40 | "clean-css-cli": "^4.3.0",
41 | "husky": "^5.0.6",
42 | "jest": "^26.6.3",
43 | "npm-css": "0.2.3",
44 | "uglify-es": "^3.3.9"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------