can
Version:
MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.
305 lines (283 loc) • 7.6 kB
JavaScript
steal('jquery', 'can/util/can.js', 'can/util/attr', "can/event", "can/util/fragment.js",'can/util/array/each.js', "can/util/inserted", function ($, can, attr, event) {
var isBindableElement = function (node) {
// In IE8 window.window !== window.window, so we allow == here.
/*jshint eqeqeq:false*/
return (node.nodeName && (node.nodeType === 1 || node.nodeType === 9)) || node == window || node.addEventListener;
};
$ = $ || window.jQuery;
// _jQuery node list._
$.extend(can, $, {
trigger: function (obj, event, args, bubbles) {
if (isBindableElement( obj ) ) {
$.event.trigger(event, args, obj, !bubbles);
} else if (obj.trigger) {
obj.trigger(event, args);
} else {
if (typeof event === 'string') {
event = {
type: event
};
}
event.target = event.target || obj;
if(args){
if( args.length && typeof args === "string") {
args = [args];
} else if(! args.length ) {
args = [args];
}
}
if(!args){
args = [];
}
can.dispatch.call(obj, event, args);
}
},
event: can.event,
addEvent: can.addEvent,
removeEvent: can.removeEvent,
buildFragment: can.buildFragment,
$: $,
each: can.each,
bind: function (ev, cb) {
// don't set up event listeners for document fragments
// since events will not be triggered and the handlers
// could lead to memory leaks
if (this.nodeType === 11) {
return;
}
// If we can bind to it...
if (this.bind && this.bind !== can.bind) {
this.bind(ev, cb);
} else if (isBindableElement(this)) {
$.event.add(this, ev, cb);
} else {
// Make it bind-able...
can.addEvent.call(this, ev, cb);
}
return this;
},
unbind: function (ev, cb) {
// event handlers are not set up on document fragments
// so they do not need to be removed
if (this.nodeType === 11) {
return;
}
// If we can bind to it...
if (this.unbind && this.unbind !== can.unbind) {
this.unbind(ev, cb);
} else if (isBindableElement(this)) {
$.event.remove(this, ev, cb);
} else {
// Make it bind-able...
can.removeEvent.call(this, ev, cb);
}
return this;
},
delegate: function (selector, ev, cb) {
if (this.delegate) {
this.delegate(selector, ev, cb);
} else if (isBindableElement(this)) {
$(this)
.delegate(selector, ev, cb);
} else {
// make it bind-able ...
can.bind.call(this, ev, cb);
}
return this;
},
undelegate: function (selector, ev, cb) {
if (this.undelegate) {
this.undelegate(selector, ev, cb);
} else if (isBindableElement(this)) {
$(this)
.undelegate(selector, ev, cb);
} else {
can.unbind.call(this, ev, cb);
}
return this;
},
proxy: can.proxy,
attr: attr
});
// Wrap binding functions.
/*$.each(['bind','unbind','undelegate','delegate'],function(i,func){
can[func] = function(){
var t = this[func] ? this : $([this]);
t[func].apply(t, arguments);
return this;
};
});*/
// Aliases
can.on = can.bind;
can.off = can.unbind;
// Wrap modifier functions.
$.each([
'append',
'filter',
'addClass',
'remove',
'data',
'get',
'has'
], function (i, name) {
can[name] = function (wrapped) {
return wrapped[name].apply(wrapped, can.makeArray(arguments)
.slice(1));
};
});
// Memory safe destruction.
var oldClean = $.cleanData;
$.cleanData = function (elems) {
$.each(elems, function (i, elem) {
if (elem) {
can.trigger(elem, 'removed', [], false);
}
});
oldClean(elems);
};
var oldDomManip = $.fn.domManip,
cbIndex;
// feature detect which domManip we are using
$.fn.domManip = function (args, cb1, cb2) {
for (var i = 1; i < arguments.length; i++) {
if (typeof arguments[i] === 'function') {
cbIndex = i;
break;
}
}
return oldDomManip.apply(this, arguments);
};
$(document.createElement("div"))
.append(document.createElement("div"));
var getChildNodes = function(node){
var childNodes = node.childNodes;
if("length" in childNodes) {
return can.makeArray(childNodes);
} else {
var cur = node.firstChild;
var nodes = [];
while(cur) {
nodes.push(cur);
cur = cur.nextSibling;
}
return nodes;
}
};
if(cbIndex === undefined) {
$.fn.domManip = oldDomManip;
// we must manually overwrite
can.each(['after', 'prepend', 'before', 'append','replaceWith'], function (name) {
var original = $.fn[name];
$.fn[name] = function () {
var elems = [],
args = can.makeArray(arguments);
if (args[0] != null) {
// documentFragment
if (typeof args[0] === "string") {
args[0] = can.buildFragment(args[0]);
}
if (args[0].nodeType === 11) {
elems = getChildNodes(args[0]);
} else if( can.isArrayLike( args[0] ) ) {
elems = can.makeArray(args[0]);
} else {
elems = [args[0]];
}
}
var ret = original.apply(this, args);
can.inserted(elems);
return ret;
};
});
} else {
// Older jQuery that supports domManip
$.fn.domManip = (cbIndex === 2 ?
function (args, table, callback) {
return oldDomManip.call(this, args, table, function (elem) {
var elems;
if (elem.nodeType === 11) {
elems = can.makeArray( can.childNodes(elem) );
}
var ret = callback.apply(this, arguments);
can.inserted(elems ? elems : [elem]);
return ret;
});
} :
function (args, callback) {
return oldDomManip.call(this, args, function (elem) {
var elems;
if (elem.nodeType === 11) {
elems = can.makeArray( can.childNodes(elem) );
}
var ret = callback.apply(this, arguments);
can.inserted(elems ? elems : [elem]);
return ret;
});
});
}
// handle via calls to attr
var oldAttr = $.attr;
$.attr = function (el, attrName) {
if( can.isDOM(el) && can.attr.MutationObserver) {
return oldAttr.apply(this, arguments);
} else {
var oldValue, newValue;
if (arguments.length >= 3) {
oldValue = oldAttr.call(this, el, attrName);
}
var res = oldAttr.apply(this, arguments);
if (arguments.length >= 3) {
newValue = oldAttr.call(this, el, attrName);
}
if (newValue !== oldValue) {
can.attr.trigger(el, attrName, oldValue);
}
return res;
}
};
var oldRemove = $.removeAttr;
$.removeAttr = function (el, attrName) {
if( can.isDOM(el) && can.attr.MutationObserver) {
return oldRemove.apply(this, arguments);
} else {
var oldValue = oldAttr.call(this, el, attrName),
res = oldRemove.apply(this, arguments);
if (oldValue != null) {
can.attr.trigger(el, attrName, oldValue);
}
return res;
}
};
$.event.special.attributes = {
setup: function () {
if( can.isDOM(this) && can.attr.MutationObserver) {
var self = this;
var observer = new can.attr.MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
var copy = can.simpleExtend({}, mutation);
can.trigger(self, copy, []);
});
});
observer.observe(this, {
attributes: true,
attributeOldValue: true
});
can.data(can.$(this), "canAttributesObserver", observer);
} else {
can.data(can.$(this), "canHasAttributesBindings", true);
}
},
teardown: function () {
if( can.isDOM(this) && can.attr.MutationObserver) {
can.data(can.$(this), "canAttributesObserver")
.disconnect();
$.removeData(this, "canAttributesObserver");
} else {
$.removeData(this, "canHasAttributesBindings");
}
}
};
$.event.special.inserted = {};
$.event.special.removed = {};
return can;
});