diff --git a/lib/angular-dragdrop-rails/version.rb b/lib/angular-dragdrop-rails/version.rb index c52e867..a682960 100644 --- a/lib/angular-dragdrop-rails/version.rb +++ b/lib/angular-dragdrop-rails/version.rb @@ -1,5 +1,5 @@ module AngularDragdrop module Rails - VERSION = "1.0.2" + VERSION = "1.0.6" end end diff --git a/vendor/assets/javascripts/angular-dragdrop-original.js b/vendor/assets/javascripts/angular-dragdrop-original.js index f765662..37fc843 100644 --- a/vendor/assets/javascripts/angular-dragdrop-original.js +++ b/vendor/assets/javascripts/angular-dragdrop-original.js @@ -22,24 +22,39 @@ * Implementing Drag and Drop functionality in AngularJS is easier than ever. * Demo: http://codef0rmer.github.com/angular-dragdrop/ * - * @version 1.0.2 + * @version 1.0.6 * * (c) 2013 Amit Gharat a.k.a codef0rmer - amitgharat.wordpress.com */ +(function (window, angular, undefined) { +'use strict'; + var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$timeout', '$parse', function($timeout, $parse) { this.callEventCallback = function (scope, callbackName, event, ui) { - if (!callbackName) { - return; - } - var args = [event, ui]; - var match = callbackName.match(/^(.+)\((.+)\)$/); - if (match !== null) { - callbackName = match[1]; - values = eval('[' + match[0].replace(/^(.+)\(/, '').replace(/\)/, '') + ']'); - args.push.apply(args, values); + if (!callbackName) return; + + var objExtract = extract(callbackName), + callback = objExtract.callback, + constructor = objExtract.constructor, + args = [event, ui].concat(objExtract.args); + + // call either $scoped method i.e. $scope.dropCallback or constructor's method i.e. this.dropCallback + scope.$apply((scope[callback] || scope[constructor][callback]).apply(scope, args)); + + function extract(callbackName) { + var atStartBracket = callbackName.indexOf('(') !== -1 ? callbackName.indexOf('(') : callbackName.length, + atEndBracket = callbackName.lastIndexOf(')') !== -1 ? callbackName.lastIndexOf(')') : callbackName.length, + args = callbackName.substring(atStartBracket + 1, atEndBracket), // matching function arguments inside brackets + constructor = callbackName.match(/^[^.]+.\s*/)[0].slice(0, -1); // matching a string upto a dot to check ctrl as syntax + constructor = scope[constructor] && typeof scope[constructor].constructor === 'function' ? constructor : null; + + return { + callback: callbackName.substring(constructor && constructor.length + 1 || 0, atStartBracket), + args: (args && args.split(',') || []).map(function(item) { return $parse(item)(scope); }), + constructor: constructor + } } - scope[callbackName].apply(scope, args); }; this.invokeDrop = function ($draggable, $droppable, event, ui) { @@ -56,14 +71,18 @@ var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$ti droppableScope = $droppable.scope(), draggableScope = $draggable.scope(); - dragModel = $draggable.attr('ng-model'); - dropModel = $droppable.attr('ng-model'); + dragModel = $draggable.ngattr('ng-model'); + dropModel = $droppable.ngattr('ng-model'); dragModelValue = draggableScope.$eval(dragModel); dropModelValue = droppableScope.$eval(dropModel); - $droppableDraggable = $droppable.find('[jqyoui-draggable]:last'); - dropSettings = droppableScope.$eval($droppable.attr('jqyoui-droppable')) || []; - dragSettings = draggableScope.$eval($draggable.attr('jqyoui-draggable')) || []; + $droppableDraggable = $droppable.find('[jqyoui-draggable]:last,[data-jqyoui-draggable]:last'); + dropSettings = droppableScope.$eval($droppable.attr('jqyoui-droppable') || $droppable.attr('data-jqyoui-droppable')) || []; + dragSettings = draggableScope.$eval($draggable.attr('jqyoui-draggable') || $draggable.attr('data-jqyoui-draggable')) || []; + + // Helps pick up the right item + dragSettings.index = this.fixIndex(draggableScope, dragSettings, dragModelValue); + dropSettings.index = this.fixIndex(droppableScope, dropSettings, dropModelValue); jqyoui_pos = angular.isArray(dragModelValue) ? dragSettings.index : null; dragItem = angular.isArray(dragModelValue) ? dragModelValue[jqyoui_pos] : dragModelValue; @@ -78,23 +97,25 @@ var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$ti if (dragSettings.animate === true) { this.move($draggable, $droppableDraggable.length > 0 ? $droppableDraggable : $droppable, null, 'fast', dropSettings, null); - this.move($droppableDraggable.length > 0 && !dropSettings.multiple ? $droppableDraggable : [], $draggable.parent('[jqyoui-droppable]'), jqyoui.startXY, 'fast', dropSettings, function() { - $timeout(function() { + this.move($droppableDraggable.length > 0 && !dropSettings.multiple ? $droppableDraggable : [], $draggable.parent('[jqyoui-droppable],[data-jqyoui-droppable]'), jqyoui.startXY, 'fast', dropSettings, angular.bind(this, function() { + $timeout(angular.bind(this, function() { // Do not move this into move() to avoid flickering issue $draggable.css({'position': 'relative', 'left': '', 'top': ''}); - $droppableDraggable.css({'position': 'relative', 'left': '', 'top': ''}); + // Angular v1.2 uses ng-hide to hide an element not display property + // so we've to manually remove display:none set in this.move() + $droppableDraggable.css({'position': 'relative', 'left': '', 'top': '', 'display': ''}); this.mutateDraggable(draggableScope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable); this.mutateDroppable(droppableScope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos); this.callEventCallback(droppableScope, dropSettings.onDrop, event, ui); - }.bind(this)); - }.bind(this)); + })); + })); } else { - $timeout(function() { + $timeout(angular.bind(this, function() { this.mutateDraggable(draggableScope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable); this.mutateDroppable(droppableScope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos); this.callEventCallback(droppableScope, dropSettings.onDrop, event, ui); - }.bind(this)); + })); } }; @@ -110,10 +131,11 @@ var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$ti var zIndex = 9999, fromPos = $fromEl.offset(), - wasVisible = $toEl && $toEl.is(':visible'); + wasVisible = $toEl && $toEl.is(':visible'), + hadNgHideCls = $toEl.hasClass('ng-hide'); if (toPos === null && $toEl.length > 0) { - if ($toEl.attr('jqyoui-draggable') !== undefined && $toEl.attr('ng-model') !== undefined && $toEl.is(':visible') && dropSettings && dropSettings.multiple) { + if (($toEl.attr('jqyoui-draggable') || $toEl.attr('data-jqyoui-draggable')) !== undefined && $toEl.ngattr('ng-model') !== undefined && $toEl.is(':visible') && dropSettings && dropSettings.multiple) { toPos = $toEl.offset(); if (dropSettings.stack === false) { toPos.left+= $toEl.outerWidth(true); @@ -121,14 +143,22 @@ var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$ti toPos.top+= $toEl.outerHeight(true); } } else { + // Angular v1.2 uses ng-hide to hide an element + // so we've to remove it in order to grab its position + if (hadNgHideCls) $toEl.removeClass('ng-hide'); toPos = $toEl.css({'visibility': 'hidden', 'display': 'block'}).offset(); - $toEl.css({'visibility': '','display': wasVisible ? '' : 'none'}); + $toEl.css({'visibility': '','display': wasVisible ? 'block' : 'none'}); } } $fromEl.css({'position': 'absolute', 'z-index': zIndex}) .css(fromPos) .animate(toPos, duration, function() { + // Angular v1.2 uses ng-hide to hide an element + // and as we remove it above, we've to put it back to + // hide the element (while swapping) if it was hidden already + // because we remove the display:none in this.invokeDrop() + if (hadNgHideCls) $toEl.addClass('ng-hide'); if (callback) callback(); }); }; @@ -136,7 +166,7 @@ var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$ti this.mutateDroppable = function(scope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos) { var dropModelValue = scope.$eval(dropModel); - scope.__dragItem = dragItem; + scope.dndDragItem = dragItem; if (angular.isArray(dropModelValue)) { if (dropSettings && dropSettings.index >= 0) { @@ -148,7 +178,7 @@ var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$ti dropModelValue[dropModelValue.length - 1]['jqyoui_pos'] = jqyoui_pos; } } else { - $parse(dropModel + ' = __dragItem')(scope); + $parse(dropModel + ' = dndDragItem')(scope); if (dragSettings && dragSettings.placeholder === true) { dropModelValue['jqyoui_pos'] = jqyoui_pos; } @@ -156,17 +186,17 @@ var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$ti }; this.mutateDraggable = function(scope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable) { - var isEmpty = $.isEmptyObject(angular.copy(dropItem)), + var isEmpty = angular.equals(angular.copy(dropItem), {}), dragModelValue = scope.$eval(dragModel); - scope.__dropItem = dropItem; + scope.dndDropItem = dropItem; if (dragSettings && dragSettings.placeholder) { if (dragSettings.placeholder != 'keep'){ if (angular.isArray(dragModelValue) && dragSettings.index !== undefined) { dragModelValue[dragSettings.index] = dropItem; } else { - $parse(dragModel + ' = __dropItem')(scope); + $parse(dragModel + ' = dndDropItem')(scope); } } } else { @@ -181,36 +211,55 @@ var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$ti } else { // Fix: LIST(object) to LIST(array) - model does not get updated using just scope[dragModel] = {...} // P.S.: Could not figure out why it happened - $parse(dragModel + ' = __dropItem')(scope); + $parse(dragModel + ' = dndDropItem')(scope); if (scope.$parent) { - $parse(dragModel + ' = __dropItem')(scope.$parent); + $parse(dragModel + ' = dndDropItem')(scope.$parent); } } } $draggable.css({'z-index': '', 'left': '', 'top': ''}); }; + + this.fixIndex = function(scope, settings, modelValue) { + if (settings.applyFilter && angular.isArray(modelValue) && modelValue.length > 0) { + var dragModelValueFiltered = scope[settings.applyFilter](), + lookup = dragModelValueFiltered[settings.index], + actualIndex = undefined; + + modelValue.forEach(function(item, i) { + if (angular.equals(item, lookup)) { + actualIndex = i; + } + }); + + return actualIndex; + } + + return settings.index; + }; }]).directive('jqyouiDraggable', ['ngDragDropService', function(ngDragDropService) { return { require: '?jqyouiDroppable', restrict: 'A', link: function(scope, element, attrs) { - var dragSettings, zIndex; + var dragSettings, jqyouiOptions, zIndex; var updateDraggable = function(newValue, oldValue) { if (newValue) { - dragSettings = scope.$eval(element.attr('jqyoui-draggable')) || []; + dragSettings = scope.$eval(element.attr('jqyoui-draggable') || element.attr('data-jqyoui-draggable')) || {}; + jqyouiOptions = scope.$eval(attrs.jqyouiOptions) || {}; element .draggable({disabled: false}) - .draggable(scope.$eval(attrs.jqyouiOptions) || {}) + .draggable(jqyouiOptions) .draggable({ start: function(event, ui) { - zIndex = $(this).css('z-index'); - $(this).css('z-index', 99999); - jqyoui.startXY = $(this).offset(); + zIndex = angular.element(jqyouiOptions.helper ? ui.helper : this).css('z-index'); + angular.element(jqyouiOptions.helper ? ui.helper : this).css('z-index', 9999); + jqyoui.startXY = angular.element(this).offset(); ngDragDropService.callEventCallback(scope, dragSettings.onStart, event, ui); }, stop: function(event, ui) { - $(this).css('z-index', zIndex); + angular.element(jqyouiOptions.helper ? ui.helper : this).css('z-index', zIndex); ngDragDropService.callEventCallback(scope, dragSettings.onStop, event, ui); }, drag: function(event, ui) { @@ -223,6 +272,10 @@ var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$ti }; scope.$watch(function() { return scope.$eval(attrs.drag); }, updateDraggable); updateDraggable(); + + element.on('$destroy', function() { + element.draggable('destroy'); + }); } }; }]).directive('jqyouiDroppable', ['ngDragDropService', function(ngDragDropService) { @@ -230,22 +283,26 @@ var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$ti restrict: 'A', priority: 1, link: function(scope, element, attrs) { + var dropSettings; var updateDroppable = function(newValue, oldValue) { if (newValue) { + dropSettings = scope.$eval(angular.element(this).attr('jqyoui-droppable') || angular.element(this).attr('data-jqyoui-droppable')) || {}; element .droppable({disabled: false}) .droppable(scope.$eval(attrs.jqyouiOptions) || {}) .droppable({ over: function(event, ui) { - var dropSettings = scope.$eval(angular.element(this).attr('jqyoui-droppable')) || []; ngDragDropService.callEventCallback(scope, dropSettings.onOver, event, ui); }, out: function(event, ui) { - var dropSettings = scope.$eval(angular.element(this).attr('jqyoui-droppable')) || []; ngDragDropService.callEventCallback(scope, dropSettings.onOut, event, ui); }, drop: function(event, ui) { - ngDragDropService.invokeDrop(angular.element(ui.draggable), angular.element(this), event, ui); + if (angular.element(ui.draggable).ngattr('ng-model') && attrs.ngModel) { + ngDragDropService.invokeDrop(angular.element(ui.draggable), angular.element(this), event, ui); + } else { + ngDragDropService.callEventCallback(scope, dropSettings.onDrop, event, ui); + } } }); } else { @@ -255,6 +312,17 @@ var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$ti scope.$watch(function() { return scope.$eval(attrs.drop); }, updateDroppable); updateDroppable(); + + element.on('$destroy', function() { + element.droppable('destroy'); + }); } }; }]); + + $.fn.ngattr = function(name, value) { + var element = angular.element(this).get(0); + + return element.getAttribute(name) || element.getAttribute('data-' + name); + }; +})(window, window.angular);