From 340b48941bdb97655b5c375b0347bcf4d92fb49c Mon Sep 17 00:00:00 2001
From: fcamblor <fcamblor@gmail.com>
Date: Wed, 24 Oct 2018 11:49:52 +0200
Subject: [PATCH 1/3] Provided test case reproducing getManagerForEl() case
 returning undefined when root element is provided

---
 spec/SpecRunner.html                          |  1 +
 .../CollectionBinder/getManagerForEl.spec.js  | 60 +++++++++++++++++++
 2 files changed, 61 insertions(+)
 create mode 100644 spec/javascripts/CollectionBinder/getManagerForEl.spec.js

diff --git a/spec/SpecRunner.html b/spec/SpecRunner.html
index 24effd0..b1d69d6 100644
--- a/spec/SpecRunner.html
+++ b/spec/SpecRunner.html
@@ -74,6 +74,7 @@
   <script type="text/javascript" src="javascripts/oneElementToMultipleAttributes.spec.js"></script>
 
   <script type="text/javascript" src="javascripts/CollectionBinder/sorting.spec.js"></script>
+  <script type="text/javascript" src="javascripts/CollectionBinder/getManagerForEl.spec.js"></script>
 
 </head>
   <body>
diff --git a/spec/javascripts/CollectionBinder/getManagerForEl.spec.js b/spec/javascripts/CollectionBinder/getManagerForEl.spec.js
new file mode 100644
index 0000000..ed97064
--- /dev/null
+++ b/spec/javascripts/CollectionBinder/getManagerForEl.spec.js
@@ -0,0 +1,60 @@
+describe("CollectionBinder: sorting", function(){
+
+    function createViewWithClickCallback(clickCallback) {
+        var View = Backbone.View.extend({
+            events: function(){
+                return {
+                    'click .clickable-div': this.onElementClicked,
+                    'click .clickable-span': this.onElementClicked
+                };
+            },
+
+            initialize: function () {
+                var elManagerFactory = new Backbone.CollectionBinder.ElManagerFactory("<div class='clickable-div'><span class='clickable-span'>Hello !</span></div>");
+                this.collectionBinder = new Backbone.CollectionBinder(elManagerFactory);
+
+                this.collection = new Backbone.Collection([1,2,3]);
+            },
+
+            render: function(){
+                this.collectionBinder.bind(this.collection, this.$el);
+                return this;
+            },
+
+            onElementClicked: function(event) {
+                var targetManager = this.collectionBinder.getManagerForEl($(event.currentTarget));
+                clickCallback(targetManager);
+
+                // Stopping propagation (in order to avoid bubbling from span to parent div)
+                event.stopPropagation();
+            }
+        });
+        return new View();
+    }
+
+    var createViewThenClickAndExpectManagerResolved = function(selectorElementToClick) {
+        var resolvedManager = undefined;
+        var clickCallbackCalled = false;
+        var view = createViewWithClickCallback(function(manager) {
+            clickCallbackCalled = true;
+            resolvedManager = manager;
+        });
+
+        view.render();
+        view.$(selectorElementToClick).click();
+
+        expect(clickCallbackCalled).toBe(true);
+        expect(resolvedManager).toBeTruthy();
+
+        view.remove();
+    };
+
+    it('should resolve el manager when span inner element is clicked', function () {
+        createViewThenClickAndExpectManagerResolved('span.clickable-span:eq(0)');
+    });
+
+    it('should resolve el manager when div root element is clicked', function () {
+        createViewThenClickAndExpectManagerResolved('div.clickable-div:eq(0)');
+    });
+
+});

From 9faeda9c2431f23684247b06b5b2bd8cd0ee2f15 Mon Sep 17 00:00:00 2001
From: fcamblor <fcamblor@gmail.com>
Date: Wed, 24 Oct 2018 11:09:14 +0200
Subject: [PATCH 2/3] Allowing to consider isElContained(findEl) will return
 true when findEl is the same node than this._el in collection binder managers

---
 Backbone.CollectionBinder.js | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/Backbone.CollectionBinder.js b/Backbone.CollectionBinder.js
index 41482c7..4e808c4 100644
--- a/Backbone.CollectionBinder.js
+++ b/Backbone.CollectionBinder.js
@@ -232,7 +232,12 @@
                 },
 
                 isElContained: function(findEl){
-                    return this._el === findEl || $(this._el).has(findEl).length > 0;
+                    if(this._el === findEl){
+                        return true;
+                    }
+
+                    var $el = $(this._el);
+                    return $el.has(findEl).length > 0 || $el.is(findEl);
                 },
 
                 getModel: function(){
@@ -295,7 +300,7 @@
                 },
 
                 isElContained: function(findEl){
-                    return this._view.el === findEl || this._view.$el.has(findEl).length > 0;
+                    return this._view.el === findEl || this._view.$el.has(findEl).length > 0 || this._view.$el.is(findEl);
                 },
 
                 getModel: function(){

From 22bd129ea85de66d477a1cdd89beb3152d0bc8dc Mon Sep 17 00:00:00 2001
From: fcamblor <fcamblor@gmail.com>
Date: Wed, 24 Oct 2018 11:11:36 +0200
Subject: [PATCH 3/3] manually minified Backbone.CollectionBinder.js using
 https://jscompress.com/

---
 Backbone.CollectionBinder.min.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Backbone.CollectionBinder.min.js b/Backbone.CollectionBinder.min.js
index 8911b14..beb260b 100644
--- a/Backbone.CollectionBinder.min.js
+++ b/Backbone.CollectionBinder.min.js
@@ -2,4 +2,4 @@
 // (c) 2015 Bart Wood
 // Distributed Under MIT License
 
-!function(e){"function"==typeof define&&define.amd?define(["underscore","jquery","backbone","Backbone.ModelBinder"],e):"undefined"!=typeof module&&module.exports?module.exports=e(require("underscore"),require("jquery"),require("backbone")):e(_,$,Backbone)}(function(e,t,i){if(!i)throw"Please include Backbone.js before Backbone.ModelBinder.js";if(!i.ModelBinder)throw"Please include Backbone.ModelBinder.js before Backbone.CollectionBinder.js";i.CollectionBinder=function(t,n){if(e.bindAll.apply(e,[this].concat(e.functions(this))),this._elManagers={},this._elManagerFactory=t,!this._elManagerFactory)throw"elManagerFactory must be defined.";this._elManagerFactory.trigger=this.trigger,this._options=e.extend({},i.CollectionBinder.options,n)},i.CollectionBinder.SetOptions=function(e){i.CollectionBinder.options=e},i.CollectionBinder.VERSION="1.1.0",e.extend(i.CollectionBinder.prototype,i.Events,{bind:function(e,t){if(this.unbind(),!e)throw"collection must be defined";if(!t)throw"parentEl must be defined";this._collection=e,this._elManagerFactory._setParentEl(t),this._onCollectionReset(),this._collection.on("add",this._onCollectionAdd,this),this._collection.on("remove",this._onCollectionRemove,this),this._collection.on("reset",this._onCollectionReset,this),this._collection.on("sort",this._onCollectionSort,this)},unbind:function(){void 0!==this._collection&&(this._collection.off("add",this._onCollectionAdd),this._collection.off("remove",this._onCollectionRemove),this._collection.off("reset",this._onCollectionReset),this._collection.off("sort",this._onCollectionSort)),this._removeAllElManagers()},getManagerForEl:function(t){var i,n,o=e.values(this._elManagers);for(i=0;i<o.length;i++)if(n=o[i],n.isElContained(t))return n;return void 0},getManagerForModel:function(t){return this._elManagers[e.isObject(t)?t.cid:t]},_onCollectionAdd:function(e,t,i){var n=this._elManagers[e.cid]=this._elManagerFactory.makeElManager(e);n.createEl();var o=i&&i.at;this._options.autoSort&&null!=o&&o<this._collection.length-1&&this._moveElToPosition(n.getEl(),o)},_onCollectionRemove:function(e){this._removeElManager(e)},_onCollectionReset:function(){this._removeAllElManagers(),this._collection.each(function(e){this._onCollectionAdd(e)},this),this.trigger("elsReset",this._collection)},_onCollectionSort:function(){this._options.autoSort&&this.sortRootEls()},_removeAllElManagers:function(){e.each(this._elManagers,function(e){e.removeEl(),delete this._elManagers[e._model.cid]},this),delete this._elManagers,this._elManagers={}},_removeElManager:function(e){void 0!==this._elManagers[e.cid]&&(this._elManagers[e.cid].removeEl(),delete this._elManagers[e.cid])},_moveElToPosition:function(e,t){var i=this._collection.at(t+1);if(i){var n=this.getManagerForModel(i);if(n){var o=n.getEl();o&&(e.detach(),e.insertBefore(o))}}},sortRootEls:function(){this._collection.each(function(e,i){var n=this.getManagerForModel(e);if(n){var o=n.getEl(),l=t(this._elManagerFactory._getParentEl()).children();l[i]!==o[0]&&(o.detach(),o.insertBefore(l[i]))}},this)}}),i.CollectionBinder.ElManagerFactory=function(t,i){if(e.bindAll.apply(e,[this].concat(e.functions(this))),this._elHtml=t,this._bindings=i,!e.isFunction(this._elHtml)&&!e.isString(this._elHtml))throw"elHtml must be a compliled template or an html string"},e.extend(i.CollectionBinder.ElManagerFactory.prototype,{_setParentEl:function(e){this._parentEl=e},_getParentEl:function(){return this._parentEl},makeElManager:function(n){var o={_model:n,createEl:function(){if(this._el=t(e.isFunction(this._elHtml)?this._elHtml({model:this._model.toJSON()}):this._elHtml),t(this._parentEl).append(this._el),this._bindings)if(e.isString(this._bindings))this._modelBinder=new i.ModelBinder,this._modelBinder.bind(this._model,this._el,i.ModelBinder.createDefaultBindings(this._el,this._bindings));else{if(!e.isObject(this._bindings))throw"Unsupported bindings type, please use a boolean or a bindings hash";this._modelBinder=new i.ModelBinder,this._modelBinder.bind(this._model,this._el,this._bindings)}this.trigger("elCreated",this._model,this._el)},removeEl:function(){void 0!==this._modelBinder&&this._modelBinder.unbind(),this._el.remove(),this.trigger("elRemoved",this._model,this._el)},isElContained:function(e){return this._el===e||t(this._el).has(e).length>0},getModel:function(){return this._model},getEl:function(){return this._el}};return e.extend(o,this),o}}),i.CollectionBinder.ViewManagerFactory=function(t){if(e.bindAll.apply(e,[this].concat(e.functions(this))),this._viewCreator=t,!e.isFunction(this._viewCreator))throw"viewCreator must be a valid function that accepts a model and returns a backbone view"},e.extend(i.CollectionBinder.ViewManagerFactory.prototype,{_setParentEl:function(e){this._parentEl=e},_getParentEl:function(){return this._parentEl},makeElManager:function(i){var n={_model:i,createEl:function(){this._view=this._viewCreator(i),this._view.render(this._model),t(this._parentEl).append(this._view.el),this.trigger("elCreated",this._model,this._view)},removeEl:function(){void 0!==this._view.close?this._view.close():(this._view.$el.remove(),console&&console.log&&console.log("warning, you should implement a close() function for your view, you might end up with zombies")),this.trigger("elRemoved",this._model,this._view)},isElContained:function(e){return this._view.el===e||this._view.$el.has(e).length>0},getModel:function(){return this._model},getView:function(){return this._view},getEl:function(){return this._view.$el}};return e.extend(n,this),n}})});
\ No newline at end of file
+!function(e){"function"==typeof define&&define.amd?define(["underscore","jquery","backbone","Backbone.ModelBinder"],e):"undefined"!=typeof module&&module.exports?module.exports=e(require("underscore"),require("jquery"),require("backbone")):e(_,$,Backbone)}(function(o,l,i){if(!i)throw"Please include Backbone.js before Backbone.ModelBinder.js";if(!i.ModelBinder)throw"Please include Backbone.ModelBinder.js before Backbone.CollectionBinder.js";i.CollectionBinder=function(e,t){if(o.bindAll.apply(o,[this].concat(o.functions(this))),this._elManagers={},this._elManagerFactory=e,!this._elManagerFactory)throw"elManagerFactory must be defined.";this._elManagerFactory.trigger=this.trigger,this._options=o.extend({},i.CollectionBinder.options,t)},i.CollectionBinder.SetOptions=function(e){i.CollectionBinder.options=e},i.CollectionBinder.VERSION="1.1.0",o.extend(i.CollectionBinder.prototype,i.Events,{bind:function(e,t){if(this.unbind(),!e)throw"collection must be defined";if(!t)throw"parentEl must be defined";this._collection=e,this._elManagerFactory._setParentEl(t),this._onCollectionReset(),this._collection.on("add",this._onCollectionAdd,this),this._collection.on("remove",this._onCollectionRemove,this),this._collection.on("reset",this._onCollectionReset,this),this._collection.on("sort",this._onCollectionSort,this)},unbind:function(){void 0!==this._collection&&(this._collection.off("add",this._onCollectionAdd),this._collection.off("remove",this._onCollectionRemove),this._collection.off("reset",this._onCollectionReset),this._collection.off("sort",this._onCollectionSort)),this._removeAllElManagers()},getManagerForEl:function(e){var t,i,n=o.values(this._elManagers);for(t=0;t<n.length;t++)if((i=n[t]).isElContained(e))return i},getManagerForModel:function(e){return this._elManagers[o.isObject(e)?e.cid:e]},_onCollectionAdd:function(e,t,i){var n=this._elManagers[e.cid]=this._elManagerFactory.makeElManager(e);n.createEl();var o=i&&i.at;this._options.autoSort&&null!=o&&o<this._collection.length-1&&this._moveElToPosition(n.getEl(),o)},_onCollectionRemove:function(e){this._removeElManager(e)},_onCollectionReset:function(){this._removeAllElManagers(),this._collection.each(function(e){this._onCollectionAdd(e)},this),this.trigger("elsReset",this._collection)},_onCollectionSort:function(){this._options.autoSort&&this.sortRootEls()},_removeAllElManagers:function(){o.each(this._elManagers,function(e){e.removeEl(),delete this._elManagers[e._model.cid]},this),delete this._elManagers,this._elManagers={}},_removeElManager:function(e){void 0!==this._elManagers[e.cid]&&(this._elManagers[e.cid].removeEl(),delete this._elManagers[e.cid])},_moveElToPosition:function(e,t){var i=this._collection.at(t+1);if(i){var n=this.getManagerForModel(i);if(n){var o=n.getEl();o&&(e.detach(),e.insertBefore(o))}}},sortRootEls:function(){this._collection.each(function(e,t){var i=this.getManagerForModel(e);if(i){var n=i.getEl(),o=l(this._elManagerFactory._getParentEl()).children();o[t]!==n[0]&&(n.detach(),n.insertBefore(o[t]))}},this)}}),i.CollectionBinder.ElManagerFactory=function(e,t){if(o.bindAll.apply(o,[this].concat(o.functions(this))),this._elHtml=e,this._bindings=t,!o.isFunction(this._elHtml)&&!o.isString(this._elHtml))throw"elHtml must be a compliled template or an html string"},o.extend(i.CollectionBinder.ElManagerFactory.prototype,{_setParentEl:function(e){this._parentEl=e},_getParentEl:function(){return this._parentEl},makeElManager:function(e){var t={_model:e,createEl:function(){if(this._el=o.isFunction(this._elHtml)?l(this._elHtml({model:this._model.toJSON()})):l(this._elHtml),l(this._parentEl).append(this._el),this._bindings)if(o.isString(this._bindings))this._modelBinder=new i.ModelBinder,this._modelBinder.bind(this._model,this._el,i.ModelBinder.createDefaultBindings(this._el,this._bindings));else{if(!o.isObject(this._bindings))throw"Unsupported bindings type, please use a boolean or a bindings hash";this._modelBinder=new i.ModelBinder,this._modelBinder.bind(this._model,this._el,this._bindings)}this.trigger("elCreated",this._model,this._el)},removeEl:function(){void 0!==this._modelBinder&&this._modelBinder.unbind(),this._el.remove(),this.trigger("elRemoved",this._model,this._el)},isElContained:function(e){if(this._el===e)return!0;var t=l(this._el);return 0<t.has(e).length||t.is(e)},getModel:function(){return this._model},getEl:function(){return this._el}};return o.extend(t,this),t}}),i.CollectionBinder.ViewManagerFactory=function(e){if(o.bindAll.apply(o,[this].concat(o.functions(this))),this._viewCreator=e,!o.isFunction(this._viewCreator))throw"viewCreator must be a valid function that accepts a model and returns a backbone view"},o.extend(i.CollectionBinder.ViewManagerFactory.prototype,{_setParentEl:function(e){this._parentEl=e},_getParentEl:function(){return this._parentEl},makeElManager:function(e){var t={_model:e,createEl:function(){this._view=this._viewCreator(e),this._view.render(this._model),l(this._parentEl).append(this._view.el),this.trigger("elCreated",this._model,this._view)},removeEl:function(){void 0!==this._view.close?this._view.close():(this._view.$el.remove(),console&&console.log&&console.log("warning, you should implement a close() function for your view, you might end up with zombies")),this.trigger("elRemoved",this._model,this._view)},isElContained:function(e){return this._view.el===e||0<this._view.$el.has(e).length||this._view.$el.is(e)},getModel:function(){return this._model},getView:function(){return this._view},getEl:function(){return this._view.$el}};return o.extend(t,this),t}})});