Skip to content

Instantly share code, notes, and snippets.

@ryan-scott-dev
Created October 11, 2015 23:56
Show Gist options
  • Save ryan-scott-dev/1e030804042046d2c2a0 to your computer and use it in GitHub Desktop.
Save ryan-scott-dev/1e030804042046d2c2a0 to your computer and use it in GitHub Desktop.
Handy JS
Function.prototype.method = function (name, func) {
this.prototype[name] = func;
return this;
};
Function.method('mixin', function (properties) {
Object.keys(properties).forEach(function(property) {
if (!this.prototype.hasOwnProperty(property)) {
this.prototype[property] = properties[property];
}
}.bind(this));
});
Function.method('override', function (properties) {
Object.keys(properties).forEach(function(property) {
this.prototype[property] = properties[property];
}.bind(this));
});
Function.method('properties', function (propertyDefinitions) {
Object.keys(propertyDefinitions).forEach(function(property) {
Object.defineProperty(this.prototype, property, propertyDefinitions[property]);
}.bind(this));
});
Function.method('delegate', function (properties) {
Object.keys(properties).forEach(function(property) {
var delegatedMethods = properties[property];
for (var i = 0; i < delegatedMethods.length; i++) {
var delegatedMethod = delegatedMethods[i];
this.method(delegatedMethod, function(property, delegatedMethod) {
/* Nested in a function so that the variables are pushed to the stack, otherwise delegatedMethod is always the last value */
return function() {
var delegate = this[property]();
return delegate[delegatedMethod].apply(delegate, arguments);
};
}(property, delegatedMethod));
}
});
});
Prototype = (function() {
function createPropertyDefinitions(properties) {
return Object.keys(properties)
.reduce(function(reducedProperties, property) {
if (typeof properties[property] === 'function') {
reducedProperties[property] = {
get: properties[property]
};
} else {
reducedProperties[property] = {
value: properties[property],
writable: true,
configurable: true,
enumerable: true
};
}
return reducedProperties;
}, {});
}
return function(params) {
params = params || {};
var base = params.base ? params.base.prototype : Object;
var events = params.events || [];
var constructor = params.constructor;
var methods = params.methods || {};
var staticMethods = params.static || {};
var properties = params.properties || {};
var name = params.name || (params.base ? params.base.name : 'unknown');
var builtPrototype = (new Function("return function (call) { return function " + name +
" () { return call(this, arguments) }; };")())(Function.apply.bind(function() {
if (events && events.length > 0) {
this.events = {};
events.forEach(function(eventName) {
this.events[eventName] = new Signal();
}.bind(this));
}
constructor.apply(this, arguments);
}));
builtPrototype.prototype = Object.create(base);
builtPrototype.prototype.constructor = builtPrototype;
builtPrototype.prototype.name = name;
if (base) {
builtPrototype.prototype.base = base;
builtPrototype.prototype.superConstructor = function() { base.constructor.apply(this, arguments) };
}
var propertyDefinitions = createPropertyDefinitions(properties);
builtPrototype.properties(propertyDefinitions);
Object.keys(staticMethods).forEach(function(staticMethodName) {
builtPrototype[staticMethodName] = staticMethods[staticMethodName];
});
builtPrototype.mixin(methods);
builtPrototype.mixin({
clearEventHandlers: function() {
Object.keys(this.events).forEach(function(eventName) {
this.events[eventName].removeAll();
}.bind(this));
}
});
return builtPrototype;
};
})();
// https://github.com/Hypercubed/mini-signals
/**
* Representation of a single Signal function.
*
* @param {Function} fn Event handler to be called.
* @param {Mixed} context Context for function execution.
* @api private
*/
function Node(fn, context) {
this.fn = fn;
this.context = context;
this.next = this.prev = null;
}
/**
* Minimal Signal interface modeled against the js-signals
* interface.
*/
function Signal() {
this._head = this._tail = undefined;
}
Signal.mixin({
/**
* Return a list of assigned event listeners.
*
* @param {Boolean} exists We only need to know if there are listeners.
* @returns {Array|Boolean}
* @api public
*/
listeners: function(exists) {
var node = this._head;
if (exists) { return !!node; }
var ee = [];
while (node) {
ee.push(node.fn);
node = node.next;
}
return ee;
},
/**
* Emit an event to all registered event listeners.
*
* @returns {Boolean} Indication if we've emitted an event.
* @api public
*/
dispatch: function() {
var node = this._head;
if (!node) { return false; }
while (node) {
node.fn.apply(node.context, arguments);
node = node.next;
}
return true;
},
/**
* Register a new EventListener.
*
* @param {Functon} fn Callback function.
* @param {Mixed} context The context of the function.
* @api public
*/
add: function(fn, context) {
var node = new Node(fn, context || this);
if (!this._head) {
this._head = node;
this._tail = node;
} else {
this._tail.next = node;
node.prev = this._tail;
this._tail = node;
}
return this;
},
/**
* Remove event listeners.
*
* @param {Function} fn The listener that we need to find.
* @param {Mixed} context Only remove listeners matching this context.
* @api public
*/
remove: function(fn, context) {
if (!fn) { return this.removeAll(); } // maybe change this
var node = this._head;
while (node) {
if (node.fn === fn && (!context || node.context === context)) {
this._removeNode(node);
}
node = node.next;
}
return this;
},
/**
* Remove all event listeners with the matching contexts.
*
* @param {Mixed} context Remove all listeners matching this context.
* @api public
*/
removeAllWithContext: function(context) {
var node = this._head;
while (node) {
if (node.context === context) {
this._removeNode(node);
}
node = node.next;
}
return this;
},
_removeNode: function(node) {
if (node === this._head) { // first node
this._head = node.next;
if (!this._head){
this._tail = null;
} else {
this._head.prev = null;
}
} else if (node === this._tail) { // last node
this._tail = node.prev;
this._tail.next = null;
} else { // middle
node.prev.next = node.next;
node.next.prev = node.prev;
}
},
/**
* Remove all listeners.
*
* @api public
*/
removeAll: function() {
var node = this._head;
if (!node) { return this; }
this._head = this._tail = null;
return this;
}
});
/*
_ _
| | (_)
| |_ _ ___
| __| | / __|
| |_ _| \__ \
\__(_) |___/
_/ |
|__/
t.js
a micro-templating framework in ~400 bytes gzipped
@author Jason Mooberry <[email protected]>
@license MIT
@version 0.1.0
*/
(function() {
var blockregex = /\{\{(([@!]?)(.+?))\}\}(([\s\S]+?)(\{\{:\1\}\}([\s\S]+?))?)\{\{\/\1\}\}/g,
valregex = /\{\{([=%])(.+?)\}\}/g;
function t(template) {
this.t = template;
}
function scrub(val) {
return new Option(val).innerHTML.replace(/"/g,"&quot;");
}
function get_value(vars, key) {
var parts = key.split('.');
while (parts.length) {
if (!(parts[0] in vars)) {
return false;
}
vars = vars[parts.shift()];
}
return vars;
}
function render(fragment, vars) {
return fragment
.replace(blockregex, function(_, __, meta, key, inner, if_true, has_else, if_false) {
var val = get_value(vars,key), temp = "", i;
if (!val) {
// handle if not
if (meta == '!') {
return render(inner, vars);
}
// check for else
if (has_else) {
return render(if_false, vars);
}
return "";
}
// regular if
if (!meta) {
return render(if_true, vars);
}
// process array/obj iteration
if (meta == '@') {
// store any previous vars
// reuse existing vars
_ = vars._key;
__ = vars._val;
for (i in val) {
if (val.hasOwnProperty(i)) {
vars._key = i;
vars._val = val[i];
temp += render(inner, vars);
}
}
vars._key = _;
vars._val = __;
return temp;
}
})
.replace(valregex, function(_, meta, key) {
var val = get_value(vars,key);
if (val || val === 0) {
return meta == '%' ? scrub(val) : val;
}
return "";
});
}
t.prototype.render = function (vars) {
return render(this.t, vars);
};
window.t = t;
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment