Skip to content

Instantly share code, notes, and snippets.

@Azeirah
Created October 12, 2016 18:42
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 Azeirah/0e558c439bd59646863a31540aa2534c to your computer and use it in GitHub Desktop.
Save Azeirah/0e558c439bd59646863a31540aa2534c to your computer and use it in GitHub Desktop.
function rateLimit(fn, rate) {
var queue = [];
var currentlyEmptyingQueue = false;
function emptyQueue() {
if (queue.length) {
currentlyEmptyingQueue = true;
window.setTimeout(function () {
queue.shift().call();
emptyQueue();
}, rate);
} else {
currentlyEmptyingQueue = false;
}
}
return function () {
var args = Array.prototype.slice.call(arguments);
var context = this;
queue.push(function () {fn.apply(context, args);});
if (!currentlyEmptyingQueue) {
emptyQueue();
}
};
}
function clone(item) {
if (!item) { return item; } // null, undefined values check
var types = [ Number, String, Boolean ],
result;
// normalizing primitives if someone did new String('aaa'), or new Number('444');
types.forEach(function(type) {
if (item instanceof type) {
result = type( item );
}
});
if (typeof result == "undefined") {
if (Object.prototype.toString.call( item ) === "[object Array]") {
result = [];
item.forEach(function(child, index, array) {
result[index] = clone( child );
});
} else if (typeof item == "object") {
// testing that this is DOM
if (item.nodeType && typeof item.cloneNode == "function") {
var result = item.cloneNode( true );
} else if (!item.prototype) { // check that this is a literal
if (item instanceof Date) {
result = new Date(item);
} else {
// it is an object literal
result = {};
for (var i in item) {
result[i] = clone( item[i] );
}
}
} else {
// depending what you would like here,
// just keep the reference, or create new object
if (false && item.constructor) {
// would not advice to do that, reason? Read below
result = new item.constructor();
} else {
result = item;
}
}
} else {
result = item;
}
}
return result;
}
/*
cycle.js
2015-02-25
Public Domain.
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
This code should be minified before deployment.
See http://javascript.crockford.com/jsmin.html
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
NOT CONTROL.
*/
/*jslint eval, for */
/*property
$ref, apply, call, decycle, hasOwnProperty, length, prototype, push,
retrocycle, stringify, test, toString
*/
if (typeof JSON.decycle !== 'function') {
JSON.decycle = function decycle(object) {
'use strict';
// Make a deep copy of an object or array, assuring that there is at most
// one instance of each object or array in the resulting structure. The
// duplicate references (which might be forming cycles) are replaced with
// an object of the form
// {$ref: PATH}
// where the PATH is a JSONPath string that locates the first occurance.
// So,
// var a = [];
// a[0] = a;
// return JSON.stringify(JSON.decycle(a));
// produces the string '[{"$ref":"$"}]'.
// JSONPath is used to locate the unique object. $ indicates the top level of
// the object or array. [NUMBER] or [STRING] indicates a child member or
// property.
var objects = [], // Keep a reference to each unique object or array
paths = []; // Keep the path to each unique object or array
return (function derez(value, path) {
// The derez recurses through the object, producing the deep copy.
var i, // The loop counter
name, // Property name
nu; // The new object or array
// typeof null === 'object', so go on if this value is really an object but not
// one of the weird builtin objects.
if (typeof value === 'object' && value !== null &&
!(value instanceof Boolean) &&
!(value instanceof Date) &&
!(value instanceof Number) &&
!(value instanceof RegExp) &&
!(value instanceof String)) {
// If the value is an object or array, look to see if we have already
// encountered it. If so, return a $ref/path object. This is a hard way,
// linear search that will get slower as the number of unique objects grows.
for (i = 0; i < objects.length; i += 1) {
if (objects[i] === value) {
return {$ref: paths[i]};
}
}
// Otherwise, accumulate the unique value and its path.
objects.push(value);
paths.push(path);
// If it is an array, replicate the array.
if (Object.prototype.toString.apply(value) === '[object Array]') {
nu = [];
for (i = 0; i < value.length; i += 1) {
nu[i] = derez(value[i], path + '[' + i + ']');
}
} else {
// If it is an object, replicate the object.
nu = {};
for (name in value) {
if (Object.prototype.hasOwnProperty.call(value, name)) {
nu[name] = derez(value[name],
path + '[' + JSON.stringify(name) + ']');
}
}
}
return nu;
}
return value;
}(object, '$'));
};
}
if (typeof JSON.retrocycle !== 'function') {
JSON.retrocycle = function retrocycle($) {
'use strict';
// Restore an object that was reduced by decycle. Members whose values are
// objects of the form
// {$ref: PATH}
// are replaced with references to the value found by the PATH. This will
// restore cycles. The object will be mutated.
// The eval function is used to locate the values described by a PATH. The
// root object is kept in a $ variable. A regular expression is used to
// assure that the PATH is extremely well formed. The regexp contains nested
// * quantifiers. That has been known to have extremely bad performance
// problems on some browsers for very long strings. A PATH is expected to be
// reasonably short. A PATH is allowed to belong to a very restricted subset of
// Goessner's JSONPath.
// So,
// var s = '[{"$ref":"$"}]';
// return JSON.retrocycle(JSON.parse(s));
// produces an array containing a single element which is the array itself.
var px = /^\$(?:\[(?:\d+|\"(?:[^\\\"\u0000-\u001f]|\\([\\\"\/bfnrt]|u[0-9a-zA-Z]{4}))*\")\])*$/;
(function rez(value) {
// The rez function walks recursively through the object looking for $ref
// properties. When it finds one that has a value that is a path, then it
// replaces the $ref object with a reference to the value that is found by
// the path.
var i, item, name, path;
if (value && typeof value === 'object') {
if (Object.prototype.toString.apply(value) === '[object Array]') {
for (i = 0; i < value.length; i += 1) {
item = value[i];
if (item && typeof item === 'object') {
path = item.$ref;
if (typeof path === 'string' && px.test(path)) {
value[i] = eval(path);
} else {
rez(item);
}
}
}
} else {
for (name in value) {
if (typeof value[name] === 'object') {
item = value[name];
if (item) {
path = item.$ref;
if (typeof path === 'string' && px.test(path)) {
value[name] = eval(path);
} else {
rez(item);
}
}
}
}
}
}
}($));
return $;
};
}
function replaceElement(element, toTag) {
var replacement = document.createElement(toTag);
replacement.innerHTML = element.innerHTML;
element.parentNode.insertBefore(replacement, element);
element.parentNode.removeChild(element);
return replacement;
}
function flashBackground(element) {
element.className += " flash-background";
window.setTimeout(function () {
element.className = element.className.replace(' flash-background', '');
}, 200);
}
function floatAway(element) {
element.className += " float";
window.setTimeout(function () {
element.className = element.className.replace(' float', '');
element.parentNode.removeChild(element);
}, 3600);
}
var loggy = (function () {
var loggers = [];
var container = document.createElement('div');
var draggable = false;
var pos = {};
var x = 8;
var y = 12;
var draghandle = document.createElement('div');
draghandle.style.width = "80px";
draghandle.style.height = "20px";
draghandle.style.backgroundColor = "skyblue";
draghandle.innerHTML = "drag me";
container.style.width = '640px';
container.style.height = '200px';
container.style.position = 'fixed';
container.style.top = y + "px";
container.style.left = x + "px";
container.style.zIndex = 1000;
container.style.overflow = "scroll";
container.style.backgroundColor = "white";
container.style.resize = "both";
// container.style.display = "none";
var stylesheet = document.createElement("style");
stylesheet.innerHTML = [
".flash-text {",
"-webkit-animation: flash-text .2s;",
"-moz-animation: flash-text .2s;",
"-o-animation: flash-text .2s;",
"animation: flash-text .2s;",
"}",
".flash-background {",
"-webkit-animation: flash-background .2s;",
"-moz-animation: flash-background .2s;",
"-o-animation: flash-background .2s;",
"animation: flash-background .2s;",
"}",
"@-webkit-keyframes flash-text { from { color: rgba(70, 160, 60, .8); } to { color: black; } }",
"@-moz-keyframes flash-text { from { color: rgba(70, 160, 60, .8); } to { color: black; } }",
"@-o-keyframes flash-text { from { color: rgba(70, 160, 60, .8); } to { color: black; } }",
"@keyframes flash-text { from { color: rgba(70, 160, 60, .8); } to { color: black; } }",
"@-webkit-keyframes flash-background { from { background-color: rgba(70, 140, 200, .19); } to { background-color: white; } }",
"@-moz-keyframes flash-background { from { background-color: rgba(70, 140, 200, .19); } to { background-color: white; } }",
"@-o-keyframes flash-background { from { background-color: rgba(70, 140, 200, .19); } to { background-color: white; } }",
"@keyframes flash-background { from { background-color: rgba(70, 140, 200, .19); } to { background-color: white; } }",
".float {",
"-webkit-animation: float 3.6s;",
"-moz-animation: float 3.6s;",
"-o-animation: float 3.6s;",
"animation: float 3.6s;",
"}",
"@-moz-keyframes float {",
"from {",
"-moz-transform: translate(0, 0);",
"opacity: 1;",
"}",
"to {",
"-moz-transform: translate(0, -200px);",
"opacity: 0;",
"}",
"}",
].join("\n");
container.appendChild(draghandle);
document.body.appendChild(container);
document.body.appendChild(stylesheet);
/* Draggable window functionality start */
window.addEventListener('keydown', function (event) {
draggable = event.which === 18;
});
window.addEventListener('keyup', function (event) {
if (event.which === 18) {
draggable = false;
}
});
draghandle.addEventListener('mouseenter', function () {
draghandle.style.cursor = "pointer";
draghandle.style.backgroundColor = "blue";
});
draghandle.addEventListener('mouseleave', function () {
draghandle.style.cursor = "auto";
draghandle.style.backgroundColor = "skyblue";
});
draghandle.addEventListener('mousedown', function (event) {
draggable = true;
pos = {x: event.clientX, y: event.clientY};
});
window.addEventListener('mouseup', function () {
draggable = false;
});
window.addEventListener('mousemove', function (event) {
var newpos = {x: event.clientX, y: event.clientY};
if (draggable) {
var delta = {x: newpos.x - pos.x, y: newpos.y - pos.y};
x += delta.x;
y += delta.y;
loggy('x', x);
loggy('y', y);
container.style.left = x + "px";
container.style.top = y + "px";
}
pos = newpos;
});
/* Draggable window functionality end */
function loggy(name, value) {
var isFound = loggers.filter(function (logger) {
return logger.name === name;
})[0];
var isObject = typeof value === "object";
var isCoordinates = isObject && typeof value.x === "number" && typeof value.y === "number";
displayValue = isObject? JSON.stringify(JSON.decycle(value), null, 2) : value;
var tag = isObject? "pre" : "div";
var node = isFound? isFound.node : document.createElement(tag);
if (!isFound) {
container.appendChild(node);
node.style.fontFamily = "consolas";
node.style.fontSize = "12px";
node.innerHTML = name + ": " + displayValue;
node.style.zIndex = 1000;
loggers.push({name: name, node: node});
} else {
if (name + ": " + displayValue !== node.innerHTML) {
node.className = "flash-background ";
}
node.innerHTML = name + ": " + displayValue;
flashBackground(node);
}
if (isCoordinates) {
if (node.tagName === "PRE") {
node = replaceElement(node, "div");
}
node.style.position = "fixed";
node.style.left = value.x - (node.innerHTML.length * 12 / 4) + "px";
node.style.top = (value.y - 25) + "px";
loggers[loggers.findIndex(function (logger) {return logger.name === name;})].node = node;
}
}
loggy.notify = function (text) {
var node = document.createElement('div');
var pos = clone(container.getBoundingClientRect());
var middle = pos.x + (pos.width / 2);
node.innerHTML = text;
node.style.position = 'absolute';
node.style.top = pos.y + 'px';
node.style.left = middle + 'px';
node.style.zIndex = 1500;
node.style.pointerEvents = "none";
if (container.style.display === "none") {
node.style.top = window.innerHeight / 2 + "px";
node.style.left = window.innerWidth / 2 + "px";
}
document.body.appendChild(node);
console.log(text);
flashBackground(node);
floatAway(node);
};
loggy.notify = rateLimit(loggy.notify, 100);
loggy.toggle = function () {
if (container.style.display === "none") {
container.style.display = "";
} else {
container.style.display = "none";
}
};
return loggy;
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment