Last active
October 12, 2015 02:47
-
-
Save malko/3959231 to your computer and use it in GitHub Desktop.
minimalist jquery/zepto compatibility layer targeting mobile platforms library development
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Moved to its own repository: https://github.com/malko/basic-compat