Skip to content

Commit

Permalink
Bump version to 0.6.0; Update KO reference.
Browse files Browse the repository at this point in the history
  • Loading branch information
rniemeyer committed Jun 25, 2015
1 parent f0e52fa commit 0575442
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 196 deletions.
1 change: 0 additions & 1 deletion bower.json
@@ -1,6 +1,5 @@
{
"name": "knockout-delegatedEvents",
"version": "0.5.0",
"main": "build/knockout-delegatedEvents.min.js",
"ignore": [
"examples",
Expand Down
172 changes: 94 additions & 78 deletions build/knockout-delegatedEvents.js
@@ -1,4 +1,4 @@
// knockout-delegatedEvents 0.5.0 | (c) 2015 Ryan Niemeyer | http://www.opensource.org/licenses/mit-license
// knockout-delegatedEvents 0.6.0 | (c) 2015 Ryan Niemeyer | http://www.opensource.org/licenses/mit-license
;(function(factory) {
//CommonJS
if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
Expand All @@ -11,79 +11,98 @@
factory(ko, ko.actions = {});
}
}(function(ko, actions) {
var prefix = "ko_delegated_", prefixParent = "ko_delegated_parent_";
var prefix = "ko_delegated_",
prefixParent = "ko_delegated_parent_";

function findRoot(callback){
return function (originalElement, root, eventName){
var attr = "data-" + eventName+'-parent';
// determine the method to call from a parent binding that specifies the function(s) directly
function findMethodFromParent(callback, originalElement, root, eventName) {
var attr = "data-" + eventName + "-parent";

while (originalElement && originalElement.nodeType === 1 && !originalElement.disabled && !originalElement.hasAttribute(attr) ) {
originalElement = originalElement !== root ? originalElement.parentNode : null;
}
// locate the element containing the data-<eventName>-parent attribute
while (originalElement && originalElement.nodeType === 1 && !originalElement.disabled && !originalElement.hasAttribute(attr)) {
originalElement = originalElement !== root ? originalElement.parentNode : null;
}

if (!originalElement){
return;
}
if (!originalElement){
return;
}

var dataroot = ko.dataFor(root), clue = originalElement.getAttribute(attr),
method = clue==='true'? callback : callback[clue];
var methodName = originalElement.getAttribute(attr),
method = methodName === "true" ? callback : callback[methodName];

if (!!method && (typeof method === "function")){
return {method:method, element:originalElement, owner:dataroot};
}
};
if (method && (typeof method === "function")) {
return {
method: method,
element: originalElement,
owner: ko.dataFor(root)
};
}
}

function methodFinder(originalElement, root, eventName){
var method, attr = "data-" + eventName, key = prefix + eventName, keyParent = prefixParent+ eventName,
attrParent = "data-" + eventName+'-parent', owner, parentAttribute, contextelement;

while (!method && originalElement) {
if (originalElement.nodeType === 1 && !originalElement.disabled){
if (parentAttribute){
// find the method or method name by looking at the appropriate attributes and domData
function findMethod(originalElement, root, eventName) {
var method, owner, parentAttribute, contextElement,
attr = "data-" + eventName,
key = prefix + eventName,
keyParent = prefixParent + eventName,
attrParent = "data-" + eventName + "-parent";

while (!method && originalElement && originalElement !== root) {
if (originalElement.nodeType === 1 && !originalElement.disabled) {
if (parentAttribute) {
method = ko.utils.domData.get(originalElement, keyParent);
if (method){
method = parentAttribute==='true'? method : method[parentAttribute];
if (method) {
method = parentAttribute === "true" ? method : method[parentAttribute];
owner = ko.dataFor(originalElement);
}
}
else{
else {
method = (originalElement.getAttribute(attr) || ko.utils.domData.get(originalElement, key));
if (!method){
parentAttribute =originalElement.getAttribute(attrParent);
if (!!parentAttribute) contextelement = originalElement;
if (!method) {
// set a flag that indicates that we need to find a method on the appropriate parent
parentAttribute = originalElement.getAttribute(attrParent);
if (parentAttribute) {
// we need this element later to pass the appropriate data
contextElement = originalElement;
}
}
}
}
}
}

if (!method) {
originalElement = originalElement !== root ? originalElement.parentNode : null;
originalElement = originalElement.parentNode;
}
}

if (method){
return {method:method, element: contextelement || originalElement, owner:owner};
if (method) {
return {
method: method,
element: contextElement || originalElement,
owner: owner
};
}
}


var createDelegatedHandler = function(eventName, root, bubble, methodFinderCallBack) {
// create an event handler that locates the appropriate method and executes it with the proper context and args
function createDelegatedHandler(eventName, root, bubble, findMethodCallBack) {
return function(event) {
var search = findMethodCallBack(event.target || event.srcElement, root, eventName);

var res = methodFinderCallBack(event.target || event.srcElement,root,eventName);

if (!res)
if (!search) {
return;
}

var data, context, action, matchingParent, command, result,
el = res.element, method = res.method, owner =res.owner;
el = search.element,
method = search.method,
owner = search.owner;

if (method) {
//get context of the element that actually held the action
// get context of the element that actually held the action
context = ko.contextFor(el);

if (context) {
//need to ensure that the clicked element is not inside a disabled element
// need to ensure that the clicked element is not inside a disabled element
while (el && el !== root) {
if (el.disabled) {
return;
Expand All @@ -95,21 +114,21 @@
data = context.$data;

if (typeof method === "string") {
//check defined actions
// check defined actions
if (method in actions) {
command = actions[method];
if (command) {
action = typeof command === "function" ? command : command.action;
owner = command.owner || data;
}
}
//search for the action
// search for the action
else if (data && data[method] && typeof data[method] === "function") {
action = data[method];
owner = data;
}

//search parents for the action
// search parents for the action
if (!action) {
matchingParent = ko.utils.arrayFirst(context.$parents, function(parent) {
return parent[method] && typeof parent[method] === "function";
Expand All @@ -119,25 +138,25 @@
owner = matchingParent;
}
}
//a binding handler was used to associate the element with a function
// a binding handler was used to associate the element with a function
else if (typeof method === "function") {
action = method;
owner = owner || data;
}
}

//execute the action as KO normally would
// execute the action as KO normally would
if (action) {
//if the event is a submit event, we want to just pass
//the form element, and set the context to 'this'.
//This matches the knockout behaviour for submit bindings.
// if the event is a submit event, we want to just pass
// the form element, and set the context to 'this'.
// This matches the knockout behaviour for submit bindings.
if (eventName === "submit") {
result = action.call(data, event.target);
} else {
result = action.call(owner, data, event);
}

//prevent default action, if handler does not return true
// prevent default action, if handler does not return true
if (result !== true) {
if (event.preventDefault) {
event.preventDefault();
Expand All @@ -147,7 +166,7 @@
}
}

//prevent bubbling if not enabled
// prevent bubbling if not enabled
if (bubble !== true) {
event.cancelBubble = true;

Expand All @@ -158,14 +177,15 @@
}
}
};
};
}

//create binding handler name from event name
var createBindingName = function(bindingprefix,eventName) {
return bindingprefix + eventName.substr(0, 1).toUpperCase() + eventName.slice(1);
};
// build a camel-case binding name
function getBindingName(bindingPrefix, eventName) {
return bindingPrefix + eventName.substr(0, 1).toUpperCase() + eventName.slice(1);
}

var createBinding = function (bindingName,attributeName){
// create a binding that associates a function with an element via domData
function createBinding(bindingName, attributeName){
if (!ko.bindingHandlers[bindingName]) {
ko.bindingHandlers[bindingName] = {
init: function(element, valueAccessor) {
Expand All @@ -174,19 +194,15 @@
}
};
}
};

//create a binding for an event to associate a function with the element
var createDelegatedBinding = function(event) {
if (!event) {
return;
}
}

createBinding(createBindingName("delegated",event),prefix + event);
createBinding(createBindingName("delegatedParent",event),prefixParent + event);
};
// create bindings for an event to associate a function with the element
function createDelegatedBindings(event) {
createBinding(getBindingName("delegated", event), prefix + event);
createBinding(getBindingName("delegatedParent", event), prefixParent + event);
}

//add a handler on a parent element that responds to events from the children
// add a handler on a parent element that responds to events from the children
ko.bindingHandlers.delegatedHandler = {
init: function(element, valueAccessor, allBindings) {
var events = ko.utils.unwrapObservable(valueAccessor()) || [];
Expand All @@ -196,11 +212,11 @@
}

ko.utils.arrayForEach(events, function(event) {
//check if the associated "delegated<EventName>Bubble" is true (optionally allows bubbling)
var bubble = allBindings.get(createBindingName("delegated",event + "Bubble")) === true;
// check if the associated "delegated<EventName>Bubble" is true (optionally allows bubbling)
var bubble = allBindings.get(getBindingName("delegated", event + "Bubble")) === true;

createDelegatedBinding(event);
ko.utils.registerEventHandler(element, event, createDelegatedHandler(event, element, bubble, methodFinder));
createDelegatedBindings(event);
ko.utils.registerEventHandler(element, event, createDelegatedHandler(event, element, bubble, findMethod));
});
}
};
Expand All @@ -209,11 +225,11 @@
init: function(element, valueAccessor, allBindings) {
var events = ko.utils.unwrapObservable(valueAccessor());

ko.utils.objectForEach (events,function(event) {
//check if the associated "delegated<EventName>Bubble" is true (optionally allows bubbling)
var bubble = allBindings.get(createBindingName("delegated",event + "Bubble")) === true;
ko.utils.objectForEach(events, function(event) {
// check if the associated "delegated<EventName>Bubble" is true (optionally allows bubbling)
var bubble = allBindings.get(getBindingName("delegated", event + "Bubble")) === true;

ko.utils.registerEventHandler(element, event, createDelegatedHandler(event, element, bubble, findRoot(events[event])));
ko.utils.registerEventHandler(element, event, createDelegatedHandler(event, element, bubble, findMethodFromParent.bind(null, events[event])));
});
}
};
Expand Down
4 changes: 2 additions & 2 deletions build/knockout-delegatedEvents.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/example.html
Expand Up @@ -37,7 +37,7 @@
</div>


<script src="../ext/knockout-3.2.0.js"></script>
<script src="../ext/knockout-3.3.0.js"></script>
<script src="../src/knockout-delegatedEvents.js"></script>
<script>
var Item = function(id, name, price, description) {
Expand Down
2 changes: 1 addition & 1 deletion examples/example_2.html
Expand Up @@ -37,7 +37,7 @@
</div>


<script src="../ext/knockout-3.2.0.js"></script>
<script src="../ext/knockout-3.3.0.js"></script>
<script src="../src/knockout-delegatedEvents.js"></script>
<script>
var Item = function(id, name, price, description) {
Expand Down

0 comments on commit 0575442

Please sign in to comment.