Last active
August 29, 2015 14:01
-
-
Save morten-olsen/cafa126d758c6cdba22d to your computer and use it in GitHub Desktop.
A collection of front end scripts (1.3KB gzipped)
This file contains hidden or 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
window.R.def('async', [], function (exports) { | |
exports.handle = function (target, origin) { | |
var xhr = new XMLHttpRequest(); | |
xhr.onload = function () { | |
var data = JSON.parse(xhr.responseText); | |
for (var i = 0; i < data.changes.length; i++) { | |
window.R.publish('async.changeRequest', data.changes[i]); | |
} | |
}; | |
if (target.tagName = 'form') { | |
xhr.open('POST', target.action || target.attributes['datahref'], true); | |
xhr.send(/* SERIALIZE FORM DATA */); | |
} else { | |
xhr.open('GET', target.href || target.attributes['datahref'], true); | |
xhr.send(); | |
} | |
}; | |
function init () { | |
window.R.listen('async.changeRequest', function (data) { | |
var elements = document.querySelectorAll(data.selector); | |
for (var i = 0; i < elements.length; i++) { | |
var element = elements[i]; | |
switch (data.type) { | |
case 'remove': | |
element.remove(); | |
break; | |
case 'replace': | |
element.innerHTML = data.content; | |
break; | |
case 'add': | |
var elm = document.createElement('div'); | |
elm.innerHTML = data.content; | |
for (var b = 0; b < elm.childNodes.length; b++) { | |
element.append(elm.childNodes[b]; | |
} | |
break; | |
} | |
} | |
}); | |
}; | |
init(); | |
return exports; | |
}); |
This file contains hidden or 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
/* Bootloader */ | |
(function () { | |
'use strict'; | |
var me = window.R.boot = {}, | |
cache = me.cache = {}, | |
loading = me.loading = {}, | |
waiting = me.waiting = [], | |
version = window.R.jsversion || '0', | |
lCache = window.R.cache = (window.R.cache || { | |
get : function () { return false; }, | |
set : function () {} | |
}); | |
/** | |
* Actually invoke a waiting call with the dependancies | |
* | |
* @public | |
* @param {object} call - the waiting call | |
*/ | |
function invoke(call) { | |
var dep = [], | |
i = null; | |
for (i = 0; i < call.dep.length; i += 1) { | |
// Each dependency is added to the array | |
dep.push(cache[call.dep[i]]); | |
} | |
// the original callback is called with the dependencies as arguments | |
call.cb.apply(window, dep); | |
} | |
/** | |
* Creates an xhr loader. | |
* This is selfcontained, since the downloaded code is evaled, and should | |
* contain a R.def(...) | |
* | |
* @public | |
* @param {string} name - The name of the module to be loaded. | |
*/ | |
function getLoader(name) { | |
return function () { | |
var xhr = new XMLHttpRequest(); | |
xhr.onload = function () { | |
// This is evil, but nessecary | |
eval(xhr.responseText); | |
// Only if the local-storage cache module is loaded | |
lCache.set(name, xhr.responseText); | |
}; | |
// Download the module from a versioned url | |
xhr.open('GET', '/module/' + version + '/' + name, true); | |
xhr.send(); | |
}; | |
} | |
/** | |
* Define a new module. | |
* @param {string} name - The name of the module | |
* @param {array} dependencies - A list of modules needed by the module | |
* @param {function} init - The constructor code | |
*/ | |
me.def = window.R.def = function (name, dep, init) { | |
// We actually need to load the module through the loader itself | |
// to ensure all dependencies are ready, therefor we | |
// add the creation code as the last element of the dependencies | |
// so we can use window.R.load('dep1', 'dep2', ..., callback); | |
dep.push(function () { | |
var args = Array.prototype.slice.call(arguments, 0), | |
obj = null, | |
i = 0, | |
missing = null; | |
// Add an empty object to the start of the array, to be 'exports' | |
args.unshift({}); | |
// Invoke the actual constructor, with all dependancies loaded, and | |
// with exports as the first item. | |
obj = init.apply(window, args); | |
// Add it to the cache, so we don't have to load it again. | |
cache[name] = obj; | |
// Next we need to see it this module is the last needed by any of our | |
// waiting calls, so we can invoke them. | |
for (i = 0; i < waiting.length; i += 1) { | |
// Check if the current module is a dependency for the call | |
if (!!waiting[i].waiting[name]) { | |
// If it is, remove it form the list of missing modules | |
delete waiting[i].waiting[name]; | |
// Check if any missing modules left | |
if (Object.keys(waiting[i].waiting).length === 0) { | |
// If there are no more modules to wait for, the call is invoked | |
invoke(waiting[i]); | |
} | |
} | |
} | |
}); | |
// Invoke the loader | |
me.load.apply(window, dep); | |
}; | |
/** | |
* Fetch a series of module, using load('dep1', 'dep2', ..., function (dep1, dep2, ...) { | |
* }); | |
* | |
* @public | |
*/ | |
me.load = window.R.load = function () { | |
// Creates a copy of arguments, which we can manipulate | |
var args = Array.prototype.slice.call(arguments, 0); | |
// loading waits for dom-ready | |
window.R.go(function () { | |
// Move the last argument to callback, so left is only the modules | |
var cb = args.pop(), | |
i = 0, | |
call = null, | |
data = null; | |
call = { | |
cb : cb, | |
dep : args, | |
waiting : {} | |
}; | |
// Loop though all dependencies | |
for (i = 0; i < args.length; i += 1) { | |
// Check if local storage caching is enabled | |
if (lCache.get(args[i])) { | |
// if, load the module data from it | |
data = lCache.get(args[i]); | |
// And load the module, so it is ready | |
eval(data); | |
} | |
// Check if the module is previously loaded | |
if (!!cache[args[i]]) { | |
continue; | |
} | |
// Add the module as a missing module to be loaded | |
call.waiting[args[i]] = true; | |
// See if the module is already loading | |
if (loading[args[i]]) { | |
return; | |
} | |
// Ensure that multible call to same dependencies, | |
// only download one | |
loading[args[i]] = true; | |
// Create a loader, and start it in a new thread | |
// note: might not be nessecary to use setTimeout? | |
setTimeout(getLoader(args[i]), 10); | |
} | |
// If no dependencies are missing... | |
if (Object.keys(call.waiting).length === 0) { | |
// ...then just invoke the call | |
invoke(call); | |
} else { | |
// Else add it to the list of waiting calls | |
waiting.push(call); | |
} | |
}); | |
}; | |
}()); |
This file contains hidden or 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
cat core.js pubsub.js bootloader.js primer.js utils.js | uglifyjs -o r.min.js |
This file contains hidden or 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
/** | |
* A general placeholder, where all methods/objects are attached to | |
* so we don't polute the global scope more than absolutely nessecery. | |
*/ | |
window.R = {}; |
This file contains hidden or 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
/* Primer */ | |
(function () { | |
'use strict'; | |
var events = [ | |
'click', | |
'keydown', | |
'keyup', | |
'mouseover', | |
'mouseout' | |
], i; | |
/** | |
* Finds the nearest element (searches out) which has a given attribute | |
* @private | |
* @param {string} The name of the attribute | |
* @param {element} The element to start from | |
*/ | |
function findNearest(attr, elm) { | |
// If we get to the top, we return null (non in line) | |
if (!elm) { | |
return null; | |
} | |
// It the element has the attribute, we return it | |
if (!!elm.attributes[attr]) { | |
return elm; | |
} | |
// If the element has a parent, we continue our search | |
return findNearest(elm.parentElement); | |
} | |
/** | |
* The handling of an event, where we look for a primed event | |
* and if, invoke the module and call its handler | |
* @private | |
* @param {event} evt - The event | |
*/ | |
function handle(evt) { | |
// Find the nearest element with a primer attribute corresponding | |
// to the eventtype (fx. data-primer-click="async" for a click event) | |
var target = findNearest('data-primer-' + evt.type, evt.target), | |
module = null; | |
// If any primed elements are found: | |
if (target) { | |
// Get the module name from the attribute | |
module = target.attributes['data-primer-' + evt.type].value; | |
// Load the module, and parse the primed element, and the target | |
// element of the event (the element where the event was fired from) | |
window.R.boot.load(module, function (m) { | |
m.handle(target, evt.target); | |
}); | |
} | |
} | |
// For each of the event type we want to listen to... | |
for (i = 0; i < events.length; i += 1) { | |
// ... We add the handler | |
document.documentElement.addEventListener(events[i], handle); | |
} | |
}()); |
This file contains hidden or 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
/* Pub/Sub */ | |
(function () { | |
'use strict'; | |
var me = window.R.pubsub = {}, | |
listeners = {}, | |
stickies = {}; | |
/** | |
* Subscribe to a notification channel, where callback | |
* will be called with the options published | |
* | |
* @public | |
* @param {string} channel - the channel name to listen to | |
* @param {function} callback - the callback to be called when published to channel | |
*/ | |
me.listen = window.R.listen = function (channel, callback) { | |
var i; | |
// Ensures that the channel is created | |
listeners[channel] = listeners[channel] || []; | |
//Adds the callback as a listener | |
listeners[channel].push(callback); | |
// Check if there are any sticky notification for th given channel | |
if (!!stickies[channel]) { | |
for (i = 0; i < stickies[channel].length; i += 1) { | |
// And appies them to the listener | |
callback(stickies[channel][i]); | |
} | |
} | |
}; | |
/** | |
* Removes a callback previous set as a listener. | |
* | |
* @public | |
* @param {string} channel - the name of the channel originally listened to | |
* @param {function} callback - the original callback | |
*/ | |
me.ignore = window.R.ignore = function (channel, callback) { | |
var i; | |
// Ensures that the channel is created | |
listeners[channel] = listeners[channel] || []; | |
for (i = 0; i < listeners[channel].length; i += 1) { | |
if (listeners[channel][i] === callback) { | |
// If the listener is found in the channel, we removes it | |
delete listeners[channel][i]; | |
} | |
} | |
}; | |
/** | |
* Used for calling all listeners on a channel, and send them options | |
* | |
* @public | |
* @param {string} channel - the name of the channel | |
* @param {object} options - the data to be send to the listeners | |
* @param {boolean} sticky - if the notification should be stored for later listeners | |
*/ | |
me.publish = window.R.publish = function (channel, options, sticky) { | |
var i = 0; | |
// Ensures that the channel is created | |
listeners[channel] = listeners[channel] || []; | |
for (i = 0; i < listeners[channel].length; i += 1) { | |
// Pass and invoke each listener | |
listeners[channel][i](options); | |
} | |
if (sticky) { | |
// create a sticky channel if not already created | |
stickies[channel] = stickies[channel] || []; | |
// Add the option for later listeners | |
stickies[channel].push(options); | |
} | |
}; | |
/** | |
* Simple helper function, which works much like jQuerys $(window).ready(fn) | |
* | |
* @public | |
* @param {function} callback - the function to be invoked when content is loaded | |
*/ | |
window.R.go = function (callback) { | |
window.R.listen('domready', function () { | |
callback(); | |
}); | |
}; | |
}()); |
This file contains hidden or 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
/* Utils */ | |
(function () { | |
'use strict'; | |
var me = window.R.utils = {}; | |
/** | |
* Used to ensure a function is only called when no new calls has been made | |
* for x time. Used for things like autocomplete, where you don't want to | |
* get results before after 1 sec after the user has pushed a button. | |
* @see http://remysharp.com/2010/07/21/throttling-function-calls/ | |
* | |
* @public | |
* @param {function} callback - The callback for when the wait period has passed | |
* @param {integer} wait - The ms which should have passed, before the callback is called | |
*/ | |
me.debounce = function (callback, wait) { | |
var timer; | |
return function () { | |
var context = this, args = arguments; | |
clearTimeout(timer); | |
timer = setTimeout(function () { | |
callback.apply(context, args); | |
}, wait); | |
}; | |
}; | |
/** | |
* Only call the functions once every X, regardless of how often it is called | |
* Usefull for things like scroll | |
* @see http://remysharp.com/2010/07/21/throttling-function-calls/ | |
* | |
* @public | |
* @param {function} fn - the callback to call | |
* @param {integer} threshhold - the min. time between calls | |
*/ | |
me.throttle = function (fn, threshhold) { | |
threshhold = threshhold || 250; | |
var last, | |
deferTimer; | |
return function () { | |
var context = this, | |
now = new Date(), | |
args = arguments; | |
if (last && now < last + threshhold) { | |
clearTimeout(deferTimer); | |
deferTimer = setTimeout(function () { | |
last = now; | |
fn.apply(context, args); | |
}, threshhold); | |
} else { | |
last = now; | |
fn.apply(context, args); | |
} | |
}; | |
}; | |
/** | |
* A cross browser version of log | |
* | |
* @public | |
*/ | |
me.log = function () { | |
if (!!window.console) { | |
window.console.log(arguments); | |
} | |
}; | |
/** | |
* We want to log the errors thrown, so therefore we attach | |
* to consoles error (this only works in IE9+) | |
*/ | |
(function (errHandler) { | |
var org = errHandler; | |
errHandler = function () { | |
var xhr = new XMLHttpRequest(); | |
xhr.open('POST', '/ajax/logerror'); | |
xhr.setRequestHeader('Content-type', 'application/json'); | |
xhr.send(JSON.stringify({ | |
err : arguments, | |
url : window.location.href | |
})); | |
org(arguments); | |
}; | |
}(window.console.error || function () {})); | |
/** | |
* A cross browser content loaded method, found on stackoverflow | |
* @see http://stackoverflow.com/questions/6902280/cross-browser-dom-ready | |
* | |
* @private | |
*/ | |
function contentLoaded(win, fn) { | |
var done = false, top = true, | |
doc = win.document, root = doc.documentElement, | |
add = doc.addEventListener ? 'addEventListener' : 'attachEvent', | |
rem = doc.addEventListener ? 'removeEventListener' : 'detachEvent', | |
pre = doc.addEventListener ? '' : 'on', | |
init = function (e) { | |
if (e.type === 'readystatechange' && doc.readyState !== 'complete') { | |
return; | |
} | |
(e.type === 'load' ? win : doc)[rem](pre + e.type, init, false); | |
if (!done && (done = true)) { | |
fn.call(win, e.type || e); | |
} | |
}, | |
poll = function () { | |
try { root.doScroll('left'); } catch (e) { setTimeout(poll, 50); return; } | |
init('poll'); | |
}; | |
if (doc.readyState === 'complete') { | |
fn.call(win, 'lazy'); | |
} else { | |
if (doc.createEventObject && root.doScroll) { | |
try { top = !win.frameElement; } catch (e) { } | |
if (top) { | |
poll(); | |
} | |
} | |
doc[add](pre + 'DOMContentLoaded', init, false); | |
doc[add](pre + 'readystatechange', init, false); | |
win[add](pre + 'load', init, false); | |
} | |
} | |
/** | |
* Creates a sticky notification, to get R.go(fn) going | |
*/ | |
contentLoaded(window, function () { | |
window.R.publish('domready', null, true); | |
}); | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment