Last active
May 2, 2024 02:45
-
-
Save jr-codes/4088609 to your computer and use it in GitHub Desktop.
u.js userscript
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
// ==UserScript== | |
// @name u.js | |
// @namespace http://zarjay.net/ | |
// @description Utility functions for browser console ninjas | |
// @include * | |
// @version 0.28 | |
// ==/UserScript== | |
exec(function() { | |
// Current version of u.js | |
var VERSION = '0.28', | |
// Predefined resources to be included via u.include(name) | |
RESOURCES = { | |
// resource: { css: [], js: [], script: [] } | |
// Design - http://www.sprymedia.co.uk/article/Design | |
// Grid layout, measurement, and alignment bookmarklet | |
design: { | |
js: ['http://www.sprymedia.co.uk/design/design/media/js/design-loader.js'] | |
}, | |
// DOM Monster - http://mir.aculo.us/dom-monster/ | |
// Analyzes DOM and gives performance tips | |
'dom-monster': { | |
js: ['//mir.aculo.us/dom-monster/dommonster.js'] | |
}, | |
// jQuery library - http://jquery.com/ | |
// DOM manipulation, event handling, Ajax, etc. | |
jquery: { | |
js: ['//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js'] | |
}, | |
// jQuery UI - http://jqueryui.com/ | |
// UI/effects library | |
'jquery-ui': { | |
css: ['//ajax.googleapis.com/ajax/libs/jqueryui/1/themes/base/jquery-ui.css'], | |
js: ['//ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js'] | |
}, | |
// Katamari Hack - http://kathack.com/ | |
// Turns a webpage into a game of Katamari Damacy. Use your katamari ball to pick up DOM elements. | |
katamari: { | |
js: ['//ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js', 'http://kathack.com/js/kh.js'] | |
}, | |
// Kickass - https://kickassapp.com/ | |
// Turn a webpage into a game of Asteroids. Use your ship to destroy DOM elements. | |
kickass: { | |
js: ['//hi.kickassapp.com/kickass.js'] | |
}, | |
// Lo-Dash - http://lodash.com/ | |
// Underscore alternative (utility library) | |
lodash: { | |
js: ['//cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.0-rc.3/lodash.min.js'] | |
}, | |
// Markup - http://markup.io | |
// Draw on a webpage and share with others | |
markup: { | |
script: ["(function(){window.add_js=function(s){var k=(document.getElementsByTagName('head')[0]||document.body).appendChild(document.createElement('script'));k.src=s;k.type='text/javascript';k.markup='ea33cdc7-73dd-11e2-9673-fac11f1adc9e'};window.MarkUp=window.MarkUp||{};add_js('http://api.markup.io/bootstrap.js?v=1&'+(+(new Date)))})();"] | |
}, | |
// Moment.js - http://momentjs.com/ | |
// Date library | |
moment: { | |
js: ['//cdnjs.cloudflare.com/ajax/libs/datejs/1.0/date.min.js'] | |
}, | |
// Raphael | |
// SVG library | |
raphael: { | |
js: ['//cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js'] | |
}, | |
// Stats - https://github.com/mrdoob/stats.js/ | |
// FPS counter | |
stats: { | |
js: ['https://github.com/mrdoob/stats.js/raw/master/build/stats.min.js'], | |
script: ['var interval=setInterval(function(){if(typeof Stats==\'function\'){clearInterval(interval);var stats=new Stats();stats.domElement.style.position=\'fixed\';stats.domElement.style.left=\'0px\';stats.domElement.style.top=\'0px\';stats.domElement.style.zIndex=\'10000\';document.body.appendChild(stats.domElement);setInterval(function(){stats.update();},1000/60);}},100);'] | |
}, | |
// Statsy Bookmarklet - http://www.phpied.com/statsy-more-data-points-for-markup-quality/ | |
// Displays some DOM statistics | |
statsy: { | |
js: ['http://phpied.com/files/bookmarklets/somestats.js'] | |
}, | |
// Underscore - http://underscorejs.org/ | |
// Utility library | |
underscore: { | |
js: ['//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js'] | |
}, | |
// What Font Bookmarklet - http://chengyinliu.com/whatfont.html | |
// Identifies fonts | |
whatfont: { | |
js: ['http://chengyinliu.com/wf.js'] | |
} | |
}, | |
// setInterval(function() { u.query('img').effect('rotate'); }, 3000); | |
// u.query('img').effect('spin', 'random'); // They all spin at the same time | |
// u.query('img').each(function(element) { element.effect('spin', 'random', Math.random() + 0.2); } ); // They all spin at different times | |
// u.query('img').each(function(element) { element.effect('tilt', 5, Math.random() * (5 - 0.4 + 1) + 0.4); }); // They all spin at different times | |
EFFECTS = { | |
// u.effect('flip', 'z') | |
flip: function(axis, speed) { | |
// Default to flip on y-axis if undefined | |
axis = axis || 'y'; | |
// If undefined, default to 3s | |
// If number, assume seconds and add 's' unit | |
speed = speed || '3s'; | |
if (!isNaN(speed)) speed += 's'; | |
var transform = 'rotate' + axis.toUpperCase(); | |
this.css('-webkit-transition', 'none'); | |
this.css('-webkit-transform', transform + '(0deg)'); | |
// Wait a bit before updating the same CSS properties | |
setTimeout(function() { | |
this.css('-webkit-transition', '-webkit-transform ' + speed); | |
this.css('-webkit-transform', transform + '(180deg)'); | |
}.bind(this), 0); | |
}, | |
// u.effect('rotate', '60deg', '4s') | |
rotate: function(angle, speed) { | |
// If number, add 'deg' unit | |
// If undefined, default to random angle between -1080 and 1080 | |
angle = !isNaN(angle) && angle !== null ? angle + 'deg' : angle ? angle : ( Math.floor( Math.random() * 1081 ) - 1080 + 'deg' ); | |
// If undefined, default to 2s | |
// If number, assume seconds and add 's' unit | |
speed = speed || '2s'; | |
if (!isNaN(speed)) speed += 's'; | |
this.css('-webkit-transition', '-webkit-transform ' + speed); | |
this.css('-webkit-transform', 'rotate(' + angle + ')'); | |
}, | |
// u.effect('spin') | |
// u.effect('spin', 'random') | |
// u.effect('spin', 'default', '200ms') | |
// u.effect('spin', 'backward', 5) | |
spin: function(option, speed) { | |
var speedParts = /(\d+\.?\d*)(\D*)/.exec(speed || '2s'), | |
speedMs = speedParts[2] !== 'ms' ? speedParts[1] * 1000 : speedParts[1], | |
angleIncrement = (option === 'backwards') ? -360 : (option === 'random') ? undefined : 360, | |
angle = angleIncrement; | |
fn = function() { | |
this.effect('rotate', angle, speed); | |
angle += angleIncrement; | |
}, | |
id = setInterval(fn.bind(this), speedMs); | |
}, | |
// u.effect('tilt', 5); | |
// u.effect('tile', '5deg'); | |
tilt: function(range, speed) { | |
// If deg value (e.g., '5deg'), parse out number | |
// If undefined, default to 5 | |
range = parseInt(range || 5, 10); | |
// If undefined, default to 3s | |
// If number, assume seconds and add 's' unit | |
speed = speed || '1s'; | |
if (!isNaN(speed)) speed += 's'; | |
var angleStart = 0 - range, | |
angleEnd = 0 + range; | |
var animation = addAnimation({ | |
'0%': '-webkit-transform:rotate(' + angleStart + 'deg);', | |
'50%': '-webkit-transform:rotate(' + angleEnd + 'deg);', | |
'100%': '-webkit-transform:rotate(' + angleStart + 'deg);' | |
}); | |
this.css('-webkit-animation', animation + ' infinite ' + speed); | |
} | |
}, | |
// Converts array-like objects to arrays | |
slice = Function.prototype.call.bind(Array.prototype.slice), | |
// Copies properties from the second object into the first object | |
extend = function(dest, src) { | |
for (var prop in src) dest[prop] = src[prop]; | |
return dest; | |
}, | |
// Adds a <style> to the page | |
addStyle = function(css) { | |
style = document.createElement('style'); | |
style.textContent = css; | |
document.head.appendChild(style); | |
}, | |
// Adds a <link> for the specified URL | |
addCSS = function(url) { | |
var link = document.createElement('link'); | |
link.rel = 'stylesheet'; | |
link.href = url; | |
document.head.appendChild(link); | |
console.log('Loaded ' + url); | |
}, | |
// Adds a <script> for the specified URL | |
addJS = function(url, handler) { | |
var script = document.createElement('script'); | |
script.onload = handler; | |
script.async = false; | |
script.src = url; | |
document.head.appendChild(script); | |
console.log('Loaded ' + url); | |
}, | |
// Adds a <script> for the specified JavaScript | |
addScript = function(fn, isImmediate) { | |
if (isImmediate === undefined) isImmediate = false; | |
var script = document.createElement('script'); | |
script.textContent = isImmediate ? '(' + fn + ')();' : fn; | |
document.head.appendChild(script); | |
}, | |
// Adds a CSS animation style | |
addAnimation = function(keyframes) { | |
this.counter = this.counter || 0; | |
var animationName = 'u-animation-' + this.counter++; | |
var style = '@-webkit-keyframes ' + animationName + ' { '; | |
for (var name in keyframes) { | |
style += name + ' { ' + keyframes[name] + ' } '; | |
} | |
style += '}'; | |
addStyle(style); | |
return animationName; | |
}, | |
Collection = { | |
// Sets CSS property for elements | |
// Or gets the CSS property value of first element | |
css: function(property, value) { | |
if (value === undefined && this.length) return window.getComputedStyle(this[0])[property]; | |
this.forEach(function(element) { | |
element.style[property] = value; | |
}); | |
}, | |
each: function(callback) { | |
this.forEach(function(element) { | |
callback(extend([element], Collection)); | |
}); | |
}, | |
// Make elements editable | |
edit: function(isEditable) { | |
this.forEach(function(element) { | |
if (isEditable === undefined) isEditable = true; | |
element.contentEditable = isEditable; | |
}); | |
}, | |
// Applies a CSS effect | |
effect: function(effect, value, speed) { | |
if (effect === undefined) return Object.keys(EFFECTS); | |
if (effect in EFFECTS) EFFECTS[effect].call(this, value, speed); | |
}, | |
// Add HTML at the specified position | |
insert: function(position, html) { | |
var positions = { | |
append: 'beforeend', | |
prepend: 'afterbegin', | |
before: 'beforebegin', | |
after: 'afterend' | |
}; | |
this.forEach(function(element) { | |
element.insertAdjacentHTML(positions[position] || position, html); | |
}); | |
}, | |
// Intercept events by executing first (during the capture phase) and stopping propagation | |
// Useful for sites that prevent/manipulate text selection, right-click, etc. | |
intercept: function() { | |
var events = ['keyup', 'keydown', 'keypress', 'mouseup', 'mousedown', 'selectstart', 'contextmenu', 'copy'], | |
stopEvents = function(e) { e.stopPropagation(); }; | |
this.forEach(function(element) { | |
events.forEach(function(e) { | |
element.addEventListener(e, stopEvents, true); | |
}); | |
}); | |
}, | |
// Gets the parent of each element | |
parent: function() { | |
return extend(this.map(function(element) { | |
return element.parentNode; | |
}), Collection); | |
}, | |
// Calls querySelectorAll() on each element | |
// and combines the results into a u.js-powered array | |
query: function(selector) { | |
if (this.length) { | |
return extend(this.map(function(element) { | |
return slice(element.querySelectorAll(selector)); | |
}).reduce(function (a, b) { | |
return a.concat(b); | |
}), Collection); | |
} else { | |
return this; | |
} | |
}, | |
// Replaces HTML based on the given regex | |
// If a string is used instead of a regex, it's converted to a global regex | |
replace: function(regex, html) { | |
this.forEach(function(element) { | |
if (!(regex instanceof RegExp)) regex = new RegExp(regex, 'g'); | |
element.innerHTML = element.innerHTML.replace(regex, html); | |
}); | |
}, | |
// Combine element values | |
// Optionally specify functions to filter out selections, | |
// remap values, or change how the values are summed up | |
sum: function(filter, map, reduce) { | |
return this.length && this.filter(filter || function() { | |
return true; | |
}).map(map || function(element) { | |
return +element.textContent.replace(/[$,%]/g, ''); | |
}).reduce(reduce || function(a, b) { | |
return a + b; | |
}); | |
} | |
}, | |
body = extend([document.body], Collection); | |
// u.js | |
var u = { | |
// Current version of u.js | |
_VERSION: VERSION, | |
// Get/set a CSS property on <body> | |
// u.css(property) | |
// u.css(property, value) | |
css: Collection.css.bind(body), | |
// Makes everything editable. | |
// u.edit() or u.edit(true) - Turns on editing | |
// u.edit(false) - Turns off editing | |
edit: Collection.edit.bind(body), | |
// u.effect(property, value, speed) | |
// u.effect(effect, value, speed) | |
effect: Collection.effect.bind(body), | |
// Include a CSS file, JS file, or predefined resource | |
// u.include(url) - Adds CSS or JS file onto the page | |
// u.include(url, type) - Adds CSS or JS file onto the page (specifying type in second param) | |
// u.include(resource) - Adds resource onto the page | |
// u.include() - Lists available resources to include | |
// u.include(url, handler) | |
// u.include(resource, handler) | |
// u.include(url, type, handler) | |
// u.include(resource, type, handler) | |
include: function(resource, type, handler) { | |
if (resource === undefined) return Object.keys(RESOURCES); | |
if (typeof type === 'function') { | |
handler = type; | |
type = null; | |
} | |
if (resource in RESOURCES) { | |
var r = RESOURCES[resource]; | |
if (r.css) for (var j = 0; j < r.css.length; ++j) addCSS(r.css[j]); | |
if (r.js) for (var k = 0; k < r.js.length; ++k) addJS(r.js[k], handler); | |
if (r.script) for (var l = 0; l < r.script.length; ++l) addScript(r.script[l]); | |
} else { | |
type = (type || resource.split('.').pop()).toLowerCase(); | |
if (type === 'css') { | |
addCSS(resource); | |
} else if (type === 'js') { | |
addJS(resource, handler); | |
} else { | |
throw new Error('Could not load ' + resource); | |
} | |
} | |
}, | |
// Insert HTML in <body> at the specified position | |
// u.insert(position, html) | |
insert: Collection.insert.bind(body), | |
// Stop events from manipulating/preventing user input | |
// u.intercept() | |
intercept: Collection.intercept.bind(body), | |
// Returns u.js-powered array of elements | |
// Shortcut for document.querySelectorAll() | |
// u.query(selector) | |
// Also wraps existing Node and NodeList elements into a u.js-powered arrays | |
query: function(selector) { | |
if (typeof selector === 'string') return extend(slice(document.querySelectorAll(selector)), Collection); | |
if (selector instanceof NodeList) return extend(slice(selector), Collection); | |
if (selector instanceof Node) return extend([selector], Collection); | |
}, | |
// u.ready(); | |
// u.ready(fn); | |
// u.ready(null); | |
ready: function(fn) { | |
var key = 'u.ready'; | |
if (fn === undefined) { | |
fn = this.store(key); | |
if (fn) addScript(fn, true); | |
} else { | |
this.store(key, fn); | |
} | |
}, | |
// Replace HTML in <body> usng a string or regex | |
// u.replace(regex, html) | |
replace: Collection.replace.bind(body), | |
// Store and retrieve data in localStorage (including functions) | |
// store(key, value) - Stores value in localStorage under key | |
// store(key) - Gets value of key in localStorage | |
// store() - List keys stored in localStorage | |
store: function(key, value) { | |
if (key === undefined) return Object.keys(localStorage); | |
if (value === undefined) { | |
value = localStorage[key]; | |
// Return immediately if falsey | |
if (!value || value === 'undefined') return value; | |
value = value && JSON.parse(value); | |
if (value && value.indexOf && value.indexOf('function') === 0) value = eval('(' + value + ')'); | |
return value; | |
} else { | |
if (typeof value === 'function') value = value.toString(); | |
localStorage[key] = JSON.stringify(value); | |
} | |
} | |
}; | |
/* Init */ | |
var loadu = function(u, names) { | |
for (var i = 0; i < names.length; ++i) { | |
var name = names[i]; | |
if (name in window) continue; | |
window[name] = u; | |
console.log('Loaded u.js as global variable ' + name); | |
return; | |
} | |
console.warn('Could not load u.js due to variable conflicts.'); | |
}; | |
loadu(u, ['u', 'u.js', 'zarjay.net/u.js']); | |
u.ready(); | |
}); | |
// Executes function in global scope (so it becomes available outside of userscript) | |
function exec(fn) { | |
var script = document.createElement('script'); | |
script.textContent = '(' + fn + ')();'; | |
document.head.appendChild(script); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment