Created
August 6, 2013 00:26
-
-
Save asimjalis/6160923 to your computer and use it in GitHub Desktop.
JavaScript library by Jeremy Osborne which implements a light-weight version of some ideas from jQuery.
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
(function() { | |
/** | |
* My JavaScript library. | |
* | |
* @author [your name here] | |
*/ | |
/** | |
* @class A namespace is a collection of functions and classes. | |
* This collection acts as a name to our library and helps us to keep | |
* the functions we write from accidentally colliding with other | |
* functions and objects on the page. | |
* | |
* In this case, it is also a helper function that acts as a rudimentary | |
* selector engine for us. | |
* @static | |
*/ | |
window.my$ = function(selector) { | |
// Our selector engine only works in one of two ways. | |
// IE < 8 note: can't use string[x] indexing, must use charAt. | |
if (selector.charAt(0) == "#") { | |
// byId, trim off the "#" before passing in. | |
return document.getElementById(selector.substr(1)); | |
} | |
else { | |
// We assume it's a tag name. | |
return document.getElementsByTagName(selector); | |
} | |
}; | |
//-------------------------------------------------------- Events | |
/** | |
* Slightly normalize some of the more annoying aspects of cross-browser | |
* event objects. | |
*/ | |
var fixEvent = function(event) { | |
var fixedEvent = {}, | |
i; | |
for (i in event) { | |
fixedEvent[i] = event[i]; | |
} | |
// Very simple special additions | |
if (!fixedEvent.target) { | |
// Props to jQuery for the following hint on target setting. | |
fixedEvent.target = event.srcElement || document; | |
} | |
if (!fixedEvent.which) { | |
// Again props to jQuery. All I am looking for here is the | |
// keycode. | |
fixedEvent.which = event.charCode || event.keyCode; | |
} | |
if (fixedEvent.pageX === undefined && event.clientX !== undefined) { | |
fixedEvent.pageX = event.clientX + document.body.scrollLeft | |
+ document.documentElement.scrollLeft; | |
fixedEvent.pageY = event.clientY + document.body.scrollTop | |
+ document.documentElement.scrollTop; | |
} | |
// Fix up the preventDefault method. | |
fixedEvent.preventDefault = function() { | |
if (event.preventDefault) { | |
// !IE | |
event.preventDefault(); | |
} | |
else { | |
// IE | |
event.returnValue = false; | |
} | |
}; | |
return fixedEvent; | |
}; | |
/** | |
* Add an event handler to a DOM element. This function uses the best method | |
* available to attach a function to an element depending on browser. All | |
* registered event functions will be passed the event object as the first, and | |
* only, parameter. On most modern browsers, multiple and different functions can | |
* be registered to the same event type. | |
* This is an expansion of John Resig's addEvent function found | |
* <a href="http://ejohn.org/projects/flexible-javascript-events/">here</a>. | |
* @param {DOM Object} obj The page element we wish to attach the | |
* event to. | |
* @param {string} type The base name of the event. For example, | |
* pass in "click" for single click events, "onclick" would be incorrect. | |
* A list of event names can be found | |
* <a href="http://www.quirksmode.org/dom/events/index.html">here</a>. | |
* There is no validity checking on the name of the event. | |
* @param {function} fn The event handling function to attach to this | |
* event. Irrespective of browser type, all functions will be passed the event | |
* object as the parameter. Function is still responsible for dealing with | |
* event object property browser inconsistencies. | |
* @static | |
*/ | |
my$.on = function (obj, type, fn) { | |
if (obj.addEventListener) { | |
// Modern browser event registry. | |
obj.addEventListener(type, function(event) { | |
fn.call(this, fixEvent(event)); | |
}, false); | |
} | |
else if (obj.attachEvent) { | |
// Older IE event registration. | |
// Don't reregister the same event more than once. | |
if (obj['e'+type+fn]) { | |
return; | |
} | |
obj['e'+type+fn] = fn; | |
obj[type+fn] = function(){ | |
obj['e'+type+fn].call(obj, fixEvent(window.event)); | |
}; | |
obj.attachEvent('on'+type, obj[type+fn]); | |
} | |
else { | |
// old style method of adding that overwrites any function already | |
// added as a listener to this event. | |
// Make sure your code doesn't require multiple functions to be registered | |
// at the same time. | |
obj['on'+type] = fn; | |
} | |
}; | |
/** | |
* Attach drag and drop functionality to a page element. | |
* Make sure it is logical to drag the object on the page before | |
* enabling drag and drop on an object. | |
* @param {DOM Object} obj HTML element to make draggable and droppable. | |
* @param {Object} [config] Container for optional settings. | |
* @param {Function} [config.start] A function that will be called when the | |
* drag event starts. | |
* @param {Function} [config.stop] A function that will be called when the | |
* drag event stops. | |
*/ | |
my$.makeDraggable = function (obj, config) { | |
// Our initial offsets for our object | |
var deltaX, | |
deltaY, | |
isDragging = false; // Dragging state | |
var startDrag = function(e) { | |
if (!e.target === this){ | |
return; | |
} | |
// Begin the drag | |
isDragging = true; | |
if (typeof config.start == "function") { | |
// Make the dragging object the "this" | |
config.start.call(obj); | |
} | |
// One trick is that we need to make the element position-able. | |
// Here we use absolute positioning, which may not be ideal at all | |
// times. | |
obj.style.position = "absolute"; | |
deltaX = e.clientX - obj.offsetLeft; | |
deltaY = e.clientY - obj.offsetTop; | |
}; | |
var move = function(e) { | |
var x, | |
y; | |
if ( !isDragging ) { | |
return; | |
} | |
x = e.clientX; | |
y = e.clientY; | |
obj.style.left = (x - deltaX) + "px"; | |
obj.style.top = (y - deltaY) + "px"; | |
}; | |
var stopDrag = function(e) { | |
// Stop dragging of this object | |
isDragging = false; | |
if (typeof config.stop == "function") { | |
// Make the dragging object the "this" | |
config.stop.call(obj); | |
} | |
}; | |
config = config || {}; | |
my$.on(obj, "mousedown", startDrag); | |
my$.on(document.body, "mouseup", stopDrag); | |
my$.on(document.body, "mousemove", move); | |
}; | |
//-------------------------------------------------------- Classes | |
/* | |
* Build a regular expression that can match an value of an HTML class | |
* attribute. | |
*/ | |
var makeClassRE = function(className) { | |
return new RegExp("(^|\\s)"+className+"(\\s|$)"); | |
}; | |
/* | |
* If an element has a particular class, remove it. | |
*/ | |
my$.removeClass = function(el, className) { | |
var re = makeClassRE(className); | |
el.className = el.className | |
// Remove the class | |
.replace(re, " ") | |
// Trim any leading or trailing white space | |
.replace(/^\s+|\s+$/g,""); | |
}; | |
/* | |
* If an element does not yet have a particular class, add it. | |
*/ | |
my$.addClass = function(el, className) { | |
var re = makeClassRE(className); | |
if (re.test(el.className) == false) { | |
if (el.className.length > 0) { | |
// Some class exists, add a separator | |
el.className += " " + className; | |
} | |
else { | |
el.className += className; | |
} | |
} | |
}; | |
//--------------------------------------------------------- Generic Animations | |
/** | |
* Adapted from ppk and quirksmode article: | |
* http://www.quirksmode.org/dom/getstyles.html | |
* | |
* Recommend only using this functon the most significant rules available. | |
* It's probably a bad idea to check for "border". It seems to be an okay idea | |
* to check for "border-bottom-color". It's a bad idea to check for "font". | |
* It's an okay idea to check for "font-style" or "font-family". | |
* @param el {HTMLElement} Element on which we wish to check for opacity. | |
* @return {String} The value of the opacity, in decimal format (from | |
* 0 to 100). | |
*/ | |
var getOpacity = function(el) { | |
var val; | |
if (el.currentStyle) { | |
// IE and Opera, style names are JavaScriptese (camel case). | |
val = /alpha\(opacity=([0-9]+)\)/.exec(el.currentStyle["filter"]); | |
// If we have a match, we'll get an array match object, | |
// or we'll get null. | |
val = val && parseInt(val[1]); | |
} | |
else if (window.getComputedStyle) { | |
// Others, css rules must be hyphenated. | |
val = getComputedStyle(el, null).getPropertyValue("opacity"); | |
val = parseInt(val) * 100; | |
} | |
// Make sure to return a default, but zero is a valid return. | |
return (val !== null && val !== undefined && !isNaN(val)) ? val : 100; | |
}; | |
/** | |
* Fades an element out (by default) or in. | |
* | |
* @param el {DOM Element} The element to change the opacity of. | |
* @param [config] {Object} Container for optional settings. | |
* @param [config.start] {Function} A function that will be called when the | |
* animation starts. | |
* @param [config.stop] {Function} A function that will be called when the animation | |
* stops. | |
* @param [config.toOpaque] {Boolean} If true, reverses direction and fades | |
* in the element. | |
*/ | |
my$.fade = function(el, config) { | |
config = config || {}; | |
var delta = config.toOpaque ? 1 : -1, | |
end = config.toOpaque ? 100 : 0, | |
start = getOpacity(el), | |
speed = 20, | |
opacity = start; | |
var changeOpacity = function() { | |
opacity += delta; | |
// Normalize | |
opacity = (opacity > 100) ? 100 : opacity; | |
opacity = (opacity < 0) ? 0 : opacity; | |
// !IE and IE >= 9 | |
el.style.opacity = (opacity / 100); | |
// IE < 9 | |
el.style.filter = "alpha(opacity=" + opacity + ")"; | |
if (opacity < 100 && opacity > 0) { | |
setTimeout(changeOpacity, speed); | |
} | |
else { | |
// We're done | |
if (typeof config.stop == "function") { | |
config.stop.call(el); | |
} | |
} | |
}; | |
// Call start, if it exists. Pass the element. | |
if (typeof config.start == "function") { | |
config.start.call(el); | |
} | |
changeOpacity(); | |
}; | |
//--------------------------------------------------------- AJAX Data Loading | |
/** | |
* Asynchronously load an external document and make the content accessible. | |
* @example | |
* // Default call, using XMLHTTPRequest | |
* // myCallback will be passed 1 argument which is the xhr object at | |
* // the time readyState == 4 | |
* my$.xhr("http://example.com/l33t.html", myCallback); | |
* @param {string} url The location of the document to load. | |
* @param {function} callback Handles data retrieved via the XMLHttpRequest. | |
*/ | |
my$.xhr = function(url, callback){ | |
try { | |
// for testing with local files from the file:// protocol. | |
// AJAX suffers from the same domain policy. | |
// Only need to use in a pinch with CORS implemented on the server | |
// side (and obviously never used in production). | |
netscape.security.PrivilegeManager.enablePrivilege("UniversalPreferencesWrite UniversalBrowserWrite UniversalPreferencesRead UniversalBrowserRead"); | |
} catch (e) {} | |
var xhr = null; | |
if (window.XMLHttpRequest) { | |
xhr = new XMLHttpRequest(); | |
if (!("withCredentials" in xhr) && window.XDomainRequest) { | |
// XDomainRequest for IE 8 and 9, otherwise IE7 will get the | |
// old XMLHttpRequest object but will not be able to make | |
// cross domain requests. | |
xhr = new XDomainRequest(); | |
} | |
} | |
else { | |
// If the above failed, we must be within an older Internet Explorer | |
if (window.ActiveXObject) { | |
try { | |
xhr = new ActiveXObject("Microsoft.XMLHTTP"); | |
} | |
catch (e) {} | |
} | |
} | |
if (!xhr) { | |
alert("You do not have an AJAX capable browser."); | |
return; | |
} | |
xhr.open("GET", url, true); | |
xhr.onreadystatechange = function(){ | |
if (this.readyState == 4) { | |
// NOTE: we are leaving out checks for xhr.status | |
// because we are working with local files | |
callback(this); | |
} | |
} | |
xhr.send(null); | |
}; | |
// A number we will increment for each of our jsonp requests. | |
my$.jsonpSerial = 0; | |
/** | |
* Asynchronously load an external document and make the content accessible | |
* via JSONP. | |
* @example | |
* my$.jsonp("http://test.org/test?status=good&callback={callback}", callback); | |
* @param url {string} The location of the document to load. Must include the | |
* substring {callback} within the URL. | |
* @param callback {function} A function to be called when this request | |
* returns. The callback will be passed the arguments that the JSONP callback | |
* initially received. | |
*/ | |
my$.jsonp = function(url, callback) { | |
var script = document.createElement("script"), | |
head = document.getElementsByTagName("head")[0], | |
// Prefix to our global callback name for this call. | |
functionKey = "jsonpCallback" + (my$.jsonpSerial++); | |
// Generate our global callback. | |
window[functionKey] = function(data) { | |
// Call our callback when we are called. | |
callback(data); | |
}; | |
// Rewrite our URL correctly. | |
url = url.replace("{callback}", functionKey); | |
if (script.readyState){ | |
// Unload script tag from page in IE | |
script.onreadystatechange = function(){ | |
if (script.readyState == "loaded" || script.readyState == "complete"){ | |
script.onreadystatechange = null; | |
head.removeChild(script); | |
// Delete our callback. | |
delete window[functionKey]; | |
} | |
}; | |
} | |
else { | |
// Unload script tag from page in other browsers | |
script.onload = function(){ | |
head.removeChild(script); | |
// Delete our callback. | |
delete window[functionKey]; | |
}; | |
} | |
script.onerror = function() { | |
// Hopefully we never see this | |
alert("The request to: " + url + " failed."); | |
}; | |
// Set-up the script and attach to the HTML document | |
// to trigger the HTTP GET request. | |
script.type = "text/javascript"; | |
script.src = url; | |
head.appendChild(script); | |
}; | |
//--------------------------------------------- Storage Wrappers | |
// The localStorage object. | |
// We either use the actual local storage API, or we fallback | |
// to an in memory cache. | |
// The in memory cache will not persist across page reloads, | |
// obviously. | |
var localStorage = window.localStorage || {}; | |
/** | |
* Takes a string and attempts to persist it. | |
* @param key {String} Cache key we are attempting to save. | |
* @param value {String} What we are attempting to save. | |
* @return {Boolean} true if we could save the item, false if | |
* not. | |
*/ | |
my$.storeSave = function(key, value) { | |
localStorage[key] = value; | |
}; | |
/** | |
* Attempts to load a key. | |
* @param key {String} Cache key we are attempting to save. | |
* @return {String|Undefined} A string if the value exists, otherwise | |
* undefined. | |
*/ | |
my$.storeLoad = function(key) { | |
return localStorage[key]; | |
}; | |
/** | |
* Attempts to remove a specific key from storage. | |
* @param key {String} Cache key to remove. | |
*/ | |
my$.storeRemove = function(key) { | |
delete localStorage[key]; | |
}; | |
/** | |
* Clears the storage. | |
*/ | |
my$.storeClear = function() { | |
if (localStorage.clear) { | |
// The real local storage requires calling | |
// of a clear method... | |
localStorage.clear(); | |
} | |
else { | |
// ... but our in memory cache can just | |
// be garbage collected. | |
localStorage = {}; | |
} | |
}; | |
})(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment