Skip to content

Instantly share code, notes, and snippets.

@Weltraumschaf
Created July 17, 2022 15:50
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 Weltraumschaf/0ca4493110e3cd626f8d240727c6a624 to your computer and use it in GitHub Desktop.
Save Weltraumschaf/0ca4493110e3cd626f8d240727c6a624 to your computer and use it in GitHub Desktop.
This plugin provides some util functions and methods inspired by the book "JavaScript: The Good Parts" from Douglas Crockford.
/**
* This plugin provides some util functions and methods inspired
* by the book "JavaScript: The Good Parts" from Douglas Crockford
*
* @author Sven Strittmatter <ich@weltraumschaf.de>
*/
(function($) { // pass in the jQuery object as $ (see end of file)
/**
* Adds a method to an Object. This method is used
* to augment the standard objects with some usefull
* functions (see below)
*
* @param string name
* @param function func
* @return function
*/
Function.prototype.method = function(name, func) {
if (!this.prototype[name]) {
this.prototype[name] = func;
}
return this;
};
/**
* Adds the namespace() method to the Object type.
* It creates namespace object structures, e.g.:
*
* <code>
* Object.namespace('foo.bar.baz');
* // ...
* foo.bar.baz.MyType = function() {
* // ...
* };
* </code>
*
* This method is alos available as jQuery util
* function, e.g.:
*
* <code>
* $.namespace('foo.bar.baz');
* // ...
* foo.bar.baz.MyType = function() {
* // ...
* };
*
* You can also create more than one namespace at one time:
* <code>
* Object.namespace('foo.bar.baz', 'foo.bar.biz', 'foo.bar.buz');
* </code>
*
* or
* <code>
* $.namespace('foo.bar.baz', 'foo.bar.biz', 'foo.bar.buz');
* </code>
*
* @param string namespace
* @return object
*/
$.namespace = Object.method('namespace', function() {
var a = arguments,
o = null,
i, j, d;
for (i = 0; i < a.length; i = i + 1) {
d = ('' + a[i]).split('.');
o = {};
for (j = 0; j < d.length; j = j + 1) {
o[d[j]] = o[d[j]] || {};
o = o[d[j]];
}
}
return o;
};
/**
* With this method you can debounce other founctions. This is
* usefull when you have a clickhandler which you want to debounce:
* If a user clicks repetly and triggers mor than one event in short
* time these events are cummulated to a single debounced event.
*
* With the threshold parameter you can define the time in miliseconds
* in which the repeted function calles will be debounced.The default
* threshold is 100.
*
* Withe the thir parameter you can choose if the functions is triggered
* on the first event and ignores the followed calls in the threshold time.
* Or you can choose that the debounced function is called after the last
* function called which was triggered while threshold. Default is false.
*
* This example debounces for 300 milliseconds and triggers the debounced
* function imidiatelly:
* <code>
* var myClickHandler = Function.debounce(function(event) {
* // ...
* }, 300, true);
* </code>
*
* This example triggers the debounced function 100 milliseconds after the last
* occurance of a function call:
* <code>
* var myClickHandler = Function.debounce(function(event) {
* // ...
* });
* </code>
*
* @param function func
* @param number threshold
* @param boolean execAsap
* @return function
*/
$.debounce = Function.method('debounce', function (func, threshold, execAsap) {
var timeout; // handle to setTimeout async task (detection period)
/*
* return the new debounced function which executes the
* original function only once until the detection period expires.
*/
function debounced() {
var obj = this, // reference to original context object
args = arguments; // arguments at execution time
/*
* this is the detection function. it will be executed
* if/when the threshold expires
*/
function delayed () {
// if we're executing at the end of the detection period
if (!execAsap) {
func.apply(obj, args); // execute now
}
// clear timeout handle
timeout = null;
}
// stop any current detection period
if (timeout) {
clearTimeout(timeout);
} else if (execAsap) {
// otherwise, if we're not already waiting and we're executing at the beginning of the detection period
func.apply(obj, args); // execute now
}
// reset the detection period
timeout = setTimeout(delayed, threshold || 100);
}
return debounced;
};
/**
*
*/
Function.method('curry', function() {
var slice = Array.prototype.slice,
args = slice.apply(arguments),
that = this;
return function() {
return that.apply(null, args.concat(slice.apply(arguments)));
}
});
/**
* With this function you can reduce the recursiv function call
* overhead on tail recursiv functions.
*
* Examples:
*
* Fibonacci
* <code>
* var fibonnaci = Function.memorizer([0, 1], function(shell, n) {
* return shell(n - 1) + shell(n - 2);
* });
* </code>
*
* Factorial
* <code>
* var factorial = Function.memorizer([1, 1], function(shell, n) {
* return n * shell(n - 1);
* });
* </code>
*
* @param array memo
* @param function fundamental
* @return object
*/
Function.method('memorizer', function(memo, fundamental) {
var shell = function(n) {
var result = memo[n];
if (typeof result !== 'number') {
result = fundamental(shell, n);
memo[n] = result;
}
return result;
};
return shell;
});
/**
* Gives the integer part of a floating number object.
*
* <code>
* var myInt = 5.34.integer();
* </code>
*
* @return number
*/
Number.method('integer', function() {
return Math[this < 0 ? 'ceil' : 'floor'](this);
});
$.integer = function(n) {
if (typeof n === 'number' && typeof n.integer === 'function') {
return n.integer();
} else {
return 0;
}
};
Array.dim = function(dimension, initial) {
var a = [], i;
for (i = 0; i < dimension; i += 1) {
a[i] = initial;
}
return a;
};
Array.matrix = function(m, n, initial) {
var a, i, j, mat = [];
for (i = 0; i < m; i += 1) {
a = [];
for (j = 0; j < n; j += 1) {
a[j] = initial;
}
mat[i] = a;
}
return mat;
};
Array.identity = function(n) {
var i, mat = Array.matrix(n, n, 0);
for (i = 0; i < n; i += 1) {
mat[i][i] = 1;
}
return mat;
};
/**
* Trims the white space characters of an string.
*
* <code>
* var trimmedString = ' this is a string '.trim();
* </code>
*
* @return string
*/
String.method('trim', function() {
return this.replace(/^\s+|\s+$/g, '');
});
(function() { // Closure to hide the entity character maps.
var entity2char = {
lt: '<',
gt: '>',
quot: '"'
},
char2entity = {
'<': '&lt;',
'>': '&gt;',
'&': '&amp;',
'"': '&quot;'
};
/**
* Replaces characters which where entites:
* > -> &gt;
* < -> &lt;
* & -> &amp;
* " -> &quot;
*
* <code>
* '<p>This & that</p>'.entityify();
* </code>
*
* @return string
*/
String.method('entityify', function() {
return function() {
return this.replace(/[<>&"/g, function(matchedCharcter) {
return char2entity[matchedCharcter];
})
};
});
/**
* Replaces entities with the corresponding characters:
* &gt; -> >
* &lt; -> <
* &amp; -> &
* &quot; -> "
*
* <code>
* '&lt;p&gt;This &amp; that&lt;/p&gt;'.deEntityify();
* </code>
*
* @return string
*/
String.method('deEntityify', function() {
return function() {
return this.replace(/&([^&;]+);/g, function(a, b) {
var r = entity2char[b];
return (typeof r === 'string') ? r : a;
});
};
});
})(); // Self invocation
})(jQuery); // pass the global jQuery object for $.noConflict mode
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment