Skip to content

Instantly share code, notes, and snippets.

@malko
Last active October 12, 2015 02:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save malko/3959231 to your computer and use it in GitHub Desktop.
Save malko/3959231 to your computer and use it in GitHub Desktop.
minimalist jquery/zepto compatibility layer targeting mobile platforms library development
/**
* minimal jquery/zepto compatibility layer
* be aware that won't mimic jquery/zepto at all but offer a similar api for basic stuffs as querySelectorAll and addEventListener ...
* @author jgotti at modedemploi dot fr for agence-modedemploi.com
* @licence Dual licence LGPL / MIT
* @changelog
* - 2013-01-18 - add isArray/isFunction/isNumeric/isObject/isEmptyObject/isPlainObject/filter/not methods
* - is/filter/not may now use selector, domElement, function or collection to match against
* - first attempt for adding selectors and namespaces supports to on and off methods
* - 2012-12-11 - add hasClass/addClass/removeClass/toggleClass/hide/show/toggle/is/closest methods
* - 2012-12-07 - add css/attr/prop/html/remove/find methods
* - 2012-11-28 - more jquery like syntax and some events related stuffs
*/
(function($){
"use strict";
/*jshint expr:true*/
if(! $){
/**
* can be used as querySelectorAll (selector as first parameter and optionaly domElement used as context passed as second parameter )
* or if first parameter is a function just a shorthand of $.ready
*/
$ = function(selector,context){ // not supporting IE
if( selector instanceof Function ){
return $.ready(selector);
}
var c;
if( selector === window || selector===document || (selector instanceof Element) ){
c = [selector];
}else if( selector instanceof Array ){
c = selector;
}else if(! (context instanceof Array) ){
context === window && (context = document);
c = Array.prototype.slice.call((context||document).querySelectorAll(selector),0) || [];
}else{
c = [];
$.each(context,function(k,v){
$(selector,v).each(function(k,v){
c.push(v);
});
});
}
$.each($.fn,function(k,v){
c[k] = v;
});
return c;
};
/**
* take a callback to execute when dom is ready
*/
$.ready = function(cb){ // not supporting IE
if(document.readyState.match(/complete|loaded|interactive/)){
setTimeout(cb,0);
}else{
document.addEventListener('DOMContentLoaded', function(){ setTimeout(cb,0);}, false);
}
return this;
};
/**
* first parameter is destination object to extend or boolean forcing deep extension.
* all others parameters are object to copy property from to destination object
*/
$.extend = function(){
var A=arguments
, deepPassed = typeof A[0] === 'boolean' ? true : false
, deep = deepPassed ? A[0] : false
, dest = A[deepPassed?1:0]
, a = deepPassed?2:1
, al = A.length
, p
;
if( a ===2 ){
dest = A[1];
}
for(;a<al;a++){
for(p in A[a]){
if( A[a].hasOwnProperty(p) ){
if( deep && typeof(A[a][p]) === 'object' ){
dest[p] = dest[p] || {};
$.extend(deep,dest[p],A[a][p]);
}else{
dest[p] = A[a][p];
}
}
}
}
return dest;
};
/**
* bind callback on given event to given element
* don't support namespaced events
*/
var bind = (window.document.addEventListener ?
function(type, e, cb){ $.each(type.split(/\s+/),function(){e.addEventListener(this, cb, false);}); }
: function(type, e, cb){ $.each(type.split(/\s+/),function(){ e.attachEvent('on' + this, cb);}); }
);
/**
* unbind callback on given event to given element
* don't support namespaced events
*/
var unbind = (window.document.removeEventListener ?
function(type, e, cb){ $.each(type.split(/\s+/),function(){e.removeEventListener(this, cb, false);}); }
: function(type, e, cb){$.each(type.split(/\s+/),function(){e.detachEvent('on' + this, cb);}); }
);
$.on2 = function(type,elmt,cb,selector){
$.each(type.split(/\s+/),function(k,type){
var parts = type.split('.'), hid=uid(cb);
handlers.handlers[hid] = cb;
(handlers.elmts[uid(elmt)] || (handlers.elmts[uid(elmt)]=[])).push({
ns:parts.length<2?Undef:parts.slice(1).sort().join(' ')
,type:parts[0]
,handler:hid
,s:selector||Undef
});
bind(parts[0],elmt,cb);
});
};
$.off2 = function(type,elmt,cb,selector){
$.each(type.split(/\s+/),function(k,type){
var parts = type.split('.'), hid=cb?uid(cb):Undef,ns=parts.slice(2).sort().join(' '),type=parts[0];
if(! handlers.elmts[uid(elmt)]){
return;
}
for(var eid=uid(elmt), i=handlers.elmts[eid].length,hdef;~--i;){
hdef = handlers.elmts[eid][i];
(!type || hdef.type===type)
&& (!selector || hdef.s===selector)
&& (!hid || hdef.handler===hid)
&& (!ns || (hdef.ns && hdef.ns.match(new RegExp('(^| )'+ns+'( |$)'))))
&& ( unbind(hdef.type,elmt,handlers.handlers[hdef.handler]) || handlers.elmts[eid].splice(i,1) );
}
});
}
/**
* iterate over collection using a given callback (cb)
*/
$.each = function(collection, cb){
var i,l,key;
if((collection instanceof Array) || (collection instanceof NodeList) ) {
for(i=0,l=collection.length; i<l;i++){
if(cb.call(collection[i], i, collection[i]) === false){
return collection;
}
}
}else{
for(key in collection){
if(collection.hasOwnProperty(key) && cb.call(collection[key],key,collection[key]) === false){
return collection;
}
}
}
return collection;
};
$.Event=function(type,props){
var event = document.createEvent(type.match(/^(click|mousedown|mouseup|mousemove)$/) ? 'MouseEvents':'Events');
props && $.extend(event,props);
event.initEvent(type, (props && props.bubbles===false)?false:true, true, null, null, null, null, null, null, null, null, null, null, null, null);
return event;
};
// some internal helpers
var dfltDisplays={}
,Undef // undefined pointer
,handlers={elmts:{},handlers:{}}
,_uid=0
,uid=function(o){ return o._uid || (o._uid=++_uid); }
,ok=function(){return true;}
,nok=function(){return false;}
,hasClass = function (elmt,className){ return ( 'className' in elmt && elmt.className.match(new RegExp('(^|\\s)'+className+'($|\\s)')) ) ? true : false; }
,getComputed = function (elmt,propName){ return ( window.getComputedStyle && window.getComputedStyle(elmt,null).getPropertyValue(propName) );}
,matchSelector = function(elmt,selector){
var fn = elmt.webkitMatchesSelector || elmt.mozMatchesSelector || elmt.oMatchesSelector || elmt.matchesSelector;
return fn ? fn.call(elmt,selector) : !!~ $(selector,elmt.parentNode).indexOf(elmt);
}
,matchFunction = function(elmt,fn,k){ return fn.call(elmt,k); }
,matchCollection = function(elmt,collection){ return !!~collection.indexOf(elmt) }
,getMatchFn = function(selector){ var sType = typeof selector; return sType === 'string' ? matchSelector : (sType ==='function' ? matchFunction : matchCollection ); }
,isArray = function(a){ return a instanceof Array;}
,isFunction = function(f){ return f instanceof Function;}
,isNumeric = function(n){ return !isNaN( parseFloat(n) ) && isFinite(n);} // from jquery source code
,isEmptyObject = function(o){ var res=true; $.each(o,function(){ return (res=false);}); return res;}
,isObject = function(o){ return typeof o === 'object'}
,isPlainObject = function(o){
if((! (o instanceof Object) ) || o.nodeType || (o.window && o.window===o) ){
return false;
}
try{
if( o.constructor.prototype.hasOwnProperty( "isPrototypeOf" ) ){
return true;
}
}catch(e){}
return false;
}
,isDomNode = function(n){ return (isObject(n) && n.nodeType) }
;
$.isArray = isArray;
$.isFunction = isFunction;
$.isNumeric = isNumeric
$.isEmptyObject = isEmptyObject;
$.isPlainObject = isPlainObject;
$.isDomNode= isDomNode;
$.fn = {
each:function(cb){ $.each(this,cb); return this; }
//- ,on:function(type,cb){ return $.each(this,function(k,v){ $.on(type,v,cb); }); }
,on:function(types,selector,data,handler){
var a = arguments,al=a.length,cb;
handler || (handler = a[a.length-1]);
if(a.length === 2 ){
selector = data = Undef ;
}else if(a.length === 3 ){
if( isObject(selector) ){
data = selector; data=Undef;
}else{
data=Undef;
}
}
var hid = uid(handler);
handlers.handlers[hid]=handler;
cb = (selector || data) ? function(e){ if( selector===Undef || $(e.target).is(selector) ){ data && (e.data = data); return handler.call(e.target,e); } } : handler ;
return $.each(this,function(k,elmt){
$.on2(types,elmt,cb,selector);
});
}
//- ,off:function(type,cb){ return $.each(this,function(k,v){ $.off(type,v,cb); }); }
,off:function(types,selector,handler){
if((! handler) && isFunction(selector) ){
handler=selector; selector=Undef;
}
return $.each(this,function(k,elmt){ $.off2(types,elmt,handler,selector) });
}
,trigger:function(event, data){
if( typeof event === 'string' ){
event = $.Event(event);
}
data && (event.data = data);
return $.each(this,function(k,v){
if( 'dispatchEvent' in v){ v.dispatchEvent(event);}
});
}
,css:function(propName,value){ // don't support .css( propertyName, function(index, value) ) or any compatibility as on opacity or other stuffs
if( typeof propName === 'object' ){
var collection = this;
$.each(propName,function(name,val){
collection.css(name,val);
});
return this;
}
if( propName.match(/[A-Z]/) ){ // @todo this part need review
propName = propName.replace(/([A-Z])/g,'-$1');
}
var camelCasedPropName = propName.replace(/-([a-z])/g,function(m,l){ return l.toUpperCase();});
if( value === Undef){
return this[0].style[camelCasedPropName] || getComputed(this[0],propName);
}
return $.each(this,function(k,v){
v.style[camelCasedPropName]=value;
});
}
,attr:function(attrName,value){
if( value === Undef){
var res = this[0].getAttribute(attrName);
return ((! res ) && attrName in this[0]) ? this[0][attrName]:res;
}
return $.each(this,function(k,v){
if( k in v ){
v[attrName] = value;
}else{
value ? v.setAttribute(attrName,value) : v.removeAttribute(attrName);
}
});
}
,prop:function(propName,value){
return value!==Undef?(this[0] && this[0][propName]) : $.each(this,function(k,v){ v[propName] = value;});
}
,html:function(html){
var prop = this[0].nodeType === 1 ? 'innerHTML' : 'value';
return html===Undef?this[0][this[0].nodeType===1?'innerHTML':'value'] : $.each(this,function(k,elmt){ elmt[elmt.nodeType === 1 ? 'innerHTML' : 'value']=html;});
}
,remove:function(){
return $.each(this,function(){ this.parentNode && this.parentNode.removeChild(this);});
}
,find:function(selector){
var res = [];
$(selector,this).each(function(){
res.push(this);
});
return $(res);
}
,hasClass:function(className){
var res=false;
$.each(this,function(){
if( hasClass(this,className) ){
return !(res = true);
}
});
return res;
}
,addClass:function(className){
className = className.split(/\s+/);
return $.each(this,function(k,elmt){
$.each(className,function(){
hasClass(elmt,this) || (elmt.className += (elmt.className.length?' ':'')+this);
});
});
}
,removeClass:function(className){
var exp = new RegExp('(^|\\s)('+className.replace(/\s+/g,'|')+')($|\\s)','g')
, replace=function(m,a,className,b){ return a===b?' ':'';}
;
return $.each(this,function(){
this.className = this.className.replace(exp,replace);
});
}
,toggleClass:function(className,addOrRemove){
addOrRemove = (addOrRemove===Undef) ? hasClass : (addOrRemove?ok:nok);
className = className.split(/\s+/);
return $.each(this,function(k,elmt){
$.each(className,function(){
$(elmt)[ addOrRemove(elmt,this)?'addClass':'removeClass' ](this);
});
});
}
,hide:function(){
return $.each(this,function(){
var e=$(this), curDisplay = e.css('display');
if( curDisplay === 'none' ){
return;
}
this.setAttribute('data-bc-oldisplay',curDisplay);
e.css('display','none');
});
}
,show:function(){
return $.each(this,function(){
this.style.display = null;
if( getComputed(this,'display') === 'none' ){
this.style.display = this.getAttribute('data-bc-olddisplay') || (function(e){
if(! dfltDisplays[e.nodeName]){
var d = document.createElement(e.nodeName),b = document.body;
b.appendChild(d);
dfltDisplays[e.nodeName] = getComputed(d,'display');
dfltDisplays[e.nodeName] === 'none' && (dfltDisplays[e.nodeName] = 'block');
b.removeChild(d);
}
return dfltDisplays[e.nodeName];
})(this);
}
});
}
,toggle:function(showOrHide){
showOrHide = (showOrHide===Undef)?function(e){ return e.css('display')==='none';}:(showOrHide?ok:nok);
return $.each(this,function(){
var e = $(this); e[showOrHide(e)?'show':'hide']();
});
}
,is:function(selector){ // selector may be a string, a function(index), a Dom node or collection
if( isDomNode(selector) ){ // match against a node
return !!~this.indexOf(selector);
}
var res = false ,testFn = getMatchFn(selector);
$.each(this,function(k,elmt){
if(testFn(elmt,selector,k)){
return ! (res = true);
}
});
return res;
}
,filter:function(filter){
if( isDomNode(filter) ){
return $(~this.indexOf(filter)?filter:[]);
}
var res = [],testFn = getMatchFn(filter);
$.each(this,function(k,elmt){
testFn(elmt,filter,k) && res.push(elmt);
});
return $(res);
}
,not:function(selector){
if( isDomNode(selector) ){
return $( ~this.indexOf(selector)?[]:selector);
}
var res = [],testFn = getMatchFn(selector);
$.each(this,function(k,elmt){
testFn(elmt,selector,k) && res.push(elmt);
});
return $(res);
}
,closest:function(selector){ // no context support
var res=[];
$.each(this,function(k,node){
do{
if( $(node).is(selector) ){
return ! res.push(node);
}
}while(node = node.parentNode);
});
return $(res);
}
};
window.$ = $;
}})(typeof $ !== 'undefined'? $ : false);
@malko
Copy link
Author

malko commented Jul 26, 2013

Moved to its own repository: https://github.com/malko/basic-compat

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment